diff --git a/.github/workflows/build-pull-request.yml b/.github/workflows/build-pull-request.yml index 5a612a6..d773105 100644 --- a/.github/workflows/build-pull-request.yml +++ b/.github/workflows/build-pull-request.yml @@ -12,7 +12,7 @@ jobs: PR_NUMBER: ${{github.event.number}} steps: - name: Checkout repository - uses: actions/checkout@v3.2.0 + uses: actions/checkout@v3.5.3 - name: Setup node uses: actions/setup-node@v3.6.0 with: diff --git a/.github/workflows/deploy-pull-request.yml b/.github/workflows/deploy-pull-request.yml index fdb0e81..c4d8243 100644 --- a/.github/workflows/deploy-pull-request.yml +++ b/.github/workflows/deploy-pull-request.yml @@ -45,7 +45,7 @@ jobs: NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID_PR_CINNY }} timeout-minutes: 1 - name: Comment preview on PR - uses: thollander/actions-comment-pull-request@632cf9ce90574d125be56b5f3405cda41a84e2fd + uses: thollander/actions-comment-pull-request@dadb7667129e23f12ca3925c90dc5cd7121ab57e env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/docker-pr.yml b/.github/workflows/docker-pr.yml index 47dbfe3..3edc3c1 100644 --- a/.github/workflows/docker-pr.yml +++ b/.github/workflows/docker-pr.yml @@ -11,9 +11,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3.2.0 + uses: actions/checkout@v3.5.3 - name: Build Docker image - uses: docker/build-push-action@v3.2.0 + uses: docker/build-push-action@v4.1.0 with: context: . push: false diff --git a/.github/workflows/lockfile.yml b/.github/workflows/lockfile.yml index b417df1..30d0076 100644 --- a/.github/workflows/lockfile.yml +++ b/.github/workflows/lockfile.yml @@ -14,7 +14,7 @@ jobs: pull-requests: write steps: - name: Checkout - uses: actions/checkout@v3.2.0 + uses: actions/checkout@v3.5.3 - name: NPM Lockfile Changes uses: codepunkt/npm-lockfile-changes@b40543471c36394409466fdb277a73a0856d7891 with: diff --git a/.github/workflows/netlify-dev.yml b/.github/workflows/netlify-dev.yml index 319fc4c..8935c51 100644 --- a/.github/workflows/netlify-dev.yml +++ b/.github/workflows/netlify-dev.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3.2.0 + uses: actions/checkout@v3.5.3 - name: Setup node uses: actions/setup-node@v3.6.0 with: diff --git a/.github/workflows/prod-deploy.yml b/.github/workflows/prod-deploy.yml index 6f9edd4..484ecba 100644 --- a/.github/workflows/prod-deploy.yml +++ b/.github/workflows/prod-deploy.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3.2.0 + uses: actions/checkout@v3.5.3 - name: Setup node uses: actions/setup-node@v3.6.0 with: @@ -66,11 +66,11 @@ jobs: packages: write steps: - name: Checkout repository - uses: actions/checkout@v3.2.0 + uses: actions/checkout@v3.5.3 - name: Set up QEMU uses: docker/setup-qemu-action@v2.1.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2.2.1 + uses: docker/setup-buildx-action@v2.6.0 - name: Login to Docker Hub uses: docker/login-action@v2.1.0 with: @@ -84,13 +84,13 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v4.1.1 + uses: docker/metadata-action@v4.5.0 with: images: | ${{ secrets.DOCKER_USERNAME }}/cinny ghcr.io/${{ github.repository }} - name: Build and push Docker image - uses: docker/build-push-action@v3.2.0 + uses: docker/build-push-action@v4.1.0 with: context: . platforms: linux/amd64,linux/arm64 diff --git a/Dockerfile b/Dockerfile index af9abbd..3cae982 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ RUN npm run build ## App -FROM nginx:1.23.3-alpine +FROM nginx:1.25.0-alpine COPY --from=builder /src/dist /app diff --git a/src/app/components/editor/Editor.tsx b/src/app/components/editor/Editor.tsx index edf1ac6..2657b21 100644 --- a/src/app/components/editor/Editor.tsx +++ b/src/app/components/editor/Editor.tsx @@ -93,7 +93,8 @@ export const CustomEditor = forwardRef( const handleKeydown: KeyboardEventHandler = useCallback( (evt) => { onKeyDown?.(evt); - toggleKeyboardShortcut(editor, evt); + const shortcutToggled = toggleKeyboardShortcut(editor, evt); + if (shortcutToggled) evt.preventDefault(); }, [editor, onKeyDown] ); diff --git a/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx b/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx index e5af3fa..17712b8 100644 --- a/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx +++ b/src/app/components/editor/autocomplete/EmoticonAutocomplete.tsx @@ -104,7 +104,7 @@ export function EmoticonAutocomplete({ as="img" src={mx.mxcUrlToHttp(key) || key} alt={emoticon.shortcode} - style={{ width: toRem(24), height: toRem(24) }} + style={{ width: toRem(24), height: toRem(24), objectFit: 'contain' }} /> ) : ( ( validPrefixes: readonly TPrefix[] ): TPrefix | undefined => { const world = Editor.string(editor, queryRange); - const prefix = world[0] as TPrefix | undefined; - if (!prefix) return undefined; - return validPrefixes.includes(prefix) ? prefix : undefined; + return validPrefixes.find((p) => world.startsWith(p)); }; -export const getAutocompleteQueryText = (editor: Editor, queryRange: BaseRange): string => - Editor.string(editor, queryRange).slice(1); +export const getAutocompleteQueryText = ( + editor: Editor, + queryRange: BaseRange, + prefix: string +): string => Editor.string(editor, queryRange).slice(prefix.length); export const getAutocompleteQuery = ( editor: Editor, @@ -41,6 +42,6 @@ export const getAutocompleteQuery = ( return { range: queryRange, prefix, - text: getAutocompleteQueryText(editor, queryRange), + text: getAutocompleteQueryText(editor, queryRange, prefix), }; }; diff --git a/src/app/components/editor/common.ts b/src/app/components/editor/common.ts index c9cf086..619a1bf 100644 --- a/src/app/components/editor/common.ts +++ b/src/app/components/editor/common.ts @@ -2,11 +2,25 @@ import { BasePoint, BaseRange, Editor, Element, Point, Range, Transforms } from import { BlockType, MarkType } from './Elements'; import { EmoticonElement, FormattedText, HeadingLevel, LinkElement, MentionElement } from './slate'; +const ALL_MARK_TYPE: MarkType[] = [ + MarkType.Bold, + MarkType.Code, + MarkType.Italic, + MarkType.Spoiler, + MarkType.StrikeThrough, + MarkType.Underline, +]; + export const isMarkActive = (editor: Editor, format: MarkType) => { const marks = Editor.marks(editor); return marks ? marks[format] === true : false; }; +export const isAnyMarkActive = (editor: Editor) => { + const marks = Editor.marks(editor); + return marks && !!ALL_MARK_TYPE.find((type) => marks[type] === true); +}; + export const toggleMark = (editor: Editor, format: MarkType) => { const isActive = isMarkActive(editor, format); @@ -17,6 +31,10 @@ export const toggleMark = (editor: Editor, format: MarkType) => { } }; +export const removeAllMark = (editor: Editor) => { + ALL_MARK_TYPE.forEach((mark) => Editor.removeMark(editor, mark)); +}; + export const isBlockActive = (editor: Editor, format: BlockType) => { const [match] = Editor.nodes(editor, { match: (node) => Element.isElement(node) && node.type === format, @@ -140,11 +158,11 @@ export const replaceWithElement = (editor: Editor, selectRange: BaseRange, eleme }; export const moveCursor = (editor: Editor, withSpace?: boolean) => { - // without timeout it works properly when we select autocomplete with Tab or Space + // without timeout move cursor doesn't works properly. setTimeout(() => { Transforms.move(editor); if (withSpace) editor.insertText(' '); - }, 1); + }, 100); }; interface PointUntilCharOptions { diff --git a/src/app/components/editor/keyboard.ts b/src/app/components/editor/keyboard.ts index 52217dd..3fbe536 100644 --- a/src/app/components/editor/keyboard.ts +++ b/src/app/components/editor/keyboard.ts @@ -1,7 +1,7 @@ import { isHotkey } from 'is-hotkey'; import { KeyboardEvent } from 'react'; import { Editor } from 'slate'; -import { isBlockActive, toggleBlock, toggleMark } from './common'; +import { isAnyMarkActive, isBlockActive, removeAllMark, toggleBlock, toggleMark } from './common'; import { BlockType, MarkType } from './Elements'; export const INLINE_HOTKEYS: Record = { @@ -22,19 +22,42 @@ export const BLOCK_HOTKEYS: Record = { }; const BLOCK_KEYS = Object.keys(BLOCK_HOTKEYS); -export const toggleKeyboardShortcut = (editor: Editor, event: KeyboardEvent) => { - BLOCK_KEYS.forEach((hotkey) => { +/** + * @return boolean true if shortcut is toggled. + */ +export const toggleKeyboardShortcut = (editor: Editor, event: KeyboardEvent): boolean => { + if (isHotkey('escape', event)) { + if (isAnyMarkActive(editor)) { + removeAllMark(editor); + return true; + } + console.log(isBlockActive(editor, BlockType.Paragraph)); + if (!isBlockActive(editor, BlockType.Paragraph)) { + toggleBlock(editor, BlockType.Paragraph); + return true; + } + return false; + } + + const blockToggled = BLOCK_KEYS.find((hotkey) => { if (isHotkey(hotkey, event)) { event.preventDefault(); toggleBlock(editor, BLOCK_HOTKEYS[hotkey]); + return true; } + return false; }); + if (blockToggled) return true; - if (!isBlockActive(editor, BlockType.CodeBlock)) - INLINE_KEYS.forEach((hotkey) => { - if (isHotkey(hotkey, event)) { - event.preventDefault(); - toggleMark(editor, INLINE_HOTKEYS[hotkey]); - } - }); + const inlineToggled = isBlockActive(editor, BlockType.CodeBlock) + ? false + : INLINE_KEYS.find((hotkey) => { + if (isHotkey(hotkey, event)) { + event.preventDefault(); + toggleMark(editor, INLINE_HOTKEYS[hotkey]); + return true; + } + return false; + }); + return !!inlineToggled; }; diff --git a/src/app/components/editor/output.ts b/src/app/components/editor/output.ts index 091dab7..38c5449 100644 --- a/src/app/components/editor/output.ts +++ b/src/app/components/editor/output.ts @@ -88,7 +88,7 @@ const elementToPlainText = (node: CustomElement, children: string): string => { export const toPlainText = (node: Descendant | Descendant[]): string => { if (Array.isArray(node)) return node.map((n) => toPlainText(n)).join(''); - if (Text.isText(node)) return sanitizeText(node.text); + if (Text.isText(node)) return node.text; const children = node.children.map((n) => toPlainText(n)).join(''); return elementToPlainText(node, children); diff --git a/src/app/components/emoji-board/EmojiBoard.css.tsx b/src/app/components/emoji-board/EmojiBoard.css.tsx index 0fefc5b..adeb250 100644 --- a/src/app/components/emoji-board/EmojiBoard.css.tsx +++ b/src/app/components/emoji-board/EmojiBoard.css.tsx @@ -122,6 +122,7 @@ export const CustomEmojiImg = style([ { width: toRem(32), height: toRem(32), + objectFit: 'contain', }, ]); @@ -130,5 +131,6 @@ export const StickerImg = style([ { width: toRem(96), height: toRem(96), + objectFit: 'contain', }, ]); diff --git a/src/app/components/emoji-board/EmojiBoard.tsx b/src/app/components/emoji-board/EmojiBoard.tsx index c5f5038..3b1ccc5 100644 --- a/src/app/components/emoji-board/EmojiBoard.tsx +++ b/src/app/components/emoji-board/EmojiBoard.tsx @@ -373,6 +373,7 @@ function ImagePackSidebarStack({ style={{ width: toRem(24), height: toRem(24), + objectFit: 'contain', }} src={mx.mxcUrlToHttp(pack.getPackAvatarUrl(usage) ?? '') || pack.avatarUrl} alt={label || 'Unknown Pack'} diff --git a/src/app/organisms/room/RoomInput.tsx b/src/app/organisms/room/RoomInput.tsx index 17830ad..e79f488 100644 --- a/src/app/organisms/room/RoomInput.tsx +++ b/src/app/organisms/room/RoomInput.tsx @@ -184,12 +184,13 @@ export const RoomInput = forwardRef( body, formattedBody, }); + ReactEditor.focus(editor); }; navigation.on(cons.events.navigation.REPLY_TO_CLICKED, handleReplyTo); return () => { navigation.removeListener(cons.events.navigation.REPLY_TO_CLICKED, handleReplyTo); }; - }, [setReplyDraft]); + }, [setReplyDraft, editor]); const handleRemoveUpload = useCallback( (upload: TUploadContent | TUploadContent[]) => {