diff --git a/Dockerfile b/Dockerfile index 8ee6bb04..0aa712d8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,7 @@ 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 diff --git a/src/components/common/AutoComplete.tsx b/src/components/common/AutoComplete.tsx index 72c0d885..0ba18214 100644 --- a/src/components/common/AutoComplete.tsx +++ b/src/components/common/AutoComplete.tsx @@ -1,4 +1,7 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { Link } from "react-router-dom"; import { Channel, User } from "revolt.js"; +import { Emoji as CustomEmoji } from "revolt.js/esm/maps/Emojis"; import styled, { css } from "styled-components/macro"; import { StateUpdater, useState } from "preact/hooks"; @@ -7,6 +10,8 @@ import { emojiDictionary } from "../../assets/emojis"; import { useClient } from "../../controllers/client/ClientController"; import ChannelIcon from "./ChannelIcon"; import Emoji from "./Emoji"; +import ServerIcon from "./ServerIcon"; +import Tooltip from "./Tooltip"; import UserIcon from "./user/UserIcon"; export type AutoCompleteState = @@ -14,7 +19,7 @@ export type AutoCompleteState = | ({ selected: number; within: boolean } & ( | { type: "emoji"; - matches: string[]; + matches: (string | CustomEmoji)[]; } | { type: "user"; @@ -104,16 +109,23 @@ export function useAutoComplete( if (type === "emoji") { // ! TODO: we should convert it to a Binary Search Tree and use that - const matches = Object.keys(emojiDictionary) - .filter((emoji: string) => emoji.match(regex)) - .splice(0, 5); + const matches = [ + ...Object.keys(emojiDictionary).filter((emoji: string) => + emoji.match(regex), + ), + ...Array.from(client.emojis.values()).filter((emoji) => + emoji.name.match(regex), + ), + ].splice(0, 5); if (matches.length > 0) { const currentPosition = state.type !== "none" ? state.selected : 0; setState({ + // @ts-ignore-next-line are you high type: "emoji", + // @ts-ignore-next-line matches, selected: Math.min(currentPosition, matches.length - 1), within: false, @@ -233,10 +245,13 @@ export function useAutoComplete( const content = el.value.split(""); if (state.type === "emoji") { + const selected = state.matches[state.selected]; content.splice( index, search.length, - state.matches[state.selected], + selected instanceof CustomEmoji + ? selected._id + : selected, ": ", ); } else if (state.type === "user") { @@ -388,12 +403,17 @@ export default function AutoComplete({ setState, onClick, }: Pick) { + const client = useClient(); return (
{state.type === "emoji" && state.matches.map((match, i) => ( ))} {state.type === "user" && diff --git a/src/components/common/messaging/MessageBox.tsx b/src/components/common/messaging/MessageBox.tsx index 35b438cf..caaa6a84 100644 --- a/src/components/common/messaging/MessageBox.tsx +++ b/src/components/common/messaging/MessageBox.tsx @@ -45,6 +45,8 @@ import { PermissionTooltip } from "../Tooltip"; import FilePreview from "./bars/FilePreview"; import ReplyBar from "./bars/ReplyBar"; +import { DraftObject } from "../../../mobx/stores/Draft"; + type Props = { channel: Channel; }; @@ -277,7 +279,12 @@ export default observer(({ channel }: Props) => { // Push message content to draft. const setMessage = useCallback( - (content?: string) => state.draft.set(channel._id, content), + (content?: string) => { + const dobj: DraftObject = { + content + } + state.draft.set(channel._id, dobj) + }, [state.draft, channel._id], ); @@ -317,7 +324,7 @@ export default observer(({ channel }: Props) => { if (uploadState.type === "uploading" || uploadState.type === "sending") return; - const content = state.draft.get(channel._id)?.trim() ?? ""; + const content = state.draft.get(channel._id)?.content?.trim() ?? ""; if (uploadState.type === "attached") return sendFile(content); if (content.length === 0) return; @@ -526,7 +533,7 @@ export default observer(({ channel }: Props) => { } function isInCodeBlock(cursor: number): boolean { - const content = state.draft.get(channel._id) || ""; + const content = state.draft.get(channel._id)?.content || ""; const contentBeforeCursor = content.substring(0, cursor); let delimiterCount = 0; @@ -607,9 +614,12 @@ export default observer(({ channel }: Props) => { { const v = state.draft.get(channel._id); + const cnt: DraftObject = { + content: (v == null ? "" : `${v.content} `) + `:${emoji}:` + } state.draft.set( channel._id, - `${v ? `${v} ` : ""}:${emoji}:`, + cnt, ); }} onClose={closePicker} @@ -664,7 +674,7 @@ export default observer(({ channel }: Props) => { id="message" maxLength={2000} onKeyUp={onKeyUp} - value={state.draft.get(channel._id) ?? ""} + value={state.draft.get(channel._id)?.content ?? ""} padding="var(--message-box-padding)" onKeyDown={(e) => { if (e.ctrlKey && e.key === "Enter") { diff --git a/src/lib/links.ts b/src/lib/links.ts index 211c91d6..269e84de 100644 --- a/src/lib/links.ts +++ b/src/lib/links.ts @@ -17,6 +17,7 @@ const ALLOWED_ORIGINS = [ "app.revolt.chat", "nightly.revolt.chat", "local.revolt.chat", + "rolt.chat", ]; /** diff --git a/src/mobx/stores/Draft.ts b/src/mobx/stores/Draft.ts index 345351d7..de81ea8a 100644 --- a/src/mobx/stores/Draft.ts +++ b/src/mobx/stores/Draft.ts @@ -5,7 +5,7 @@ import { mapToRecord } from "../../lib/conversion"; import Persistent from "../interfaces/Persistent"; import Store from "../interfaces/Store"; -interface DraftObject { +export interface DraftObject { content?: string; masquerade?: { avatar: string; diff --git a/src/pages/home/Home.tsx b/src/pages/home/Home.tsx index f6c82ba1..1d6e6d2f 100644 --- a/src/pages/home/Home.tsx +++ b/src/pages/home/Home.tsx @@ -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 = []; + const flakes: string[] = []; if (isDecember) { for (let i = 0; i < 15; i++) {