From 52d8a01a50d2ec4ca00ba813f36c6dbb72256d4b Mon Sep 17 00:00:00 2001 From: Arkadzi Rakouski Date: Fri, 18 Aug 2023 19:37:14 +0300 Subject: [PATCH 01/42] add floating panel for profile settings (#186) * add floating panel for profile settings * resolve conversation * don't show beforeunload message after save profile form --------- Co-authored-by: ilya-bkv --- .../EditorFloatingMenu/EditorFloatingMenu.tsx | 3 +- src/components/Editor/SimplifiedEditor.tsx | 1 - .../TableOfContents/TableOfContents.tsx | 2 +- src/components/Views/Edit.tsx | 2 +- src/components/_shared/Button/Button.tsx | 2 +- .../FloatingPanel/FloatingPanel.module.scss | 32 +++++++++++++++++ .../_shared/FloatingPanel/FloatingPanel.tsx | 35 +++++++++++++++++++ src/pages/profile/profileSettings.page.tsx | 32 +++++++++++++---- 8 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 src/components/_shared/FloatingPanel/FloatingPanel.module.scss create mode 100644 src/components/_shared/FloatingPanel/FloatingPanel.tsx diff --git a/src/components/Editor/EditorFloatingMenu/EditorFloatingMenu.tsx b/src/components/Editor/EditorFloatingMenu/EditorFloatingMenu.tsx index c8150f04..cae2606c 100644 --- a/src/components/Editor/EditorFloatingMenu/EditorFloatingMenu.tsx +++ b/src/components/Editor/EditorFloatingMenu/EditorFloatingMenu.tsx @@ -8,10 +8,9 @@ import { useLocalize } from '../../../context/localize' import { Modal } from '../../Nav/Modal' import { Menu } from './Menu' import type { MenuItem } from './Menu/Menu' -import { hideModal, showModal } from '../../../stores/ui' +import { showModal } from '../../../stores/ui' import { UploadModalContent } from '../UploadModalContent' import { useOutsideClickHandler } from '../../../utils/useOutsideClickHandler' -import { imageProxy } from '../../../utils/imageProxy' import { UploadedFile } from '../../../pages/types' import { renderUploadedImage } from '../../../utils/renderUploadedImage' diff --git a/src/components/Editor/SimplifiedEditor.tsx b/src/components/Editor/SimplifiedEditor.tsx index afed92d2..30de5cc4 100644 --- a/src/components/Editor/SimplifiedEditor.tsx +++ b/src/components/Editor/SimplifiedEditor.tsx @@ -30,7 +30,6 @@ import { UploadedFile } from '../../pages/types' import { Figure } from './extensions/Figure' import { Image } from '@tiptap/extension-image' import { Figcaption } from './extensions/Figcaption' -import { useOutsideClickHandler } from '../../utils/useOutsideClickHandler' type Props = { initialContent?: string diff --git a/src/components/TableOfContents/TableOfContents.tsx b/src/components/TableOfContents/TableOfContents.tsx index 70af55f8..c5d5a5f8 100644 --- a/src/components/TableOfContents/TableOfContents.tsx +++ b/src/components/TableOfContents/TableOfContents.tsx @@ -1,4 +1,4 @@ -import { For, Show, createSignal, createEffect, on, onMount } from 'solid-js' +import { For, Show, createSignal, createEffect, on } from 'solid-js' import { clsx } from 'clsx' import { DEFAULT_HEADER_OFFSET } from '../../stores/router' diff --git a/src/components/Views/Edit.tsx b/src/components/Views/Edit.tsx index a19ba586..4658f545 100644 --- a/src/components/Views/Edit.tsx +++ b/src/components/Views/Edit.tsx @@ -15,7 +15,7 @@ import { AudioUploader } from '../Editor/AudioUploader' import { slugify } from '../../utils/slugify' import { SolidSwiper } from '../_shared/SolidSwiper' import { DropArea } from '../_shared/DropArea' -import { LayoutType, MediaItem, UploadedFile } from '../../pages/types' +import { LayoutType, MediaItem } from '../../pages/types' import { clone } from '../../utils/clone' import deepEqual from 'fast-deep-equal' import { AutoSaveNotice } from '../Editor/AutoSaveNotice' diff --git a/src/components/_shared/Button/Button.tsx b/src/components/_shared/Button/Button.tsx index ac8f744e..42d7d5ec 100644 --- a/src/components/_shared/Button/Button.tsx +++ b/src/components/_shared/Button/Button.tsx @@ -9,7 +9,7 @@ type Props = { type?: 'submit' | 'button' loading?: boolean disabled?: boolean - onClick?: () => void + onClick?: (event?: MouseEvent) => void class?: string ref?: HTMLButtonElement | ((el: HTMLButtonElement) => void) } diff --git a/src/components/_shared/FloatingPanel/FloatingPanel.module.scss b/src/components/_shared/FloatingPanel/FloatingPanel.module.scss new file mode 100644 index 00000000..b21e0326 --- /dev/null +++ b/src/components/_shared/FloatingPanel/FloatingPanel.module.scss @@ -0,0 +1,32 @@ +.PanelWrapper { + position: fixed; + bottom: 20px; + left: 0; + right: 0; + + display: none; + align-items: center; + justify-content: space-between; + + max-width: 430px; + width: auto; + height: auto; + + margin: 0 auto; + padding: 14px; + + background-color: var(--background-color); + border: 2px solid black; + + @include media-breakpoint-down(sm) { + flex-wrap: wrap; + + input { + min-width: 250px; + } + } +} + +.PanelWrapperVisible { + display: flex; +} diff --git a/src/components/_shared/FloatingPanel/FloatingPanel.tsx b/src/components/_shared/FloatingPanel/FloatingPanel.tsx new file mode 100644 index 00000000..3bc7de66 --- /dev/null +++ b/src/components/_shared/FloatingPanel/FloatingPanel.tsx @@ -0,0 +1,35 @@ +import { clsx } from 'clsx' + +import { Button } from '../Button' + +import styles from './FloatingPanel.module.scss' + +type Props = { + isVisible: boolean + confirmTitle: string + confirmAction: () => void + declineTitle: string + declineAction: () => void +} + +export default (props: Props) => { + return ( +
+
+ ) +} diff --git a/src/pages/profile/profileSettings.page.tsx b/src/pages/profile/profileSettings.page.tsx index 6b086435..838d50d6 100644 --- a/src/pages/profile/profileSettings.page.tsx +++ b/src/pages/profile/profileSettings.page.tsx @@ -9,7 +9,7 @@ import { useProfileForm } from '../../context/profile' import { validateUrl } from '../../utils/validateUrl' import { createFileUploader } from '@solid-primitives/upload' import { useSession } from '../../context/session' -import { Button } from '../../components/_shared/Button' +import FloatingPanel from '../../components/_shared/FloatingPanel/FloatingPanel' import { useSnackbar } from '../../context/snackbar' import { useLocalize } from '../../context/localize' import { handleFileUpload } from '../../utils/handleFileUpload' @@ -21,8 +21,8 @@ export const ProfileSettingsPage = () => { const { t } = useLocalize() const [addLinkForm, setAddLinkForm] = createSignal(false) const [incorrectUrl, setIncorrectUrl] = createSignal(false) - const [isSubmitting, setIsSubmitting] = createSignal(false) const [isUserpicUpdating, setIsUserpicUpdating] = createSignal(false) + const [isFloatingPanelVisible, setIsFloatingPanelVisible] = createSignal(false) const { actions: { showSnackbar } @@ -31,6 +31,7 @@ export const ProfileSettingsPage = () => { const { actions: { loadSession } } = useSession() + const { form, updateFormField, submit, slugError } = useProfileForm() const [prevForm, setPrevForm] = createStore(clone(form)) @@ -45,8 +46,6 @@ export const ProfileSettingsPage = () => { const handleSubmit = async (event: Event) => { event.preventDefault() - setIsSubmitting(true) - try { await submit(form) setPrevForm(clone(form)) @@ -56,7 +55,6 @@ export const ProfileSettingsPage = () => { } loadSession() - setIsSubmitting(false) } const { selectFiles } = createFileUploader({ multiple: false, accept: 'image/*' }) @@ -68,6 +66,7 @@ export const ProfileSettingsPage = () => { const result = await handleFileUpload(uploadFile) updateFormField('userpic', result.url) setIsUserpicUpdating(false) + setIsFloatingPanelVisible(true) } catch (error) { console.error('[upload avatar] error', error) } @@ -92,6 +91,11 @@ export const ProfileSettingsPage = () => { onCleanup(() => window.removeEventListener('beforeunload', handleBeforeUnload)) }) + const handleSaveProfile = () => { + setIsFloatingPanelVisible(false) + setPrevForm(clone(form)) + } + return ( @@ -107,7 +111,15 @@ export const ProfileSettingsPage = () => {

{t('Profile settings')}

{t('Here you can customize your profile the way you want.')}

-
+ { + if (!deepEqual(form, prevForm)) { + setIsFloatingPanelVisible(true) + } + }} + enctype="multipart/form-data" + >

{t('Userpic')}

{

-
From a6cae1abb7b7d8e4af715e2e5f4115d78fc5d75f Mon Sep 17 00:00:00 2001 From: Arkadzi Rakouski Date: Mon, 21 Aug 2023 14:11:18 +0300 Subject: [PATCH 02/42] fixes for register form (#188) --- .../Nav/AuthModal/ForgotPasswordForm.tsx | 54 ++++++++++--------- src/components/Nav/AuthModal/RegisterForm.tsx | 28 ++++++---- 2 files changed, 48 insertions(+), 34 deletions(-) diff --git a/src/components/Nav/AuthModal/ForgotPasswordForm.tsx b/src/components/Nav/AuthModal/ForgotPasswordForm.tsx index c18e2092..19278d13 100644 --- a/src/components/Nav/AuthModal/ForgotPasswordForm.tsx +++ b/src/components/Nav/AuthModal/ForgotPasswordForm.tsx @@ -80,31 +80,7 @@ export const ForgotPasswordForm = () => {

{t('Forgot password?')}

{t('Everything is ok, please give us your email address')}
- -
-
    -
  • {submitError()}
  • -
-
-
- -
- {/*TODO: text*/} - {t("We can't find you, check email or")}{' '} - { - event.preventDefault() - changeSearchParam('mode', 'register') - }} - > - {t('register')} - -
-
- -
{validationErrors().email}
-
+
{
+ +
+
    +
  • {submitError()}
  • +
+
+
+ + +
+ {/*TODO: text*/} + {t("We can't find you, check email or")}{' '} + { + event.preventDefault() + changeSearchParam('mode', 'register') + }} + > + {t('register')} + +
+
+ + +
{validationErrors().email}
+
+
From f0bb04b33d7f0c17fafdf640726271b18f94cdca Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Mon, 21 Aug 2023 14:15:32 +0300 Subject: [PATCH 03/42] Update tooltip (#187) * Show article tooltip onClick * remove unused imports * Resolve conversations --- src/components/Article/Article.module.scss | 23 +++++++-- src/components/Article/FullArticle.tsx | 59 +++++++++++++++++++--- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/src/components/Article/Article.module.scss b/src/components/Article/Article.module.scss index efe5996f..8b5a1405 100644 --- a/src/components/Article/Article.module.scss +++ b/src/components/Article/Article.module.scss @@ -554,9 +554,26 @@ a[data-toggle='tooltip'] { } .tooltip { + @include font-size(1.4rem); + + position: relative; padding: 8px; - background: #141414; - font-size: 12px; - color: white; + border-radius: 4px; max-width: 400px; + box-sizing: border-box; + background: var(--black-500); + color: var(--default-color-invert); + + &::after { + content: ''; + position: absolute; + left: 50%; + bottom: -4px; + transform: translateX(-50%); + width: 0; + height: 0; + border-style: solid; + border-width: 4px 4px 0 4px; + border-color: var(--black-500) transparent transparent transparent; + } } diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx index 2d9e8499..e972bc5d 100644 --- a/src/components/Article/FullArticle.tsx +++ b/src/components/Article/FullArticle.tsx @@ -1,4 +1,4 @@ -import { createEffect, For, createMemo, onMount, Show, createSignal } from 'solid-js' +import { createEffect, For, createMemo, onMount, Show, createSignal, onCleanup } from 'solid-js' import { Title } from '@solidjs/meta' import { clsx } from 'clsx' import { getPagePath } from '@nanostores/router' @@ -128,23 +128,66 @@ export const FullArticle = (props: Props) => { setIsReactionsLoaded(true) }) + const clickHandlers = [] + const documentClickHandlers = [] + onMount(() => { const tooltipElements: NodeListOf = document.querySelectorAll('[data-toggle="tooltip"]') if (!tooltipElements) return + tooltipElements.forEach((element) => { const tooltip = document.createElement('div') tooltip.classList.add(styles.tooltip) tooltip.textContent = element.dataset.originalTitle document.body.appendChild(tooltip) - createPopper(element, tooltip, { placement: 'top' }) + element.setAttribute('href', 'javascript: void(0);') + createPopper(element, tooltip, { + placement: 'top', + modifiers: [ + { + name: 'offset', + options: { + offset: [0, 8] + } + } + ] + }) + tooltip.style.visibility = 'hidden' - element.addEventListener('mouseenter', () => { - tooltip.style.visibility = 'visible' - }) - element.addEventListener('mouseleave', () => { - tooltip.style.visibility = 'hidden' - }) + let isTooltipVisible = false + + const handleClick = () => { + if (isTooltipVisible) { + tooltip.style.visibility = 'hidden' + isTooltipVisible = false + } else { + tooltip.style.visibility = 'visible' + isTooltipVisible = true + } + } + + const handleDocumentClick = (e) => { + if (isTooltipVisible && e.target !== element && e.target !== tooltip) { + tooltip.style.visibility = 'hidden' + isTooltipVisible = false + } + } + + element.addEventListener('click', handleClick) + document.addEventListener('click', handleDocumentClick) + + clickHandlers.push({ element, handler: handleClick }) + documentClickHandlers.push(handleDocumentClick) + }) + }) + + onCleanup(() => { + clickHandlers.forEach(({ element, handler }) => { + element.removeEventListener('click', handler) + }) + documentClickHandlers.forEach((handler) => { + document.removeEventListener('click', handler) }) }) From 328bd89d8dc96c81da0a50cc607995ca64e324df Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Tue, 22 Aug 2023 16:37:54 +0300 Subject: [PATCH 04/42] Article Lead and Description with simple editor (#189) * Article Lead and Description --- src/components/Article/Article.module.scss | 11 ++ src/components/Article/FullArticle.tsx | 3 + src/components/Editor/Editor.tsx | 2 +- .../Editor/SimplifiedEditor.module.scss | 45 +++++ src/components/Editor/SimplifiedEditor.tsx | 182 ++++++++++++------ src/components/Feed/ArticleCard.module.scss | 6 +- src/components/Feed/ArticleCard.tsx | 8 +- src/components/Views/Edit.tsx | 23 +-- .../Views/PublishSettings/PublishSettings.tsx | 27 +-- .../GrowingTextarea.module.scss | 2 +- src/context/editor.tsx | 5 +- src/graphql/mutation/article-update.ts | 2 + src/graphql/query/article-load.ts | 2 + src/graphql/query/articles-load-by.ts | 2 + src/graphql/types.gen.ts | 3 + 15 files changed, 221 insertions(+), 102 deletions(-) diff --git a/src/components/Article/Article.module.scss b/src/components/Article/Article.module.scss index 8b5a1405..650ede1a 100644 --- a/src/components/Article/Article.module.scss +++ b/src/components/Article/Article.module.scss @@ -577,3 +577,14 @@ a[data-toggle='tooltip'] { border-color: var(--black-500) transparent transparent transparent; } } + +.lead { + @include font-size(1.8rem); + + font-weight: 600; + + b, + strong { + font-weight: 700; + } +} diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx index e972bc5d..f8812e8d 100644 --- a/src/components/Article/FullArticle.tsx +++ b/src/components/Article/FullArticle.tsx @@ -233,6 +233,9 @@ export const FullArticle = (props: Props) => {
+ +
+ { Image, Figcaption, Embed, - CharacterCount, + CharacterCount.configure(), // https://github.com/ueberdosis/tiptap/issues/2589#issuecomment-1093084689 BubbleMenu.configure({ pluginKey: 'textBubbleMenu', element: textBubbleMenuRef.current, diff --git a/src/components/Editor/SimplifiedEditor.module.scss b/src/components/Editor/SimplifiedEditor.module.scss index 7f15041b..7f774dc9 100644 --- a/src/components/Editor/SimplifiedEditor.module.scss +++ b/src/components/Editor/SimplifiedEditor.module.scss @@ -5,6 +5,7 @@ background: var(--black-50); border-radius: 16px; padding: 16px 16px 8px; + position: relative; .simplifiedEditorField { @include font-size(1.4rem); @@ -92,4 +93,48 @@ bottom: 0; } } + + &.minimal { + background: unset; + padding: 0; + + & div[contenteditable] { + font-size: 1.6rem; + font-weight: 500; + } + } + + &.bordered { + box-sizing: border-box; + padding: 16px 12px 6px 12px; + border-radius: 2px; + border: 2px solid var(--black-100); + background: var(--white-500); + + & div[contenteditable] { + font-size: 1.6rem; + font-weight: 500; + } + } + + &.labelVisible { + padding-top: 22px; + } + + .limit { + position: absolute; + right: 1rem; + bottom: 0.5rem; + font-weight: 500; + font-size: 1.2rem; + } + + .label { + @include font-size(1.2rem); + + position: absolute; + top: 6px; + left: 12px; + color: var(--black-400); + } } diff --git a/src/components/Editor/SimplifiedEditor.tsx b/src/components/Editor/SimplifiedEditor.tsx index 30de5cc4..ec6b0ac9 100644 --- a/src/components/Editor/SimplifiedEditor.tsx +++ b/src/components/Editor/SimplifiedEditor.tsx @@ -1,4 +1,4 @@ -import { createEffect, onCleanup, onMount, Show } from 'solid-js' +import { createEffect, createSignal, onCleanup, onMount, Show } from 'solid-js' import { createEditorTransaction, createTiptapEditor, @@ -30,12 +30,19 @@ import { UploadedFile } from '../../pages/types' import { Figure } from './extensions/Figure' import { Image } from '@tiptap/extension-image' import { Figcaption } from './extensions/Figcaption' +import { TextBubbleMenu } from './TextBubbleMenu' +import { BubbleMenu } from '@tiptap/extension-bubble-menu' +import { CharacterCount } from '@tiptap/extension-character-count' +import { createStore } from 'solid-js/store' type Props = { initialContent?: string + label?: string onSubmit?: (text: string) => void onChange?: (text: string) => void placeholder: string + variant?: 'minimal' | 'bordered' + maxLength?: number submitButtonText?: string quoteEnabled?: boolean imageEnabled?: boolean @@ -43,10 +50,13 @@ type Props = { smallHeight?: boolean submitByEnter?: boolean submitByShiftEnter?: boolean + onlyBubbleControls?: boolean } +export const MAX_DESCRIPTION_LIMIT = 400 const SimplifiedEditor = (props: Props) => { const { t } = useLocalize() + const [counter, setCounter] = createSignal() const wrapperEditorElRef: { current: HTMLElement @@ -60,6 +70,12 @@ const SimplifiedEditor = (props: Props) => { current: null } + const textBubbleMenuRef: { + current: HTMLDivElement + } = { + current: null + } + const { actions: { setEditor } } = useEditorContext() @@ -69,6 +85,7 @@ const SimplifiedEditor = (props: Props) => { content: 'figcaption image' }) + const content = props.initialContent const editor = createTiptapEditor(() => ({ element: editorElRef.current, editorProps: { @@ -85,11 +102,25 @@ const SimplifiedEditor = (props: Props) => { Link.configure({ openOnClick: false }), + + CharacterCount.configure({ + limit: MAX_DESCRIPTION_LIMIT + }), Blockquote.configure({ HTMLAttributes: { class: styles.blockQuote } }), + BubbleMenu.configure({ + pluginKey: 'textBubbleMenu', + element: textBubbleMenuRef.current, + shouldShow: ({ view, state }) => { + if (!props.onlyBubbleControls) return + const { selection } = state + const { empty } = selection + return view.hasFocus() && !empty + } + }), ImageFigure, Image, Figcaption, @@ -98,7 +129,7 @@ const SimplifiedEditor = (props: Props) => { placeholder: props.placeholder }) ], - content: props.initialContent ?? null + content: content ?? null })) setEditor(editor) @@ -193,94 +224,110 @@ const SimplifiedEditor = (props: Props) => { const handleInsertLink = () => !editor().state.selection.empty && showModal('editorInsertLink') + createEffect(() => { + if (html()) { + setCounter(editor().storage.characterCount.characters()) + } + }) return (
(wrapperEditorElRef.current = el)} class={clsx(styles.SimplifiedEditor, { [styles.smallHeight]: props.smallHeight, - [styles.isFocused]: isFocused() || !isEmpty() + [styles.minimal]: props.variant === 'minimal', + [styles.bordered]: props.variant === 'bordered', + [styles.isFocused]: isFocused() || !isEmpty(), + [styles.labelVisible]: props.label && counter() > 0 })} > + +
{MAX_DESCRIPTION_LIMIT - counter()}
+
+ 0}> +
{props.label}
+
(editorElRef.current = el)} /> -
-
- - {(triggerRef: (el) => void) => ( - - )} - - - {(triggerRef: (el) => void) => ( - - )} - - - {(triggerRef: (el) => void) => ( - - )} - - - + +
+
+ {(triggerRef: (el) => void) => ( )} - - - + {(triggerRef: (el) => void) => ( )} + + {(triggerRef: (el) => void) => ( + + )} + + + + {(triggerRef: (el) => void) => ( + + )} + + + + + {(triggerRef: (el) => void) => ( + + )} + + +
+ +
+
- -
-
-
-
+ hideModal()} /> @@ -293,6 +340,13 @@ const SimplifiedEditor = (props: Props) => { /> + + (textBubbleMenuRef.current = el)} + /> +
) } diff --git a/src/components/Feed/ArticleCard.module.scss b/src/components/Feed/ArticleCard.module.scss index b61190bf..20343da5 100644 --- a/src/components/Feed/ArticleCard.module.scss +++ b/src/components/Feed/ArticleCard.module.scss @@ -180,12 +180,10 @@ } } -.shoutCardLead { +.shoutCardDescription { @include font-size(1.6rem); - color: var(--secondary-color); - font-weight: 400; - line-height: 1.3; + color: var(--default-color); margin-bottom: 1.4rem; } diff --git a/src/components/Feed/ArticleCard.tsx b/src/components/Feed/ArticleCard.tsx index 9c32b6c6..732bfb19 100644 --- a/src/components/Feed/ArticleCard.tsx +++ b/src/components/Feed/ArticleCard.tsx @@ -164,10 +164,6 @@ export const ArticleCard = (props: ArticleCardProps) => {
- - -
{props.article.lead}
-
@@ -196,7 +192,9 @@ export const ArticleCard = (props: ArticleCardProps) => {
- + +
+
diff --git a/src/components/Views/Edit.tsx b/src/components/Views/Edit.tsx index 4658f545..8461eaa2 100644 --- a/src/components/Views/Edit.tsx +++ b/src/components/Views/Edit.tsx @@ -21,13 +21,13 @@ import deepEqual from 'fast-deep-equal' import { AutoSaveNotice } from '../Editor/AutoSaveNotice' import { PublishSettings } from './PublishSettings' import { createStore } from 'solid-js/store' +import SimplifiedEditor from '../Editor/SimplifiedEditor' type Props = { shout: Shout } export const MAX_HEADER_LIMIT = 100 -export const MAX_LEAD_LIMIT = 400 export const EMPTY_TOPIC: Topic = { id: -1, slug: '' @@ -64,6 +64,8 @@ export const EditView = (props: Props) => { slug: props.shout.slug, shoutId: props.shout.id, title: props.shout.title, + lead: props.shout.lead, + description: props.shout.description, subtitle: props.shout.subtitle, selectedTopics: shoutTopics, mainTopic: shoutTopics.find((topic) => topic.slug === props.shout.mainTopic) || EMPTY_TOPIC, @@ -75,7 +77,6 @@ export const EditView = (props: Props) => { } const subtitleInput: { current: HTMLTextAreaElement } = { current: null } - const leadInput: { current: HTMLTextAreaElement } = { current: null } const [prevForm, setPrevForm] = createStore(clone(form)) const [saving, setSaving] = createSignal(false) @@ -226,7 +227,6 @@ export const EditView = (props: Props) => { } const showLeadInput = () => { setIsLeadVisible(true) - leadInput.current.focus() } return ( @@ -320,16 +320,13 @@ export const EditView = (props: Props) => { /> - { - leadInput.current = el - }} - allowEnterKey={true} - value={(value) => setForm('lead', value)} - class={styles.leadInput} - placeholder={t('Description')} - initialValue={form.subtitle} - maxLength={MAX_LEAD_LIMIT} + setForm('lead', value)} /> diff --git a/src/components/Views/PublishSettings/PublishSettings.tsx b/src/components/Views/PublishSettings/PublishSettings.tsx index 287b67f5..baa4e5ce 100644 --- a/src/components/Views/PublishSettings/PublishSettings.tsx +++ b/src/components/Views/PublishSettings/PublishSettings.tsx @@ -10,7 +10,7 @@ import { useLocalize } from '../../../context/localize' import { Modal } from '../../Nav/Modal' import { Topic } from '../../../graphql/types.gen' import { apiClient } from '../../../utils/apiClient' -import { EMPTY_TOPIC, MAX_LEAD_LIMIT } from '../Edit' +import { EMPTY_TOPIC } from '../Edit' import { useSession } from '../../../context/session' import { Icon } from '../../_shared/Icon' import stylesBeside from '../../Feed/Beside.module.scss' @@ -19,6 +19,7 @@ import { router } from '../../../stores/router' import { GrowingTextarea } from '../../_shared/GrowingTextarea' import { createStore } from 'solid-js/store' import { UploadedFile } from '../../../pages/types' +import SimplifiedEditor, { MAX_DESCRIPTION_LIMIT } from '../../Editor/SimplifiedEditor' type Props = { shoutId: number @@ -35,12 +36,12 @@ export const PublishSettings = (props: Props) => { const { t } = useLocalize() const { user } = useSession() - const composeLead = () => { - if (!props.form.lead) { + const composeDescription = () => { + if (!props.form.description) { const leadText = props.form.body.replaceAll(/<\/?[^>]+(>|$)/gi, ' ') - return shorten(leadText, MAX_LEAD_LIMIT).trim() + return shorten(leadText, MAX_DESCRIPTION_LIMIT).trim() } - return props.form.lead + return props.form.description } const initialData: Partial = { @@ -49,7 +50,7 @@ export const PublishSettings = (props: Props) => { slug: props.form.slug, title: props.form.title, subtitle: props.form.subtitle, - lead: composeLead() + description: composeDescription() } const { @@ -183,15 +184,15 @@ export const PublishSettings = (props: Props) => { allowEnterKey={false} maxLength={100} /> - setSettingsForm('lead', value)} - allowEnterKey={false} - maxLength={MAX_LEAD_LIMIT} + label={t('Description')} + initialContent={composeDescription()} + onChange={(value) => setForm('description', value)} + maxLength={MAX_DESCRIPTION_LIMIT} />
diff --git a/src/components/_shared/GrowingTextarea/GrowingTextarea.module.scss b/src/components/_shared/GrowingTextarea/GrowingTextarea.module.scss index 967f3d1d..db819c9e 100644 --- a/src/components/_shared/GrowingTextarea/GrowingTextarea.module.scss +++ b/src/components/_shared/GrowingTextarea/GrowingTextarea.module.scss @@ -9,7 +9,7 @@ padding: 16px 12px; border-radius: 2px; border: 2px solid var(--black-100); - background: var(--white-500, #fff); + background: var(--white-500); } &.hasFieldName { diff --git a/src/context/editor.tsx b/src/context/editor.tsx index 28316a7d..d65080c2 100644 --- a/src/context/editor.tsx +++ b/src/context/editor.tsx @@ -21,12 +21,13 @@ export type ShoutForm = { slug: string title: string subtitle: string + lead?: string + description?: string selectedTopics: Topic[] mainTopic?: Topic body: string coverImageUrl: string media?: string - lead?: string } type EditorContextType = { @@ -136,6 +137,8 @@ export const EditorProvider = (props: { children: JSX.Element }) => { slug: formToUpdate.slug, subtitle: formToUpdate.subtitle, title: formToUpdate.title, + lead: formToUpdate.lead, + description: formToUpdate.description, cover: formToUpdate.coverImageUrl, media: formToUpdate.media }, diff --git a/src/graphql/mutation/article-update.ts b/src/graphql/mutation/article-update.ts index da53afe7..a41c5c1a 100644 --- a/src/graphql/mutation/article-update.ts +++ b/src/graphql/mutation/article-update.ts @@ -9,6 +9,8 @@ export default gql` slug title subtitle + lead + description body visibility } diff --git a/src/graphql/query/article-load.ts b/src/graphql/query/article-load.ts index 1fac1b12..d16589ba 100644 --- a/src/graphql/query/article-load.ts +++ b/src/graphql/query/article-load.ts @@ -5,6 +5,8 @@ export default gql` loadShout(slug: $slug, shout_id: $shoutId) { id title + lead + description visibility subtitle slug diff --git a/src/graphql/query/articles-load-by.ts b/src/graphql/query/articles-load-by.ts index 9e8dd616..5db2edca 100644 --- a/src/graphql/query/articles-load-by.ts +++ b/src/graphql/query/articles-load-by.ts @@ -5,6 +5,8 @@ export default gql` loadShouts(options: $options) { id title + lead + description subtitle slug layout diff --git a/src/graphql/types.gen.ts b/src/graphql/types.gen.ts index ccf57ba9..61516a6b 100644 --- a/src/graphql/types.gen.ts +++ b/src/graphql/types.gen.ts @@ -554,6 +554,7 @@ export type Shout = { createdAt: Scalars['DateTime'] deletedAt?: Maybe deletedBy?: Maybe + description?: Maybe id: Scalars['Int'] lang?: Maybe layout?: Maybe @@ -577,7 +578,9 @@ export type ShoutInput = { body?: InputMaybe community?: InputMaybe cover?: InputMaybe + description?: InputMaybe layout?: InputMaybe + lead?: InputMaybe mainTopic?: InputMaybe media?: InputMaybe slug?: InputMaybe From 0f34bd47d96984a3a3b3cf89045057291b281026 Mon Sep 17 00:00:00 2001 From: Kosta <47947996+dobrodob@users.noreply.github.com> Date: Tue, 22 Aug 2023 20:03:05 +0300 Subject: [PATCH 05/42] New translations (#190) Co-authored-by: akotik --- public/locales/en/translation.json | 4 +++- public/locales/ru/translation.json | 10 ++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 0cafd13d..7264dc15 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -5,6 +5,7 @@ "Add a few topics so that the reader knows what your content is about and can find it on pages of topics that interest them. Topics can be swapped, the first topic becomes the title": "Add a few topics so that the reader knows what your content is about and can find it on pages of topics that interest them. Topics can be swapped, the first topic becomes the title", "Add another image": "Add another image", "Add audio": "Add audio", + "Add blockquote": "Add blockquote", "Add comment": "Comment", "Add cover": "Add cover", "Add image": "Add image", @@ -61,6 +62,7 @@ "Collaborate": "Help Edit", "Come up with a subtitle for your story": "Come up with a subtitle for your story", "Come up with a title for your story": "Come up with a title for your story", + "Comment successfully deleted": "Comment successfully deleted", "Comments": "Comments", "Communities": "Communities", "Confirm": "Confirm", @@ -260,7 +262,7 @@ "Share": "Share", "Short opening": "Short opening", "Show": "Show", - "Show lyrics": "Текст песни", + "Show lyrics": "Show lyrics", "Slug": "Slug", "Social networks": "Social networks", "Something went wrong, check email and password": "Something went wrong. Check your email and password", diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index 5dc7bc6d..121fb672 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -1,12 +1,13 @@ { "...subscribing": "...подписываем", - "A short introduction to keep the reader interested": "Небольшое вступление, чтобы заинтересовать читателя", + "A short introduction to keep the reader interested": "Добавьте вступление, чтобы заинтересовать читателя", "About myself": "О себе", "About the project": "О проекте", "Accomplices": "Соучастники", "Add a few topics so that the reader knows what your content is about and can find it on pages of topics that interest them. Topics can be swapped, the first topic becomes the title": "Добавьте несколько тем, чтобы читатель знал, о чем ваш материал, и мог найти его на страницах интересных ему тем. Темы можно менять местами, первая тема становится заглавной", "Add another image": "Добавить другое изображение", "Add audio": "Добавить аудио", + "Add blockquote": "Добавить цитату", "Add comment": "Комментировать", "Add cover": "Добавить обложку", "Add image": "Добавить изображение", @@ -65,6 +66,7 @@ "Collaborate": "Помочь редактировать", "Come up with a subtitle for your story": "Придумайте подзаголовок вашей истории", "Come up with a title for your story": "Придумайте заголовок вашей истории", + "Comment successfully deleted": "Комментарий успешно удален", "Comments": "Комментарии", "Communities": "Сообщества", "Confirm": "Подтвердить", @@ -222,7 +224,7 @@ "Password again": "Пароль ещё раз", "Password should be at least 8 characters": "Пароль должен быть не менее 8 символов", "Password should contain at least one number": "Пароль должен содержать хотя бы одну цифру", - "Password should contain at least one special character: !@#$%^&*": "Пароль должен содержать хотя бы один специальный символ: !@#$%^&*", + "Password should contain at least one special character: !@#$%^&*": "Пароль должен содержать хотя бы один спецсимвол: !@#$%^&*", "Passwords are not equal": "Пароли не совпадают", "Paste Embed code": "Вставьте embed код", "Personal": "Личные", @@ -274,7 +276,7 @@ "Send link again": "Прислать ссылку ещё раз", "Settings": "Настройки", "Share": "Поделиться", - "Short opening": "Небольшое вступление, чтобы заинтересовать читателя", + "Short opening": "Расскажите вашу историю...", "Show": "Показать", "Show lyrics": "Текст песни", "Slug": "Постоянная ссылка", @@ -315,7 +317,7 @@ "Top authors": "Рейтинг авторов", "Top commented": "Самое комментируемое", "Top discussed": "Обсуждаемое", - "Top month articles": "Лучшее за месяц", + "Top month articles": "Лучшее материалы месяца", "Top rated": "Популярное", "Top recent": "Самое новое", "Top topics": "Интересные темы", From 4e2f4f78cbfac098bcb2d2d4d625793e4b3bca91 Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Wed, 23 Aug 2023 08:37:18 +0300 Subject: [PATCH 06/42] Editor improvements (#191) * Add tooltips to floating menu * Add noWrap in small editor --- public/locales/en/translation.json | 4 ++- public/locales/ru/translation.json | 3 ++ src/components/Editor/Editor.tsx | 2 +- .../Editor/EditorFloatingMenu/Menu/Menu.tsx | 33 ++++++++++++++----- src/components/Editor/Prosemirror.scss | 9 ++--- .../TextBubbleMenu/TextBubbleMenu.module.scss | 4 +++ .../Editor/TextBubbleMenu/TextBubbleMenu.tsx | 2 +- src/components/_shared/Popover/Popover.tsx | 2 +- 8 files changed, 40 insertions(+), 19 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 7264dc15..6758ee6a 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -3,6 +3,8 @@ "About myself": "About myself", "About the project": "About the project", "Add a few topics so that the reader knows what your content is about and can find it on pages of topics that interest them. Topics can be swapped, the first topic becomes the title": "Add a few topics so that the reader knows what your content is about and can find it on pages of topics that interest them. Topics can be swapped, the first topic becomes the title", + "Add a link or click plus to embed media": "Add a link or click plus to embed media", + "Add an embed widget": "Add an embed widget", "Add another image": "Add another image", "Add audio": "Add audio", "Add blockquote": "Add blockquote", @@ -12,6 +14,7 @@ "Add images": "Add images", "Add intro": "Add intro", "Add link": "Add link", + "Add rule": "Add rule", "Add signature": "Add signature", "Add subtitle": "Add subtitle", "Add url": "Add url", @@ -260,7 +263,6 @@ "Send link again": "Send link again", "Settings": "Settings", "Share": "Share", - "Short opening": "Short opening", "Show": "Show", "Show lyrics": "Show lyrics", "Slug": "Slug", diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index 121fb672..9121034b 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -5,6 +5,8 @@ "About the project": "О проекте", "Accomplices": "Соучастники", "Add a few topics so that the reader knows what your content is about and can find it on pages of topics that interest them. Topics can be swapped, the first topic becomes the title": "Добавьте несколько тем, чтобы читатель знал, о чем ваш материал, и мог найти его на страницах интересных ему тем. Темы можно менять местами, первая тема становится заглавной", + "Add a link or click plus to embed media": "Добавьте ссылку или нажмите плюс для вставки медиа", + "Add an embed widget": "Добавить embed-виджет", "Add another image": "Добавить другое изображение", "Add audio": "Добавить аудио", "Add blockquote": "Добавить цитату", @@ -14,6 +16,7 @@ "Add images": "Добавить изображения", "Add intro": "Добавить вступление", "Add link": "Добавить ссылку", + "Add rule": "Добавить разделитель", "Add signature": "Добавить подпись", "Add subtitle": "Добавить подзаголовок", "Add to bookmarks": "Добавить в закладки", diff --git a/src/components/Editor/Editor.tsx b/src/components/Editor/Editor.tsx index 21e8beaa..102e599f 100644 --- a/src/components/Editor/Editor.tsx +++ b/src/components/Editor/Editor.tsx @@ -159,7 +159,7 @@ export const Editor = (props: Props) => { } }), Placeholder.configure({ - placeholder: t('Short opening') + placeholder: t('Add a link or click plus to embed media') }), Focus, Gapcursor, diff --git a/src/components/Editor/EditorFloatingMenu/Menu/Menu.tsx b/src/components/Editor/EditorFloatingMenu/Menu/Menu.tsx index 74b5c52b..f21d4048 100644 --- a/src/components/Editor/EditorFloatingMenu/Menu/Menu.tsx +++ b/src/components/Editor/EditorFloatingMenu/Menu/Menu.tsx @@ -1,5 +1,7 @@ import styles from './Menu.module.scss' import { Icon } from '../../../_shared/Icon' +import { Popover } from '../../../_shared/Popover' +import { useLocalize } from '../../../../context/localize' export type MenuItem = 'image' | 'embed' | 'horizontal-rule' @@ -8,21 +10,34 @@ type Props = { } export const Menu = (props: Props) => { + const { t } = useLocalize() const setSelectedMenuItem = (value: MenuItem) => { props.selectedItem(value) } return (
- - - + + {(triggerRef: (el) => void) => ( + + )} + + + {(triggerRef: (el) => void) => ( + + )} + + + {(triggerRef: (el) => void) => ( + + )} +
) } diff --git a/src/components/Editor/Prosemirror.scss b/src/components/Editor/Prosemirror.scss index 6c36272a..cee99424 100644 --- a/src/components/Editor/Prosemirror.scss +++ b/src/components/Editor/Prosemirror.scss @@ -9,14 +9,10 @@ float: left; height: 0; pointer-events: none; - font-weight: 500; - font-size: 20px; - line-height: 30px; opacity: 0.3; } // Keeping the cursor active when moving outside the editable area - .articleEditor p, .articleEditor ul, .articleEditor h4, @@ -25,16 +21,17 @@ flex: 0 0 auto; @media (width >= 768px) { - padding-left: calc(21.9% + 3px); + padding-left: calc(21.9%); max-width: 72.7%; } @media (width >= 1200px) { - padding-left: calc(21.5% + 3px); + padding-left: calc(21.5% + 1px); max-width: 64.9%; } } +.articleEditor hr, .articleEditor blockquote, .articleEditor figure, .articleEditor article[data-type='incut'] { diff --git a/src/components/Editor/TextBubbleMenu/TextBubbleMenu.module.scss b/src/components/Editor/TextBubbleMenu/TextBubbleMenu.module.scss index f9f613e5..2845d6ca 100644 --- a/src/components/Editor/TextBubbleMenu/TextBubbleMenu.module.scss +++ b/src/components/Editor/TextBubbleMenu/TextBubbleMenu.module.scss @@ -86,4 +86,8 @@ height: 0; color: transparent; } + + .noWrap { + white-space: nowrap; + } } diff --git a/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx b/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx index eeb67c5c..147f5f3f 100644 --- a/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx +++ b/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx @@ -252,7 +252,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
- + {t('Add url')}
}> {(triggerRef: (el) => void) => ( diff --git a/src/components/Views/Edit.module.scss b/src/components/Views/Edit.module.scss index b1fb8d2a..27221436 100644 --- a/src/components/Views/Edit.module.scss +++ b/src/components/Views/Edit.module.scss @@ -133,7 +133,7 @@ pointer-events: none; user-select: none; cursor: pointer; - left: 2rem; + left: 0; position: sticky; top: calc(100vh - 40px); width: 2.8rem; @@ -151,7 +151,7 @@ } @include media-breakpoint-up(xl) { - left: 4rem; + left: 0; } &:hover { diff --git a/src/components/Views/Edit.tsx b/src/components/Views/Edit.tsx index 8461eaa2..dbaa5f84 100644 --- a/src/components/Views/Edit.tsx +++ b/src/components/Views/Edit.tsx @@ -235,7 +235,6 @@ export const EditView = (props: Props) => { {pageTitle()}
- + +
From 7a467e313baf431f456fc15a16c45f5fbfc7deac Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Fri, 25 Aug 2023 00:19:26 +0300 Subject: [PATCH 08/42] Fixed table of contents style --- src/components/Article/FullArticle.tsx | 5 ++++- src/components/TableOfContents/TableOfContents.module.scss | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx index f8812e8d..234ac307 100644 --- a/src/components/Article/FullArticle.tsx +++ b/src/components/Article/FullArticle.tsx @@ -277,8 +277,11 @@ export const FullArticle = (props: Props) => {
+ - +
+ +
diff --git a/src/components/TableOfContents/TableOfContents.module.scss b/src/components/TableOfContents/TableOfContents.module.scss index 36332850..29afacf1 100644 --- a/src/components/TableOfContents/TableOfContents.module.scss +++ b/src/components/TableOfContents/TableOfContents.module.scss @@ -1,6 +1,10 @@ .TableOfContentsFixedWrapper { min-height: 100%; top: 0; + + &:not(.TableOfContentsFixedWrapperLefted) .TableOfContentsPrimaryButton { + transform: rotate(180deg); + } } .TableOfContentsFixedWrapperLefted { From 8e26824554be4261d81d9cbc0b95d71fbc9c2b5d Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Fri, 25 Aug 2023 00:51:32 +0300 Subject: [PATCH 09/42] Fixed table of contents style --- .../TableOfContents.module.scss | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/components/TableOfContents/TableOfContents.module.scss b/src/components/TableOfContents/TableOfContents.module.scss index 29afacf1..bcd06260 100644 --- a/src/components/TableOfContents/TableOfContents.module.scss +++ b/src/components/TableOfContents/TableOfContents.module.scss @@ -2,14 +2,32 @@ min-height: 100%; top: 0; - &:not(.TableOfContentsFixedWrapperLefted) .TableOfContentsPrimaryButton { - transform: rotate(180deg); + &:not(.TableOfContentsFixedWrapperLefted) { + .TableOfContentsPrimaryButton { + margin-top: 5.4rem; + transform: rotate(180deg); + } + + .TableOfContentsContainer { + padding-top: 2.7em; + } } } .TableOfContentsFixedWrapperLefted { + margin-top: -2em; right: auto; left: 70px; + + .TableOfContentsPrimaryButton { + left: auto; + right: 40px; + } + + .TableOfContentsPrimaryButtonLefted { + left: 0; + right: auto; + } } .TableOfContentsContainer { @@ -18,11 +36,15 @@ right: 20px; display: flex; width: 100%; - height: calc(100vh - 250px); + height: calc(100vh - 120px); overflow: auto; flex-direction: column; align-items: flex-start; background-color: transparent; + + .TableOfContentsFixedWrapperLefted & { + height: calc(100vh - 250px); + } } .TableOfContentsHeader { @@ -43,7 +65,7 @@ .TableOfContentsPrimaryButton { position: absolute; right: 0; - top: 10px; + top: 0; display: flex; align-items: center; justify-content: center; @@ -52,11 +74,6 @@ cursor: pointer; } -.TableOfContentsPrimaryButtonLefted { - right: auto; - left: 0; -} - .TableOfContentsHeadingsList { position: relative; display: flex; From f423f22741f13721d34b037674bf158df4363fc4 Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Sat, 26 Aug 2023 12:41:21 +0300 Subject: [PATCH 10/42] Update header z index (#194) --- src/components/Article/FullArticle.tsx | 2 +- src/components/Feed/ArticleCard.tsx | 2 +- .../Nav/{ => Header}/Header.module.scss | 2 +- src/components/Nav/{ => Header}/Header.tsx | 24 +++++++++---------- src/components/Nav/Header/index.ts | 1 + src/components/Nav/HeaderAuth.tsx | 2 +- src/components/Nav/Modal/Modal.module.scss | 2 +- 7 files changed, 18 insertions(+), 17 deletions(-) rename src/components/Nav/{ => Header}/Header.module.scss (99%) rename src/components/Nav/{ => Header}/Header.tsx (91%) create mode 100644 src/components/Nav/Header/index.ts diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx index 234ac307..93578908 100644 --- a/src/components/Article/FullArticle.tsx +++ b/src/components/Article/FullArticle.tsx @@ -24,7 +24,7 @@ import { AudioPlayer } from './AudioPlayer' import { SharePopup } from './SharePopup' import { ShoutRatingControl } from './ShoutRatingControl' import { CommentsTree } from './CommentsTree' -import stylesHeader from '../Nav/Header.module.scss' +import stylesHeader from '../Nav/Header/Header.module.scss' import { AudioHeader } from './AudioHeader' import { Popover } from '../_shared/Popover' diff --git a/src/components/Feed/ArticleCard.tsx b/src/components/Feed/ArticleCard.tsx index 732bfb19..3b4ed6ff 100644 --- a/src/components/Feed/ArticleCard.tsx +++ b/src/components/Feed/ArticleCard.tsx @@ -7,7 +7,7 @@ import { clsx } from 'clsx' import { CardTopic } from './CardTopic' import { ShoutRatingControl } from '../Article/ShoutRatingControl' import { getShareUrl, SharePopup } from '../Article/SharePopup' -import stylesHeader from '../Nav/Header.module.scss' +import stylesHeader from '../Nav/Header/Header.module.scss' import { getDescription } from '../../utils/meta' import { FeedArticlePopup } from './FeedArticlePopup' import { useLocalize } from '../../context/localize' diff --git a/src/components/Nav/Header.module.scss b/src/components/Nav/Header/Header.module.scss similarity index 99% rename from src/components/Nav/Header.module.scss rename to src/components/Nav/Header/Header.module.scss index 6c6c59bb..1a43eaec 100644 --- a/src/components/Nav/Header.module.scss +++ b/src/components/Nav/Header/Header.module.scss @@ -5,7 +5,7 @@ margin-bottom: 2.2rem; position: absolute; width: 100%; - z-index: 10; + z-index: 10000; .wide-container { background: #fff; diff --git a/src/components/Nav/Header.tsx b/src/components/Nav/Header/Header.tsx similarity index 91% rename from src/components/Nav/Header.tsx rename to src/components/Nav/Header/Header.tsx index 614597da..5bc6f628 100644 --- a/src/components/Nav/Header.tsx +++ b/src/components/Nav/Header/Header.tsx @@ -2,21 +2,21 @@ import { Show, createSignal, createEffect, onMount, onCleanup } from 'solid-js' import { getPagePath, redirectPage } from '@nanostores/router' import { clsx } from 'clsx' -import { Modal } from './Modal' -import { AuthModal } from './AuthModal' -import { HeaderAuth } from './HeaderAuth' -import { ConfirmModal } from './ConfirmModal' -import { getShareUrl, SharePopup } from '../Article/SharePopup' -import { Snackbar } from './Snackbar' -import { Icon } from '../_shared/Icon' +import { Modal } from '../Modal' +import { AuthModal } from '../AuthModal' +import { HeaderAuth } from '../HeaderAuth' +import { ConfirmModal } from '../ConfirmModal' +import { getShareUrl, SharePopup } from '../../Article/SharePopup' +import { Snackbar } from '../Snackbar' +import { Icon } from '../../_shared/Icon' -import { useModalStore } from '../../stores/ui' -import { router, useRouter } from '../../stores/router' +import { useModalStore } from '../../../stores/ui' +import { router, useRouter } from '../../../stores/router' -import { getDescription } from '../../utils/meta' +import { getDescription } from '../../../utils/meta' -import { useLocalize } from '../../context/localize' -import { useSession } from '../../context/session' +import { useLocalize } from '../../../context/localize' +import { useSession } from '../../../context/session' import styles from './Header.module.scss' diff --git a/src/components/Nav/Header/index.ts b/src/components/Nav/Header/index.ts new file mode 100644 index 00000000..e0e2673a --- /dev/null +++ b/src/components/Nav/Header/index.ts @@ -0,0 +1 @@ +export { Header } from './Header' diff --git a/src/components/Nav/HeaderAuth.tsx b/src/components/Nav/HeaderAuth.tsx index 6eaae6a8..1c7de728 100644 --- a/src/components/Nav/HeaderAuth.tsx +++ b/src/components/Nav/HeaderAuth.tsx @@ -1,4 +1,4 @@ -import styles from './Header.module.scss' +import styles from './Header/Header.module.scss' import { clsx } from 'clsx' import { router, useRouter } from '../../stores/router' import { Icon } from '../_shared/Icon' diff --git a/src/components/Nav/Modal/Modal.module.scss b/src/components/Nav/Modal/Modal.module.scss index 47098b1f..f0b55f3e 100644 --- a/src/components/Nav/Modal/Modal.module.scss +++ b/src/components/Nav/Modal/Modal.module.scss @@ -10,7 +10,7 @@ position: fixed; top: 0; width: 100%; - z-index: 100; + z-index: 11000; } .modal { From a619610c07a88509fb2bd209c5fa879fdfcfc180 Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Sat, 26 Aug 2023 13:45:35 +0300 Subject: [PATCH 11/42] Fix delete sliders from swiper (#195) --- src/components/_shared/SolidSwiper/SolidSwiper.tsx | 6 +++--- src/components/_shared/SolidSwiper/Swiper.module.scss | 11 ++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/components/_shared/SolidSwiper/SolidSwiper.tsx b/src/components/_shared/SolidSwiper/SolidSwiper.tsx index 3037127f..4417dd06 100644 --- a/src/components/_shared/SolidSwiper/SolidSwiper.tsx +++ b/src/components/_shared/SolidSwiper/SolidSwiper.tsx @@ -316,18 +316,18 @@ export const SolidSwiper = (props: Props) => { type="text" class={clsx(styles.input, styles.title)} placeholder={t('Enter image title')} - value={props.images[slideIndex()].title} + value={props.images[slideIndex()]?.title} onChange={(event) => handleSlideDescriptionChange(slideIndex(), 'title', event.target.value)} /> handleSlideDescriptionChange(slideIndex(), 'source', event.target.value)} /> setSlideBody(value)} diff --git a/src/components/_shared/SolidSwiper/Swiper.module.scss b/src/components/_shared/SolidSwiper/Swiper.module.scss index 02044bb0..33a4eb1e 100644 --- a/src/components/_shared/SolidSwiper/Swiper.module.scss +++ b/src/components/_shared/SolidSwiper/Swiper.module.scss @@ -93,8 +93,9 @@ $navigation-reserve: 32px; position: absolute; top: 16px; right: 16px; - background: rgba(#000, 0.3); + background: rgba(var(--default-color), 0.3); cursor: pointer; + z-index: 12; display: none; .icon { @@ -129,14 +130,14 @@ $navigation-reserve: 32px; background: var(--placeholder-color-semi); position: relative; - &:hover .action { - display: flex; - } - img { max-height: 100%; } } + + &:hover .action { + display: flex; + } } &.editorMode { .holder { From 188642bb2c94fa4d30b093b16b586c4747d09450 Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Sun, 27 Aug 2023 19:48:28 +0300 Subject: [PATCH 12/42] Minor table of contents fixes --- public/icons/hide-table-of-contents.svg | 2 +- public/icons/show-table-of-contents.svg | 4 ++-- public/locales/en/translation.json | 2 ++ public/locales/ru/translation.json | 2 ++ .../TableOfContents/TableOfContents.module.scss | 10 ++++++---- src/components/TableOfContents/TableOfContents.tsx | 2 +- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/public/icons/hide-table-of-contents.svg b/public/icons/hide-table-of-contents.svg index f1df16eb..fd50f43e 100644 --- a/public/icons/hide-table-of-contents.svg +++ b/public/icons/hide-table-of-contents.svg @@ -1,4 +1,4 @@ - + diff --git a/public/icons/show-table-of-contents.svg b/public/icons/show-table-of-contents.svg index 8b13672d..37075c24 100644 --- a/public/icons/show-table-of-contents.svg +++ b/public/icons/show-table-of-contents.svg @@ -1,4 +1,4 @@ - - + + diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 6758ee6a..d4714bac 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -144,6 +144,7 @@ "Help": "Помощь", "Help to edit": "Help to edit", "Here you can customize your profile the way you want.": "Here you can customize your profile the way you want.", + "Hide table of contents": "Hide table of contents", "Highlight": "Highlight", "Hooray! Welcome!": "Hooray! Welcome!", "Horizontal collaborative journalistic platform": "Horizontal collaborative journalism platform", @@ -265,6 +266,7 @@ "Share": "Share", "Show": "Show", "Show lyrics": "Show lyrics", + "Show table of contents": "Show table of contents", "Slug": "Slug", "Social networks": "Social networks", "Something went wrong, check email and password": "Something went wrong. Check your email and password", diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index 9121034b..da916c9b 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -152,6 +152,7 @@ "Help": "Помощь", "Help to edit": "Помочь редактировать", "Here you can customize your profile the way you want.": "Здесь можно настроить свой профиль так, как вы хотите.", + "Hide table of contents": "Скрыть главление", "Highlight": "Подсветка", "Hooray! Welcome!": "Ура! Добро пожаловать!", "Horizontal collaborative journalistic platform": "Горизонтальная платформа для коллаборативной журналистики", @@ -282,6 +283,7 @@ "Short opening": "Расскажите вашу историю...", "Show": "Показать", "Show lyrics": "Текст песни", + "Show table of contents": "Показать главление", "Slug": "Постоянная ссылка", "Social networks": "Социальные сети", "Something went wrong, check email and password": "Что-то пошло не так. Проверьте адрес электронной почты и пароль", diff --git a/src/components/TableOfContents/TableOfContents.module.scss b/src/components/TableOfContents/TableOfContents.module.scss index bcd06260..af54438c 100644 --- a/src/components/TableOfContents/TableOfContents.module.scss +++ b/src/components/TableOfContents/TableOfContents.module.scss @@ -3,14 +3,12 @@ top: 0; &:not(.TableOfContentsFixedWrapperLefted) { + margin-top: -0.2em; + .TableOfContentsPrimaryButton { margin-top: 5.4rem; transform: rotate(180deg); } - - .TableOfContentsContainer { - padding-top: 2.7em; - } } } @@ -72,6 +70,10 @@ background: transparent; border: none; cursor: pointer; + + &:hover { + filter: invert(1); + } } .TableOfContentsHeadingsList { diff --git a/src/components/TableOfContents/TableOfContents.tsx b/src/components/TableOfContents/TableOfContents.tsx index 12b4f310..b4f354e6 100644 --- a/src/components/TableOfContents/TableOfContents.tsx +++ b/src/components/TableOfContents/TableOfContents.tsx @@ -99,9 +99,9 @@ export const TableOfContents = (props: Props) => { })} onClick={(e) => { e.preventDefault() - toggleIsVisible() }} + title={isVisible() ? t('Hide table of contents') : t('Show table of contents')} > }> From 6d49e4e2f02c11f12dca48cec978d43e6112aada Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Mon, 28 Aug 2023 00:09:20 +0300 Subject: [PATCH 13/42] Fixed header style on the editor page --- src/components/Nav/Header/Header.module.scss | 7 +++++-- src/components/Nav/HeaderAuth.tsx | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/Nav/Header/Header.module.scss b/src/components/Nav/Header/Header.module.scss index 1a43eaec..f3da4d7e 100644 --- a/src/components/Nav/Header/Header.module.scss +++ b/src/components/Nav/Header/Header.module.scss @@ -95,9 +95,7 @@ .usernav { display: inline-flex; - flex: 0 0 40% !important; font-weight: 500; - max-width: 400px !important; position: relative; @include media-breakpoint-down(md) { @@ -105,6 +103,11 @@ max-width: 100% !important; padding: 0 !important; } + + &:not(.usernavEditor) { + flex: 0 0 40% !important; + max-width: 400px !important; + } } .mainNavigationWrapper { diff --git a/src/components/Nav/HeaderAuth.tsx b/src/components/Nav/HeaderAuth.tsx index 1c7de728..ff924be3 100644 --- a/src/components/Nav/HeaderAuth.tsx +++ b/src/components/Nav/HeaderAuth.tsx @@ -107,7 +107,7 @@ export const HeaderAuth = (props: Props) => { return ( -
+
From 20d7c585e83fd5ab5d10d56d48ae3dbb1283146e Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Mon, 28 Aug 2023 00:21:40 +0300 Subject: [PATCH 14/42] Change author page style --- public/icons/user-link-default.svg | 9 ++- public/icons/user-link-facebook.svg | 4 ++ public/icons/user-link-instagram.svg | 4 ++ public/icons/user-link-telegram.svg | 5 ++ public/icons/user-link-twitter.svg | 3 + src/components/Author/AuthorCard.module.scss | 30 +++++---- src/components/Author/AuthorCard.tsx | 66 +++++++++++++------ src/components/Author/Full.module.scss | 9 +++ src/components/Author/Full.scss | 30 --------- src/components/Author/Full.tsx | 9 ++- .../Author/Userpic/Userpic.module.scss | 16 +++-- .../Views/Author/Author.module.scss | 18 ++++- src/components/Views/Author/Author.tsx | 55 ++++++++++------ 13 files changed, 154 insertions(+), 104 deletions(-) create mode 100644 public/icons/user-link-facebook.svg create mode 100644 public/icons/user-link-instagram.svg create mode 100644 public/icons/user-link-telegram.svg create mode 100644 public/icons/user-link-twitter.svg create mode 100644 src/components/Author/Full.module.scss delete mode 100644 src/components/Author/Full.scss diff --git a/public/icons/user-link-default.svg b/public/icons/user-link-default.svg index d1e03c91..06b47b48 100644 --- a/public/icons/user-link-default.svg +++ b/public/icons/user-link-default.svg @@ -1,5 +1,4 @@ - - + + + diff --git a/public/icons/user-link-facebook.svg b/public/icons/user-link-facebook.svg new file mode 100644 index 00000000..47632b3c --- /dev/null +++ b/public/icons/user-link-facebook.svg @@ -0,0 +1,4 @@ + + + diff --git a/public/icons/user-link-instagram.svg b/public/icons/user-link-instagram.svg new file mode 100644 index 00000000..bce6d6a2 --- /dev/null +++ b/public/icons/user-link-instagram.svg @@ -0,0 +1,4 @@ + + + diff --git a/public/icons/user-link-telegram.svg b/public/icons/user-link-telegram.svg new file mode 100644 index 00000000..9320b127 --- /dev/null +++ b/public/icons/user-link-telegram.svg @@ -0,0 +1,5 @@ + + + + diff --git a/public/icons/user-link-twitter.svg b/public/icons/user-link-twitter.svg new file mode 100644 index 00000000..cb8cb03e --- /dev/null +++ b/public/icons/user-link-twitter.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Author/AuthorCard.module.scss b/src/components/Author/AuthorCard.module.scss index fe8e9d29..9ae93a71 100644 --- a/src/components/Author/AuthorCard.module.scss +++ b/src/components/Author/AuthorCard.module.scss @@ -18,6 +18,10 @@ display: flex; flex: 1; + &.authorDetailsShrinked { + flex: 0 0 auto; + } + @include media-breakpoint-down(sm) { flex-wrap: wrap; } @@ -65,22 +69,21 @@ .authorSubscribe { align-items: center; - display: flex; + //display: flex; @include media-breakpoint-down(md) { flex-wrap: wrap; } a { - background: #f7f7f7; border: none; display: inline-block; - height: 32px; + height: 24px; margin-right: 0.4rem; position: relative; transition: background-color 0.2s; vertical-align: middle; - width: 32px; + width: 24px; &::before { background-image: url(/icons/user-link-default.svg); @@ -88,40 +91,39 @@ background-position: 50% 50%; background-size: contain; content: ''; - filter: invert(1); - height: 18px; + height: 100%; left: 50%; position: absolute; top: 50%; transform: translate(-50%, -50%); transition: filter 0.2s; - width: 18px; + width: 100%; } &:hover { background: #000; &::before { - filter: invert(0); + filter: invert(1); } } } a[href*='facebook.com/'] { &::before { - background-image: url(/icons/facebook-white.svg); + background-image: url(/icons/user-link-facebook.svg); } } a[href*='twitter.com/'] { &::before { - background-image: url(/icons/twitter-white.svg); + background-image: url(/icons/user-link-twitter.svg); } } a[href*='telegram.com/'] { &::before { - background-image: url(/icons/telegram-white.svg); + background-image: url(/icons/user-link-telegram.svg); } } @@ -140,7 +142,7 @@ a[href*='instagram.com/'] { &::before { - background-image: url(/icons/instagram-white.svg); + background-image: url(/icons/user-link-instagram.svg); } } @@ -171,6 +173,7 @@ .authorSubscribeSocial { align-items: center; display: flex; + margin: 0 0.8rem 1.6rem; @include media-breakpoint-down(sm) { flex: 1 100%; @@ -237,8 +240,7 @@ } .authorAbout { - @include font-size(1.7rem); - + @include font-size(2rem); color: #696969; } diff --git a/src/components/Author/AuthorCard.tsx b/src/components/Author/AuthorCard.tsx index 3b1e4004..306b62b6 100644 --- a/src/components/Author/AuthorCard.tsx +++ b/src/components/Author/AuthorCard.tsx @@ -93,6 +93,7 @@ export const AuthorCard = (props: AuthorCardProps) => {
{ [styles.nowrapView]: props.isNowrap }} > - + + } + > +
+ +
+
-
+
@@ -140,6 +164,12 @@ export const AuthorCard = (props: AuthorCardProps) => { diff --git a/src/components/Author/Full.module.scss b/src/components/Author/Full.module.scss new file mode 100644 index 00000000..0fe5e7bc --- /dev/null +++ b/src/components/Author/Full.module.scss @@ -0,0 +1,9 @@ +.userDetails { + border-bottom: 2px solid #000; + margin: 0 0 3.6rem; + padding-bottom: 3.6rem; + + @include media-breakpoint-down(md) { + text-align: center; + } +} diff --git a/src/components/Author/Full.scss b/src/components/Author/Full.scss deleted file mode 100644 index d4a8c16c..00000000 --- a/src/components/Author/Full.scss +++ /dev/null @@ -1,30 +0,0 @@ -.user-details { - margin: 0 0 5.4rem; - - @include media-breakpoint-up(md) { - margin-left: 160px; - } - - @include media-breakpoint-up(lg) { - margin-left: 235px; - } - - @include media-breakpoint-down(md) { - text-align: center; - } -} - -.author-page { - .view-switcher { - margin-top: 0; - - button { - font-size: 100%; - } - } - - .group__controls { - margin-bottom: 2em !important; - margin-top: 0 !important; - } -} diff --git a/src/components/Author/Full.tsx b/src/components/Author/Full.tsx index 4b280250..73395e37 100644 --- a/src/components/Author/Full.tsx +++ b/src/components/Author/Full.tsx @@ -1,13 +1,12 @@ import type { Author } from '../../graphql/types.gen' import { AuthorCard } from './AuthorCard' -import './Full.scss' +import styles from './Full.module.scss' +import clsx from 'clsx' export const AuthorFull = (props: { author: Author }) => { return ( -
-
- -
+
+
) } diff --git a/src/components/Author/Userpic/Userpic.module.scss b/src/components/Author/Userpic/Userpic.module.scss index 1dc106ac..7e3d1edd 100644 --- a/src/components/Author/Userpic/Userpic.module.scss +++ b/src/components/Author/Userpic/Userpic.module.scss @@ -55,19 +55,23 @@ } &.big { - margin-right: 0; + aspect-ratio: 1/1; + margin: 0 auto; max-width: 168px; - min-width: 168px; - height: 168px; - width: 168px; + height: auto; + width: 100%; @include media-breakpoint-up(md) { - margin-right: 4.8rem; + margin: 0; + max-width: 100%; } .letters { + align-items: center; + display: flex; font-size: 2em; - line-height: 168px; + justify-content: center; + line-height: normal; max-width: 100%; width: 100%; } diff --git a/src/components/Views/Author/Author.module.scss b/src/components/Views/Author/Author.module.scss index 5098043d..3cfd2bbc 100644 --- a/src/components/Views/Author/Author.module.scss +++ b/src/components/Views/Author/Author.module.scss @@ -1,13 +1,26 @@ +.authorPage { + :global(.view-switcher) { + margin-top: 0; + + button { + font-size: 100%; + } + } + + .groupControls { + margin-bottom: 2em !important; + margin-top: 0 !important; + } +} + .ratingContainer { @include font-size(1.5rem); - display: inline-flex; vertical-align: top; } .ratingControl { @include font-size(1.5rem); - display: inline-flex; margin-left: 1em; vertical-align: middle; @@ -38,7 +51,6 @@ .subscribersCounter { @include font-size(1rem); - background: #fff; border: 2px solid #000; border-radius: 100%; diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx index 171e2e2c..6e0981b1 100644 --- a/src/components/Views/Author/Author.tsx +++ b/src/components/Views/Author/Author.tsx @@ -2,6 +2,7 @@ import { Show, createMemo, createSignal, Switch, onMount, For, Match, createEffe import type { Author, Shout, Topic } from '../../../graphql/types.gen' import { Row1 } from '../../Feed/Row1' import { Row2 } from '../../Feed/Row2' +import { Row3 } from '../../Feed/Row3' import { AuthorFull } from '../../Author/Full' import { useAuthorsStore } from '../../../stores/zine/authors' @@ -136,12 +137,12 @@ export const AuthorView = (props: AuthorProps) => { } }) return ( -
+
-
+
  • @@ -285,25 +286,39 @@ export const AuthorView = (props: AuthorProps) => {
- - - - - - + + + - - {(page) => ( - <> - - - - - - - - )} - + + + + + + + + + 3}> + + + + + + + + + {(page) => ( + <> + + + + + + + + )} + +

From cf3d30ab5534c642853167dd7dbc814875bd3c03 Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Mon, 28 Aug 2023 00:30:08 +0300 Subject: [PATCH 15/42] Removed undeclared props --- src/components/Views/Author/Author.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx index 6e0981b1..adb986d0 100644 --- a/src/components/Views/Author/Author.tsx +++ b/src/components/Views/Author/Author.tsx @@ -295,7 +295,7 @@ export const AuthorView = (props: AuthorProps) => { - + 3}> From ce69b9318f05b2f4a993372a7f906cbb63b0465b Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Mon, 28 Aug 2023 01:05:09 +0300 Subject: [PATCH 16/42] Change banner style --- .../Discours/{Hero.scss => Hero.module.scss} | 33 ++++++++----------- src/components/Discours/Hero.tsx | 17 ++++------ 2 files changed, 21 insertions(+), 29 deletions(-) rename src/components/Discours/{Hero.scss => Hero.module.scss} (51%) diff --git a/src/components/Discours/Hero.scss b/src/components/Discours/Hero.module.scss similarity index 51% rename from src/components/Discours/Hero.scss rename to src/components/Discours/Hero.module.scss index 10a45736..1d293ef0 100644 --- a/src/components/Discours/Hero.scss +++ b/src/components/Discours/Hero.module.scss @@ -1,34 +1,30 @@ -.about-discours { - @include font-size(1.7rem); - - background: #000; - color: #fff; - font-weight: 400; +.aboutDiscours { + @include font-size(1.6rem); + background: #fef2f2; + font-weight: 500; margin-bottom: 6.4rem; - padding: 3.6rem 0; + padding: 8rem 0 6.4rem; text-align: center; h4 { - margin-bottom: 4rem; + @include font-size(4rem); + font-weight: bold; + line-height: 1.1; + margin-bottom: 2rem; } em { font-weight: inherit; } - - ::selection { - background: #fff; - color: #000; - } } -.about-discours__actions { +.aboutDiscoursActions { margin-top: 4.8rem; - .button { - border: 3px solid; - border-radius: 1.2em; - color: inherit; + :global(.button) { + border: 3px solid #000; + border-radius: 0.8rem; + color: #fff; cursor: pointer; display: inline-block; font-weight: bold; @@ -39,7 +35,6 @@ &:hover { background: #fff; - border-color: #fff; color: #000; } } diff --git a/src/components/Discours/Hero.tsx b/src/components/Discours/Hero.tsx index 18dff663..136dfc4b 100644 --- a/src/components/Discours/Hero.tsx +++ b/src/components/Discours/Hero.tsx @@ -1,4 +1,4 @@ -import './Hero.scss' +import styles from './Hero.module.scss' import { showModal } from '../../stores/ui' import { useLocalize } from '../../context/localize' @@ -6,10 +6,10 @@ import { useLocalize } from '../../context/localize' export default () => { const { t } = useLocalize() return ( -

+
-
+

{t('Horizontal collaborative journalistic platform')}

{t( @@ -24,16 +24,13 @@ export default () => { .

-
+
+ + {t('Create post')} + showModal('auth')}> {t('Join the community')} - - {t('Become an author')} - - - {t('About the project')} - {t('Support us')} From 0ec7e5ceedc89225865f7ebad3ccd454d971f9a0 Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Mon, 28 Aug 2023 01:26:39 +0300 Subject: [PATCH 17/42] Fixed error page height --- src/components/Views/FourOuFour.tsx | 2 +- src/styles/FourOuFour.module.scss | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/Views/FourOuFour.tsx b/src/components/Views/FourOuFour.tsx index e35a6448..0f362fe1 100644 --- a/src/components/Views/FourOuFour.tsx +++ b/src/components/Views/FourOuFour.tsx @@ -8,7 +8,7 @@ export const FourOuFourView = (_props) => { return (
-
+
diff --git a/src/styles/FourOuFour.module.scss b/src/styles/FourOuFour.module.scss index b8a01d3e..f3c6b86f 100644 --- a/src/styles/FourOuFour.module.scss +++ b/src/styles/FourOuFour.module.scss @@ -1,12 +1,13 @@ .errorPageWrapper { height: 100vh; + margin: -120px 0 -2em; padding-top: 100px; } .errorPage { position: relative; top: 35%; - transform: translateY(-45%); + transform: translateY(-50%); .image-link:hover { background: none; @@ -36,6 +37,7 @@ .errorImage { display: block; margin: auto; + max-height: 60vh; width: 85%; } From ead332184db3d6a46265933274bd8c539b3eb8df Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Mon, 28 Aug 2023 14:48:54 +0300 Subject: [PATCH 18/42] Feature/footnotes (#193) * Add Footnote extension --- public/locales/en/translation.json | 1 + public/locales/ru/translation.json | 3 +- src/components/Article/Article.module.scss | 4 + src/components/Article/FullArticle.tsx | 25 +++-- src/components/Editor/Editor.tsx | 2 + src/components/Editor/Prosemirror.scss | 21 ++++ src/components/Editor/SimplifiedEditor.tsx | 40 ++++--- .../TextBubbleMenu/TextBubbleMenu.module.scss | 4 + .../Editor/TextBubbleMenu/TextBubbleMenu.tsx | 55 ++++++++- src/components/Editor/extensions/Footnote.ts | 104 ++++++++++++++++++ src/components/Nav/AuthModal/RegisterForm.tsx | 8 +- src/components/Nav/Modal/Modal.module.scss | 2 +- .../Views/PublishSettings/PublishSettings.tsx | 3 +- src/context/editor.tsx | 3 - src/stores/ui.ts | 6 +- 15 files changed, 239 insertions(+), 42 deletions(-) create mode 100644 src/components/Editor/extensions/Footnote.ts diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index d4714bac..31222f03 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -105,6 +105,7 @@ "Email": "Mail", "Enter": "Enter", "Enter URL address": "Enter URL address", + "Enter footnote text": "Enter footnote text", "Enter image description": "Enter image description", "Enter image title": "Enter image title", "Enter text": "Enter text", diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index da916c9b..47d0ff9a 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -420,5 +420,6 @@ "user already exist": "пользователь уже существует", "video": "видео", "view": "просмотр", - "zine": "журнал" + "zine": "журнал", + "Enter footnote text": "Введите текст сноски" } diff --git a/src/components/Article/Article.module.scss b/src/components/Article/Article.module.scss index 650ede1a..d4d3440a 100644 --- a/src/components/Article/Article.module.scss +++ b/src/components/Article/Article.module.scss @@ -564,6 +564,10 @@ a[data-toggle='tooltip'] { background: var(--black-500); color: var(--default-color-invert); + p:last-child { + margin: 0; + } + &::after { content: ''; position: absolute; diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx index 93578908..473d2efa 100644 --- a/src/components/Article/FullArticle.tsx +++ b/src/components/Article/FullArticle.tsx @@ -2,18 +2,13 @@ import { createEffect, For, createMemo, onMount, Show, createSignal, onCleanup } import { Title } from '@solidjs/meta' import { clsx } from 'clsx' import { getPagePath } from '@nanostores/router' - import MD from './MD' - import type { Author, Shout } from '../../graphql/types.gen' import { useSession } from '../../context/session' import { useLocalize } from '../../context/localize' import { useReactions } from '../../context/reactions' - import { MediaItem } from '../../pages/types' - import { router, useRouter } from '../../stores/router' - import { formatDate } from '../../utils' import { getDescription } from '../../utils/meta' import { imageProxy } from '../../utils/imageProxy' @@ -26,7 +21,6 @@ import { ShoutRatingControl } from './ShoutRatingControl' import { CommentsTree } from './CommentsTree' import stylesHeader from '../Nav/Header/Header.module.scss' import { AudioHeader } from './AudioHeader' - import { Popover } from '../_shared/Popover' import { VideoPlayer } from '../_shared/VideoPlayer' import { Icon } from '../_shared/Icon' @@ -47,6 +41,7 @@ export const FullArticle = (props: Props) => { isAuthenticated, actions: { requireAuthentication } } = useSession() + const [isReactionsLoaded, setIsReactionsLoaded] = createSignal(false) const formattedDate = createMemo(() => formatDate(new Date(props.article.createdAt))) @@ -131,17 +126,23 @@ export const FullArticle = (props: Props) => { const clickHandlers = [] const documentClickHandlers = [] - onMount(() => { - const tooltipElements: NodeListOf = - document.querySelectorAll('[data-toggle="tooltip"]') - if (!tooltipElements) return + createEffect(() => { + if (!body()) { + return + } + const tooltipElements: NodeListOf = document.querySelectorAll( + '[data-toggle="tooltip"], footnote' + ) + if (!tooltipElements) return tooltipElements.forEach((element) => { const tooltip = document.createElement('div') tooltip.classList.add(styles.tooltip) - tooltip.textContent = element.dataset.originalTitle + tooltip.innerHTML = element.dataset.originalTitle || element.dataset.value document.body.appendChild(tooltip) - element.setAttribute('href', 'javascript: void(0);') + if (element.tagName === 'a') { + element.setAttribute('href', 'javascript: void(0);') + } createPopper(element, tooltip, { placement: 'top', modifiers: [ diff --git a/src/components/Editor/Editor.tsx b/src/components/Editor/Editor.tsx index d6ccd409..7aa96413 100644 --- a/src/components/Editor/Editor.tsx +++ b/src/components/Editor/Editor.tsx @@ -47,6 +47,7 @@ import { isDesktop } from '../../utils/media-query' import './Prosemirror.scss' import { Image } from '@tiptap/extension-image' +import { Footnote } from './extensions/Footnote' type Props = { shoutId: number @@ -173,6 +174,7 @@ export const Editor = (props: Props) => { ImageFigure, Image, Figcaption, + Footnote, Embed, CharacterCount.configure(), // https://github.com/ueberdosis/tiptap/issues/2589#issuecomment-1093084689 BubbleMenu.configure({ diff --git a/src/components/Editor/Prosemirror.scss b/src/components/Editor/Prosemirror.scss index 3c133e87..e9ab8c8e 100644 --- a/src/components/Editor/Prosemirror.scss +++ b/src/components/Editor/Prosemirror.scss @@ -258,3 +258,24 @@ mark.highlight { figure[data-type='capturedImage'] { flex-direction: column-reverse; } + +footnote { + display: inline-flex; + position: relative; + cursor: pointer; + &:before { + content: ''; + position: absolute; + width: 10px; + height: 10px; + border-radius: 50%; + top: -2px; + border: unset; + background-size: 10px; + background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgY2xhc3M9ImJpIGJpLWluZm8tY2lyY2xlIiBmaWxsPSJjdXJyZW50Q29sb3IiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik04IDE1QTcgNyAwIDEgMSA4IDFhNyA3IDAgMCAxIDAgMTR6bTAgMUE4IDggMCAxIDAgOCAwYTggOCAwIDAgMCAwIDE2eiIvPjxwYXRoIGQ9Im04LjkzIDYuNTg4LTIuMjkuMjg3LS4wODIuMzguNDUuMDgzYy4yOTQuMDcuMzUyLjE3Ni4yODguNDY5bC0uNzM4IDMuNDY4Yy0uMTk0Ljg5Ny4xMDUgMS4zMTkuODA4IDEuMzE5LjU0NSAwIDEuMTc4LS4yNTIgMS40NjUtLjU5OGwuMDg4LS40MTZjLS4yLjE3Ni0uNDkyLjI0Ni0uNjg2LjI0Ni0uMjc1IDAtLjM3NS0uMTkzLS4zMDQtLjUzM0w4LjkzIDYuNTg4ek05IDQuNWExIDEgMCAxIDEtMiAwIDEgMSAwIDAgMSAyIDB6Ii8+PC9zdmc+'); + } + + &:hover { + background-color: unset; + } +} diff --git a/src/components/Editor/SimplifiedEditor.tsx b/src/components/Editor/SimplifiedEditor.tsx index ec6b0ac9..f8fd3939 100644 --- a/src/components/Editor/SimplifiedEditor.tsx +++ b/src/components/Editor/SimplifiedEditor.tsx @@ -1,4 +1,5 @@ import { createEffect, createSignal, onCleanup, onMount, Show } from 'solid-js' +import { Portal } from 'solid-js/web' import { createEditorTransaction, createTiptapEditor, @@ -33,14 +34,14 @@ import { Figcaption } from './extensions/Figcaption' import { TextBubbleMenu } from './TextBubbleMenu' import { BubbleMenu } from '@tiptap/extension-bubble-menu' import { CharacterCount } from '@tiptap/extension-character-count' -import { createStore } from 'solid-js/store' type Props = { + placeholder: string initialContent?: string label?: string onSubmit?: (text: string) => void + onCancel?: () => void onChange?: (text: string) => void - placeholder: string variant?: 'minimal' | 'bordered' maxLength?: number submitButtonText?: string @@ -179,6 +180,9 @@ const SimplifiedEditor = (props: Props) => { } const handleClear = () => { + if (props.onCancel) { + props.onCancel() + } editor().commands.clearContent(true) } @@ -204,7 +208,7 @@ const SimplifiedEditor = (props: Props) => { if (event.code === 'KeyK' && (event.metaKey || event.ctrlKey) && !editor().state.selection.empty) { event.preventDefault() - showModal('editorInsertLink') + showModal('simplifiedEditorInsertLink') } } @@ -222,7 +226,7 @@ const SimplifiedEditor = (props: Props) => { }) } - const handleInsertLink = () => !editor().state.selection.empty && showModal('editorInsertLink') + const handleInsertLink = () => !editor().state.selection.empty && showModal('simplifiedEditorInsertLink') createEffect(() => { if (html()) { @@ -306,7 +310,7 @@ const SimplifiedEditor = (props: Props) => {
-
- - hideModal()} /> - - - - { - renderImage(value) - }} - /> + + + hideModal()} /> + + + + + { + renderImage(value) + }} + /> + + { const [textSizeBubbleOpen, setTextSizeBubbleOpen] = createSignal(false) const [listBubbleOpen, setListBubbleOpen] = createSignal(false) const [linkEditorOpen, setLinkEditorOpen] = createSignal(false) + const [footnoteEditorOpen, setFootnoteEditorOpen] = createSignal(false) + const [footNote, setFootNote] = createSignal() const isBold = isActive('bold') const isItalic = isActive('italic') @@ -38,6 +43,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { const isBulletList = isActive('isBulletList') const isLink = isActive('link') const isHighlight = isActive('highlight') + const isFootnote = isActive('footnote') const toggleTextSizePopup = () => { if (listBubbleOpen()) { @@ -58,6 +64,28 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { } } + const currentFootnoteValue = createEditorTransaction( + () => props.editor, + (ed) => { + return (ed && ed.getAttributes('footnote').value) || '' + } + ) + + const handleAddFootnote = (footnote) => { + if (footNote()) { + props.editor.chain().focus().updateFootnote(footnote).run() + } else { + props.editor.chain().focus().setFootnote({ value: footnote }).run() + } + setFootNote() + setFootnoteEditorOpen(false) + } + + const handleOpenFootnoteEditor = () => { + setFootNote(currentFootnoteValue()) + setFootnoteEditorOpen(true) + } + onMount(() => { window.addEventListener('keydown', handleKeyDown) }) @@ -67,12 +95,26 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { }) return ( -
+
setLinkEditorOpen(false)} /> - + + )} diff --git a/src/components/Editor/extensions/Footnote.ts b/src/components/Editor/extensions/Footnote.ts new file mode 100644 index 00000000..327df0af --- /dev/null +++ b/src/components/Editor/extensions/Footnote.ts @@ -0,0 +1,104 @@ +import { mergeAttributes, Node } from '@tiptap/core' + +declare module '@tiptap/core' { + interface Commands { + Footnote: { + setFootnote: (options: { value: string }) => ReturnType + updateFootnote: (options: { value: string }) => ReturnType + deleteFootnote: () => ReturnType + } + } +} + +export const Footnote = Node.create({ + name: 'footnote', + addOptions() { + return { + HTMLAttributes: {} + } + }, + group: 'inline', + content: 'text*', + inline: true, + isolating: true, + + addAttributes() { + return { + value: { + default: null, + parseHTML: (element) => { + return { + value: element.dataset.value + } + }, + renderHTML: (attributes) => { + return { + 'data-value': attributes.value + } + } + } + } + }, + + parseHTML() { + return [ + { + tag: 'footnote' + } + ] + }, + + renderHTML({ HTMLAttributes }) { + return ['footnote', mergeAttributes(HTMLAttributes), 0] + }, + + addCommands() { + return { + setFootnote: + (attributes) => + ({ tr, state }) => { + const { selection } = state + const position = selection.$to.pos + + console.log('!!! attributes:', attributes) + const node = this.type.create(attributes) + tr.insert(position, node) + tr.insertText('\u00A0', position + 1) // it's make selection visible + return true + }, + updateFootnote: + (newValue) => + ({ tr, state }) => { + const { selection } = state + const { $from, $to } = selection + + if ($from.parent.type.name === 'footnote' || $to.parent.type.name === 'footnote') { + const node = $from.parent.type.name === 'footnote' ? $from.parent : $to.parent + const pos = $from.parent.type.name === 'footnote' ? $from.pos - 1 : $to.pos - 1 + + const newNode = node.type.create({ value: newValue }) + tr.setNodeMarkup(pos, null, newNode.attrs) + + return true + } + + return false + }, + deleteFootnote: + () => + ({ tr, state }) => { + const { selection } = state + const { $from, $to } = selection + + if ($from.parent.type.name === 'footnote' || $to.parent.type.name === 'footnote') { + const startPos = $from.start($from.depth) + const endPos = $to.end($to.depth) + tr.delete(startPos, endPos) + return true + } + + return false + } + } + } +}) diff --git a/src/components/Nav/AuthModal/RegisterForm.tsx b/src/components/Nav/AuthModal/RegisterForm.tsx index 6363eeb6..6b38e428 100644 --- a/src/components/Nav/AuthModal/RegisterForm.tsx +++ b/src/components/Nav/AuthModal/RegisterForm.tsx @@ -23,6 +23,10 @@ type FormFields = { type ValidationErrors = Partial> +const handleEmailInput = (newEmail: string) => { + setEmail(newEmail) +} + export const RegisterForm = () => { const { changeSearchParam } = useRouter() const { t } = useLocalize() @@ -38,10 +42,6 @@ export const RegisterForm = () => { const authFormRef: { current: HTMLFormElement } = { current: null } - const handleEmailInput = (newEmail: string) => { - setEmail(newEmail) - } - const handleEmailBlur = () => { if (validateEmail(email())) { checkEmail(email()) diff --git a/src/components/Nav/Modal/Modal.module.scss b/src/components/Nav/Modal/Modal.module.scss index f0b55f3e..6a9b655b 100644 --- a/src/components/Nav/Modal/Modal.module.scss +++ b/src/components/Nav/Modal/Modal.module.scss @@ -10,7 +10,7 @@ position: fixed; top: 0; width: 100%; - z-index: 11000; + z-index: 10002; } .modal { diff --git a/src/components/Views/PublishSettings/PublishSettings.tsx b/src/components/Views/PublishSettings/PublishSettings.tsx index baa4e5ce..b9f62582 100644 --- a/src/components/Views/PublishSettings/PublishSettings.tsx +++ b/src/components/Views/PublishSettings/PublishSettings.tsx @@ -38,7 +38,8 @@ export const PublishSettings = (props: Props) => { const composeDescription = () => { if (!props.form.description) { - const leadText = props.form.body.replaceAll(/<\/?[^>]+(>|$)/gi, ' ') + const cleanFootnotes = props.form.body.replaceAll(/.*?<\/footnote>/g, '') + const leadText = cleanFootnotes.replaceAll(/<\/?[^>]+(>|$)/gi, ' ') return shorten(leadText, MAX_DESCRIPTION_LIMIT).trim() } return props.form.description diff --git a/src/context/editor.tsx b/src/context/editor.tsx index d65080c2..75812c53 100644 --- a/src/context/editor.tsx +++ b/src/context/editor.tsx @@ -92,15 +92,12 @@ export const EditorProvider = (props: { children: JSX.Element }) => { const [form, setForm] = createStore(null) const [formErrors, setFormErrors] = createStore>(null) - const [wordCounter, setWordCounter] = createSignal({ characters: 0, words: 0 }) - const toggleEditorPanel = () => setIsEditorPanelVisible((value) => !value) const countWords = (value) => setWordCounter(value) - const validate = () => { if (!form.title) { setFormErrors('title', t('Required')) diff --git a/src/stores/ui.ts b/src/stores/ui.ts index ed376d7a..b01bd52d 100644 --- a/src/stores/ui.ts +++ b/src/stores/ui.ts @@ -16,8 +16,10 @@ export type ModalType = | 'donate' | 'inviteToChat' | 'uploadImage' + | 'simplifiedEditorUploadImage' | 'uploadCoverImage' | 'editorInsertLink' + | 'simplifiedEditorInsertLink' type WarnKind = 'error' | 'warn' | 'info' @@ -36,8 +38,10 @@ export const MODALS: Record = { donate: 'donate', inviteToChat: 'inviteToChat', uploadImage: 'uploadImage', + simplifiedEditorUploadImage: 'simplifiedEditorUploadImage', uploadCoverImage: 'uploadCoverImage', - editorInsertLink: 'editorInsertLink' + editorInsertLink: 'editorInsertLink', + simplifiedEditorInsertLink: 'simplifiedEditorInsertLink' } const [modal, setModal] = createSignal(null) From ae7c3e92bdd43a3ae5404c79f850582470f5ab6c Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Wed, 30 Aug 2023 23:55:33 +0300 Subject: [PATCH 19/42] Banner text fixes --- public/locales/en/translation.json | 4 +--- public/locales/ru/translation.json | 6 ++---- src/components/Discours/Hero.tsx | 16 ++++------------ 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 31222f03..2203af75 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -91,7 +91,7 @@ "Delete cover": "Delete cover", "Description": "Description", "Discours": "Discours", - "Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects": "Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects", + "Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects": "Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects.
We are convinced that one voice is good, but many is better. We create the most amazing stories together", "Discours is created with our common effort": "Discours exists because of our common effort", "Discussing": "Discussing", "Discussion rules": "Discussion rules", @@ -325,9 +325,7 @@ "Video": "Video", "Video format not supported": "Video format not supported", "Views": "Views", - "We are convinced that one voice is good, but many is better": "We are convinced that one voice is good, but many is better", "We can't find you, check email or": "We can't find you, check email or", - "We create the most amazing stories together": "We create the most amazing stories together", "We know you, please try to login": "This email address is already registered, please try to login", "We've sent you a message with a link to enter our website.": "We've sent you an email with a link to your email. Follow the link in the email to enter our website.", "Where": "From", diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index 47d0ff9a..9c86016f 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -95,7 +95,7 @@ "Delete cover": "Удалить обложку", "Description": "Описание", "Discours": "Дискурс", - "Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects": "Дискурс — это интеллектуальная среда, веб-пространство и инструменты, которые позволяют авторам сотрудничать с читателями и объединяться для совместного создания публикаций и медиапроектов", + "Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects": "Дискурс — это интеллектуальная среда, веб-пространство и инструменты, которые позволяют авторам сотрудничать с читателями и объединяться для совместного создания публикаций и медиапроектов.
Мы убеждены, один голос хорошо, а много — лучше. Самые потрясающиe истории мы создаём вместе.", "Discours is created with our common effort": "Дискурс существует благодаря нашему общему вкладу", "Discussing": "Обсуждаемое", "Discussion rules": "Правила сообществ самиздата в соцсетях", @@ -155,7 +155,7 @@ "Hide table of contents": "Скрыть главление", "Highlight": "Подсветка", "Hooray! Welcome!": "Ура! Добро пожаловать!", - "Horizontal collaborative journalistic platform": "Горизонтальная платформа для коллаборативной журналистики", + "Horizontal collaborative journalistic platform": "Горизонтальная платформа
для коллаборативной журналистики", "Hot topics": "Горячие темы", "Hotkeys": "Горячие клавиши", "How can I help/skills": "Чем могу помочь/навыки", @@ -342,9 +342,7 @@ "Video": "Видео", "Video format not supported": "Тип видео не поддерживается", "Views": "Просмотры", - "We are convinced that one voice is good, but many is better": "Мы убеждены, один голос хорошо, а много — лучше", "We can't find you, check email or": "Не можем вас найти, проверьте адрес электронной почты или", - "We create the most amazing stories together": "Самые потрясающиe истории мы создаём вместе", "We know you, please try to login": "Такой адрес почты уже зарегистрирован, попробуйте залогиниться", "We've sent you a message with a link to enter our website.": "Мы выслали вам письмо с ссылкой на почту. Перейдите по ссылке в письме, чтобы войти на сайт.", "Welcome!": "Добро пожаловать!", diff --git a/src/components/Discours/Hero.tsx b/src/components/Discours/Hero.tsx index 136dfc4b..61f999f9 100644 --- a/src/components/Discours/Hero.tsx +++ b/src/components/Discours/Hero.tsx @@ -10,20 +10,12 @@ export default () => {
-

{t('Horizontal collaborative journalistic platform')}

-

- {t( +

+

- - {t('We are convinced that one voice is good, but many is better') + - '. ' + - t('We create the most amazing stories together')} - . - -

+ />
{t('Create post')} From 14953657ecccf188035c372a08123aeb1837452b Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Wed, 30 Aug 2023 23:59:11 +0300 Subject: [PATCH 20/42] Fixed error page width on mobile --- src/styles/FourOuFour.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/FourOuFour.module.scss b/src/styles/FourOuFour.module.scss index f3c6b86f..09a87b79 100644 --- a/src/styles/FourOuFour.module.scss +++ b/src/styles/FourOuFour.module.scss @@ -44,7 +44,7 @@ .errorText { font-weight: 300; left: 52px; - margin-bottom: 1em; + margin: 0 60px 1em 0; position: relative; top: -2.25em; From e4a2e07c8e076c3f6b75fb69b43fc3ffb0c2655e Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Thu, 31 Aug 2023 00:16:55 +0300 Subject: [PATCH 21/42] Add social icons --- public/icons/user-link-behance.svg | 6 ++ public/icons/user-link-dribbble.svg | 4 + public/icons/user-link-dzen.svg | 4 + public/icons/user-link-github.svg | 3 + public/icons/user-link-linkedin.svg | 6 ++ public/icons/user-link-medium.svg | 3 + public/icons/user-link-ok.svg | 5 ++ public/icons/user-link-pinterest.svg | 3 + public/icons/user-link-reddit.svg | 4 + public/icons/user-link-tiktok.svg | 3 + public/icons/user-link-twitter.svg | 2 +- public/icons/user-link-vk.svg | 3 + public/icons/user-link-youtube.svg | 3 + src/components/Author/AuthorCard.module.scss | 77 +++++++++++++++++++- 14 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 public/icons/user-link-behance.svg create mode 100644 public/icons/user-link-dribbble.svg create mode 100644 public/icons/user-link-dzen.svg create mode 100644 public/icons/user-link-github.svg create mode 100644 public/icons/user-link-linkedin.svg create mode 100644 public/icons/user-link-medium.svg create mode 100644 public/icons/user-link-ok.svg create mode 100644 public/icons/user-link-pinterest.svg create mode 100644 public/icons/user-link-reddit.svg create mode 100644 public/icons/user-link-tiktok.svg create mode 100644 public/icons/user-link-vk.svg create mode 100644 public/icons/user-link-youtube.svg diff --git a/public/icons/user-link-behance.svg b/public/icons/user-link-behance.svg new file mode 100644 index 00000000..f9ccd2cc --- /dev/null +++ b/public/icons/user-link-behance.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/icons/user-link-dribbble.svg b/public/icons/user-link-dribbble.svg new file mode 100644 index 00000000..759ccd9d --- /dev/null +++ b/public/icons/user-link-dribbble.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/user-link-dzen.svg b/public/icons/user-link-dzen.svg new file mode 100644 index 00000000..e730bde0 --- /dev/null +++ b/public/icons/user-link-dzen.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/user-link-github.svg b/public/icons/user-link-github.svg new file mode 100644 index 00000000..b7c1ef2d --- /dev/null +++ b/public/icons/user-link-github.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/user-link-linkedin.svg b/public/icons/user-link-linkedin.svg new file mode 100644 index 00000000..973dadd8 --- /dev/null +++ b/public/icons/user-link-linkedin.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/icons/user-link-medium.svg b/public/icons/user-link-medium.svg new file mode 100644 index 00000000..38cc100c --- /dev/null +++ b/public/icons/user-link-medium.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/user-link-ok.svg b/public/icons/user-link-ok.svg new file mode 100644 index 00000000..99faa68b --- /dev/null +++ b/public/icons/user-link-ok.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/user-link-pinterest.svg b/public/icons/user-link-pinterest.svg new file mode 100644 index 00000000..f9602b73 --- /dev/null +++ b/public/icons/user-link-pinterest.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/user-link-reddit.svg b/public/icons/user-link-reddit.svg new file mode 100644 index 00000000..ddfce137 --- /dev/null +++ b/public/icons/user-link-reddit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/user-link-tiktok.svg b/public/icons/user-link-tiktok.svg new file mode 100644 index 00000000..8741f6c9 --- /dev/null +++ b/public/icons/user-link-tiktok.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/user-link-twitter.svg b/public/icons/user-link-twitter.svg index cb8cb03e..8cdfcc67 100644 --- a/public/icons/user-link-twitter.svg +++ b/public/icons/user-link-twitter.svg @@ -1,3 +1,3 @@ - + diff --git a/public/icons/user-link-vk.svg b/public/icons/user-link-vk.svg new file mode 100644 index 00000000..d2a49375 --- /dev/null +++ b/public/icons/user-link-vk.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/user-link-youtube.svg b/public/icons/user-link-youtube.svg new file mode 100644 index 00000000..25a90f08 --- /dev/null +++ b/public/icons/user-link-youtube.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Author/AuthorCard.module.scss b/src/components/Author/AuthorCard.module.scss index 9ae93a71..69e39749 100644 --- a/src/components/Author/AuthorCard.module.scss +++ b/src/components/Author/AuthorCard.module.scss @@ -130,13 +130,13 @@ a[href*='vk.cc/'], a[href*='vk.com/'] { &::before { - background-image: url(/icons/vk-white.svg); + background-image: url(/icons/user-link-vk.svg); } } a[href*='tumblr.com/'] { &::before { - background-image: url(/icons/tumblr-white.svg); + background-image: url(/icons/user-link-tumblr.svg); } } @@ -146,6 +146,79 @@ } } + a[href*='behance.net/'] { + &::before { + background-image: url(/icons/user-link-behance.svg); + } + } + + a[href*='dribbble.com/'] { + &::before { + background-image: url(/icons/user-link-dribbble.svg); + } + } + + a[href*='github.com/'] { + &::before { + background-image: url(/icons/user-link-github.svg); + } + } + + a[href*='linkedin.com/'] { + &::before { + background-image: url(/icons/user-link-linkedin.svg); + } + } + + a[href*='medium.com/'] { + &::before { + background-image: url(/icons/user-link-medium.svg); + } + } + + a[href*='ok.ru/'] { + &::before { + background-image: url(/icons/user-link-ok.svg); + } + } + + a[href*='pinterest.com/'] { + &::before { + background-image: url(/icons/user-link-pinterest.svg); + } + } + + a[href*='reddit.com/'] { + &::before { + background-image: url(/icons/user-link-reddit.svg); + } + } + + a[href*='tiktok.com/'] { + &::before { + background-image: url(/icons/user-link-tiktok.svg); + } + } + + a[href*='vk.com/'] { + &::before { + background-image: url(/icons/user-link-vk.svg); + } + } + + a[href*='youtube.com/'], + a[href*='youtu.be/'] { + &::before { + background-image: url(/icons/user-link-youtube.svg); + } + } + + a[href*='dzen.ru/'] { + &::before { + background-image: url(/icons/user-link-dzen.svg); + } + } + .button { margin-right: 0.5em; width: 9em; From 031d89881ff895ef949bf9f340bb7df0f4a2a9c7 Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Thu, 31 Aug 2023 00:30:15 +0300 Subject: [PATCH 22/42] Fixed error messages style --- .../Nav/AuthModal/AuthModal.module.scss | 10 ++++---- .../Nav/AuthModal/ForgotPasswordForm.tsx | 7 +++--- src/components/Nav/AuthModal/LoginForm.tsx | 12 +++++----- src/components/Nav/AuthModal/RegisterForm.tsx | 24 ++++++++++--------- src/styles/app.scss | 1 + 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/components/Nav/AuthModal/AuthModal.module.scss b/src/components/Nav/AuthModal/AuthModal.module.scss index 361cebbe..1e097b54 100644 --- a/src/components/Nav/AuthModal/AuthModal.module.scss +++ b/src/components/Nav/AuthModal/AuthModal.module.scss @@ -141,9 +141,10 @@ } .authInfo { - min-height: 5em; font-weight: 400; font-size: smaller; + margin-top: -2em; + position: absolute; .warn { color: #a00; @@ -158,6 +159,7 @@ h4 { font-weight: bold; + margin-bottom: 1em; } } @@ -166,11 +168,11 @@ } .validationError { - position: relative; - top: -8px; + position: absolute; + top: 100%; font-size: 12px; line-height: 16px; - margin-bottom: 8px; + margin-top: 0.3em; &.registerPassword { margin-bottom: -32px; diff --git a/src/components/Nav/AuthModal/ForgotPasswordForm.tsx b/src/components/Nav/AuthModal/ForgotPasswordForm.tsx index 19278d13..48d58603 100644 --- a/src/components/Nav/AuthModal/ForgotPasswordForm.tsx +++ b/src/components/Nav/AuthModal/ForgotPasswordForm.tsx @@ -120,13 +120,12 @@ export const ForgotPasswordForm = () => { > {t('register')} + +
{validationErrors().email}
+
- -
{validationErrors().email}
-
-
- -
{validationErrors().email}
-
{ > + +
{validationErrors().password}
+
- -
{validationErrors().password}
-
- -
{validationErrors().fullName}
-
+
{ onBlur={handleEmailBlur} /> + +
{validationErrors().email}
+
- -
{validationErrors().email}
-
+
{t("This email is already taken. If it's you")},{' '} @@ -226,12 +228,12 @@ export const RegisterForm = () => { > + +
+ {validationErrors().password} +
+
- -
- {validationErrors().password} -
-
- - -
+ +
  • + +
  • +
  • + +
  • + +
    +
    + + {(subscription: Author | Topic) => ( +
    + {isAuthor(subscription) ? ( + + ) : ( + + )} +
    + )} +
    +
    +
    + + +
    ) } diff --git a/src/components/Author/AuthorCard/index.ts b/src/components/Author/AuthorCard/index.ts new file mode 100644 index 00000000..ceaabe6d --- /dev/null +++ b/src/components/Author/AuthorCard/index.ts @@ -0,0 +1 @@ +export { AuthorCard } from './AuthorCard' diff --git a/src/components/Author/Full.module.scss b/src/components/Author/Full.module.scss deleted file mode 100644 index 0fe5e7bc..00000000 --- a/src/components/Author/Full.module.scss +++ /dev/null @@ -1,9 +0,0 @@ -.userDetails { - border-bottom: 2px solid #000; - margin: 0 0 3.6rem; - padding-bottom: 3.6rem; - - @include media-breakpoint-down(md) { - text-align: center; - } -} diff --git a/src/components/Author/Full.tsx b/src/components/Author/Full.tsx deleted file mode 100644 index 73395e37..00000000 --- a/src/components/Author/Full.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type { Author } from '../../graphql/types.gen' -import { AuthorCard } from './AuthorCard' -import styles from './Full.module.scss' -import clsx from 'clsx' - -export const AuthorFull = (props: { author: Author }) => { - return ( -
    - -
    - ) -} diff --git a/src/components/Views/Author/Author.module.scss b/src/components/Views/Author/Author.module.scss index 3cfd2bbc..6f176203 100644 --- a/src/components/Views/Author/Author.module.scss +++ b/src/components/Views/Author/Author.module.scss @@ -34,56 +34,6 @@ } } -.userpic { - background: #fff; - box-shadow: 0 0 0 2px #fff; - display: inline-block; - margin-right: -1.2rem; - vertical-align: top; -} - -.subscribers { - cursor: pointer; - display: inline-block; - margin: -0.4rem 2em 0 0; - vertical-align: top; -} - -.subscribersCounter { - @include font-size(1rem); - background: #fff; - border: 2px solid #000; - border-radius: 100%; - font-weight: bold; - height: 32px; - line-height: 30px; - position: relative; - text-align: center; - width: 32px; - z-index: 1; -} - -.subscribersList { - max-height: 15em; - overflow: auto; - position: relative; - - .subscriber { - white-space: nowrap; - display: flex; - flex-direction: row; - align-items: center; - margin: 0; - border-radius: 4px; - padding: 8px 4px; - transition: background 0.2s ease-in-out; - - &:hover { - background: #f7f7f7; - } - } -} - .loadingWrapper { position: relative; min-height: 40vh; diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx index adb986d0..d7e6f1e8 100644 --- a/src/components/Views/Author/Author.tsx +++ b/src/components/Views/Author/Author.tsx @@ -3,7 +3,6 @@ import type { Author, Shout, Topic } from '../../../graphql/types.gen' import { Row1 } from '../../Feed/Row1' import { Row2 } from '../../Feed/Row2' import { Row3 } from '../../Feed/Row3' -import { AuthorFull } from '../../Author/Full' import { useAuthorsStore } from '../../../stores/zine/authors' import { loadShouts, useArticlesStore } from '../../../stores/zine/articles' @@ -13,8 +12,6 @@ import { splitToPages } from '../../../utils/splitToPages' import styles from './Author.module.scss' import stylesArticle from '../../Article/Article.module.scss' import { clsx } from 'clsx' -import { Userpic } from '../../Author/Userpic' -import { Popup } from '../../_shared/Popup' import { AuthorCard } from '../../Author/AuthorCard' import { apiClient } from '../../../utils/apiClient' import { Comment } from '../../Article/Comment' @@ -22,6 +19,7 @@ import { useLocalize } from '../../../context/localize' import { AuthorRatingControl } from '../../Author/AuthorRatingControl' import { TopicCard } from '../../Topic/Card' import { Loading } from '../../_shared/Loading' +import { hideModal } from '../../../stores/ui' type AuthorProps = { shouts: Shout[] @@ -30,24 +28,12 @@ type AuthorProps = { } export type AuthorPageSearchParams = { - by: - | '' - | 'viewed' - | 'rating' - | 'commented' - | 'recent' - | 'subscriptions' - | 'followers' - | 'about' - | 'popular' + by: '' | 'viewed' | 'rating' | 'commented' | 'recent' | 'about' | 'popular' } export const PRERENDERED_ARTICLES_COUNT = 12 const LOAD_MORE_PAGE_SIZE = 9 -function isAuthor(value: Author | Topic): value is Author { - return 'name' in value -} export const AuthorView = (props: AuthorProps) => { const { t } = useLocalize() const { sortedArticles } = useArticlesStore({ shouts: props.shouts }) @@ -59,7 +45,6 @@ export const AuthorView = (props: AuthorProps) => { const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) const [followers, setFollowers] = createSignal([]) const [subscriptions, setSubscriptions] = createSignal>([]) - const [isLoaded, setIsLoaded] = createSignal() const fetchSubscriptions = async (): Promise<{ authors: Author[]; topics: Topic[] }> => { try { @@ -77,6 +62,7 @@ export const AuthorView = (props: AuthorProps) => { } onMount(async () => { + hideModal() try { const userSubscribers = await apiClient.getAuthorFollowers({ slug: props.authorSlug }) setFollowers(userSubscribers) @@ -89,6 +75,8 @@ export const AuthorView = (props: AuthorProps) => { if (sortedArticles().length === PRERENDERED_ARTICLES_COUNT) { await loadMore() } + const { authors, topics } = await fetchSubscriptions() + setSubscriptions([...authors, ...topics]) }) const loadMore = async () => { @@ -118,13 +106,6 @@ export const AuthorView = (props: AuthorProps) => { const [commented, setCommented] = createSignal([]) createEffect(async () => { - if (searchParams().by === 'subscriptions') { - setIsLoaded(false) - const { authors, topics } = await fetchSubscriptions() - setSubscriptions([...authors, ...topics]) - setIsLoaded(true) - } - if (searchParams().by === 'commented') { try { const data = await apiClient.getReactionsBy({ @@ -140,7 +121,12 @@ export const AuthorView = (props: AuthorProps) => {
    - +
    @@ -150,16 +136,6 @@ export const AuthorView = (props: AuthorProps) => { {t('Publications')} -
  • - -
  • -
  • - -
  • - - - - - {(f) => } - - - 3}> - - {(f) => } - -
    - {followers().length} -
    -
    -
    -
    - } - variant="tiny" - > -
      - - {(item: Author) => ( -
    • - -
    • - )} -
      -
    - -
    {t('Karma')} @@ -239,52 +176,7 @@ export const AuthorView = (props: AuthorProps) => {
    - -
    -
    - - {(follower: Author) => ( -
    - -
    - )} -
    -
    -
    -
    - -
    -
    - - -
    - } - > - - {(subscription: Author | Topic) => ( -
    - {isAuthor(subscription) ? ( -
    - -
    - ) : ( - - )} -
    - )} -
    - -
    -
    - + diff --git a/src/components/Views/Feed.tsx b/src/components/Views/Feed.tsx index c763f9da..a37dd66a 100644 --- a/src/components/Views/Feed.tsx +++ b/src/components/Views/Feed.tsx @@ -55,13 +55,18 @@ export const FeedView = () => { actions: { loadReactionsBy } } = useReactions() + onMount(() => { + loadMore() + }) + createEffect( on( () => page().route + searchParams().by, () => { resetSortedArticles() loadMore() - } + }, + { defer: true } ) ) diff --git a/src/components/_shared/SolidSwiper/SolidSwiper.tsx b/src/components/_shared/SolidSwiper/SolidSwiper.tsx index 4417dd06..0c670873 100644 --- a/src/components/_shared/SolidSwiper/SolidSwiper.tsx +++ b/src/components/_shared/SolidSwiper/SolidSwiper.tsx @@ -63,7 +63,8 @@ export const SolidSwiper = (props: Props) => { () => { mainSwipeRef.current?.swiper.update() thumbSwipeRef.current?.swiper.update() - } + }, + { defer: true } ) ) @@ -95,7 +96,7 @@ export const SolidSwiper = (props: Props) => { const results: UploadedFile[] = [] for (const file of selectedFiles) { const result = await handleFileUpload(file) - results.push(result.url) + results.push(result) } props.onImagesAdd(composeMediaItems(results)) setLoading(false) diff --git a/src/pages/author.page.tsx b/src/pages/author.page.tsx index 7346f789..c0533f19 100644 --- a/src/pages/author.page.tsx +++ b/src/pages/author.page.tsx @@ -17,14 +17,15 @@ export const AuthorPage = (props: PageProps) => { Boolean(props.authorShouts) && Boolean(props.author) && props.author.slug === slug() ) - const preload = () => - Promise.all([ + const preload = () => { + return Promise.all([ loadShouts({ filters: { author: slug(), visibility: 'community' }, limit: PRERENDERED_ARTICLES_COUNT }), loadAuthor({ slug: slug() }) ]) + } onMount(async () => { if (isLoaded()) { @@ -44,7 +45,8 @@ export const AuthorPage = (props: PageProps) => { resetSortedArticles() await preload() setIsLoaded(true) - } + }, + { defer: true } ) ) diff --git a/src/pages/topic.page.tsx b/src/pages/topic.page.tsx index c1f3d3c9..8988a719 100644 --- a/src/pages/topic.page.tsx +++ b/src/pages/topic.page.tsx @@ -41,7 +41,8 @@ export const TopicPage = (props: PageProps) => { resetSortedArticles() await preload() setIsLoaded(true) - } + }, + { defer: true } ) ) diff --git a/src/stores/ui.ts b/src/stores/ui.ts index b01bd52d..1477d57d 100644 --- a/src/stores/ui.ts +++ b/src/stores/ui.ts @@ -20,6 +20,8 @@ export type ModalType = | 'uploadCoverImage' | 'editorInsertLink' | 'simplifiedEditorInsertLink' + | 'followers' + | 'subscriptions' type WarnKind = 'error' | 'warn' | 'info' @@ -41,7 +43,9 @@ export const MODALS: Record = { simplifiedEditorUploadImage: 'simplifiedEditorUploadImage', uploadCoverImage: 'uploadCoverImage', editorInsertLink: 'editorInsertLink', - simplifiedEditorInsertLink: 'simplifiedEditorInsertLink' + simplifiedEditorInsertLink: 'simplifiedEditorInsertLink', + followers: 'followers', + subscriptions: 'subscriptions' } const [modal, setModal] = createSignal(null) diff --git a/src/stores/zine/articles.ts b/src/stores/zine/articles.ts index b333aa99..6335513b 100644 --- a/src/stores/zine/articles.ts +++ b/src/stores/zine/articles.ts @@ -1,7 +1,6 @@ import type { Author, Shout, ShoutInput, Topic, LoadShoutsOptions } from '../../graphql/types.gen' import { apiClient } from '../../utils/apiClient' import { addAuthorsByTopic } from './authors' -import { addTopicsByAuthor } from './topics' import { byStat } from '../../utils/sortby' import { createSignal } from 'solid-js' import { createLazyMemo } from '@solid-primitives/memo' @@ -97,26 +96,6 @@ const addArticles = (...args: Shout[][]) => { }, {} as { [topicSlug: string]: Author[] }) addAuthorsByTopic(authorsByTopic) - - const topicsByAuthor = allArticles.reduce((acc, article) => { - const { authors, topics } = article - - authors.forEach((author) => { - if (!acc[author.slug]) { - acc[author.slug] = [] - } - - topics.forEach((topic) => { - if (!acc[author.slug].some((t) => t.slug === topic.slug)) { - acc[author.slug].push(topic) - } - }) - }) - - return acc - }, {} as { [authorSlug: string]: Topic[] }) - - addTopicsByAuthor(topicsByAuthor) } const addSortedArticles = (articles: Shout[]) => { diff --git a/src/stores/zine/authors.ts b/src/stores/zine/authors.ts index c133b8ec..df3f3385 100644 --- a/src/stores/zine/authors.ts +++ b/src/stores/zine/authors.ts @@ -1,6 +1,6 @@ import { apiClient } from '../../utils/apiClient' import type { Author } from '../../graphql/types.gen' -import { createSignal } from 'solid-js' +import { createEffect, createSignal } from 'solid-js' import { createLazyMemo } from '@solid-primitives/memo' import { byStat } from '../../utils/sortby' @@ -38,12 +38,18 @@ const addAuthors = (authors: Author[]) => { return acc }, {} as Record) - setAuthorEntities((prevAuthorEntities) => { - return { - ...prevAuthorEntities, - ...newAuthorEntities - } - }) + setAuthorEntities((prevAuthorEntities) => + Object.keys(newAuthorEntities).reduce( + (acc, authorSlug) => { + acc[authorSlug] = { + ...acc[authorSlug], + ...newAuthorEntities[authorSlug] + } + return acc + }, + { ...prevAuthorEntities } + ) + ) } export const loadAuthor = async ({ slug }: { slug: string }): Promise => { diff --git a/src/stores/zine/topics.ts b/src/stores/zine/topics.ts index 568695b0..c8d81caf 100644 --- a/src/stores/zine/topics.ts +++ b/src/stores/zine/topics.ts @@ -12,7 +12,6 @@ export const setTopicsSort = (sortBy: TopicsSortBy) => setSortAllBy(sortBy) const [topicEntities, setTopicEntities] = createSignal<{ [topicSlug: string]: Topic }>({}) const [randomTopics, setRandomTopics] = createSignal([]) -const [topicsByAuthor, setTopicByAuthor] = createSignal<{ [authorSlug: string]: Topic[] }>({}) const sortedTopics = createLazyMemo(() => { const topics = Object.values(topicEntities()) @@ -68,27 +67,6 @@ const addTopics = (...args: Topic[][]) => { }) } -export const addTopicsByAuthor = (newTopicsByAuthors: { [authorSlug: string]: Topic[] }) => { - const allTopics = Object.values(newTopicsByAuthors).flat() - addTopics(allTopics) - - setTopicByAuthor((prevTopicsByAuthor) => { - return Object.entries(newTopicsByAuthors).reduce((acc, [authorSlug, topics]) => { - if (!acc[authorSlug]) { - acc[authorSlug] = [] - } - - topics.forEach((topic) => { - if (!acc[authorSlug].some((t) => t.slug === topic.slug)) { - acc[authorSlug].push(topic) - } - }) - - return acc - }, prevTopicsByAuthor) - }) -} - export const loadAllTopics = async (): Promise => { const topics = await apiClient.getAllTopics() addTopics(topics) @@ -121,5 +99,5 @@ export const useTopicsStore = (initialState: InitialState = {}) => { setRandomTopics(initialState.randomTopics) } - return { topicEntities, sortedTopics, randomTopics, topTopics, topicsByAuthor } + return { topicEntities, sortedTopics, randomTopics, topTopics } } diff --git a/src/utils/getNumeralsDeclension.ts b/src/utils/getNumeralsDeclension.ts new file mode 100644 index 00000000..dafc15c0 --- /dev/null +++ b/src/utils/getNumeralsDeclension.ts @@ -0,0 +1,3 @@ +// Usage in tsx: {getNumeralsDeclension(NUMBER, ['яблоко', 'яблока', 'яблок'])} +export const getNumeralsDeclension = (number: number, words: string[], cases = [2, 0, 1, 1, 1, 2]) => + words[number % 100 > 4 && number % 100 < 20 ? 2 : cases[number % 10 < 5 ? number % 10 : 5]] diff --git a/src/utils/handleFileUpload.ts b/src/utils/handleFileUpload.ts index c9c925d4..ddf5d4ef 100644 --- a/src/utils/handleFileUpload.ts +++ b/src/utils/handleFileUpload.ts @@ -1,9 +1,10 @@ import { UploadFile } from '@solid-primitives/upload' import { apiBaseUrl } from './config' +import { UploadedFile } from '../pages/types' const apiUrl = `${apiBaseUrl}/upload` -export const handleFileUpload = async (uploadFile: UploadFile) => { +export const handleFileUpload = async (uploadFile: UploadFile): Promise => { const formData = new FormData() formData.append('file', uploadFile.file, uploadFile.name) const response = await fetch(apiUrl, { From 0e9a75ba6b8a56416b9142a31095870c2890199f Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Fri, 1 Sep 2023 17:46:07 +0300 Subject: [PATCH 33/42] subscribtions_tabs hotfix (#204) --- src/components/Author/AuthorCard/AuthorCard.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Author/AuthorCard/AuthorCard.tsx b/src/components/Author/AuthorCard/AuthorCard.tsx index d73f3022..0eec0536 100644 --- a/src/components/Author/AuthorCard/AuthorCard.tsx +++ b/src/components/Author/AuthorCard/AuthorCard.tsx @@ -341,17 +341,17 @@ export const AuthorCard = (props: AuthorCardProps) => { <>

    {t('Subscriptions')}

      -
    • +
    • -
    • +
    • -
    • +
    • From 7df56405c49cc0123097dd706d0c770484eb56e4 Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Sat, 2 Sep 2023 20:13:58 +0300 Subject: [PATCH 34/42] subscribtions_tabs hotfix (#205) --- src/graphql/query/author-followers.ts | 1 + src/graphql/query/author-following-users.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/graphql/query/author-followers.ts b/src/graphql/query/author-followers.ts index 13cf82b0..cccd202c 100644 --- a/src/graphql/query/author-followers.ts +++ b/src/graphql/query/author-followers.ts @@ -7,6 +7,7 @@ export default gql` slug name userpic + bio } } ` diff --git a/src/graphql/query/author-following-users.ts b/src/graphql/query/author-following-users.ts index 9b7df27e..1dc05858 100644 --- a/src/graphql/query/author-following-users.ts +++ b/src/graphql/query/author-following-users.ts @@ -7,6 +7,7 @@ export default gql` slug name userpic + bio } } ` From 50baf5b60ff39ae2ca136f4c37f1eb992d6bf643 Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Sat, 2 Sep 2023 21:04:12 +0300 Subject: [PATCH 35/42] Feature/add stat in author card (#206) * add stat in AuthorCard --- src/components/Author/AuthorCard/AuthorCard.tsx | 4 ++-- src/graphql/query/author-followers.ts | 3 +++ src/graphql/query/author-following-users.ts | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/Author/AuthorCard/AuthorCard.tsx b/src/components/Author/AuthorCard/AuthorCard.tsx index 0eec0536..5257495c 100644 --- a/src/components/Author/AuthorCard/AuthorCard.tsx +++ b/src/components/Author/AuthorCard/AuthorCard.tsx @@ -173,8 +173,8 @@ export const AuthorCard = (props: AuthorCardProps) => { {name()}
    - - + {/*TODO: implement plurals by i18n*/} + {props.author.stat?.shouts} публикаций
    }>
    Date: Sun, 3 Sep 2023 01:14:34 +0300 Subject: [PATCH 36/42] Author page fixes --- .../Author/AuthorCard/AuthorCard.module.scss | 30 +- .../Author/AuthorCard/AuthorCard.tsx | 412 ++++++++++-------- src/components/Feed/ArticleCard.module.scss | 12 +- src/components/Topic/Card.module.scss | 1 + 4 files changed, 256 insertions(+), 199 deletions(-) diff --git a/src/components/Author/AuthorCard/AuthorCard.module.scss b/src/components/Author/AuthorCard/AuthorCard.module.scss index 460be48a..fa6574c4 100644 --- a/src/components/Author/AuthorCard/AuthorCard.module.scss +++ b/src/components/Author/AuthorCard/AuthorCard.module.scss @@ -14,10 +14,13 @@ } .authorDetails { - align-items: baseline; - display: flex; flex: 1; + @include media-breakpoint-up(sm) { + align-items: baseline; + display: flex; + } + &.authorDetailsShrinked { flex: 0 0 auto; } @@ -62,7 +65,7 @@ .authorAbout { color: rgb(0 0 0 / 60%); - font-size: 1.5rem; + font-size: 1.4rem; line-height: 1.4; word-break: break-word; } @@ -442,11 +445,19 @@ } } +.subscribersContainer { + display: flex; + flex-wrap: wrap; + font-size: 1.4rem; + margin-top: 1em; +} + .subscribers { + align-items: center; cursor: pointer; display: inline-flex; + margin-right: 3rem; vertical-align: top; - align-items: center; .userpic { background: var(--background-color); @@ -459,7 +470,12 @@ } } -.listWrapper { - overflow: auto; - max-height: 70vh; +.subscribersCounter { + margin-left: -0.6rem; +} + +.listWrapper { + max-height: 70vh; + overflow: auto; + padding-right: 2rem; } diff --git a/src/components/Author/AuthorCard/AuthorCard.tsx b/src/components/Author/AuthorCard/AuthorCard.tsx index 5257495c..8bd7bc79 100644 --- a/src/components/Author/AuthorCard/AuthorCard.tsx +++ b/src/components/Author/AuthorCard/AuthorCard.tsx @@ -39,6 +39,7 @@ type AuthorCardProps = { class?: string followers?: Author[] subscriptions?: Array + showPublicationsCounter?: boolean } function isAuthor(value: Author | Topic): value is Author { @@ -115,128 +116,168 @@ export const AuthorCard = (props: AuthorCardProps) => { }) return ( -
    - - } - > -
    - -
    -
    - + <>
    -
    -
    - ( - - {children} - - )} - > - {name()} - -
    - {/*TODO: implement plurals by i18n*/} - {props.author.stat?.shouts} публикаций
    }> -
    - + } + > +
    + +
    + - 0}> -
    showModal('followers')}> - - {(f) => } - -
    - {props.followers.length}  - {getNumeralsDeclension(props.followers.length, [ - t('subscriber'), - t('subscriber_rp'), - t('subscribers') - ])} -
    +
    +
    +
    + ( + + {children} + + )} + > + {name()} +
    - - 0}> -
    -
    showModal('subscriptions')}> - - {(f) => { - if ('name' in f) { - return - } else if ('title' in f) { - return - } - return null - }} - -
    - {props.subscriptions.length}  - {getNumeralsDeclension(props.subscriptions.length, [ - t('subscription'), - t('subscription_rp'), - t('subscriptions') - ])} -
    -
    -
    -
    -
    - - - -
    - - + ) : ( + '' + ) + } + > +
    + + + 0) || + (props.subscriptions && props.subscriptions.length > 0) + } + > +
    + 0}> +
    showModal('followers')}> + + {(f) => } + +
    + {props.followers.length}  + {getNumeralsDeclension(props.followers.length, [ + t('subscriber'), + t('subscriber_rp'), + t('subscribers') + ])} +
    + 0}> +
    showModal('subscriptions')}> + + {(f) => { + if ('name' in f) { + return + } else if ('title' in f) { + return + } + return null + }} + +
    + {props.subscriptions.length}  + {getNumeralsDeclension(props.subscriptions.length, [ + t('subscription'), + t('subscription_rp'), + t('subscriptions') + ])} +
    +
    +
    +
    +
    +
    + + + +
    + + + +
    +
    - -
    + +
    + <>

    {t('Followers')}

    - - {(follower: Author) => ( -
    - -
    - )} -
    +
    + + {(follower: Author) => ( + + )} + +
    + <> @@ -357,29 +386,32 @@ export const AuthorCard = (props: AuthorCardProps) => { +
    - - {(subscription: Author | Topic) => ( -
    - {isAuthor(subscription) ? ( +
    + + {(subscription: Author | Topic) => + isAuthor(subscription) ? ( ) : ( - - )} -
    - )} - + + ) + } + +
    -
    + ) } diff --git a/src/components/Feed/ArticleCard.module.scss b/src/components/Feed/ArticleCard.module.scss index 5ed8a383..b7761634 100644 --- a/src/components/Feed/ArticleCard.module.scss +++ b/src/components/Feed/ArticleCard.module.scss @@ -398,8 +398,10 @@ padding: 0 2.4rem; width: 100%; - @include media-breakpoint-down(sm) { - padding-top: 100%; + @include media-breakpoint-down(xl) { + aspect-ratio: auto; + height: 100%; + padding-top: 30%; } &.swiper-slide { @@ -450,6 +452,12 @@ justify-content: end; padding: 2.4rem; z-index: 1; + + @include media-breakpoint-down(xl) { + padding-left: 0; + padding-right: 0; + position: relative; + } } .shoutCardCover { diff --git a/src/components/Topic/Card.module.scss b/src/components/Topic/Card.module.scss index 2dee7902..c0d58d03 100644 --- a/src/components/Topic/Card.module.scss +++ b/src/components/Topic/Card.module.scss @@ -19,6 +19,7 @@ align-items: center; display: flex; justify-content: space-between; + margin-top: 0; button { margin-top: 0; From 23a9b9d98a7b242ee6eb7819a7040208feb7ff92 Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Mon, 4 Sep 2023 09:06:12 +0300 Subject: [PATCH 37/42] Separate blockquote and punchline logic (#207) --- public/icons/editor-blockquote.svg | 4 +++- public/icons/editor-squib.svg | 4 +++- .../Editor/TextBubbleMenu/TextBubbleMenu.tsx | 23 ++++++++++--------- src/components/Editor/extensions/Article.ts | 1 - .../Editor/extensions/CustomBlockquote.ts | 6 ++--- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/public/icons/editor-blockquote.svg b/public/icons/editor-blockquote.svg index b6e52b40..67c1ae6c 100644 --- a/public/icons/editor-blockquote.svg +++ b/public/icons/editor-blockquote.svg @@ -1,3 +1,5 @@ - + diff --git a/public/icons/editor-squib.svg b/public/icons/editor-squib.svg index 519a3975..db5e4a79 100644 --- a/public/icons/editor-squib.svg +++ b/public/icons/editor-squib.svg @@ -1,3 +1,5 @@ - + diff --git a/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx b/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx index 99eee252..65b91f4a 100644 --- a/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx +++ b/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx @@ -1,4 +1,4 @@ -import { Switch, Match, createSignal, Show, onMount, onCleanup } from 'solid-js' +import { Switch, Match, createSignal, Show, onMount, onCleanup, createEffect } from 'solid-js' import type { Editor } from '@tiptap/core' import styles from './TextBubbleMenu.module.scss' import { Icon } from '../../_shared/Icon' @@ -23,10 +23,9 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { const isActive = (name: string, attributes?: unknown) => createEditorTransaction( () => props.editor, - (editor) => { - return editor && editor.isActive(name, attributes) - } + (editor) => editor && editor.isActive(name, attributes) ) + const [textSizeBubbleOpen, setTextSizeBubbleOpen] = createSignal(false) const [listBubbleOpen, setListBubbleOpen] = createSignal(false) const [linkEditorOpen, setLinkEditorOpen] = createSignal(false) @@ -38,12 +37,14 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { const isH1 = isActive('heading', { level: 2 }) const isH2 = isActive('heading', { level: 3 }) const isH3 = isActive('heading', { level: 4 }) - const isBlockQuote = isActive('blockquote') + const isQuote = isActive('blockquote', { 'data-type': 'quote' }) + const isPunchLine = isActive('blockquote', { 'data-type': 'punchline' }) const isOrderedList = isActive('isOrderedList') const isBulletList = isActive('isBulletList') const isLink = isActive('link') const isHighlight = isActive('highlight') const isFootnote = isActive('footnote') + const isIncut = isActive('article') const toggleTextSizePopup = () => { if (listBubbleOpen()) { @@ -66,9 +67,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { const currentFootnoteValue = createEditorTransaction( () => props.editor, - (ed) => { - return (ed && ed.getAttributes('footnote').value) || '' - } + (ed) => (ed && ed.getAttributes('footnote').value) || '' ) const handleAddFootnote = (footnote) => { @@ -193,9 +192,10 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { ref={triggerRef} type="button" class={clsx(styles.bubbleMenuButton, { - [styles.bubbleMenuButtonActive]: isBlockQuote() + [styles.bubbleMenuButtonActive]: isQuote() })} onClick={() => { + if (isPunchLine()) return props.editor.chain().focus().toggleBlockquote('quote').run() toggleTextSizePopup() }} @@ -210,9 +210,10 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { ref={triggerRef} type="button" class={clsx(styles.bubbleMenuButton, { - [styles.bubbleMenuButtonActive]: isBlockQuote() + [styles.bubbleMenuButtonActive]: isPunchLine() })} onClick={() => { + if (isQuote()) return props.editor.chain().focus().toggleBlockquote('punchline').run() toggleTextSizePopup() }} @@ -230,7 +231,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { ref={triggerRef} type="button" class={clsx(styles.bubbleMenuButton, { - [styles.bubbleMenuButtonActive]: isBlockQuote() + [styles.bubbleMenuButtonActive]: isIncut() })} onClick={() => { props.editor.chain().focus().toggleArticle().run() diff --git a/src/components/Editor/extensions/Article.ts b/src/components/Editor/extensions/Article.ts index 6a50a528..0f5b1ba0 100644 --- a/src/components/Editor/extensions/Article.ts +++ b/src/components/Editor/extensions/Article.ts @@ -12,7 +12,6 @@ declare module '@tiptap/core' { export default Node.create({ name: 'article', - defaultOptions: { HTMLAttributes: { 'data-type': 'incut' diff --git a/src/components/Editor/extensions/CustomBlockquote.ts b/src/components/Editor/extensions/CustomBlockquote.ts index 51b77796..8f436e4b 100644 --- a/src/components/Editor/extensions/CustomBlockquote.ts +++ b/src/components/Editor/extensions/CustomBlockquote.ts @@ -14,10 +14,10 @@ declare module '@tiptap/core' { export const CustomBlockquote = Blockquote.extend({ name: 'blockquote', defaultOptions: { - HTMLAttributes: {}, - group: 'block', - content: 'block+' + HTMLAttributes: {} }, + group: 'block', + content: 'block+', addAttributes() { return { 'data-float': { From de194aaaf6594e6a63350f44f108afde472ee46a Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Tue, 5 Sep 2023 00:50:11 +0300 Subject: [PATCH 38/42] Change table of contents hide icon --- public/icons/hide-table-of-contents-2.svg | 5 +++++ src/components/TableOfContents/TableOfContents.module.scss | 4 ++++ src/components/TableOfContents/TableOfContents.tsx | 6 +++++- 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 public/icons/hide-table-of-contents-2.svg diff --git a/public/icons/hide-table-of-contents-2.svg b/public/icons/hide-table-of-contents-2.svg new file mode 100644 index 00000000..f51dbe12 --- /dev/null +++ b/public/icons/hide-table-of-contents-2.svg @@ -0,0 +1,5 @@ + + + + diff --git a/src/components/TableOfContents/TableOfContents.module.scss b/src/components/TableOfContents/TableOfContents.module.scss index 858776da..501bae9f 100644 --- a/src/components/TableOfContents/TableOfContents.module.scss +++ b/src/components/TableOfContents/TableOfContents.module.scss @@ -19,6 +19,10 @@ .TableOfContentsPrimaryButton { left: auto; right: 40px; + + &:hover { + filter: invert(1); + } } .TableOfContentsPrimaryButtonLefted { diff --git a/src/components/TableOfContents/TableOfContents.tsx b/src/components/TableOfContents/TableOfContents.tsx index b4f354e6..97807d14 100644 --- a/src/components/TableOfContents/TableOfContents.tsx +++ b/src/components/TableOfContents/TableOfContents.tsx @@ -104,7 +104,11 @@ export const TableOfContents = (props: Props) => { title={isVisible() ? t('Hide table of contents') : t('Show table of contents')} > }> - + {props.variant === 'editor' ? ( + + ) : ( + + )}
    From 37a6547d57ba11f18c2b33cdf52d386cd0d9a5cc Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Tue, 5 Sep 2023 00:50:48 +0300 Subject: [PATCH 39/42] Minor fixes --- src/components/Article/Article.module.scss | 8 +++--- src/components/Discours/Hero.tsx | 12 +++++++- src/components/Nav/AuthModal/RegisterForm.tsx | 28 +++++++++---------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/components/Article/Article.module.scss b/src/components/Article/Article.module.scss index d4d3440a..d5287d23 100644 --- a/src/components/Article/Article.module.scss +++ b/src/components/Article/Article.module.scss @@ -540,13 +540,13 @@ a[data-toggle='tooltip'] { vertical-align: text-top; justify-content: center; position: relative; - width: 10px; - height: 10px; + width: 14px; + height: 14px; border-radius: 50%; margin: -2px 0 0 1px; border: unset; - background-size: 10px; - background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgY2xhc3M9ImJpIGJpLWluZm8tY2lyY2xlIiBmaWxsPSJjdXJyZW50Q29sb3IiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgd2lkdGg9IjE2IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik04IDE1QTcgNyAwIDEgMSA4IDFhNyA3IDAgMCAxIDAgMTR6bTAgMUE4IDggMCAxIDAgOCAwYTggOCAwIDAgMCAwIDE2eiIvPjxwYXRoIGQ9Im04LjkzIDYuNTg4LTIuMjkuMjg3LS4wODIuMzguNDUuMDgzYy4yOTQuMDcuMzUyLjE3Ni4yODguNDY5bC0uNzM4IDMuNDY4Yy0uMTk0Ljg5Ny4xMDUgMS4zMTkuODA4IDEuMzE5LjU0NSAwIDEuMTc4LS4yNTIgMS40NjUtLjU5OGwuMDg4LS40MTZjLS4yLjE3Ni0uNDkyLjI0Ni0uNjg2LjI0Ni0uMjc1IDAtLjM3NS0uMTkzLS4zMDQtLjUzM0w4LjkzIDYuNTg4ek05IDQuNWExIDEgMCAxIDEtMiAwIDEgMSAwIDAgMSAyIDB6Ii8+PC9zdmc+'); + background-size: contain; + background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIHZpZXdCb3g9IjAgMCAxNCAxNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTcgMTRDMTAuODY2IDE0IDE0IDEwLjg2NiAxNCA3QzE0IDMuMTM0MDEgMTAuODY2IDAgNyAwQzMuMTM0MDEgMCAwIDMuMTM0MDEgMCA3QzAgMTAuODY2IDMuMTM0MDEgMTQgNyAxNFpNNy44ODQzNSAzLjQyMzA3QzcuODg0MzUgMi45MTQyNCA3LjQ3MDExIDIuNSA2Ljk2MTI4IDIuNUM2LjQ1MjM2IDIuNSA2LjAyNjMyIDIuOTE0MjQgNi4wMzgyIDMuNDIzMDdDNi4wMzgyIDMuOTMxOTEgNi40NTI0NSA0LjM0NjE1IDYuOTYxMjggNC4zNDYxNUM3LjQ3MDExIDQuMzQ2MTUgNy44ODQzNSAzLjkzMTkxIDcuODg0MzUgMy40MjMwN1pNOC40NDA1NiAxMC41QzguNTU4OTUgMTAuNSA4LjY2NTQ0IDEwLjM5MzUgOC42NjU0NCAxMC4yNzUxTDguNjY1NDYgOS4xMDM1NUM4LjY2NTQ2IDguOTczMzYgOC41NTg5NiA4Ljg3ODY4IDguNDQwNTggOC44Nzg2OEg3Ljk0MzU0VjUuMjIxODhDNy45NDM1NCA1LjEwMzUgNy44MzcwNSA0Ljk5NzAxIDcuNzE4NjcgNC45OTcwMUg1LjU1M0M1LjQzNDYxIDQuOTk3MDEgNS4zMjgxMiA1LjA5MTcgNS4zMjgxMiA1LjIyMTg4VjYuMzkzNUM1LjMyODEyIDYuNTIzNjkgNS40MzQ2MiA2LjYxODM3IDUuNTUzIDYuNjE4MzdINi4wNTAwNFY4Ljg3ODcyTDUuNTUzIDguODc4NjRDNS40MzQ2MSA4Ljg3ODY0IDUuMzI4MTIgOC45NzMzMyA1LjMyODEyIDkuMTAzNTFWMTAuMjc1MUM1LjMyODEyIDEwLjM5MzUgNS40MzQ2MiAxMC41IDUuNTUzIDEwLjVIOC40NDA1NloiIGZpbGw9IiM0MDQwNDAiLz48L3N2Zz4='); &:hover { background-color: unset; diff --git a/src/components/Discours/Hero.tsx b/src/components/Discours/Hero.tsx index 61f999f9..f4b0701c 100644 --- a/src/components/Discours/Hero.tsx +++ b/src/components/Discours/Hero.tsx @@ -2,9 +2,13 @@ import styles from './Hero.module.scss' import { showModal } from '../../stores/ui' import { useLocalize } from '../../context/localize' +import { useRouter } from '../../stores/router' +import { AuthModalSearchParams } from '../Nav/AuthModal/types' export default () => { const { t } = useLocalize() + const { changeSearchParam } = useRouter() + return (
    - - -
    Date: Tue, 5 Sep 2023 08:49:19 +0300 Subject: [PATCH 40/42] Footnote fixpack (#208) * Footnote hotfixies --- src/components/Article/Article.module.scss | 18 ++++++++++--- src/components/Article/FullArticle.tsx | 7 ++++- src/components/Editor/Editor.tsx | 14 ++++++++-- src/components/Editor/Prosemirror.scss | 3 +++ .../Editor/SimplifiedEditor.module.scss | 5 ++++ src/components/Editor/SimplifiedEditor.tsx | 7 ++++- .../TextBubbleMenu/TextBubbleMenu.module.scss | 2 ++ .../Editor/TextBubbleMenu/TextBubbleMenu.tsx | 26 ++++++++++++++----- src/components/Editor/extensions/Footnote.ts | 9 +------ 9 files changed, 69 insertions(+), 22 deletions(-) diff --git a/src/components/Article/Article.module.scss b/src/components/Article/Article.module.scss index d5287d23..7916cc50 100644 --- a/src/components/Article/Article.module.scss +++ b/src/components/Article/Article.module.scss @@ -562,10 +562,22 @@ a[data-toggle='tooltip'] { max-width: 400px; box-sizing: border-box; background: var(--black-500); - color: var(--default-color-invert); - p:last-child { - margin: 0; + .tooltipContent { + max-height: 300px; + overflow: auto; + + & * { + color: var(--default-color-invert); + } + + a { + text-decoration: underline; + } + + p:last-child { + margin: 0; + } } &::after { diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx index 473d2efa..f9d45e29 100644 --- a/src/components/Article/FullArticle.tsx +++ b/src/components/Article/FullArticle.tsx @@ -138,7 +138,12 @@ export const FullArticle = (props: Props) => { tooltipElements.forEach((element) => { const tooltip = document.createElement('div') tooltip.classList.add(styles.tooltip) - tooltip.innerHTML = element.dataset.originalTitle || element.dataset.value + const tooltipContent = document.createElement('div') + tooltipContent.classList.add(styles.tooltipContent) + tooltipContent.innerHTML = element.dataset.originalTitle || element.dataset.value + + tooltip.appendChild(tooltipContent) + document.body.appendChild(tooltip) if (element.tagName === 'a') { element.setAttribute('href', 'javascript: void(0);') diff --git a/src/components/Editor/Editor.tsx b/src/components/Editor/Editor.tsx index 6b71be56..1118bdab 100644 --- a/src/components/Editor/Editor.tsx +++ b/src/components/Editor/Editor.tsx @@ -1,4 +1,4 @@ -import { createEffect, createSignal, Show } from 'solid-js' +import { createEffect, createSignal, onCleanup, Show } from 'solid-js' import { createTiptapEditor, useEditorHTML } from 'solid-tiptap' import uniqolor from 'uniqolor' import * as Y from 'yjs' @@ -63,6 +63,7 @@ export const Editor = (props: Props) => { const { user } = useSession() const [isCommonMarkup, setIsCommonMarkup] = createSignal(false) + const [shouldShowTextBubbleMenu, setShouldShowTextBubbleMenu] = createSignal(false) const docName = `shout-${props.shoutId}` @@ -185,7 +186,11 @@ export const Editor = (props: Props) => { const { empty } = selection const isEmptyTextBlock = doc.textBetween(from, to).length === 0 && isTextSelection(selection) setIsCommonMarkup(e.isActive('figcaption')) - return view.hasFocus() && !empty && !isEmptyTextBlock && !e.isActive('image') + const result = + (view.hasFocus() && !empty && !isEmptyTextBlock && !e.isActive('image')) || + e.isActive('footnote') + setShouldShowTextBubbleMenu(result) + return result }, tippyOptions: { sticky: true @@ -246,6 +251,10 @@ export const Editor = (props: Props) => { } }) + onCleanup(() => { + editor().destroy() + }) + return ( <>
    @@ -256,6 +265,7 @@ export const Editor = (props: Props) => {
    (textBubbleMenuRef.current = el)} diff --git a/src/components/Editor/Prosemirror.scss b/src/components/Editor/Prosemirror.scss index e9ab8c8e..91367d48 100644 --- a/src/components/Editor/Prosemirror.scss +++ b/src/components/Editor/Prosemirror.scss @@ -263,6 +263,9 @@ footnote { display: inline-flex; position: relative; cursor: pointer; + width: 0.8rem; + height: 1em; + &:before { content: ''; position: absolute; diff --git a/src/components/Editor/SimplifiedEditor.module.scss b/src/components/Editor/SimplifiedEditor.module.scss index 7f774dc9..81312dc0 100644 --- a/src/components/Editor/SimplifiedEditor.module.scss +++ b/src/components/Editor/SimplifiedEditor.module.scss @@ -55,6 +55,11 @@ bottom: -1rem; transition: 0.3s ease-in-out; + &.alwaysVisible { + opacity: unset; + bottom: unset; + } + .actions { display: flex; align-items: center; diff --git a/src/components/Editor/SimplifiedEditor.tsx b/src/components/Editor/SimplifiedEditor.tsx index f8fd3939..b5576994 100644 --- a/src/components/Editor/SimplifiedEditor.tsx +++ b/src/components/Editor/SimplifiedEditor.tsx @@ -52,9 +52,11 @@ type Props = { submitByEnter?: boolean submitByShiftEnter?: boolean onlyBubbleControls?: boolean + controlsAlwaysVisible?: boolean } export const MAX_DESCRIPTION_LIMIT = 400 + const SimplifiedEditor = (props: Props) => { const { t } = useLocalize() const [counter, setCounter] = createSignal() @@ -217,6 +219,7 @@ const SimplifiedEditor = (props: Props) => { }) onCleanup(() => { + editor().destroy() window.removeEventListener('keydown', handleKeyDown) }) @@ -233,6 +236,7 @@ const SimplifiedEditor = (props: Props) => { setCounter(editor().storage.characterCount.characters()) } }) + return (
    (wrapperEditorElRef.current = el)} @@ -252,7 +256,7 @@ const SimplifiedEditor = (props: Props) => {
    (editorElRef.current = el)} /> -
    +
    {(triggerRef: (el) => void) => ( @@ -350,6 +354,7 @@ const SimplifiedEditor = (props: Props) => { (textBubbleMenuRef.current = el)} diff --git a/src/components/Editor/TextBubbleMenu/TextBubbleMenu.module.scss b/src/components/Editor/TextBubbleMenu/TextBubbleMenu.module.scss index 9f022771..e6b15233 100644 --- a/src/components/Editor/TextBubbleMenu/TextBubbleMenu.module.scss +++ b/src/components/Editor/TextBubbleMenu/TextBubbleMenu.module.scss @@ -1,6 +1,8 @@ .TextBubbleMenu { background: var(--editor-bubble-menu-background); box-shadow: 0 4px 10px rgba(#000, 0.25); + max-height: 300px; + overflow: auto; &.growWidth { min-width: 460px; diff --git a/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx b/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx index 65b91f4a..c384fc0a 100644 --- a/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx +++ b/src/components/Editor/TextBubbleMenu/TextBubbleMenu.tsx @@ -8,13 +8,12 @@ import { useLocalize } from '../../../context/localize' import { Popover } from '../../_shared/Popover' import { InsertLinkForm } from '../InsertLinkForm' import SimplifiedEditor from '../SimplifiedEditor' -import { Button } from '../../_shared/Button' -import { showModal } from '../../../stores/ui' type BubbleMenuProps = { editor: Editor isCommonMarkup: boolean ref: (el: HTMLDivElement) => void + shouldShow: boolean } export const TextBubbleMenu = (props: BubbleMenuProps) => { @@ -32,6 +31,13 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { const [footnoteEditorOpen, setFootnoteEditorOpen] = createSignal(false) const [footNote, setFootNote] = createSignal() + createEffect(() => { + if (!props.shouldShow) { + setFootNote() + setFootnoteEditorOpen(false) + } + }) + const isBold = isActive('bold') const isItalic = isActive('italic') const isH1 = isActive('heading', { level: 2 }) @@ -65,9 +71,15 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { } } - const currentFootnoteValue = createEditorTransaction( + const updateCurrentFootnoteValue = createEditorTransaction( () => props.editor, - (ed) => (ed && ed.getAttributes('footnote').value) || '' + (ed) => { + if (!isFootnote()) { + return + } + const value = ed.getAttributes('footnote').value + setFootNote(value) + } ) const handleAddFootnote = (footnote) => { @@ -81,7 +93,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { } const handleOpenFootnoteEditor = () => { - setFootNote(currentFootnoteValue()) + updateCurrentFootnoteValue() setFootnoteEditorOpen(true) } @@ -100,13 +112,13 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { setLinkEditorOpen(false)} /> - @@ -225,11 +236,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => { class={clsx(styles.bubbleMenuButton, { [styles.bubbleMenuButtonActive]: isPunchLine() })} - onClick={() => { - if (isQuote()) return - props.editor.chain().focus().toggleBlockquote('punchline').run() - toggleTextSizePopup() - }} + onClick={handleSetQuote} > diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx index d7e6f1e8..6f6dff8b 100644 --- a/src/components/Views/Author/Author.tsx +++ b/src/components/Views/Author/Author.tsx @@ -3,7 +3,6 @@ import type { Author, Shout, Topic } from '../../../graphql/types.gen' import { Row1 } from '../../Feed/Row1' import { Row2 } from '../../Feed/Row2' import { Row3 } from '../../Feed/Row3' - import { useAuthorsStore } from '../../../stores/zine/authors' import { loadShouts, useArticlesStore } from '../../../stores/zine/articles' import { useRouter } from '../../../stores/router' @@ -17,8 +16,6 @@ import { apiClient } from '../../../utils/apiClient' import { Comment } from '../../Article/Comment' import { useLocalize } from '../../../context/localize' import { AuthorRatingControl } from '../../Author/AuthorRatingControl' -import { TopicCard } from '../../Topic/Card' -import { Loading } from '../../_shared/Loading' import { hideModal } from '../../../stores/ui' type AuthorProps = { diff --git a/src/stores/zine/articles.ts b/src/stores/zine/articles.ts index 6335513b..f680353a 100644 --- a/src/stores/zine/articles.ts +++ b/src/stores/zine/articles.ts @@ -1,4 +1,4 @@ -import type { Author, Shout, ShoutInput, Topic, LoadShoutsOptions } from '../../graphql/types.gen' +import type { Author, Shout, ShoutInput, LoadShoutsOptions } from '../../graphql/types.gen' import { apiClient } from '../../utils/apiClient' import { addAuthorsByTopic } from './authors' import { byStat } from '../../utils/sortby' diff --git a/src/stores/zine/authors.ts b/src/stores/zine/authors.ts index df3f3385..9c3abd30 100644 --- a/src/stores/zine/authors.ts +++ b/src/stores/zine/authors.ts @@ -1,6 +1,6 @@ import { apiClient } from '../../utils/apiClient' import type { Author } from '../../graphql/types.gen' -import { createEffect, createSignal } from 'solid-js' +import { createSignal } from 'solid-js' import { createLazyMemo } from '@solid-primitives/memo' import { byStat } from '../../utils/sortby'