2 Commits

Author SHA1 Message Date
Jan
0f0d41615b fix: properly align emoji name in autocompletion 2022-10-26 09:13:14 +02:00
Jan
35fb6b4381 feat: list custom emojis in autocomplete 2022-10-26 08:22:39 +02:00
30 changed files with 76 additions and 429 deletions

View File

@@ -15,27 +15,22 @@ jobs:
gh api graphql -f query='
query {
organization(login: "revoltchat"){
projectV2(number: 3) {
projectNext(number: 3) {
id
fields(first:20) {
nodes {
... on ProjectV2SingleSelectField {
id
name
options {
id
name
}
}
id
name
settings
}
}
}
}
}' > project_data.json
echo 'PROJECT_ID='$(jq '.data.organization.projectV2.id' project_data.json) >> $GITHUB_ENV
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
echo 'TODO_OPTION_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name=="Todo") |.id' project_data.json) >> $GITHUB_ENV
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
echo 'TODO_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="Todo") |.id' project_data.json) >> $GITHUB_ENV
- name: Add issue to project
env:
@@ -44,11 +39,11 @@ jobs:
run: |
item_id="$( gh api graphql -f query='
mutation($project:ID!, $issue:ID!) {
addProjectV2ItemById(input: {projectId: $project, contentId: $issue}) {
item {
addProjectNextItem(input: {projectId: $project, contentId: $issue}) {
projectNextItem {
id
}
}
}' -f project=$PROJECT_ID -f issue=$ISSUE_ID --jq '.data.addProjectV2ItemById.item.id')"
}' -f project=$PROJECT_ID -f issue=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
echo 'ITEM_ID='$item_id >> $GITHUB_ENV

View File

@@ -15,27 +15,22 @@ jobs:
gh api graphql -f query='
query {
organization(login: "revoltchat"){
projectV2(number: 3) {
projectNext(number: 3) {
id
fields(first:20) {
nodes {
... on ProjectV2SingleSelectField {
id
name
options {
id
name
}
}
id
name
settings
}
}
}
}
}' > project_data.json
echo 'PROJECT_ID='$(jq '.data.organization.projectV2.id' project_data.json) >> $GITHUB_ENV
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
echo 'INCOMING_OPTION_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name=="Incoming PRs") |.id' project_data.json) >> $GITHUB_ENV
echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV
echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV
echo 'INCOMING_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="Incoming PRs") |.id' project_data.json) >> $GITHUB_ENV
- name: Add PR to project
env:
@@ -44,13 +39,13 @@ jobs:
run: |
item_id="$( gh api graphql -f query='
mutation($project:ID!, $pr:ID!) {
addProjectV2ItemById(input: {projectId: $project, contentId: $pr}) {
item {
addProjectNextItem(input: {projectId: $project, contentId: $pr}) {
projectNextItem {
id
}
}
}' -f project=$PROJECT_ID -f pr=$PR_ID --jq '.data.addProjectV2ItemById.item.id')"
}' -f project=$PROJECT_ID -f pr=$PR_ID --jq '.data.addProjectNextItem.projectNextItem.id')"
echo 'ITEM_ID='$item_id >> $GITHUB_ENV
- name: Set fields
@@ -64,16 +59,14 @@ jobs:
$status_field: ID!
$status_value: String!
) {
set_status: updateProjectV2ItemFieldValue(input: {
set_status: updateProjectNextItemField(input: {
projectId: $project
itemId: $item
fieldId: $status_field
value: {
singleSelectOptionId: $status_value
}
value: $status_value
}) {
projectV2Item {
projectNextItem {
id
}
}
}
}' -f project=$PROJECT_ID -f item=$ITEM_ID -f status_field=$STATUS_FIELD_ID -f status_value=${{ env.INCOMING_OPTION_ID }} --silent

View File

@@ -5,7 +5,6 @@ COPY . .
COPY .env.build .env
RUN yarn install --frozen-lockfile
RUN yarn build:deps
RUN yarn typecheck
RUN yarn build:highmem
RUN yarn workspaces focus --production --all

2
external/lang vendored

1
packages/components Submodule

Submodule packages/components added at d314b2d191

Submodule packages/hast-util-table-cell-style added at 7803fa5441

1
packages/revolt.js Submodule

Submodule packages/revolt.js added at 39d1f596e2

View File

@@ -2,10 +2,7 @@
# Build and publish release to production server
# Remote Server
if [ -z "$REMOTE" ]; then
echo "Please set REMOTE!"
exit
fi
REMOTE=revolt-de-nrb-1
# Remote Directory
REMOTE_DIR=/root/revite
@@ -21,7 +18,7 @@ export REVOLT_SAAS=https://github.com/revoltchat/assets
set -e
# 1. Build Revite
yarn build:highmem
yarn build
# 2. Archive built files
tar -czvf build.tar.gz dist

View File

@@ -3,7 +3,6 @@ type Element =
| {
type: "image";
src: string;
shadow?: boolean;
};
export interface ChangelogPost {
@@ -30,19 +29,6 @@ export const changelogEntries: Record<number, ChangelogPost> = {
"Other authentication methods coming later, stay tuned!",
],
},
2: {
date: new Date("2023-02-23T20:00:00.000Z"),
title: "In-App Reporting Is Here",
content: [
"You can now report any user, server, or message directly from the app.",
{
type: "image",
src: "https://autumn.revolt.chat/attachments/ZuDVIjGiCl61Pk9XGk5qfc8-idN9EnFAk55DUQp713/the.png",
shadow: true,
},
"If you want to learn more about how we're making Revolt safer for you, check out our new blog post :point_right: [https://revolt.chat/posts/improving-user-safety](https://revolt.chat/posts/improving-user-safety)",
],
},
};
export const changelogEntryArray = Object.keys(changelogEntries).map(

View File

@@ -64,7 +64,7 @@ export function useAutoComplete(
const cursor = el.selectionStart;
const content = el.value.slice(0, cursor);
const valid = /[\w\-]/;
const valid = /\w/;
let j = content.length - 1;
if (content[j] === "@") {

View File

@@ -23,7 +23,6 @@ import {
} from "../../../lib/renderer/Singleton";
import { state, useApplicationState } from "../../../mobx/State";
import { DraftObject } from "../../../mobx/stores/Draft";
import { Reply } from "../../../mobx/stores/MessageQueue";
import { dayjs } from "../../../context/Locale";
@@ -278,12 +277,7 @@ export default observer(({ channel }: Props) => {
// Push message content to draft.
const setMessage = useCallback(
(content?: string) => {
const dobj: DraftObject = {
content,
};
state.draft.set(channel._id, dobj);
},
(content?: string) => state.draft.set(channel._id, content),
[state.draft, channel._id],
);
@@ -323,7 +317,7 @@ export default observer(({ channel }: Props) => {
if (uploadState.type === "uploading" || uploadState.type === "sending")
return;
const content = state.draft.get(channel._id)?.content?.trim() ?? "";
const content = state.draft.get(channel._id)?.trim() ?? "";
if (uploadState.type === "attached") return sendFile(content);
if (content.length === 0) return;
@@ -532,7 +526,7 @@ export default observer(({ channel }: Props) => {
}
function isInCodeBlock(cursor: number): boolean {
const content = state.draft.get(channel._id)?.content || "";
const content = state.draft.get(channel._id) || "";
const contentBeforeCursor = content.substring(0, cursor);
let delimiterCount = 0;
@@ -613,12 +607,10 @@ export default observer(({ channel }: Props) => {
<HackAlertThisFileWillBeReplaced
onSelect={(emoji) => {
const v = state.draft.get(channel._id);
const cnt: DraftObject = {
content:
(v?.content ? `${v.content} ` : "") +
`:${emoji}:`,
};
state.draft.set(channel._id, cnt);
state.draft.set(
channel._id,
`${v ? `${v} ` : ""}:${emoji}:`,
);
}}
onClose={closePicker}
/>
@@ -672,7 +664,7 @@ export default observer(({ channel }: Props) => {
id="message"
maxLength={2000}
onKeyUp={onKeyUp}
value={state.draft.get(channel._id)?.content ?? ""}
value={state.draft.get(channel._id) ?? ""}
padding="var(--message-box-padding)"
onKeyDown={(e) => {
if (e.ctrlKey && e.key === "Enter") {

View File

@@ -67,8 +67,7 @@ export default function Embed({ embed }: Props) {
break;
}
case "Twitch":
case "Lightspeed":
case "Streamable": {
case "Lightspeed": {
mw = 1280;
mh = 720;
break;

View File

@@ -92,16 +92,6 @@ export default function EmbedMedia({ embed, width, height }: Props) {
/>
);
}
case "Streamable": {
return (
<iframe
src={`https://streamable.com/e/${embed.special.id}?loop=0`}
seamless
loading="lazy"
style={{ height }}
/>
);
}
default: {
if (embed.video) {
const url = embed.video.url;

View File

@@ -25,7 +25,7 @@ const Emoji = styled.img`
}
`;
const RE_EMOJI = /:([a-zA-Z0-9\-_]+):/g;
const RE_EMOJI = /:([a-zA-Z0-9_+]+):/g;
const RE_ULID = /^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$/;
export function RenderEmoji({ match }: CustomComponentProps) {

View File

@@ -41,8 +41,6 @@ import MFARecovery from "./components/MFARecovery";
import ModifyAccount from "./components/ModifyAccount";
import OutOfDate from "./components/OutOfDate";
import PendingFriendRequests from "./components/PendingFriendRequests";
import ReportContent from "./components/Report";
import ReportSuccess from "./components/ReportSuccess";
import ServerIdentity from "./components/ServerIdentity";
import ServerInfo from "./components/ServerInfo";
import ShowToken from "./components/ShowToken";
@@ -278,6 +276,4 @@ export const modalController = new ModalControllerExtended({
sign_out_sessions: SignOutSessions,
user_picker: UserPicker,
user_profile: UserProfile,
report: ReportContent,
report_success: ReportSuccess,
});

View File

@@ -9,17 +9,16 @@ export default observer(() => {
const history = useHistory();
useEffect(() => {
function keyDown(event: KeyboardEvent) {
function keyUp(event: KeyboardEvent) {
if (event.key === "Escape") {
modalController.pop("close");
} else if (event.key === "Enter") {
if (event.target instanceof HTMLSelectElement) return;
modalController.pop("confirm");
}
}
document.addEventListener("keydown", keyDown);
return () => document.removeEventListener("keydown", keyDown);
document.addEventListener("keyup", keyUp);
return () => document.removeEventListener("keyup", keyUp);
}, []);
return (

View File

@@ -1,5 +1,5 @@
import dayjs from "dayjs";
import styled, { css } from "styled-components";
import styled from "styled-components";
import { Text } from "preact-i18n";
import { useMemo, useState } from "preact/hooks";
@@ -14,17 +14,10 @@ import {
changelogEntryArray,
ChangelogPost,
} from "../../../assets/changelogs";
import Markdown from "../../../components/markdown/Markdown";
import { ModalProps } from "../types";
const Image = styled.img<{ shadow?: boolean }>`
const Image = styled.img`
border-radius: var(--border-radius);
${(props) =>
props.shadow &&
css`
filter: drop-shadow(4px 4px 10px rgba(0, 0, 0, 0.5));
`}
`;
function RenderLog({ post }: { post: ChangelogPost }) {
@@ -32,9 +25,9 @@ function RenderLog({ post }: { post: ChangelogPost }) {
<Column>
{post.content.map((entry) =>
typeof entry === "string" ? (
<Markdown content={entry} />
<span>{entry}</span>
) : (
<Image src={entry.src} shadow={entry.shadow} />
<Image src={entry.src} />
),
)}
</Column>

View File

@@ -1,146 +0,0 @@
import { API, Message as MessageInterface, User } from "revolt.js";
import styled from "styled-components";
import { Text } from "preact-i18n";
import { ModalForm, Row } from "@revoltchat/ui";
import Message from "../../../components/common/messaging/Message";
import UserShort from "../../../components/common/user/UserShort";
import { useClient } from "../../client/ClientController";
import { modalController } from "../ModalController";
import { ModalProps } from "../types";
const CONTENT_REASONS: API.ContentReportReason[] = [
"NoneSpecified",
"Illegal",
"PromotesHarm",
"SpamAbuse",
"Malware",
"Harassment",
];
const USER_REASONS: API.UserReportReason[] = [
"NoneSpecified",
"SpamAbuse",
"InappropriateProfile",
"Impersonation",
"BanEvasion",
"Underage",
];
/**
* Add padding to the message container
*/
const MessageContainer = styled.div`
margin-block-end: 16px;
`;
/**
* Report creation modal
*/
export default function ReportContent({
target,
...props
}: ModalProps<"report">) {
const client = useClient();
return (
<ModalForm
{...props}
title={
target instanceof MessageInterface ? (
<Text id="app.special.modals.report.message" />
) : (
<Text
id="app.special.modals.report.by_name"
fields={{
name:
target instanceof User
? target.username
: target.name,
}}
/>
)
}
schema={{
selected: "custom",
reason: "combo",
additional_context: "text",
}}
data={{
selected: {
element:
target instanceof MessageInterface ? (
<MessageContainer>
<Message message={target} head attachContext />
</MessageContainer>
) : target instanceof User ? (
<Row centred>
<UserShort user={target} size={32} />
</Row>
) : (
<></>
),
},
reason: {
field: (
<Text id="app.special.modals.report.reason" />
) as React.ReactChild,
options: (target instanceof User
? USER_REASONS
: CONTENT_REASONS
).map((value) => ({
name: (
<Text
id={
value === "NoneSpecified"
? "app.special.modals.report.no_reason"
: `app.special.modals.report.${
target instanceof User
? "user"
: "content"
}_reason.${value}`
}
/>
),
value,
})),
},
additional_context: {
field: (
<Text id="app.special.modals.report.additional_context" />
) as React.ReactChild,
},
}}
callback={async ({ reason, additional_context }) => {
await client.api.post("/safety/report", {
content: {
id: target._id,
type:
target instanceof MessageInterface
? "Message"
: target instanceof User
? "User"
: "Server",
report_reason: reason as any,
},
additional_context,
});
modalController.push({
type: "report_success",
user:
target instanceof MessageInterface
? target.author
: target instanceof User
? target
: undefined,
});
}}
submit={{
children: <Text id="app.special.modals.actions.report" />,
}}
/>
);
}

View File

@@ -1,65 +0,0 @@
import { Text } from "preact-i18n";
import { Modal } from "@revoltchat/ui";
import { noopTrue } from "../../../lib/js";
import { ModalProps } from "../types";
/**
* Report success modal
*/
export default function ReportSuccess({
user,
...props
}: ModalProps<"report_success">) {
return (
<Modal
{...props}
title={<Text id="app.special.modals.report.reported" />}
description={
<>
<Text id="app.special.modals.report.thank_you" />
{user && (
<>
<br />
<br />
<Text id="app.special.modals.report.block_user" />
</>
)}
</>
}
actions={
user
? [
{
palette: "plain",
onClick: async () => {
user.blockUser();
return true;
},
children: (
<Text id="app.special.modals.actions.block" />
),
},
{
palette: "plain-secondary",
onClick: noopTrue,
children: (
<Text id="app.special.modals.actions.dont_block" />
),
},
]
: [
{
palette: "plain",
onClick: noopTrue,
children: (
<Text id="app.special.modals.actions.done" />
),
},
]
}
/>
);
}

View File

@@ -28,24 +28,16 @@ export default function ServerInfo({
}
actions={[
{
onClick: () => {
onClick: () =>
modalController.push({
type: "server_identity",
member: server.member!,
});
return true;
},
}),
children: "Edit Identity",
palette: "primary",
},
{
onClick: () => {
modalController.push({
type: "report",
target: server,
});
return true;
},
onClick: () => report(server),
children: <Text id="app.special.modals.actions.report" />,
palette: "error",
},

View File

@@ -179,14 +179,6 @@ export type Modal = {
| {
type: "import_theme";
}
| {
type: "report";
target: Server | User | Message;
}
| {
type: "report_success";
user?: User;
}
);
export type ModalProps<T extends Modal["type"]> = Modal & { type: T } & {

View File

@@ -111,8 +111,7 @@ type Action =
action: "set_notification_state";
key: string;
state?: NotificationState;
}
| { action: "report"; target: User | Server | Message };
};
// ! FIXME: I dare someone to re-write this
// Tip: This should just be split into separate context menus per logical area.
@@ -450,12 +449,6 @@ export default function ContextMenus() {
case "open_server_settings":
history.push(`/server/${data.id}/settings`);
break;
case "report":
modalController.push({
type: "report",
target: data.target,
});
break;
}
})().catch((err) => {
modalController.push({
@@ -676,19 +669,6 @@ export default function ContextMenus() {
} as unknown as Action);
}
}
if (user._id !== userId) {
generateAction(
{
action: "report",
target: user,
},
"report_user",
undefined,
undefined,
"var(--error)",
);
}
}
if (contextualChannel) {
@@ -815,33 +795,14 @@ export default function ContextMenus() {
});
}
if (message.author_id !== userId) {
generateAction(
{
action: "report",
target: message,
},
"report_message",
undefined,
undefined,
"var(--error)",
);
}
if (
message.author_id === userId ||
channelPermissions & Permission.ManageMessages
) {
generateAction(
{
action: "delete_message",
target: message,
},
undefined,
undefined,
undefined,
"var(--error)",
);
generateAction({
action: "delete_message",
target: message,
});
}
if (
@@ -1074,17 +1035,6 @@ export default function ContextMenus() {
"var(--error)",
);
} else {
generateAction(
{
action: "report",
target: server,
},
"report_server",
undefined,
undefined,
"var(--error)",
);
generateAction(
{ action: "leave_server", target: server },
"leave_server",

View File

@@ -17,7 +17,6 @@ const ALLOWED_ORIGINS = [
"app.revolt.chat",
"nightly.revolt.chat",
"local.revolt.chat",
"rolt.chat",
];
/**

View File

@@ -5,7 +5,7 @@ import { mapToRecord } from "../../lib/conversion";
import Persistent from "../interfaces/Persistent";
import Store from "../interfaces/Store";
export interface DraftObject {
interface DraftObject {
content?: string;
masquerade?: {
avatar: string;
@@ -59,12 +59,10 @@ export default class Draft implements Store, Persistent<Data> {
* @param channel Channel ID
*/
@computed has(channel: string) {
if (!this.drafts.has(channel)) return false;
// fetch the draft object
const potentialDraft = this.drafts.get(channel)?.content;
// if it doesn't have any content return false
if (!potentialDraft) return false;
return potentialDraft.length > 0;
return (
this.drafts.has(channel) &&
this.drafts.get(channel)!.content!.length > 0
);
}
/**

View File

@@ -53,7 +53,7 @@ export default observer(() => {
const isDecember = !isTouchscreenDevice && new Date().getMonth() === 11;
const isOctober = !isTouchscreenDevice && new Date().getMonth() === 9
const snowflakes = useMemo(() => {
const flakes: string[] = [];
const flakes = [];
if (isDecember) {
for (let i = 0; i < 15; i++) {

View File

@@ -1,7 +1,7 @@
import styles from "../Login.module.scss";
import { Text } from "preact-i18n";
import { Button, Tip } from "@revoltchat/ui";
import { Button } from "@revoltchat/ui";
interface Props {
email?: string;
@@ -115,15 +115,6 @@ export function MailProvider({ email }: Props) {
/>
</Button>
</a>
{provider[0] === "iCloud Mail" && (
<Tip palette="error">
<span>
iCloud users may not receive any emails due to a block
by Proofpoint. Please use a different email provider if
you do not receive anything.
</span>
</Tip>
)}
</div>
);
}

View File

@@ -340,13 +340,11 @@ function ListElement({
const save = useCallback(() => {
setEditing(undefined);
if (editing !== "") {
setTitle!(editing!);
}
setTitle!(editing!);
}, [editing, setTitle]);
useEffect(() => {
if (editing === undefined) return;
if (!editing) return;
function onClick(ev: MouseEvent) {
if ((ev.target as HTMLElement)?.id !== category.id) {
@@ -370,7 +368,7 @@ function ListElement({
<div className="inner">
<Row>
<KanbanListHeader {...provided.dragHandleProps}>
{editing !== undefined ? (
{editing ? (
<input
value={editing}
onChange={(e) =>

View File

@@ -70,10 +70,6 @@ export const Roles = observer(({ server }: Props) => {
}
`;
const DeleteRoleButton = styled(Button)`
margin: 16px 0;
`;
return (
<PermissionsLayout
server={server}
@@ -270,12 +266,12 @@ export const Roles = observer(({ server }: Props) => {
<h1>
<Text id="app.settings.categories.danger_zone" />
</h1>
<DeleteRoleButton
<Button
palette="error"
compact
onClick={deleteRole}>
<Text id="app.settings.permissions.delete_role" />
</DeleteRoleButton>
</Button>
</>
)}
</div>

View File

@@ -8016,14 +8016,14 @@ __metadata:
languageName: node
linkType: hard
"revolt-api@npm:0.5.8":
version: 0.5.8
resolution: "revolt-api@npm:0.5.8"
"revolt-api@npm:0.5.5-4":
version: 0.5.5-4
resolution: "revolt-api@npm:0.5.5-4"
dependencies:
"@insertish/oapi": 0.1.18
axios: ^0.26.1
lodash.defaultsdeep: ^4.6.1
checksum: d488fa87774e4e9d0b3136779c555cbed257d2b76a298b17dca346cc3909f81e84aa516c10a415a7c20cd31990d540ade5489f69e06f69e14ba4974a26aa145e
checksum: dfb374d58f1b8b5a6de2e7fa05e386b3df3ffb85a450a6894f23c6b9760af8bff0d198d8352063c33084f0dbc6ff84a234300efb0c62c7b27f709887402787f1
languageName: node
linkType: hard
@@ -8040,7 +8040,7 @@ __metadata:
lodash.isequal: ^4.5.0
long: ^5.2.0
mobx: ^6.3.2
revolt-api: 0.5.8
revolt-api: 0.5.5-4
ulid: ^2.3.0
ws: ^8.2.2
languageName: node