Merge branch 'dev' of https://github.com/Discours/discoursio-webapp into dev
This commit is contained in:
commit
199f845610
|
@ -22,7 +22,7 @@ export const Userpic = (props: Props) => {
|
||||||
const letters = () => {
|
const letters = () => {
|
||||||
if (!props.name) return
|
if (!props.name) return
|
||||||
const names = props.name ? props.name.split(' ') : []
|
const names = props.name ? props.name.split(' ') : []
|
||||||
return `${names[0][0 ?? names[0][0]]}.${names.length > 1 ? `${names[1][0]}.` : ''}`
|
return `${names[0][0] ? names[0][0] : ''}.${names.length > 1 ? `${names[1][0]}.` : ''}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const avatarSize = createMemo(() => {
|
const avatarSize = createMemo(() => {
|
||||||
|
|
|
@ -22,7 +22,7 @@ export const BlockquoteBubbleMenu = (props: Props) => {
|
||||||
type="button"
|
type="button"
|
||||||
class={styles.bubbleMenuButton}
|
class={styles.bubbleMenuButton}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.editor.chain().focus().setBlockQuoteFloat('left').run()
|
props.editor?.chain().focus().setBlockQuoteFloat('left').run()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Icon name="editor-image-align-left" />
|
<Icon name="editor-image-align-left" />
|
||||||
|
@ -35,7 +35,7 @@ export const BlockquoteBubbleMenu = (props: Props) => {
|
||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
type="button"
|
type="button"
|
||||||
class={styles.bubbleMenuButton}
|
class={styles.bubbleMenuButton}
|
||||||
onClick={() => props.editor.chain().focus().setBlockQuoteFloat(null).run()}
|
onClick={() => props.editor?.chain().focus().setBlockQuoteFloat(null).run()}
|
||||||
>
|
>
|
||||||
<Icon name="editor-image-align-center" />
|
<Icon name="editor-image-align-center" />
|
||||||
</button>
|
</button>
|
||||||
|
@ -47,7 +47,7 @@ export const BlockquoteBubbleMenu = (props: Props) => {
|
||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
type="button"
|
type="button"
|
||||||
class={styles.bubbleMenuButton}
|
class={styles.bubbleMenuButton}
|
||||||
onClick={() => props.editor.chain().focus().setBlockQuoteFloat('right').run()}
|
onClick={() => props.editor?.chain().focus().setBlockQuoteFloat('right').run()}
|
||||||
>
|
>
|
||||||
<Icon name="editor-image-align-right" />
|
<Icon name="editor-image-align-right" />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -33,7 +33,7 @@ export const FigureBubbleMenu = (props: Props) => {
|
||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
type="button"
|
type="button"
|
||||||
class={styles.bubbleMenuButton}
|
class={styles.bubbleMenuButton}
|
||||||
onClick={() => props.editor.chain().focus().setFigureFloat('left').run()}
|
onClick={() => props.editor?.chain().focus().setFigureFloat('left').run()}
|
||||||
>
|
>
|
||||||
<Icon name="editor-image-align-left" />
|
<Icon name="editor-image-align-left" />
|
||||||
</button>
|
</button>
|
||||||
|
@ -45,7 +45,7 @@ export const FigureBubbleMenu = (props: Props) => {
|
||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
type="button"
|
type="button"
|
||||||
class={styles.bubbleMenuButton}
|
class={styles.bubbleMenuButton}
|
||||||
onClick={() => props.editor.chain().focus().setFigureFloat(null).run()}
|
onClick={() => props.editor?.chain().focus().setFigureFloat(null).run()}
|
||||||
>
|
>
|
||||||
<Icon name="editor-image-align-center" />
|
<Icon name="editor-image-align-center" />
|
||||||
</button>
|
</button>
|
||||||
|
@ -57,7 +57,7 @@ export const FigureBubbleMenu = (props: Props) => {
|
||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
type="button"
|
type="button"
|
||||||
class={styles.bubbleMenuButton}
|
class={styles.bubbleMenuButton}
|
||||||
onClick={() => props.editor.chain().focus().setFigureFloat('right').run()}
|
onClick={() => props.editor?.chain().focus().setFigureFloat('right').run()}
|
||||||
>
|
>
|
||||||
<Icon name="editor-image-align-right" />
|
<Icon name="editor-image-align-right" />
|
||||||
</button>
|
</button>
|
||||||
|
@ -67,7 +67,7 @@ export const FigureBubbleMenu = (props: Props) => {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class={styles.bubbleMenuButton}
|
class={styles.bubbleMenuButton}
|
||||||
onClick={() => props.editor.chain().focus().setFigcaptionFocus(true).run()}
|
onClick={() => props.editor?.chain().focus().setFigcaptionFocus(true).run()}
|
||||||
>
|
>
|
||||||
<span style={{ color: 'white' }}>{t('Add signature')}</span>
|
<span style={{ color: 'white' }}>{t('Add signature')}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -19,7 +19,7 @@ export const IncutBubbleMenu = (props: Props) => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const [substratBubbleOpen, setSubstratBubbleOpen] = createSignal(false)
|
const [substratBubbleOpen, setSubstratBubbleOpen] = createSignal(false)
|
||||||
const handleChangeBg = (bg: string | null) => {
|
const handleChangeBg = (bg: string | null) => {
|
||||||
props.editor.chain().focus().setArticleBg(bg).run()
|
props.editor?.chain().focus().setArticleBg(bg).run()
|
||||||
setSubstratBubbleOpen(false)
|
setSubstratBubbleOpen(false)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
@ -27,14 +27,14 @@ export const IncutBubbleMenu = (props: Props) => {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class={styles.bubbleMenuButton}
|
class={styles.bubbleMenuButton}
|
||||||
onClick={() => props.editor.chain().focus().setArticleFloat('half-left').run()}
|
onClick={() => props.editor?.chain().focus().setArticleFloat('half-left').run()}
|
||||||
>
|
>
|
||||||
<Icon name="editor-image-half-align-left" />
|
<Icon name="editor-image-half-align-left" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class={styles.bubbleMenuButton}
|
class={styles.bubbleMenuButton}
|
||||||
onClick={() => props.editor.chain().focus().setArticleFloat(null).run()}
|
onClick={() => props.editor?.chain().focus().setArticleFloat(null).run()}
|
||||||
>
|
>
|
||||||
<Icon name="editor-image-align-center" />
|
<Icon name="editor-image-align-center" />
|
||||||
</button>
|
</button>
|
||||||
|
@ -42,7 +42,7 @@ export const IncutBubbleMenu = (props: Props) => {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class={styles.bubbleMenuButton}
|
class={styles.bubbleMenuButton}
|
||||||
onClick={() => props.editor.chain().focus().setArticleFloat('half-right').run()}
|
onClick={() => props.editor?.chain().focus().setArticleFloat('half-right').run()}
|
||||||
>
|
>
|
||||||
<Icon name="editor-image-half-align-right" />
|
<Icon name="editor-image-half-align-right" />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -91,9 +91,9 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
||||||
|
|
||||||
const handleAddFootnote = (footnote: string) => {
|
const handleAddFootnote = (footnote: string) => {
|
||||||
if (footNote()) {
|
if (footNote()) {
|
||||||
props.editor.chain().focus().updateFootnote({ value: footnote }).run()
|
props.editor?.chain().focus().updateFootnote({ value: footnote }).run()
|
||||||
} else {
|
} else {
|
||||||
props.editor.chain().focus().setFootnote({ value: footnote }).run()
|
props.editor?.chain().focus().setFootnote({ value: footnote }).run()
|
||||||
}
|
}
|
||||||
setFootNote()
|
setFootNote()
|
||||||
setLinkEditorOpen(false)
|
setLinkEditorOpen(false)
|
||||||
|
@ -108,16 +108,16 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
||||||
|
|
||||||
const handleSetPunchline = () => {
|
const handleSetPunchline = () => {
|
||||||
if (isPunchLine()) {
|
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()
|
toggleTextSizePopup()
|
||||||
}
|
}
|
||||||
const handleSetQuote = () => {
|
const handleSetQuote = () => {
|
||||||
if (isQuote()) {
|
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()
|
toggleTextSizePopup()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,13 +130,13 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleOpenLinkForm = () => {
|
const handleOpenLinkForm = () => {
|
||||||
props.editor.chain().focus().addTextWrap({ class: 'highlight-fake-selection' }).run()
|
props.editor?.chain().focus().addTextWrap({ class: 'highlight-fake-selection' }).run()
|
||||||
setLinkEditorOpen(true)
|
setLinkEditorOpen(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCloseLinkForm = () => {
|
const handleCloseLinkForm = () => {
|
||||||
setLinkEditorOpen(false)
|
setLinkEditorOpen(false)
|
||||||
props.editor.chain().focus().removeTextWrap({ class: 'highlight-fake-selection' }).run()
|
props.editor?.chain().focus().removeTextWrap({ class: 'highlight-fake-selection' }).run()
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -188,7 +188,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
||||||
[styles.bubbleMenuButtonActive]: isH1()
|
[styles.bubbleMenuButtonActive]: isH1()
|
||||||
})}
|
})}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.editor.chain().focus().toggleHeading({ level: 2 }).run()
|
props.editor?.chain().focus().toggleHeading({ level: 2 }).run()
|
||||||
toggleTextSizePopup()
|
toggleTextSizePopup()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -205,7 +205,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
||||||
[styles.bubbleMenuButtonActive]: isH2()
|
[styles.bubbleMenuButtonActive]: isH2()
|
||||||
})}
|
})}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.editor.chain().focus().toggleHeading({ level: 3 }).run()
|
props.editor?.chain().focus().toggleHeading({ level: 3 }).run()
|
||||||
toggleTextSizePopup()
|
toggleTextSizePopup()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -222,7 +222,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
||||||
[styles.bubbleMenuButtonActive]: isH3()
|
[styles.bubbleMenuButtonActive]: isH3()
|
||||||
})}
|
})}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.editor.chain().focus().toggleHeading({ level: 4 }).run()
|
props.editor?.chain().focus().toggleHeading({ level: 4 }).run()
|
||||||
toggleTextSizePopup()
|
toggleTextSizePopup()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -273,7 +273,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
||||||
[styles.bubbleMenuButtonActive]: isIncut()
|
[styles.bubbleMenuButtonActive]: isIncut()
|
||||||
})}
|
})}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.editor.chain().focus().toggleArticle().run()
|
props.editor?.chain().focus().toggleArticle().run()
|
||||||
toggleTextSizePopup()
|
toggleTextSizePopup()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -296,7 +296,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
||||||
class={clsx(styles.bubbleMenuButton, {
|
class={clsx(styles.bubbleMenuButton, {
|
||||||
[styles.bubbleMenuButtonActive]: isBold()
|
[styles.bubbleMenuButtonActive]: isBold()
|
||||||
})}
|
})}
|
||||||
onClick={() => props.editor.chain().focus().toggleBold().run()}
|
onClick={() => props.editor?.chain().focus().toggleBold().run()}
|
||||||
>
|
>
|
||||||
<Icon name="editor-bold" />
|
<Icon name="editor-bold" />
|
||||||
</button>
|
</button>
|
||||||
|
@ -310,7 +310,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
||||||
class={clsx(styles.bubbleMenuButton, {
|
class={clsx(styles.bubbleMenuButton, {
|
||||||
[styles.bubbleMenuButtonActive]: isItalic()
|
[styles.bubbleMenuButtonActive]: isItalic()
|
||||||
})}
|
})}
|
||||||
onClick={() => props.editor.chain().focus().toggleItalic().run()}
|
onClick={() => props.editor?.chain().focus().toggleItalic().run()}
|
||||||
>
|
>
|
||||||
<Icon name="editor-italic" />
|
<Icon name="editor-italic" />
|
||||||
</button>
|
</button>
|
||||||
|
@ -326,7 +326,9 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
||||||
class={clsx(styles.bubbleMenuButton, {
|
class={clsx(styles.bubbleMenuButton, {
|
||||||
[styles.bubbleMenuButtonActive]: isHighlight()
|
[styles.bubbleMenuButtonActive]: isHighlight()
|
||||||
})}
|
})}
|
||||||
onClick={() => props.editor.chain().focus().toggleHighlight({ color: '#f6e3a1' }).run()}
|
onClick={() =>
|
||||||
|
props.editor?.chain().focus().toggleHighlight({ color: '#f6e3a1' }).run()
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div class={styles.toggleHighlight} />
|
<div class={styles.toggleHighlight} />
|
||||||
</button>
|
</button>
|
||||||
|
@ -389,7 +391,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
||||||
[styles.bubbleMenuButtonActive]: isBulletList()
|
[styles.bubbleMenuButtonActive]: isBulletList()
|
||||||
})}
|
})}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.editor.chain().focus().toggleBulletList().run()
|
props.editor?.chain().focus().toggleBulletList().run()
|
||||||
toggleListPopup()
|
toggleListPopup()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -406,7 +408,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
||||||
[styles.bubbleMenuButtonActive]: isOrderedList()
|
[styles.bubbleMenuButtonActive]: isOrderedList()
|
||||||
})}
|
})}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.editor.chain().focus().toggleOrderedList().run()
|
props.editor?.chain().focus().toggleOrderedList().run()
|
||||||
toggleListPopup()
|
toggleListPopup()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { Popover } from '~/components/_shared/Popover'
|
||||||
import { useLocalize } from '~/context/localize'
|
import { useLocalize } from '~/context/localize'
|
||||||
import { useSession } from '~/context/session'
|
import { useSession } from '~/context/session'
|
||||||
import type { Author, Maybe, Shout, Topic } from '~/graphql/schema/core.gen'
|
import type { Author, Maybe, Shout, Topic } from '~/graphql/schema/core.gen'
|
||||||
|
import { sentenceSeparator } from '~/intl/chars'
|
||||||
import { capitalize } from '~/utils/capitalize'
|
import { capitalize } from '~/utils/capitalize'
|
||||||
import { descFromBody } from '~/utils/meta'
|
import { descFromBody } from '~/utils/meta'
|
||||||
import { CoverImage } from '../../Article/CoverImage'
|
import { CoverImage } from '../../Article/CoverImage'
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
@mixin search-filter-control {
|
@mixin search-filter-control {
|
||||||
@include font-size(1.4rem);
|
|
||||||
|
|
||||||
height: 4rem;
|
height: 4rem;
|
||||||
padding: 0 2rem;
|
padding: 0 2rem;
|
||||||
background: rgb(64 64 64 / 50%);
|
background: rgb(64 64 64 / 50%);
|
||||||
|
@ -9,6 +7,8 @@
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
|
@include font-size(1.4rem);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #404040;
|
background: #404040;
|
||||||
}
|
}
|
||||||
|
@ -23,8 +23,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.searchInput {
|
.searchInput {
|
||||||
@include font-size(4.8rem);
|
|
||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 0 0.5rem;
|
padding: 0 0 0.5rem;
|
||||||
background: none;
|
background: none;
|
||||||
|
@ -34,6 +32,8 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|
||||||
|
@include font-size(4.8rem);
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
color: rgb(255 255 255 / 32%);
|
color: rgb(255 255 255 / 32%);
|
||||||
}
|
}
|
||||||
|
@ -60,10 +60,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.searchDescription {
|
.searchDescription {
|
||||||
@include font-size(1.6rem);
|
|
||||||
|
|
||||||
margin-bottom: 44px;
|
margin-bottom: 44px;
|
||||||
color: rgb(255 255 255 / 64%);
|
color: rgb(255 255 255 / 64%);
|
||||||
|
|
||||||
|
@include font-size(1.6rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
.topicsList {
|
.topicsList {
|
||||||
|
|
|
@ -72,7 +72,7 @@
|
||||||
|
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
margin: 0.8rem 0;
|
margin: 0.8rem 0;
|
||||||
-webkit-line-clamp: 2;
|
line-clamp: 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,4 +165,4 @@
|
||||||
word-break: keep-all;
|
word-break: keep-all;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,16 +5,15 @@ import { Icon } from '~/components/_shared/Icon'
|
||||||
import { useLocalize } from '~/context/localize'
|
import { useLocalize } from '~/context/localize'
|
||||||
import { useTopics } from '~/context/topics'
|
import { useTopics } from '~/context/topics'
|
||||||
import type { Topic } from '~/graphql/schema/core.gen'
|
import type { Topic } from '~/graphql/schema/core.gen'
|
||||||
|
import { ruChars } from '~/intl/chars'
|
||||||
import { getRandomItemsFromArray } from '~/utils/random'
|
import { getRandomItemsFromArray } from '~/utils/random'
|
||||||
import styles from './TopicsNav.module.scss'
|
import styles from './TopicsNav.module.scss'
|
||||||
|
|
||||||
const russianChars = /[ЁА-яё]/
|
|
||||||
|
|
||||||
export const RandomTopics = () => {
|
export const RandomTopics = () => {
|
||||||
const { sortedTopics } = useTopics()
|
const { sortedTopics } = useTopics()
|
||||||
const { lang, t } = useLocalize()
|
const { lang, t } = useLocalize()
|
||||||
const tag = (topic: Topic) =>
|
const tag = (topic: Topic) =>
|
||||||
russianChars.test(topic.title || '') && lang() !== 'ru' ? topic.slug : topic.title
|
ruChars.test(topic.title || '') && lang() !== 'ru' ? topic.slug : topic.title
|
||||||
const [randomTopics, setRandomTopics] = createSignal<Topic[]>([])
|
const [randomTopics, setRandomTopics] = createSignal<Topic[]>([])
|
||||||
createEffect(
|
createEffect(
|
||||||
on(sortedTopics, (ttt: Topic[]) => {
|
on(sortedTopics, (ttt: Topic[]) => {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { SearchField } from '~/components/_shared/SearchField'
|
||||||
import { useLocalize } from '~/context/localize'
|
import { useLocalize } from '~/context/localize'
|
||||||
import { useTopics } from '~/context/topics'
|
import { useTopics } from '~/context/topics'
|
||||||
import type { Topic } from '~/graphql/schema/core.gen'
|
import type { Topic } from '~/graphql/schema/core.gen'
|
||||||
|
import { enChars, ruChars } from '~/intl/chars'
|
||||||
import { dummyFilter } from '~/intl/dummyFilter'
|
import { dummyFilter } from '~/intl/dummyFilter'
|
||||||
import { scrollHandler } from '~/utils/scroll'
|
import { scrollHandler } from '~/utils/scroll'
|
||||||
import { TopicBadge } from '../../Topic/TopicBadge'
|
import { TopicBadge } from '../../Topic/TopicBadge'
|
||||||
|
@ -21,9 +22,6 @@ export const ABC = {
|
||||||
en: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ#'
|
en: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ#'
|
||||||
}
|
}
|
||||||
|
|
||||||
const russianChars = /[^ËА-яё]/
|
|
||||||
const latinChars = /[^A-z]/
|
|
||||||
|
|
||||||
export const AllTopics = (props: Props) => {
|
export const AllTopics = (props: Props) => {
|
||||||
const { t, lang } = useLocalize()
|
const { t, lang } = useLocalize()
|
||||||
const alphabet = createMemo(() => ABC[lang()])
|
const alphabet = createMemo(() => ABC[lang()])
|
||||||
|
@ -38,8 +36,8 @@ export const AllTopics = (props: Props) => {
|
||||||
return topics().reduce(
|
return topics().reduce(
|
||||||
(acc, topic) => {
|
(acc, topic) => {
|
||||||
let letter = lang() === 'en' ? topic.slug[0].toUpperCase() : (topic?.title?.[0] || '').toUpperCase()
|
let letter = lang() === 'en' ? topic.slug[0].toUpperCase() : (topic?.title?.[0] || '').toUpperCase()
|
||||||
if (russianChars.test(letter) && lang() === 'ru') letter = '#'
|
if (enChars.test(letter) && lang() === 'ru') letter = '#'
|
||||||
if (latinChars.test(letter) && lang() === 'en') letter = '#'
|
if (ruChars.test(letter) && lang() === 'en') letter = '#'
|
||||||
if (!acc[letter]) acc[letter] = []
|
if (!acc[letter]) acc[letter] = []
|
||||||
acc[letter].push(topic)
|
acc[letter].push(topic)
|
||||||
return acc
|
return acc
|
||||||
|
|
|
@ -340,8 +340,16 @@ export const ProfileSettings = () => {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<h4>{t('About')}</h4>
|
<h4>{t('About')}</h4>
|
||||||
<MiniEditor
|
<SimplifiedEditor
|
||||||
content={about() || ''}
|
resetToInitial={clearAbout()}
|
||||||
|
noLimits={true}
|
||||||
|
variant="bordered"
|
||||||
|
onlyBubbleControls={true}
|
||||||
|
smallHeight={true}
|
||||||
|
placeholder={t('About')}
|
||||||
|
label={t('About')}
|
||||||
|
initialContent={form.about || ''}
|
||||||
|
autoFocus={false}
|
||||||
onChange={(value) => updateFormField('about', value)}
|
onChange={(value) => updateFormField('about', value)}
|
||||||
placeholder={t('About')}
|
placeholder={t('About')}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -37,9 +37,8 @@ export const DropArea = (props: Props) => {
|
||||||
const runUpload = async (files: UploadFile[]) => {
|
const runUpload = async (files: UploadFile[]) => {
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
const handler = props.fileType === 'image' ? handleImageUpload : handleFileUpload
|
|
||||||
const tkn = session()?.access_token as string
|
const tkn = session()?.access_token as string
|
||||||
// Since handler returns a promise, we need to await the results
|
const handler = props.fileType === 'image' ? handleImageUpload : handleFileUpload
|
||||||
tkn &&
|
tkn &&
|
||||||
Promise.all(files.map((file) => handler(file, tkn)))
|
Promise.all(files.map((file) => handler(file, tkn)))
|
||||||
.then(props.onUpload)
|
.then(props.onUpload)
|
||||||
|
|
|
@ -172,7 +172,7 @@ export const FollowingProvider = (props: { children: JSX.Element }) => {
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// case 'AUTHOR':
|
// case 'AUTHOR': {
|
||||||
default: {
|
default: {
|
||||||
if (value) {
|
if (value) {
|
||||||
if (!updatedFollows.authors?.some((author) => author.slug === slug)) {
|
if (!updatedFollows.authors?.some((author) => author.slug === slug)) {
|
||||||
|
|
6
src/intl/chars.ts
Normal file
6
src/intl/chars.ts
Normal file
|
@ -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
|
|
@ -1,12 +1,8 @@
|
||||||
import { Author } from '~/graphql/schema/core.gen'
|
import { Author } from '~/graphql/schema/core.gen'
|
||||||
import { capitalize } from '~/utils/capitalize'
|
import { capitalize } from '~/utils/capitalize'
|
||||||
|
import { allChars, cyrillicRegex, enChars, ruChars } from './chars'
|
||||||
import { translit } from './translit'
|
import { translit } from './translit'
|
||||||
|
|
||||||
const cyrillicRegex = /[\u0400-\u04FF]/ // Range for Cyrillic characters
|
|
||||||
const allChars = /[^\dA-zА-я]/
|
|
||||||
const rusChars = /[^ËА-яё]/
|
|
||||||
const enChars = /[^A-z]/
|
|
||||||
|
|
||||||
export const isCyrillic = (s: string): boolean => {
|
export const isCyrillic = (s: string): boolean => {
|
||||||
return cyrillicRegex.test(s)
|
return cyrillicRegex.test(s)
|
||||||
}
|
}
|
||||||
|
@ -32,7 +28,7 @@ export const authorLetterReduce = (acc: { [x: string]: Author[] }, author: Autho
|
||||||
letter = found[0].toUpperCase()
|
letter = found[0].toUpperCase()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rusChars.test(letter) && lng === 'ru') letter = '@'
|
if (ruChars.test(letter) && lng === 'ru') letter = '@'
|
||||||
if (enChars.test(letter) && lng === 'en') letter = '@'
|
if (enChars.test(letter) && lng === 'en') letter = '@'
|
||||||
|
|
||||||
if (!acc[letter]) acc[letter] = []
|
if (!acc[letter]) acc[letter] = []
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import translitConfig from './abc-translit.json'
|
import translitConfig from './abc-translit.json'
|
||||||
|
import { ruChars, slugChars } from './chars'
|
||||||
|
|
||||||
const ru2en: { [key: string]: string } = translitConfig
|
const ru2en: { [key: string]: string } = translitConfig
|
||||||
const rusChars = /[ЁА-яё]/
|
const rusChars = /[ЁА-яё]/
|
||||||
|
@ -7,7 +8,7 @@ export const translit = (str: string) => {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const isCyrillic = rusChars.test(str)
|
const isCyrillic = ruChars.test(str)
|
||||||
|
|
||||||
if (!isCyrillic) {
|
if (!isCyrillic) {
|
||||||
return str
|
return str
|
||||||
|
@ -17,7 +18,5 @@ export const translit = (str: string) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const slugify = (text: string) => {
|
export const slugify = (text: string) => {
|
||||||
return translit(text.toLowerCase())
|
return translit(text.toLowerCase()).replaceAll(' ', '-').replaceAll(slugChars, '')
|
||||||
.replaceAll(' ', '-')
|
|
||||||
.replaceAll(/[^\da-z]/g, '')
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ export default (props: RouteSectionProps<{ shouts: Shout[]; topics: Topic[] }>)
|
||||||
|
|
||||||
const order = createMemo(() => {
|
const order = createMemo(() => {
|
||||||
return (
|
return (
|
||||||
(paramOrderPattern.test(props.params.order)
|
(paramPattern.test(props.params.order)
|
||||||
? props.params.order === 'hot'
|
? props.params.order === 'hot'
|
||||||
? 'last_comment'
|
? 'last_comment'
|
||||||
: props.params.order
|
: props.params.order
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { type Page, expect, test } from '@playwright/test'
|
||||||
/* Global starting test config */
|
/* Global starting test config */
|
||||||
|
|
||||||
let page: Page
|
let page: Page
|
||||||
const discoursPattern = /Дискурс/
|
|
||||||
function httpsGet(url: string): Promise<void> {
|
function httpsGet(url: string): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
https
|
https
|
||||||
|
@ -50,7 +50,8 @@ test.beforeAll(async ({ browser }) => {
|
||||||
page = await browser.newPage()
|
page = await browser.newPage()
|
||||||
test.setTimeout(150000)
|
test.setTimeout(150000)
|
||||||
await page.goto(baseURL)
|
await page.goto(baseURL)
|
||||||
await expect(page).toHaveTitle(discoursPattern)
|
// biome-ignore lint/performance/useTopLevelRegex: <explanation>
|
||||||
|
await expect(page).toHaveTitle(/Дискурс/)
|
||||||
console.log('Localhost server started successfully!')
|
console.log('Localhost server started successfully!')
|
||||||
})
|
})
|
||||||
test.afterAll(async () => {
|
test.afterAll(async () => {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { type Page, expect, test } from '@playwright/test'
|
||||||
/* Global starting test config */
|
/* Global starting test config */
|
||||||
|
|
||||||
let page: Page
|
let page: Page
|
||||||
const discoursPattern = /Дискурс/
|
|
||||||
function httpsGet(url: string): Promise<void> {
|
function httpsGet(url: string): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
https
|
https
|
||||||
|
@ -50,7 +49,8 @@ test.beforeAll(async ({ browser }) => {
|
||||||
page = await browser.newPage()
|
page = await browser.newPage()
|
||||||
test.setTimeout(150000)
|
test.setTimeout(150000)
|
||||||
await page.goto(baseURL)
|
await page.goto(baseURL)
|
||||||
await expect(page).toHaveTitle(discoursPattern)
|
// biome-ignore lint/performance/useTopLevelRegex: <explanation>
|
||||||
|
await expect(page).toHaveTitle(/Дискурс/)
|
||||||
await page.getByRole('link', { name: 'Войти' }).click()
|
await page.getByRole('link', { name: 'Войти' }).click()
|
||||||
console.log('Localhost server started successfully!')
|
console.log('Localhost server started successfully!')
|
||||||
await page.close()
|
await page.close()
|
||||||
|
|
|
@ -6,8 +6,6 @@ import { type Page, expect, test } from '@playwright/test'
|
||||||
|
|
||||||
let page: Page
|
let page: Page
|
||||||
|
|
||||||
const discoursPattern = /Дискурс/
|
|
||||||
|
|
||||||
function httpsGet(url: string): Promise<void> {
|
function httpsGet(url: string): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
https
|
https
|
||||||
|
@ -52,7 +50,8 @@ test.beforeAll(async ({ browser }) => {
|
||||||
page = await browser.newPage()
|
page = await browser.newPage()
|
||||||
test.setTimeout(150000)
|
test.setTimeout(150000)
|
||||||
await page.goto(baseURL)
|
await page.goto(baseURL)
|
||||||
await expect(page).toHaveTitle(discoursPattern)
|
// biome-ignore lint/performance/useTopLevelRegex: <explanation>
|
||||||
|
await expect(page).toHaveTitle(/Дискурс/)
|
||||||
console.log('Localhost server started successfully!')
|
console.log('Localhost server started successfully!')
|
||||||
await page.close()
|
await page.close()
|
||||||
})
|
})
|
||||||
|
|
|
@ -8,8 +8,6 @@ let page: Page
|
||||||
|
|
||||||
/* Global starting test config */
|
/* Global starting test config */
|
||||||
|
|
||||||
const discoursPattern = /Дискурс/
|
|
||||||
|
|
||||||
function httpsGet(url: string): Promise<void> {
|
function httpsGet(url: string): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
https
|
https
|
||||||
|
@ -56,7 +54,8 @@ test.beforeAll(async ({ browser }) => {
|
||||||
page = await context.newPage()
|
page = await context.newPage()
|
||||||
test.setTimeout(150000)
|
test.setTimeout(150000)
|
||||||
await page.goto(baseURL)
|
await page.goto(baseURL)
|
||||||
await expect(page).toHaveTitle(discoursPattern)
|
// biome-ignore lint/performance/useTopLevelRegex: <explanation>
|
||||||
|
await expect(page).toHaveTitle(/Дискурс/)
|
||||||
await page.getByRole('link', { name: 'Войти' }).click()
|
await page.getByRole('link', { name: 'Войти' }).click()
|
||||||
console.log('Localhost server started successfully!')
|
console.log('Localhost server started successfully!')
|
||||||
await page.close()
|
await page.close()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user