mirror of
https://github.com/array-in-a-matrix/xinny.git
synced 2024-05-21 05:37:23 -04:00
Merge branch 'cinnyapp:dev' into ts
This commit is contained in:
commit
c072b1281c
2
.github/workflows/build-pull-request.yml
vendored
2
.github/workflows/build-pull-request.yml
vendored
|
@ -12,7 +12,7 @@ jobs:
|
||||||
PR_NUMBER: ${{github.event.number}}
|
PR_NUMBER: ${{github.event.number}}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3.2.0
|
uses: actions/checkout@v3.5.3
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v3.6.0
|
uses: actions/setup-node@v3.6.0
|
||||||
with:
|
with:
|
||||||
|
|
2
.github/workflows/deploy-pull-request.yml
vendored
2
.github/workflows/deploy-pull-request.yml
vendored
|
@ -45,7 +45,7 @@ jobs:
|
||||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID_PR_CINNY }}
|
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID_PR_CINNY }}
|
||||||
timeout-minutes: 1
|
timeout-minutes: 1
|
||||||
- name: Comment preview on PR
|
- name: Comment preview on PR
|
||||||
uses: thollander/actions-comment-pull-request@632cf9ce90574d125be56b5f3405cda41a84e2fd
|
uses: thollander/actions-comment-pull-request@dadb7667129e23f12ca3925c90dc5cd7121ab57e
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
|
|
4
.github/workflows/docker-pr.yml
vendored
4
.github/workflows/docker-pr.yml
vendored
|
@ -11,9 +11,9 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3.2.0
|
uses: actions/checkout@v3.5.3
|
||||||
- name: Build Docker image
|
- name: Build Docker image
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v4.1.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: false
|
push: false
|
||||||
|
|
2
.github/workflows/lockfile.yml
vendored
2
.github/workflows/lockfile.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3.2.0
|
uses: actions/checkout@v3.5.3
|
||||||
- name: NPM Lockfile Changes
|
- name: NPM Lockfile Changes
|
||||||
uses: codepunkt/npm-lockfile-changes@b40543471c36394409466fdb277a73a0856d7891
|
uses: codepunkt/npm-lockfile-changes@b40543471c36394409466fdb277a73a0856d7891
|
||||||
with:
|
with:
|
||||||
|
|
2
.github/workflows/netlify-dev.yml
vendored
2
.github/workflows/netlify-dev.yml
vendored
|
@ -11,7 +11,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3.2.0
|
uses: actions/checkout@v3.5.3
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v3.6.0
|
uses: actions/setup-node@v3.6.0
|
||||||
with:
|
with:
|
||||||
|
|
10
.github/workflows/prod-deploy.yml
vendored
10
.github/workflows/prod-deploy.yml
vendored
|
@ -10,7 +10,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3.2.0
|
uses: actions/checkout@v3.5.3
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v3.6.0
|
uses: actions/setup-node@v3.6.0
|
||||||
with:
|
with:
|
||||||
|
@ -66,11 +66,11 @@ jobs:
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3.2.0
|
uses: actions/checkout@v3.5.3
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2.1.0
|
uses: docker/setup-qemu-action@v2.1.0
|
||||||
- name: Set up Docker Buildx
|
- 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
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2.1.0
|
uses: docker/login-action@v2.1.0
|
||||||
with:
|
with:
|
||||||
|
@ -84,13 +84,13 @@ jobs:
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
- name: Extract metadata (tags, labels) for Docker
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v4.1.1
|
uses: docker/metadata-action@v4.5.0
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
${{ secrets.DOCKER_USERNAME }}/cinny
|
${{ secrets.DOCKER_USERNAME }}/cinny
|
||||||
ghcr.io/${{ github.repository }}
|
ghcr.io/${{ github.repository }}
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
uses: docker/build-push-action@v3.2.0
|
uses: docker/build-push-action@v4.1.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
|
|
@ -11,7 +11,7 @@ RUN npm run build
|
||||||
|
|
||||||
|
|
||||||
## App
|
## App
|
||||||
FROM nginx:1.23.3-alpine
|
FROM nginx:1.25.0-alpine
|
||||||
|
|
||||||
COPY --from=builder /src/dist /app
|
COPY --from=builder /src/dist /app
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,8 @@ export const CustomEditor = forwardRef<HTMLDivElement, CustomEditorProps>(
|
||||||
const handleKeydown: KeyboardEventHandler = useCallback(
|
const handleKeydown: KeyboardEventHandler = useCallback(
|
||||||
(evt) => {
|
(evt) => {
|
||||||
onKeyDown?.(evt);
|
onKeyDown?.(evt);
|
||||||
toggleKeyboardShortcut(editor, evt);
|
const shortcutToggled = toggleKeyboardShortcut(editor, evt);
|
||||||
|
if (shortcutToggled) evt.preventDefault();
|
||||||
},
|
},
|
||||||
[editor, onKeyDown]
|
[editor, onKeyDown]
|
||||||
);
|
);
|
||||||
|
|
|
@ -104,7 +104,7 @@ export function EmoticonAutocomplete({
|
||||||
as="img"
|
as="img"
|
||||||
src={mx.mxcUrlToHttp(key) || key}
|
src={mx.mxcUrlToHttp(key) || key}
|
||||||
alt={emoticon.shortcode}
|
alt={emoticon.shortcode}
|
||||||
style={{ width: toRem(24), height: toRem(24) }}
|
style={{ width: toRem(24), height: toRem(24), objectFit: 'contain' }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Box
|
<Box
|
||||||
|
|
|
@ -23,13 +23,14 @@ export const getAutocompletePrefix = <TPrefix extends string>(
|
||||||
validPrefixes: readonly TPrefix[]
|
validPrefixes: readonly TPrefix[]
|
||||||
): TPrefix | undefined => {
|
): TPrefix | undefined => {
|
||||||
const world = Editor.string(editor, queryRange);
|
const world = Editor.string(editor, queryRange);
|
||||||
const prefix = world[0] as TPrefix | undefined;
|
return validPrefixes.find((p) => world.startsWith(p));
|
||||||
if (!prefix) return undefined;
|
|
||||||
return validPrefixes.includes(prefix) ? prefix : undefined;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAutocompleteQueryText = (editor: Editor, queryRange: BaseRange): string =>
|
export const getAutocompleteQueryText = (
|
||||||
Editor.string(editor, queryRange).slice(1);
|
editor: Editor,
|
||||||
|
queryRange: BaseRange,
|
||||||
|
prefix: string
|
||||||
|
): string => Editor.string(editor, queryRange).slice(prefix.length);
|
||||||
|
|
||||||
export const getAutocompleteQuery = <TPrefix extends string>(
|
export const getAutocompleteQuery = <TPrefix extends string>(
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
|
@ -41,6 +42,6 @@ export const getAutocompleteQuery = <TPrefix extends string>(
|
||||||
return {
|
return {
|
||||||
range: queryRange,
|
range: queryRange,
|
||||||
prefix,
|
prefix,
|
||||||
text: getAutocompleteQueryText(editor, queryRange),
|
text: getAutocompleteQueryText(editor, queryRange, prefix),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,11 +2,25 @@ import { BasePoint, BaseRange, Editor, Element, Point, Range, Transforms } from
|
||||||
import { BlockType, MarkType } from './Elements';
|
import { BlockType, MarkType } from './Elements';
|
||||||
import { EmoticonElement, FormattedText, HeadingLevel, LinkElement, MentionElement } from './slate';
|
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) => {
|
export const isMarkActive = (editor: Editor, format: MarkType) => {
|
||||||
const marks = Editor.marks(editor);
|
const marks = Editor.marks(editor);
|
||||||
return marks ? marks[format] === true : false;
|
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) => {
|
export const toggleMark = (editor: Editor, format: MarkType) => {
|
||||||
const isActive = isMarkActive(editor, format);
|
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) => {
|
export const isBlockActive = (editor: Editor, format: BlockType) => {
|
||||||
const [match] = Editor.nodes(editor, {
|
const [match] = Editor.nodes(editor, {
|
||||||
match: (node) => Element.isElement(node) && node.type === format,
|
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) => {
|
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(() => {
|
setTimeout(() => {
|
||||||
Transforms.move(editor);
|
Transforms.move(editor);
|
||||||
if (withSpace) editor.insertText(' ');
|
if (withSpace) editor.insertText(' ');
|
||||||
}, 1);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface PointUntilCharOptions {
|
interface PointUntilCharOptions {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { isHotkey } from 'is-hotkey';
|
import { isHotkey } from 'is-hotkey';
|
||||||
import { KeyboardEvent } from 'react';
|
import { KeyboardEvent } from 'react';
|
||||||
import { Editor } from 'slate';
|
import { Editor } from 'slate';
|
||||||
import { isBlockActive, toggleBlock, toggleMark } from './common';
|
import { isAnyMarkActive, isBlockActive, removeAllMark, toggleBlock, toggleMark } from './common';
|
||||||
import { BlockType, MarkType } from './Elements';
|
import { BlockType, MarkType } from './Elements';
|
||||||
|
|
||||||
export const INLINE_HOTKEYS: Record<string, MarkType> = {
|
export const INLINE_HOTKEYS: Record<string, MarkType> = {
|
||||||
|
@ -22,19 +22,42 @@ export const BLOCK_HOTKEYS: Record<string, BlockType> = {
|
||||||
};
|
};
|
||||||
const BLOCK_KEYS = Object.keys(BLOCK_HOTKEYS);
|
const BLOCK_KEYS = Object.keys(BLOCK_HOTKEYS);
|
||||||
|
|
||||||
export const toggleKeyboardShortcut = (editor: Editor, event: KeyboardEvent<Element>) => {
|
/**
|
||||||
BLOCK_KEYS.forEach((hotkey) => {
|
* @return boolean true if shortcut is toggled.
|
||||||
|
*/
|
||||||
|
export const toggleKeyboardShortcut = (editor: Editor, event: KeyboardEvent<Element>): 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)) {
|
if (isHotkey(hotkey, event)) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
toggleBlock(editor, BLOCK_HOTKEYS[hotkey]);
|
toggleBlock(editor, BLOCK_HOTKEYS[hotkey]);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
if (blockToggled) return true;
|
||||||
|
|
||||||
if (!isBlockActive(editor, BlockType.CodeBlock))
|
const inlineToggled = isBlockActive(editor, BlockType.CodeBlock)
|
||||||
INLINE_KEYS.forEach((hotkey) => {
|
? false
|
||||||
if (isHotkey(hotkey, event)) {
|
: INLINE_KEYS.find((hotkey) => {
|
||||||
event.preventDefault();
|
if (isHotkey(hotkey, event)) {
|
||||||
toggleMark(editor, INLINE_HOTKEYS[hotkey]);
|
event.preventDefault();
|
||||||
}
|
toggleMark(editor, INLINE_HOTKEYS[hotkey]);
|
||||||
});
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return !!inlineToggled;
|
||||||
};
|
};
|
||||||
|
|
|
@ -88,7 +88,7 @@ const elementToPlainText = (node: CustomElement, children: string): string => {
|
||||||
|
|
||||||
export const toPlainText = (node: Descendant | Descendant[]): string => {
|
export const toPlainText = (node: Descendant | Descendant[]): string => {
|
||||||
if (Array.isArray(node)) return node.map((n) => toPlainText(n)).join('');
|
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('');
|
const children = node.children.map((n) => toPlainText(n)).join('');
|
||||||
return elementToPlainText(node, children);
|
return elementToPlainText(node, children);
|
||||||
|
|
|
@ -122,6 +122,7 @@ export const CustomEmojiImg = style([
|
||||||
{
|
{
|
||||||
width: toRem(32),
|
width: toRem(32),
|
||||||
height: toRem(32),
|
height: toRem(32),
|
||||||
|
objectFit: 'contain',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -130,5 +131,6 @@ export const StickerImg = style([
|
||||||
{
|
{
|
||||||
width: toRem(96),
|
width: toRem(96),
|
||||||
height: toRem(96),
|
height: toRem(96),
|
||||||
|
objectFit: 'contain',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -373,6 +373,7 @@ function ImagePackSidebarStack({
|
||||||
style={{
|
style={{
|
||||||
width: toRem(24),
|
width: toRem(24),
|
||||||
height: toRem(24),
|
height: toRem(24),
|
||||||
|
objectFit: 'contain',
|
||||||
}}
|
}}
|
||||||
src={mx.mxcUrlToHttp(pack.getPackAvatarUrl(usage) ?? '') || pack.avatarUrl}
|
src={mx.mxcUrlToHttp(pack.getPackAvatarUrl(usage) ?? '') || pack.avatarUrl}
|
||||||
alt={label || 'Unknown Pack'}
|
alt={label || 'Unknown Pack'}
|
||||||
|
|
|
@ -184,12 +184,13 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
|
||||||
body,
|
body,
|
||||||
formattedBody,
|
formattedBody,
|
||||||
});
|
});
|
||||||
|
ReactEditor.focus(editor);
|
||||||
};
|
};
|
||||||
navigation.on(cons.events.navigation.REPLY_TO_CLICKED, handleReplyTo);
|
navigation.on(cons.events.navigation.REPLY_TO_CLICKED, handleReplyTo);
|
||||||
return () => {
|
return () => {
|
||||||
navigation.removeListener(cons.events.navigation.REPLY_TO_CLICKED, handleReplyTo);
|
navigation.removeListener(cons.events.navigation.REPLY_TO_CLICKED, handleReplyTo);
|
||||||
};
|
};
|
||||||
}, [setReplyDraft]);
|
}, [setReplyDraft, editor]);
|
||||||
|
|
||||||
const handleRemoveUpload = useCallback(
|
const handleRemoveUpload = useCallback(
|
||||||
(upload: TUploadContent | TUploadContent[]) => {
|
(upload: TUploadContent | TUploadContent[]) => {
|
||||||
|
|
Loading…
Reference in a new issue