From ba55780246a0eb98a81219b680409b30a12786a1 Mon Sep 17 00:00:00 2001 From: Untone Date: Sun, 15 Sep 2024 21:43:35 +0300 Subject: [PATCH] linted-update --- biome.json | 14 ++++-- package.json | 48 +++++++++---------- .../Article/AudioPlayer/PlayerHeader.tsx | 2 +- .../Article/CommentRatingControl.tsx | 2 - src/components/Article/FullArticle.tsx | 4 +- src/components/Author/Userpic/Userpic.tsx | 2 +- .../BubbleMenu/BlockquoteBubbleMenu.tsx | 6 +-- .../Editor/BubbleMenu/FigureBubbleMenu.tsx | 8 ++-- .../Editor/BubbleMenu/IncutBubbleMenu.tsx | 8 ++-- .../EditorFloatingMenu/EditorFloatingMenu.tsx | 18 ++++--- .../Editor/TextBubbleMenu/TextBubbleMenu.tsx | 36 +++++++------- src/components/Editor/extensions/Figure.ts | 2 +- .../Feed/ArticleCard/ArticleCard.tsx | 5 +- .../FeedArticlePopup/FeedArticlePopup.tsx | 11 ++--- .../SearchModal/SearchModal.module.scss | 12 ++--- src/components/Topic/Full.tsx | 2 +- .../Topic/TopicBadge/TopicBadge.module.scss | 4 +- src/components/TopicsNav/TopicsNav.tsx | 3 +- src/components/Views/AllTopics/AllTopics.tsx | 5 +- .../Views/Profile/ProfileSecurity.tsx | 2 +- .../Views/Profile/ProfileSettings.tsx | 6 +-- src/components/_shared/DropArea/DropArea.tsx | 14 +++--- src/components/_shared/Popover/Popover.tsx | 2 +- .../_shared/ShareLinks/ShareLinks.tsx | 15 +----- .../_shared/VideoPlayer/VideoPlayer.tsx | 10 ++-- src/context/following.tsx | 21 ++++---- src/intl/chars.ts | 6 +++ src/intl/translate.ts | 14 +++--- src/intl/translit.ts | 7 ++- src/lib/composeMediaItems.ts | 4 +- src/routes/feed/[...order].tsx | 15 +++--- src/routes/feed/my/[...mode]/[...order].tsx | 15 +++--- src/utils/validate.ts | 4 +- tests/1-page-sections.spec.ts | 1 + tests/2-auth-topic-actions.spec.ts | 1 + tests/3-auth-drafts-actions.spec.ts | 1 + tests/4-auth-author-actions.spec.ts | 1 + 37 files changed, 171 insertions(+), 160 deletions(-) create mode 100644 src/intl/chars.ts diff --git a/biome.json b/biome.json index bb722565..7d8cf99f 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/1.8.2/schema.json", + "$schema": "https://biomejs.dev/schemas/1.9.0/schema.json", "files": { "include": ["*.tsx", "*.ts", "*.js", "*.json"], "ignore": ["./dist", "./node_modules", ".husky", "docs", "gen", "*.gen.ts", "*.d.ts"] @@ -42,7 +42,9 @@ "noExcessiveCognitiveComplexity": "off" }, "correctness": { - "useHookAtTopLevel": "off" + "useHookAtTopLevel": "off", + "useImportExtensions": "off", + "noUndeclaredDependencies": "off" }, "a11y": { "useHeadingContent": "off", @@ -54,7 +56,8 @@ "useAltText": "off", "useButtonType": "off", "noRedundantAlt": "off", - "noSvgWithoutTitle": "off" + "noSvgWithoutTitle": "off", + "noLabelWithoutControl": "off" }, "nursery": { "useImportRestrictions": "off" @@ -63,15 +66,18 @@ "noBarrelFile": "off" }, "style": { + "noNonNullAssertion": "off", "noNamespaceImport": "warn", "useBlockStatements": "off", "noImplicitBoolean": "off", "useNamingConvention": "off", "useImportType": "off", "noDefaultExport": "off", - "useFilenamingConvention": "off" + "useFilenamingConvention": "off", + "useExplicitLengthCheck": "off" }, "suspicious": { + "noConsole": "off", "noConsoleLog": "off", "noAssignInExpressions": "off" } diff --git a/package.json b/package.json index 1bdb6512..3057d9d1 100644 --- a/package.json +++ b/package.json @@ -22,13 +22,13 @@ }, "devDependencies": { "@authorizerdev/authorizer-js": "^2.0.3", - "@biomejs/biome": "^1.8.3", + "@biomejs/biome": "^1.9.1", "@graphql-codegen/cli": "^5.0.2", "@graphql-codegen/typescript": "^4.0.9", "@graphql-codegen/typescript-operations": "^4.2.3", "@graphql-codegen/typescript-urql": "^4.0.0", "@hocuspocus/provider": "^2.13.5", - "@playwright/test": "^1.46.1", + "@playwright/test": "^1.47.1", "@popperjs/core": "^2.11.8", "@solid-primitives/media": "^2.2.9", "@solid-primitives/memo": "^1.3.9", @@ -38,22 +38,22 @@ "@solid-primitives/storage": "^3.8.0", "@solid-primitives/upload": "^0.0.117", "@solidjs/meta": "^0.29.4", - "@solidjs/router": "^0.14.3", + "@solidjs/router": "^0.14.5", "@solidjs/start": "^1.0.6", - "@storybook/addon-a11y": "^8.2.9", - "@storybook/addon-actions": "^8.2.9", - "@storybook/addon-controls": "^8.2.9", - "@storybook/addon-essentials": "^8.2.9", - "@storybook/addon-interactions": "^8.2.9", - "@storybook/addon-links": "^8.2.9", + "@storybook/addon-a11y": "^8.3.0", + "@storybook/addon-actions": "^8.3.0", + "@storybook/addon-controls": "^8.3.0", + "@storybook/addon-essentials": "^8.3.0", + "@storybook/addon-interactions": "^8.3.0", + "@storybook/addon-links": "^8.3.0", "@storybook/addon-styling": "1.3.7", - "@storybook/addon-themes": "^8.2.9", - "@storybook/addon-viewport": "^8.2.9", - "@storybook/blocks": "^8.2.9", + "@storybook/addon-themes": "^8.3.0", + "@storybook/addon-viewport": "^8.3.0", + "@storybook/blocks": "^8.3.0", "@storybook/builder-vite": "8.2.9", "@storybook/docs-tools": "8.2.9", - "@storybook/html": "^8.2.9", - "@storybook/react": "^8.2.9", + "@storybook/html": "^8.3.0", + "@storybook/react": "^8.3.0", "@storybook/test-runner": "^0.19.1", "@storybook/testing-library": "^0.2.2", "@tiptap/core": "^2.6.6", @@ -87,7 +87,7 @@ "@tiptap/extension-youtube": "^2.6.6", "@types/cookie": "^0.6.0", "@types/cookie-signature": "^1.1.2", - "@types/node": "^22.5.2", + "@types/node": "^22.5.5", "@types/throttle-debounce": "^5.0.2", "@urql/core": "^5.0.6", "axe-playwright": "^2.0.2", @@ -96,10 +96,10 @@ "cookie": "^0.6.0", "cookie-signature": "^1.2.1", "cropperjs": "^1.6.2", - "extended-eventsource": "^1.4.9", + "extended-eventsource": "^1.6.4", "fast-deep-equal": "^3.1.3", "graphql": "^16.9.0", - "i18next": "^23.14.0", + "i18next": "^23.15.1", "i18next-http-backend": "^2.6.1", "i18next-icu": "^2.3.0", "intl-messageformat": "^10.5.14", @@ -107,31 +107,31 @@ "patch-package": "^8.0.0", "prosemirror-history": "^1.4.1", "prosemirror-trailing-node": "^2.0.9", - "prosemirror-view": "^1.34.1", + "prosemirror-view": "^1.34.2", "rollup-plugin-visualizer": "^5.12.0", "sass": "1.76.0", "solid-js": "^1.8.22", "solid-popper": "^0.3.0", "solid-tiptap": "0.7.0", "solid-transition-group": "^0.2.3", - "storybook": "^8.2.9", + "storybook": "^8.3.0", "storybook-solidjs": "^1.0.0-beta.2", "storybook-solidjs-vite": "^1.0.0-beta.2", "stylelint": "^16.9.0", "stylelint-config-recommended": "^14.0.1", "stylelint-config-standard-scss": "^13.1.0", "stylelint-order": "^6.0.4", - "stylelint-scss": "^6.5.1", - "swiper": "^11.1.12", + "stylelint-scss": "^6.6.0", + "swiper": "^11.1.14", "throttle-debounce": "^5.0.2", "tslib": "^2.7.0", - "typescript": "^5.5.4", + "typescript": "^5.6.2", "typograf": "^7.4.1", "uniqolor": "^1.1.1", - "vinxi": "^0.4.2", + "vinxi": "^0.4.3", "vite-plugin-mkcert": "^1.17.6", "vite-plugin-node-polyfills": "^0.22.0", - "vite-plugin-sass-dts": "^1.3.25", + "vite-plugin-sass-dts": "^1.3.29", "y-prosemirror": "1.2.12", "yjs": "13.6.18" }, diff --git a/src/components/Article/AudioPlayer/PlayerHeader.tsx b/src/components/Article/AudioPlayer/PlayerHeader.tsx index 3894af1f..6848077f 100644 --- a/src/components/Article/AudioPlayer/PlayerHeader.tsx +++ b/src/components/Article/AudioPlayer/PlayerHeader.tsx @@ -74,7 +74,7 @@ export const PlayerHeader = (props: Props) => { onChange={({ target }) => props.onVolumeChange(Number(target.value))} /> - diff --git a/src/components/Article/CommentRatingControl.tsx b/src/components/Article/CommentRatingControl.tsx index 08c8d555..c857c2ec 100644 --- a/src/components/Article/CommentRatingControl.tsx +++ b/src/components/Article/CommentRatingControl.tsx @@ -84,7 +84,6 @@ export const CommentRatingControl = (props: Props) => { return (
@@ -47,7 +47,7 @@ export const BlockquoteBubbleMenu = (props: Props) => { ref={triggerRef} type="button" class={styles.bubbleMenuButton} - onClick={() => props.editor.chain().focus().setBlockQuoteFloat('right').run()} + onClick={() => props.editor?.chain().focus().setBlockQuoteFloat('right').run()} > diff --git a/src/components/Editor/BubbleMenu/FigureBubbleMenu.tsx b/src/components/Editor/BubbleMenu/FigureBubbleMenu.tsx index 27ce2f28..706c9048 100644 --- a/src/components/Editor/BubbleMenu/FigureBubbleMenu.tsx +++ b/src/components/Editor/BubbleMenu/FigureBubbleMenu.tsx @@ -33,7 +33,7 @@ export const FigureBubbleMenu = (props: Props) => { ref={triggerRef} type="button" class={styles.bubbleMenuButton} - onClick={() => props.editor.chain().focus().setFigureFloat('left').run()} + onClick={() => props.editor?.chain().focus().setFigureFloat('left').run()} > @@ -45,7 +45,7 @@ export const FigureBubbleMenu = (props: Props) => { ref={triggerRef} type="button" class={styles.bubbleMenuButton} - onClick={() => props.editor.chain().focus().setFigureFloat(null).run()} + onClick={() => props.editor?.chain().focus().setFigureFloat(null).run()} > @@ -57,7 +57,7 @@ export const FigureBubbleMenu = (props: Props) => { ref={triggerRef} type="button" class={styles.bubbleMenuButton} - onClick={() => props.editor.chain().focus().setFigureFloat('right').run()} + onClick={() => props.editor?.chain().focus().setFigureFloat('right').run()} > @@ -67,7 +67,7 @@ export const FigureBubbleMenu = (props: Props) => { diff --git a/src/components/Editor/BubbleMenu/IncutBubbleMenu.tsx b/src/components/Editor/BubbleMenu/IncutBubbleMenu.tsx index 01517729..4a80a680 100644 --- a/src/components/Editor/BubbleMenu/IncutBubbleMenu.tsx +++ b/src/components/Editor/BubbleMenu/IncutBubbleMenu.tsx @@ -19,7 +19,7 @@ export const IncutBubbleMenu = (props: Props) => { const { t } = useLocalize() const [substratBubbleOpen, setSubstratBubbleOpen] = createSignal(false) const handleChangeBg = (bg: string | null) => { - props.editor.chain().focus().setArticleBg(bg).run() + props.editor?.chain().focus().setArticleBg(bg).run() setSubstratBubbleOpen(false) } return ( @@ -27,14 +27,14 @@ export const IncutBubbleMenu = (props: Props) => { @@ -42,7 +42,7 @@ export const IncutBubbleMenu = (props: Props) => { diff --git a/src/components/Editor/EditorFloatingMenu/EditorFloatingMenu.tsx b/src/components/Editor/EditorFloatingMenu/EditorFloatingMenu.tsx index 2212fecf..c455a053 100644 --- a/src/components/Editor/EditorFloatingMenu/EditorFloatingMenu.tsx +++ b/src/components/Editor/EditorFloatingMenu/EditorFloatingMenu.tsx @@ -77,16 +77,14 @@ export const EditorFloatingMenu = (props: FloatingMenuProps) => { } createEffect(() => { - switch (selectedMenuItem()) { - case 'image': { - showModal('uploadImage') - return - } - case 'horizontal-rule': { - props.editor?.chain().focus().setHorizontalRule().run() - setSelectedMenuItem() - return - } + if (selectedMenuItem() === 'image') { + showModal('uploadImage') + return + } + if (selectedMenuItem() === 'horizontal-rule') { + props.editor?.chain().focus().setHorizontalRule().run() + setSelectedMenuItem() + return } }) diff --git a/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx b/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx index 633fa63e..7d72ab0a 100644 --- a/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx +++ b/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx @@ -91,9 +91,9 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { const handleAddFootnote = (footnote: string) => { if (footNote()) { - props.editor.chain().focus().updateFootnote({ value: footnote }).run() + props.editor?.chain().focus().updateFootnote({ value: footnote }).run() } else { - props.editor.chain().focus().setFootnote({ value: footnote }).run() + props.editor?.chain().focus().setFootnote({ value: footnote }).run() } setFootNote() setLinkEditorOpen(false) @@ -108,16 +108,16 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { const handleSetPunchline = () => { if (isPunchLine()) { - props.editor.chain().focus().toggleBlockquote('punchline').run() + props.editor?.chain().focus().toggleBlockquote('punchline').run() } - props.editor.chain().focus().toggleBlockquote('quote').run() + props.editor?.chain().focus().toggleBlockquote('quote').run() toggleTextSizePopup() } const handleSetQuote = () => { if (isQuote()) { - props.editor.chain().focus().toggleBlockquote('quote').run() + props.editor?.chain().focus().toggleBlockquote('quote').run() } - props.editor.chain().focus().toggleBlockquote('punchline').run() + props.editor?.chain().focus().toggleBlockquote('punchline').run() toggleTextSizePopup() } @@ -130,13 +130,13 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { }) const handleOpenLinkForm = () => { - props.editor.chain().focus().addTextWrap({ class: 'highlight-fake-selection' }).run() + props.editor?.chain().focus().addTextWrap({ class: 'highlight-fake-selection' }).run() setLinkEditorOpen(true) } const handleCloseLinkForm = () => { setLinkEditorOpen(false) - props.editor.chain().focus().removeTextWrap({ class: 'highlight-fake-selection' }).run() + props.editor?.chain().focus().removeTextWrap({ class: 'highlight-fake-selection' }).run() } return ( @@ -188,7 +188,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { [styles.bubbleMenuButtonActive]: isH1() })} onClick={() => { - props.editor.chain().focus().toggleHeading({ level: 2 }).run() + props.editor?.chain().focus().toggleHeading({ level: 2 }).run() toggleTextSizePopup() }} > @@ -205,7 +205,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { [styles.bubbleMenuButtonActive]: isH2() })} onClick={() => { - props.editor.chain().focus().toggleHeading({ level: 3 }).run() + props.editor?.chain().focus().toggleHeading({ level: 3 }).run() toggleTextSizePopup() }} > @@ -222,7 +222,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { [styles.bubbleMenuButtonActive]: isH3() })} onClick={() => { - props.editor.chain().focus().toggleHeading({ level: 4 }).run() + props.editor?.chain().focus().toggleHeading({ level: 4 }).run() toggleTextSizePopup() }} > @@ -273,7 +273,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { [styles.bubbleMenuButtonActive]: isIncut() })} onClick={() => { - props.editor.chain().focus().toggleArticle().run() + props.editor?.chain().focus().toggleArticle().run() toggleTextSizePopup() }} > @@ -296,7 +296,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { class={clsx(styles.bubbleMenuButton, { [styles.bubbleMenuButtonActive]: isBold() })} - onClick={() => props.editor.chain().focus().toggleBold().run()} + onClick={() => props.editor?.chain().focus().toggleBold().run()} > @@ -310,7 +310,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { class={clsx(styles.bubbleMenuButton, { [styles.bubbleMenuButtonActive]: isItalic() })} - onClick={() => props.editor.chain().focus().toggleItalic().run()} + onClick={() => props.editor?.chain().focus().toggleItalic().run()} > @@ -326,7 +326,9 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { class={clsx(styles.bubbleMenuButton, { [styles.bubbleMenuButtonActive]: isHighlight() })} - onClick={() => props.editor.chain().focus().toggleHighlight({ color: '#f6e3a1' }).run()} + onClick={() => + props.editor?.chain().focus().toggleHighlight({ color: '#f6e3a1' }).run() + } >
@@ -389,7 +391,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { [styles.bubbleMenuButtonActive]: isBulletList() })} onClick={() => { - props.editor.chain().focus().toggleBulletList().run() + props.editor?.chain().focus().toggleBulletList().run() toggleListPopup() }} > @@ -406,7 +408,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { [styles.bubbleMenuButtonActive]: isOrderedList() })} onClick={() => { - props.editor.chain().focus().toggleOrderedList().run() + props.editor?.chain().focus().toggleOrderedList().run() toggleListPopup() }} > diff --git a/src/components/Editor/extensions/Figure.ts b/src/components/Editor/extensions/Figure.ts index 49b972be..2605e4e3 100644 --- a/src/components/Editor/extensions/Figure.ts +++ b/src/components/Editor/extensions/Figure.ts @@ -38,7 +38,7 @@ export const Figure = Node.create({ } const img = node.querySelector('img') const iframe = node.querySelector('iframe') - let dataType = null + let dataType: string | undefined if (img) { dataType = 'image' } else if (iframe) { diff --git a/src/components/Feed/ArticleCard/ArticleCard.tsx b/src/components/Feed/ArticleCard/ArticleCard.tsx index 665a2d50..bb99fec2 100644 --- a/src/components/Feed/ArticleCard/ArticleCard.tsx +++ b/src/components/Feed/ArticleCard/ArticleCard.tsx @@ -7,6 +7,7 @@ import { Popover } from '~/components/_shared/Popover' import { useLocalize } from '~/context/localize' import { useSession } from '~/context/session' import type { Author, Maybe, Shout, Topic } from '~/graphql/schema/core.gen' +import { sentenceSeparator } from '~/intl/chars' import { capitalize } from '~/utils/capitalize' import { descFromBody } from '~/utils/meta' import { CoverImage } from '../../Article/CoverImage' @@ -69,7 +70,7 @@ const getTitleAndSubtitle = ( let titleParts = article.title?.split('. ') || [] if (titleParts?.length === 1) { - titleParts = article.title?.split(/{!|\?|:|;}\s/) || [] + titleParts = article.title?.split(sentenceSeparator) || [] } if (titleParts && titleParts.length > 1) { @@ -88,7 +89,7 @@ const getMainTopicTitle = (article: Shout, lng: string) => { const mainTopicSlug = article.main_topic || '' const mainTopic = (article.topics || []).find((tpc: Maybe) => tpc?.slug === mainTopicSlug) const mainTopicTitle = - mainTopicSlug && lng === 'en' ? mainTopicSlug.replace(/-/, ' ') : mainTopic?.title || '' + mainTopicSlug && lng === 'en' ? mainTopicSlug.replaceAll('-', ' ') : mainTopic?.title || '' return [mainTopicTitle, mainTopicSlug] } diff --git a/src/components/Feed/FeedArticlePopup/FeedArticlePopup.tsx b/src/components/Feed/FeedArticlePopup/FeedArticlePopup.tsx index 1aaf587b..d155780b 100644 --- a/src/components/Feed/FeedArticlePopup/FeedArticlePopup.tsx +++ b/src/components/Feed/FeedArticlePopup/FeedArticlePopup.tsx @@ -33,7 +33,6 @@ export const FeedArticlePopup = (props: Props) => {
  • -
  • -
  • - @@ -97,11 +90,7 @@ export const ShareLinks = (props: Props) => { + diff --git a/src/components/_shared/VideoPlayer/VideoPlayer.tsx b/src/components/_shared/VideoPlayer/VideoPlayer.tsx index eb32837a..fd61670b 100644 --- a/src/components/_shared/VideoPlayer/VideoPlayer.tsx +++ b/src/components/_shared/VideoPlayer/VideoPlayer.tsx @@ -16,7 +16,9 @@ type Props = { onVideoDelete?: () => void articleView?: boolean } - +const watchPattern = /watch=(\w+)/ +const ytPattern = /(youtu.be)\/(\w+)/ +const vimeoPattern = /vimeo.com\/(\d+)/ export const VideoPlayer = (props: Props) => { const { t } = useLocalize() const [videoId, setVideoId] = createSignal() @@ -27,14 +29,14 @@ export const VideoPlayer = (props: Props) => { setIsVimeo(!isYoutube) if (isYoutube) { if (props.videoUrl.includes('youtube.com')) { - const videoIdMatch = props.videoUrl.match(/watch=(\w+)/) + const videoIdMatch = props.videoUrl.match(watchPattern) setVideoId(videoIdMatch?.[1]) } else { - const videoIdMatch = props.videoUrl.match(/youtu.be\/(\w+)/) + const videoIdMatch = props.videoUrl.match(ytPattern) setVideoId(videoIdMatch?.[1]) } } else { - const videoIdMatch = props.videoUrl.match(/vimeo.com\/(\d+)/) + const videoIdMatch = props.videoUrl.match(vimeoPattern) setVideoId(videoIdMatch?.[1]) } }) diff --git a/src/context/following.tsx b/src/context/following.tsx index 69425d1c..4ae3e25c 100644 --- a/src/context/following.tsx +++ b/src/context/following.tsx @@ -151,16 +151,6 @@ export const FollowingProvider = (props: { children: JSX.Element }) => { setFollows((prevFollows: AuthorFollowsResult) => { const updatedFollows = { ...prevFollows } switch (what) { - case 'AUTHOR': { - if (value) { - if (!updatedFollows.authors?.some((author) => author.slug === slug)) { - updatedFollows.authors = [...(updatedFollows.authors || []), { slug } as Author] - } - } else { - updatedFollows.authors = updatedFollows.authors?.filter((author) => author.slug !== slug) || [] - } - break - } case 'TOPIC': { if (value) { if (!updatedFollows.topics?.some((topic) => topic.slug === slug)) { @@ -182,6 +172,17 @@ export const FollowingProvider = (props: { children: JSX.Element }) => { } break } + // case 'AUTHOR': { + default: { + if (value) { + if (!updatedFollows.authors?.some((author) => author.slug === slug)) { + updatedFollows.authors = [...(updatedFollows.authors || []), { slug } as Author] + } + } else { + updatedFollows.authors = updatedFollows.authors?.filter((author) => author.slug !== slug) || [] + } + break + } } return updatedFollows }) diff --git a/src/intl/chars.ts b/src/intl/chars.ts new file mode 100644 index 00000000..37e85f66 --- /dev/null +++ b/src/intl/chars.ts @@ -0,0 +1,6 @@ +export const allChars = /[^\dA-zА-я]/ +export const slugChars = /[^\da-z]/g +export const enChars = /[^A-z]/ +export const ruChars = /[^ËА-яё]/ +export const sentenceSeparator = /{!|\?|:|;}\s/ +export const cyrillicRegex = /[\u0400-\u04FF]/ // Range for Cyrillic characters diff --git a/src/intl/translate.ts b/src/intl/translate.ts index 5501af9d..6440b64e 100644 --- a/src/intl/translate.ts +++ b/src/intl/translate.ts @@ -1,16 +1,18 @@ import { Author } from '~/graphql/schema/core.gen' import { capitalize } from '~/utils/capitalize' +import { allChars, cyrillicRegex, enChars, ruChars } from './chars' import { translit } from './translit' export const isCyrillic = (s: string): boolean => { - const cyrillicRegex = /[\u0400-\u04FF]/ // Range for Cyrillic characters - return cyrillicRegex.test(s) } export const translateAuthor = (author: Author, lng: string) => lng === 'en' && isCyrillic(author?.name || '') - ? capitalize(translit((author?.name || '').replace(/ё/, 'e').replace(/ь/, '')).replace(/-/, ' '), true) + ? capitalize( + translit((author?.name || '').replaceAll('ё', 'e').replaceAll('ь', '')).replaceAll('-', ' '), + true + ) : author.name export const authorLetterReduce = (acc: { [x: string]: Author[] }, author: Author, lng: string) => { @@ -18,7 +20,7 @@ export const authorLetterReduce = (acc: { [x: string]: Author[] }, author: Autho if (!letter && author && author.name) { const name = translateAuthor(author, lng || 'ru') - ?.replace(/[^\dA-zА-я]/, ' ') + ?.replace(allChars, ' ') .trim() || '' const nameParts = name.trim().split(' ') const found = nameParts.filter(Boolean).pop() @@ -26,8 +28,8 @@ export const authorLetterReduce = (acc: { [x: string]: Author[] }, author: Autho letter = found[0].toUpperCase() } } - if (/[^ËА-яё]/.test(letter) && lng === 'ru') letter = '@' - if (/[^A-z]/.test(letter) && lng === 'en') letter = '@' + if (ruChars.test(letter) && lng === 'ru') letter = '@' + if (enChars.test(letter) && lng === 'en') letter = '@' if (!acc[letter]) acc[letter] = [] author.name = translateAuthor(author, lng) diff --git a/src/intl/translit.ts b/src/intl/translit.ts index ef34487b..2f71bf29 100644 --- a/src/intl/translit.ts +++ b/src/intl/translit.ts @@ -1,4 +1,5 @@ import translitConfig from './abc-translit.json' +import { ruChars, slugChars } from './chars' const ru2en: { [key: string]: string } = translitConfig @@ -7,7 +8,7 @@ export const translit = (str: string) => { return '' } - const isCyrillic = /[ЁА-яё]/.test(str) + const isCyrillic = ruChars.test(str) if (!isCyrillic) { return str @@ -17,7 +18,5 @@ export const translit = (str: string) => { } export const slugify = (text: string) => { - return translit(text.toLowerCase()) - .replaceAll(' ', '-') - .replaceAll(/[^\da-z]/g, '') + return translit(text.toLowerCase()).replaceAll(' ', '-').replaceAll(slugChars, '') } diff --git a/src/lib/composeMediaItems.ts b/src/lib/composeMediaItems.ts index cf49c51e..311bf9a5 100644 --- a/src/lib/composeMediaItems.ts +++ b/src/lib/composeMediaItems.ts @@ -1,5 +1,7 @@ +const audioExts = /\.(wav|flac|mp3|aac|jpg|jpeg|png|gif)$/i + const removeMediaFileExtension = (fileName: string) => { - return fileName.replace(/\.(wav|flac|mp3|aac|jpg|jpeg|png|gif)$/i, '') + return fileName.replace(audioExts, '') } export const composeMediaItems = ( diff --git a/src/routes/feed/[...order].tsx b/src/routes/feed/[...order].tsx index 2d72e795..5314880f 100644 --- a/src/routes/feed/[...order].tsx +++ b/src/routes/feed/[...order].tsx @@ -13,6 +13,8 @@ import { loadShouts } from '~/graphql/api/public' import { LoadShoutsOptions, Shout, Topic } from '~/graphql/schema/core.gen' import { SHOUTS_PER_PAGE } from '../(main)' +const paramPattern = /^(hot|likes)$/ + export type FeedPeriod = 'week' | 'month' | 'year' export type PeriodItem = { @@ -32,14 +34,15 @@ const getFromDate = (period: FeedPeriod): number => { d = new Date(now.setDate(now.getDate() - 7)) break } - case 'month': { - d = new Date(now.setMonth(now.getMonth() - 1)) - break - } case 'year': { d = new Date(now.setFullYear(now.getFullYear() - 1)) break } + // case 'month': { + default: { + d = new Date(now.setMonth(now.getMonth() - 1)) + break + } } return Math.floor(d.getTime() / 1000) } @@ -71,7 +74,6 @@ export default (props: RouteSectionProps<{ shouts: Shout[]; topics: Topic[] }>) // load more feed const loadMoreFeed = async (offset?: number) => { // /feed/:order: - select order setting - const paramPattern = /^(hot|likes)$/ const order = (props.params.order && paramPattern.test(props.params.order) ? props.params.order === 'hot' @@ -107,9 +109,8 @@ export default (props: RouteSectionProps<{ shouts: Shout[]; topics: Topic[] }>) }) const order = createMemo(() => { - const paramOrderPattern = /^(hot|likes)$/ return ( - (paramOrderPattern.test(props.params.order) + (paramPattern.test(props.params.order) ? props.params.order === 'hot' ? 'last_comment' : props.params.order diff --git a/src/routes/feed/my/[...mode]/[...order].tsx b/src/routes/feed/my/[...mode]/[...order].tsx index 1eb00be7..63f6c9b6 100644 --- a/src/routes/feed/my/[...mode]/[...order].tsx +++ b/src/routes/feed/my/[...mode]/[...order].tsx @@ -30,6 +30,8 @@ const feeds = { export type FeedPeriod = 'week' | 'month' | 'year' export type FeedSearchParams = { period?: FeedPeriod } +const paramModePattern = /^(followed|discussed|liked|coauthored|unrated)$/ +const paramPattern = /(hot|likes)/ const getFromDate = (period: FeedPeriod): number => { const now = new Date() let d: Date = now @@ -38,14 +40,15 @@ const getFromDate = (period: FeedPeriod): number => { d = new Date(now.setDate(now.getDate() - 7)) break } - case 'month': { - d = new Date(now.setMonth(now.getMonth() - 1)) - break - } case 'year': { d = new Date(now.setFullYear(now.getFullYear() - 1)) break } + // case 'month': + default: { + d = new Date(now.setMonth(now.getMonth() - 1)) + break + } } return Math.floor(d.getTime() / 1000) } @@ -67,14 +70,12 @@ export default (props: RouteSectionProps<{ shouts: Shout[]; topics: Topic[] }>) // /feed/my/:mode: const mode = createMemo(() => { - const paramModePattern = /^(followed|discussed|liked|coauthored|unrated)$/ return props.params.mode && paramModePattern.test(props.params.mode) ? props.params.mode : 'followed' }) const order = createMemo(() => { - const paramOrderPattern = /^(hot|likes)$/ return ( - (paramOrderPattern.test(props.params.order) + (paramPattern.test(props.params.order) ? props.params.order === 'hot' ? 'last_comment' : props.params.order diff --git a/src/utils/validate.ts b/src/utils/validate.ts index 8c4d14f5..8a7ed1fa 100644 --- a/src/utils/validate.ts +++ b/src/utils/validate.ts @@ -1,7 +1,9 @@ +const emailPattern = /^[\w%+.-]+@[\d.a-z-]+\.[a-z]{2,}$/i + export const validateEmail = (email: string) => { if (!email) return false - return /^[\w%+.-]+@[\d.a-z-]+\.[a-z]{2,}$/i.test(email) + return emailPattern.test(email) } export const validateUrl = (value: string) => { diff --git a/tests/1-page-sections.spec.ts b/tests/1-page-sections.spec.ts index 2bcf7cd8..a16f3854 100644 --- a/tests/1-page-sections.spec.ts +++ b/tests/1-page-sections.spec.ts @@ -50,6 +50,7 @@ test.beforeAll(async ({ browser }) => { page = await browser.newPage() test.setTimeout(150000) await page.goto(baseURL) + // biome-ignore lint/performance/useTopLevelRegex: await expect(page).toHaveTitle(/Дискурс/) console.log('Localhost server started successfully!') }) diff --git a/tests/2-auth-topic-actions.spec.ts b/tests/2-auth-topic-actions.spec.ts index c05bce4c..1cff38a1 100644 --- a/tests/2-auth-topic-actions.spec.ts +++ b/tests/2-auth-topic-actions.spec.ts @@ -50,6 +50,7 @@ test.beforeAll(async ({ browser }) => { page = await browser.newPage() test.setTimeout(150000) await page.goto(baseURL) + // biome-ignore lint/performance/useTopLevelRegex: await expect(page).toHaveTitle(/Дискурс/) await page.getByRole('link', { name: 'Войти' }).click() console.log('Localhost server started successfully!') diff --git a/tests/3-auth-drafts-actions.spec.ts b/tests/3-auth-drafts-actions.spec.ts index e61bc5ed..c9b592bc 100644 --- a/tests/3-auth-drafts-actions.spec.ts +++ b/tests/3-auth-drafts-actions.spec.ts @@ -50,6 +50,7 @@ test.beforeAll(async ({ browser }) => { page = await browser.newPage() test.setTimeout(150000) await page.goto(baseURL) + // biome-ignore lint/performance/useTopLevelRegex: await expect(page).toHaveTitle(/Дискурс/) console.log('Localhost server started successfully!') await page.close() diff --git a/tests/4-auth-author-actions.spec.ts b/tests/4-auth-author-actions.spec.ts index 6a428c9c..d47516e0 100644 --- a/tests/4-auth-author-actions.spec.ts +++ b/tests/4-auth-author-actions.spec.ts @@ -54,6 +54,7 @@ test.beforeAll(async ({ browser }) => { page = await context.newPage() test.setTimeout(150000) await page.goto(baseURL) + // biome-ignore lint/performance/useTopLevelRegex: await expect(page).toHaveTitle(/Дискурс/) await page.getByRole('link', { name: 'Войти' }).click() console.log('Localhost server started successfully!')