From c8517c85c6f3ee09fa3b277fc245e3cfa4d57209 Mon Sep 17 00:00:00 2001 From: Untone Date: Sat, 18 May 2024 20:36:22 +0300 Subject: [PATCH 01/15] edit-hotfix --- src/pages/edit.page.tsx | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/pages/edit.page.tsx b/src/pages/edit.page.tsx index 21353681..f729e50b 100644 --- a/src/pages/edit.page.tsx +++ b/src/pages/edit.page.tsx @@ -1,4 +1,4 @@ -import { Show, Suspense, createEffect, createMemo, createSignal, lazy, on, onMount } from 'solid-js' +import { Show, Suspense, createEffect, createMemo, createSignal, lazy, on } from 'solid-js' import { AuthGuard } from '../components/AuthGuard' import { Loading } from '../components/_shared/Loading' @@ -7,7 +7,7 @@ import { useLocalize } from '../context/localize' import { useSession } from '../context/session' import { apiClient } from '../graphql/client/core' import { Shout } from '../graphql/schema/core.gen' -import { router } from '../stores/router' +import { router, useRouter } from '../stores/router' import { redirectPage } from '@nanostores/router' import { useSnackbar } from '../context/snackbar' @@ -33,6 +33,7 @@ const getContentTypeTitle = (layout: LayoutType) => { export const EditPage = () => { const { t } = useLocalize() const { session } = useSession() + const { page } = useRouter() const snackbar = useSnackbar() const fail = async (error: string) => { @@ -45,12 +46,18 @@ export const EditPage = () => { const [shoutId, setShoutId] = createSignal(0) const [shout, setShout] = createSignal() - onMount(() => { - const shoutId = window.location.pathname.split('/').pop() - const shoutIdFromUrl = Number.parseInt(shoutId ?? '0', 10) - console.debug(`editing shout ${shoutIdFromUrl}`) - if (shoutIdFromUrl) setShoutId(shoutIdFromUrl) - }) + createEffect( + on( + page, + (p) => { + const shoutId = p?.path.split('/').pop() + const shoutIdFromUrl = Number.parseInt(shoutId ?? '0', 10) + console.debug(`editing shout ${shoutIdFromUrl}`) + if (shoutIdFromUrl) setShoutId(shoutIdFromUrl) + }, + { defer: true }, + ), + ) createEffect( on([session, shout, shoutId], async ([ses, sh, shid]) => { From 95c4d777b221ef6df5dfc916d2adfad09490f572 Mon Sep 17 00:00:00 2001 From: Untone Date: Sat, 18 May 2024 20:43:20 +0300 Subject: [PATCH 02/15] connect-fox --- src/context/connect.tsx | 78 ++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/src/context/connect.tsx b/src/context/connect.tsx index 2b2dc808..f8ebb1c9 100644 --- a/src/context/connect.tsx +++ b/src/context/connect.tsx @@ -30,53 +30,57 @@ const ConnectContext = createContext() export const ConnectProvider = (props: { children: JSX.Element }) => { const [messageHandlers, setHandlers] = createSignal([]) - // const [messages, setMessages] = createSignal>([]); const [connected, setConnected] = createSignal(false) const { session } = useSession() + const [retried, setRetried] = createSignal(0) const addHandler = (handler: MessageHandler) => { setHandlers((hhh) => [...hhh, handler]) } - const [retried, setRetried] = createSignal(0) createEffect(async () => { const token = session()?.access_token - if (token && !connected()) { + if (token && !connected() && retried() <= RECONNECT_TIMES) { console.info('[context.connect] init SSE connection') - await fetchEventSource('https://connect.discours.io', { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - Authorization: token, - }, - onmessage(event) { - const m: SSEMessage = JSON.parse(event.data || '{}') - console.log('[context.connect] Received message:', m) - - // Iterate over all registered handlers and call them - messageHandlers().forEach((handler) => handler(m)) - }, - async onopen(response) { - console.log('[context.connect] SSE connection opened', response) - if (response.ok && response.headers.get('content-type') === EventStreamContentType) { - setConnected(true) - } else if (response.status === 401) { - throw new Error('SSE: cannot connect to real-time updates') - } else { - setRetried((r) => r + 1) - throw new Error(`SSE: failed to connect ${retried()} times`) - } - }, - onclose() { - console.log('[context.connect] SSE connection closed by server') - setConnected(false) - }, - onerror(err) { - if (err.message === 'unauthorized' || retried() > RECONNECT_TIMES) { - throw err // rethrow to stop the operation - } - }, - }) + try { + await fetchEventSource('https://connect.discours.io', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: token, + }, + onmessage(event) { + const m: SSEMessage = JSON.parse(event.data || '{}') + console.log('[context.connect] Received message:', m) + messageHandlers().forEach((handler) => handler(m)) + }, + onopen: (response) => { + console.log('[context.connect] SSE connection opened', response) + if (response.ok && response.headers.get('content-type') === EventStreamContentType) { + setConnected(true) + setRetried(0) + return Promise.resolve() + } + return Promise.reject(`SSE: cannot connect to real-time updates, status: ${response.status}`) + }, + onclose() { + console.log('[context.connect] SSE connection closed by server') + setConnected(false) + if (retried() < RECONNECT_TIMES) { + setRetried((r) => r + 1) + } + }, + onerror(err) { + console.error('[context.connect] SSE connection error:', err) + setConnected(false) + if (retried() < RECONNECT_TIMES) { + setRetried((r) => r + 1) + } else throw Error(err) + }, + }) + } catch (error) { + console.error('[context.connect] SSE connection failed:', error) + } } }) From 60664581bdb3f7154ef165305850657454a2b199 Mon Sep 17 00:00:00 2001 From: Untone Date: Sat, 18 May 2024 20:50:27 +0300 Subject: [PATCH 03/15] logfix --- src/components/Nav/AuthModal/LoginForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Nav/AuthModal/LoginForm.tsx b/src/components/Nav/AuthModal/LoginForm.tsx index c7a26d68..1a50aeda 100644 --- a/src/components/Nav/AuthModal/LoginForm.tsx +++ b/src/components/Nav/AuthModal/LoginForm.tsx @@ -95,8 +95,8 @@ export const LoginForm = () => { try { const { errors } = await signIn({ email: email(), password: password() }) - console.error('[signIn errors]', errors) if (errors?.length > 0) { + console.debug('[signIn] errors:', errors) if ( errors.some( (error) => From 4a55271a79d4935e6e0d94c08900cb53927cdb2e Mon Sep 17 00:00:00 2001 From: Untone Date: Sun, 19 May 2024 00:48:58 +0300 Subject: [PATCH 04/15] edit-hotfix --- src/pages/edit.page.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/pages/edit.page.tsx b/src/pages/edit.page.tsx index f729e50b..aaa08eee 100644 --- a/src/pages/edit.page.tsx +++ b/src/pages/edit.page.tsx @@ -48,14 +48,18 @@ export const EditPage = () => { createEffect( on( - page, + () => page(), (p) => { - const shoutId = p?.path.split('/').pop() - const shoutIdFromUrl = Number.parseInt(shoutId ?? '0', 10) - console.debug(`editing shout ${shoutIdFromUrl}`) - if (shoutIdFromUrl) setShoutId(shoutIdFromUrl) + if (p?.path) { + console.debug(p?.path) + const shoutId = p?.path.split('/').pop() + const shoutIdFromUrl = Number.parseInt(shoutId ?? '0', 10) + console.debug(`editing shout ${shoutIdFromUrl}`) + if (shoutIdFromUrl) { + setShoutId(shoutIdFromUrl) + } + } }, - { defer: true }, ), ) @@ -70,6 +74,7 @@ export const EditPage = () => { } } }), + { defer: true }, ) const title = createMemo(() => { From 135e0d215f922e262061864a41498b1fbae6f940 Mon Sep 17 00:00:00 2001 From: Untone Date: Sun, 19 May 2024 00:55:30 +0300 Subject: [PATCH 05/15] error-catch --- src/components/Nav/AuthModal/LoginForm.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/Nav/AuthModal/LoginForm.tsx b/src/components/Nav/AuthModal/LoginForm.tsx index 1a50aeda..25867cec 100644 --- a/src/components/Nav/AuthModal/LoginForm.tsx +++ b/src/components/Nav/AuthModal/LoginForm.tsx @@ -99,8 +99,7 @@ export const LoginForm = () => { console.debug('[signIn] errors:', errors) if ( errors.some( - (error) => - error.message.includes('bad user credentials') || error.message.includes('user not found'), + (error) => error.message.includes('bad user credentials') || error.message.includes('user not'), ) ) { setValidationErrors((prev) => ({ From 22f0c9052dfb0876b92c6d055283c74f933da095 Mon Sep 17 00:00:00 2001 From: Untone Date: Sun, 19 May 2024 01:04:18 +0300 Subject: [PATCH 06/15] auth-errors-fix --- public/locales/en/translation.json | 3 ++- public/locales/ru/translation.json | 3 ++- src/components/Nav/AuthModal/LoginForm.tsx | 10 +++------- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index dea79bdb..72071723 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -417,6 +417,7 @@ "Username": "Username", "Userpic": "Userpic", "Users": "Users", + "User was not found": "User was not found", "Video format not supported": "Video format not supported", "Video": "Video", "Views": "Views", @@ -540,4 +541,4 @@ "Incorrect old password": "Incorrect old password", "Repeat new password": "Repeat new password", "Incorrect new password confirm": "Incorrect new password confirm" -} +} \ No newline at end of file diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index f9596f64..84006a9b 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -549,6 +549,7 @@ "topicKeywords": "{topic}, Discours.io, статьи, журналистика, исследования", "topics": "темы", "user already exist": "пользователь уже существует", + "User was not found": "Пользователь не найден", "verified": "уже подтверждён", "video": "видео", "view": "просмотр", @@ -567,4 +568,4 @@ "Incorrect old password": "Старый пароль не верен", "Repeat new password": "Повторите новый пароль", "Incorrect new password confirm": "Неверное подтверждение нового пароля" -} +} \ No newline at end of file diff --git a/src/components/Nav/AuthModal/LoginForm.tsx b/src/components/Nav/AuthModal/LoginForm.tsx index 25867cec..b33dabdc 100644 --- a/src/components/Nav/AuthModal/LoginForm.tsx +++ b/src/components/Nav/AuthModal/LoginForm.tsx @@ -96,18 +96,14 @@ export const LoginForm = () => { try { const { errors } = await signIn({ email: email(), password: password() }) if (errors?.length > 0) { - console.debug('[signIn] errors:', errors) - if ( - errors.some( - (error) => error.message.includes('bad user credentials') || error.message.includes('user not'), - ) - ) { + console.error('[signIn errors]', errors) + if (errors.some((error) => error.message.includes('user has not signed up email & password'))) { setValidationErrors((prev) => ({ ...prev, password: t('Something went wrong, check email and password'), })) } else if (errors.some((error) => error.message.includes('user not found'))) { - setSubmitError('Пользователь не найден') + setSubmitError(t('User was not found')) } else if (errors.some((error) => error.message.includes('email not verified'))) { setSubmitError(
From 59eaf3837da216a1b8ac7d13e00b0c80c4f6b2b1 Mon Sep 17 00:00:00 2001 From: Untone Date: Sun, 19 May 2024 01:14:28 +0300 Subject: [PATCH 07/15] handle-auth-errors --- src/components/Nav/AuthModal/LoginForm.tsx | 29 ++++++++++++---------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/components/Nav/AuthModal/LoginForm.tsx b/src/components/Nav/AuthModal/LoginForm.tsx index b33dabdc..dc5ffe54 100644 --- a/src/components/Nav/AuthModal/LoginForm.tsx +++ b/src/components/Nav/AuthModal/LoginForm.tsx @@ -96,27 +96,30 @@ export const LoginForm = () => { try { const { errors } = await signIn({ email: email(), password: password() }) if (errors?.length > 0) { - console.error('[signIn errors]', errors) - if (errors.some((error) => error.message.includes('user has not signed up email & password'))) { - setValidationErrors((prev) => ({ - ...prev, - password: t('Something went wrong, check email and password'), - })) - } else if (errors.some((error) => error.message.includes('user not found'))) { - setSubmitError(t('User was not found')) - } else if (errors.some((error) => error.message.includes('email not verified'))) { + console.warn('[signIn] errors: ', errors) + let msg = '' + if (errors.some((error) => error.message === 'user has not signed up email & password')) { + const password = t('Something went wrong, check email and password') + setValidationErrors((prev) => ({ ...prev, password })) + } else if (errors.some((error) => error.message === 'user not found')) { + msg = t('User was not found') + } else if (errors.some((error) => error.message === 'email not verified')) { + msg = t('This email is not verified') + } else { + msg = t('Error', errors[0].message) + } + + msg && setSubmitError(
- {t('This email is not verified')} + {msg} {'. '} {t('Send link again')}
, ) - } else { - setSubmitError(t('Error', errors[0].message)) - } + return } hideModal() From 38899ad8cb3aec26da62899a18f4c7402419b5a1 Mon Sep 17 00:00:00 2001 From: Untone Date: Sun, 19 May 2024 01:18:55 +0300 Subject: [PATCH 08/15] login-validations-fixes --- src/components/Nav/AuthModal/LoginForm.tsx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/components/Nav/AuthModal/LoginForm.tsx b/src/components/Nav/AuthModal/LoginForm.tsx index dc5ffe54..dc57565b 100644 --- a/src/components/Nav/AuthModal/LoginForm.tsx +++ b/src/components/Nav/AuthModal/LoginForm.tsx @@ -97,28 +97,27 @@ export const LoginForm = () => { const { errors } = await signIn({ email: email(), password: password() }) if (errors?.length > 0) { console.warn('[signIn] errors: ', errors) - let msg = '' + if (errors.some((error) => error.message === 'user has not signed up email & password')) { const password = t('Something went wrong, check email and password') setValidationErrors((prev) => ({ ...prev, password })) } else if (errors.some((error) => error.message === 'user not found')) { - msg = t('User was not found') + const email = t('User was not found') + setValidationErrors((prev) => ({ ...prev, email })) } else if (errors.some((error) => error.message === 'email not verified')) { - msg = t('This email is not verified') + const email = t('This email is not verified') + setValidationErrors((prev) => ({ ...prev, email })) } else { - msg = t('Error', errors[0].message) - } - - msg && setSubmitError(
- {msg} + {t('Error', errors[0].message)} {'. '} {t('Send link again')}
, ) + } return } From 319136474e710117771e6e69e0ae1a5546a02b1b Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Sun, 19 May 2024 01:38:56 +0300 Subject: [PATCH 09/15] Table of contents minor fixes --- public/locales/en/translation.json | 4 ++-- public/locales/ru/translation.json | 4 ++-- src/components/TableOfContents/TableOfContents.module.scss | 2 +- src/components/TableOfContents/TableOfContents.tsx | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 72071723..29fed7a8 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -91,6 +91,7 @@ "Community Principles": "Community Principles", "Community values and rules of engagement for the open editorial team": "Community values and rules of engagement for the open editorial team", "Confirm": "Confirm", + "Contents": "Contents", "Contribute to free samizdat. Support Discours - an independent non-profit publication that works only for you. Become a pillar of the open newsroom": "Contribute to free samizdat. Support Discours - an independent non-profit publication that works only for you. Become a pillar of the open newsroom", "Cooperate": "Cooperate", "Copy link": "Copy link", @@ -470,7 +471,6 @@ "cancel": "cancel", "collections": "collections", "community": "community", - "contents": "contents", "delimiter": "delimiter", "discussion": "Discours", "dogma keywords": "Discours.io, dogma, editorial principles, code of ethics, journalism, community", @@ -541,4 +541,4 @@ "Incorrect old password": "Incorrect old password", "Repeat new password": "Repeat new password", "Incorrect new password confirm": "Incorrect new password confirm" -} \ No newline at end of file +} diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index 84006a9b..91134e24 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -95,6 +95,7 @@ "Community Principles": "Принципы сообщества", "Community values and rules of engagement for the open editorial team": "Ценности сообщества и правила взаимодействия открытой редакции", "Confirm": "Подтвердить", + "Contents": "Оглавление", "Contribute to free samizdat. Support Discours - an independent non-profit publication that works only for you. Become a pillar of the open newsroom": "Внесите вклад в свободный самиздат. Поддержите Дискурс — независимое некоммерческое издание, которое работает только для вас. Станьте опорой открытой редакции", "Cooperate": "Соучаствовать", "Copy link": "Скопировать ссылку", @@ -492,7 +493,6 @@ "cancel": "отменить", "collections": "коллекции", "community": "сообщество", - "contents": "оглавление", "create_chat": "Создать чат", "create_group": "Создать группу", "delimiter": "разделитель", @@ -568,4 +568,4 @@ "Incorrect old password": "Старый пароль не верен", "Repeat new password": "Повторите новый пароль", "Incorrect new password confirm": "Неверное подтверждение нового пароля" -} \ No newline at end of file +} diff --git a/src/components/TableOfContents/TableOfContents.module.scss b/src/components/TableOfContents/TableOfContents.module.scss index 2e5fe4ac..8433e44d 100644 --- a/src/components/TableOfContents/TableOfContents.module.scss +++ b/src/components/TableOfContents/TableOfContents.module.scss @@ -157,7 +157,7 @@ color: #000; font-size: 14px; font-style: normal; - font-weight: 400; + font-weight: 500; line-height: 1.8rem; text-align: left; vertical-align: bottom; diff --git a/src/components/TableOfContents/TableOfContents.tsx b/src/components/TableOfContents/TableOfContents.tsx index 322aa925..d0a2de05 100644 --- a/src/components/TableOfContents/TableOfContents.tsx +++ b/src/components/TableOfContents/TableOfContents.tsx @@ -86,7 +86,7 @@ export const TableOfContents = (props: Props) => {
-

{t('contents')}

+

{t('Contents')}

    From 8f330ab914a835409b4f2a695aade286229fefee Mon Sep 17 00:00:00 2001 From: Untone Date: Sun, 19 May 2024 01:41:50 +0300 Subject: [PATCH 10/15] multierror --- src/components/Nav/AuthModal/LoginForm.tsx | 51 ++++++++++++---------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/components/Nav/AuthModal/LoginForm.tsx b/src/components/Nav/AuthModal/LoginForm.tsx index dc57565b..925c8864 100644 --- a/src/components/Nav/AuthModal/LoginForm.tsx +++ b/src/components/Nav/AuthModal/LoginForm.tsx @@ -97,28 +97,35 @@ export const LoginForm = () => { const { errors } = await signIn({ email: email(), password: password() }) if (errors?.length > 0) { console.warn('[signIn] errors: ', errors) - - if (errors.some((error) => error.message === 'user has not signed up email & password')) { - const password = t('Something went wrong, check email and password') - setValidationErrors((prev) => ({ ...prev, password })) - } else if (errors.some((error) => error.message === 'user not found')) { - const email = t('User was not found') - setValidationErrors((prev) => ({ ...prev, email })) - } else if (errors.some((error) => error.message === 'email not verified')) { - const email = t('This email is not verified') - setValidationErrors((prev) => ({ ...prev, email })) - } else { - setSubmitError( -
    - {t('Error', errors[0].message)} - {'. '} - - {t('Send link again')} - -
    , - ) - } - + errors.forEach((error) => { + switch (error.message) { + case 'user has not signed up email & password': { + setValidationErrors((prev) => ({ + ...prev, + password: t('Something went wrong, check email and password'), + })) + break + } + case 'user not found': { + setValidationErrors((prev) => ({ ...prev, email: t('User was not found') })) + break + } + case 'email not verified': { + setValidationErrors((prev) => ({ ...prev, email: t('This email is not verified') })) + break + } + default: + setSubmitError( +
    + {t('Error', errors[0].message)} + {'. '} + + {t('Send link again')} + +
    , + ) + } + }) return } hideModal() From 9b76a52430980d3e5424655d2b7f07c6618a8bcf Mon Sep 17 00:00:00 2001 From: Untone Date: Sun, 19 May 2024 02:22:19 +0300 Subject: [PATCH 11/15] editor-autosave-fix --- src/components/Views/EditView/EditView.tsx | 63 +++++++++++----------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/src/components/Views/EditView/EditView.tsx b/src/components/Views/EditView/EditView.tsx index 1e8650b1..2148a3e4 100644 --- a/src/components/Views/EditView/EditView.tsx +++ b/src/components/Views/EditView/EditView.tsx @@ -2,7 +2,7 @@ import { clsx } from 'clsx' import deepEqual from 'fast-deep-equal' import { Accessor, Show, createMemo, createSignal, lazy, onCleanup, onMount } from 'solid-js' import { createStore } from 'solid-js/store' -import { throttle } from 'throttle-debounce' +import { debounce } from 'throttle-debounce' import { ShoutForm, useEditorContext } from '../../../context/editor' import { useLocalize } from '../../../context/localize' @@ -42,9 +42,8 @@ export const EMPTY_TOPIC: Topic = { slug: '', } -const THROTTLING_INTERVAL = 2000 -const AUTO_SAVE_INTERVAL = 5000 -const AUTO_SAVE_DELAY = 5000 +const AUTO_SAVE_DELAY = 3000 + const handleScrollTopButtonClick = (e) => { e.preventDefault() window.scrollTo({ @@ -104,6 +103,8 @@ export const EditView = (props: Props) => { return JSON.parse(form.media || '[]') }) + const [hasChanges, setHasChanges] = createSignal(false) + onMount(() => { const handleScroll = () => { setIsScrolled(window.scrollY > 0) @@ -113,7 +114,7 @@ export const EditView = (props: Props) => { onCleanup(() => { window.removeEventListener('scroll', handleScroll) }) - // eslint-disable-next-line unicorn/consistent-function-scoping + const handleBeforeUnload = (event) => { if (!deepEqual(prevForm, form)) { event.returnValue = t( @@ -127,8 +128,8 @@ export const EditView = (props: Props) => { }) const handleTitleInputChange = (value: string) => { - setForm('title', value) - setForm('slug', slugify(value)) + handleInputChange('title', value) + handleInputChange('slug', slugify(value)) if (value) { setFormErrors('title', '') } @@ -136,21 +137,21 @@ export const EditView = (props: Props) => { const handleAddMedia = (data) => { const newMedia = [...mediaItems(), ...data] - setForm('media', JSON.stringify(newMedia)) + handleInputChange('media', JSON.stringify(newMedia)) } const handleSortedMedia = (data) => { - setForm('media', JSON.stringify(data)) + handleInputChange('media', JSON.stringify(data)) } const handleMediaDelete = (index) => { const copy = [...mediaItems()] copy.splice(index, 1) - setForm('media', JSON.stringify(copy)) + handleInputChange('media', JSON.stringify(copy)) } const handleMediaChange = (index, value) => { const updated = mediaItems().map((item, idx) => (idx === index ? value : item)) - setForm('media', JSON.stringify(updated)) + handleInputChange('media', JSON.stringify(updated)) } const [baseAudioFields, setBaseAudioFields] = createSignal({ @@ -162,7 +163,7 @@ export const EditView = (props: Props) => { const handleBaseFieldsChange = (key, value) => { if (mediaItems().length > 0) { const updated = mediaItems().map((media) => ({ ...media, [key]: value })) - setForm('media', JSON.stringify(updated)) + handleInputChange('media', JSON.stringify(updated)) } else { setBaseAudioFields({ ...baseAudioFields(), [key]: value }) } @@ -182,34 +183,32 @@ export const EditView = (props: Props) => { } } - let autoSaveTimeOutId: number | string | NodeJS.Timeout - const autoSave = async () => { - const hasChanges = !deepEqual(form, prevForm) - const hasTopic = Boolean(form.mainTopic) - if (hasChanges || hasTopic) { + console.log('autoSave called') + if (hasChanges()) { console.debug('saving draft', form) setSaving(true) saveDraftToLocalStorage(form) await saveDraft(form) setPrevForm(clone(form)) - setTimeout(() => setSaving(false), AUTO_SAVE_DELAY) + setSaving(false) + setHasChanges(false) } } - // Throttle the autoSave function - const throttledAutoSave = throttle(THROTTLING_INTERVAL, autoSave) + const debouncedAutoSave = debounce(AUTO_SAVE_DELAY, autoSave) - const autoSaveRecursive = () => { - autoSaveTimeOutId = setTimeout(() => { - throttledAutoSave() - autoSaveRecursive() - }, AUTO_SAVE_INTERVAL) + const handleInputChange = (key, value) => { + console.log(`[handleInputChange] ${key}: ${value}`) + setForm(key, value) + setHasChanges(true) + debouncedAutoSave() } onMount(() => { - autoSaveRecursive() - onCleanup(() => clearTimeout(autoSaveTimeOutId)) + onCleanup(() => { + debouncedAutoSave.cancel() + }) }) const showSubtitleInput = () => { @@ -310,7 +309,7 @@ export const EditView = (props: Props) => { subtitleInput.current = el }} allowEnterKey={false} - value={(value) => setForm('subtitle', value || '')} + value={(value) => handleInputChange('subtitle', value || '')} class={styles.subtitleInput} placeholder={t('Subheader')} initialValue={form.subtitle || ''} @@ -324,7 +323,7 @@ export const EditView = (props: Props) => { smallHeight={true} placeholder={t('A short introduction to keep the reader interested')} initialContent={form.lead} - onChange={(value) => setForm('lead', value)} + onChange={(value) => handleInputChange('lead', value)} /> @@ -345,7 +344,7 @@ export const EditView = (props: Props) => { } isMultiply={false} fileType={'image'} - onUpload={(val) => setForm('coverImageUrl', val[0].url)} + onUpload={(val) => handleInputChange('coverImageUrl', val[0].url)} /> } > @@ -362,7 +361,7 @@ export const EditView = (props: Props) => {
    setForm('coverImageUrl', null)} + onClick={() => handleInputChange('coverImageUrl', null)} >
    @@ -408,7 +407,7 @@ export const EditView = (props: Props) => { setForm('body', body)} + onChange={(body) => handleInputChange('body', body)} />
From 5bde0bc7d02d9fdeec3ee4cfaf4a7227fdbdf260 Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Mon, 20 May 2024 23:37:56 +0300 Subject: [PATCH 12/15] Add icons to the feed context popup --- public/icons/expert.svg | 4 +++ .../FeedArticlePopup.module.scss | 25 ++++++++++++++++--- .../FeedArticlePopup/FeedArticlePopup.tsx | 18 +++++++++---- 3 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 public/icons/expert.svg diff --git a/public/icons/expert.svg b/public/icons/expert.svg new file mode 100644 index 00000000..e81cb43e --- /dev/null +++ b/public/icons/expert.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/components/Feed/FeedArticlePopup/FeedArticlePopup.module.scss b/src/components/Feed/FeedArticlePopup/FeedArticlePopup.module.scss index d9141883..6b2d15d6 100644 --- a/src/components/Feed/FeedArticlePopup/FeedArticlePopup.module.scss +++ b/src/components/Feed/FeedArticlePopup/FeedArticlePopup.module.scss @@ -16,14 +16,15 @@ } .action { - display: flex; align-items: center; - width: 100%; box-sizing: border-box; - padding: 8px 16px; + display: flex; font-size: inherit; font-weight: 500; + gap: 0.8rem; + padding: 8px 16px; text-align: left; + width: 100%; white-space: nowrap; &.soon { @@ -32,11 +33,29 @@ gap: 0.6rem; width: 100%; justify-content: space-between; + + .icon { + opacity: 0.4; + } } &:hover { background: var(--black-500); color: var(--black-50) !important; + + .icon { + filter: invert(1); + opacity: 1 !important; + } + } + + .icon { + flex: 0 2.4rem; + min-width: 2.4rem; + } + + .title { + flex: 1; } } diff --git a/src/components/Feed/FeedArticlePopup/FeedArticlePopup.tsx b/src/components/Feed/FeedArticlePopup/FeedArticlePopup.tsx index ce9990bb..1597138a 100644 --- a/src/components/Feed/FeedArticlePopup/FeedArticlePopup.tsx +++ b/src/components/Feed/FeedArticlePopup/FeedArticlePopup.tsx @@ -6,6 +6,7 @@ import { Show, createSignal } from 'solid-js' import { useLocalize } from '../../../context/localize' import { Popup } from '../../_shared/Popup' import { SoonChip } from '../../_shared/SoonChip' +import { Icon } from '../../_shared/Icon' import styles from './FeedArticlePopup.module.scss' @@ -38,7 +39,8 @@ export const FeedArticlePopup = (props: Props) => { setHidePopup(true) }} > - {t('Share')} + +
{t('Share')}
@@ -51,7 +53,8 @@ export const FeedArticlePopup = (props: Props) => { setHidePopup(true) }} > - {t('Help to edit')} + +
{t('Help to edit')}
@@ -64,19 +67,24 @@ export const FeedArticlePopup = (props: Props) => { setHidePopup(false) }} > - {t('Invite experts')} + +
{t('Invite experts')}
  • {/**/} From 8397facc5e52bcd8263042b4b5128a766f71dfde Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Mon, 20 May 2024 23:39:59 +0300 Subject: [PATCH 13/15] Fixed code style --- .../Feed/FeedArticlePopup/FeedArticlePopup.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/Feed/FeedArticlePopup/FeedArticlePopup.tsx b/src/components/Feed/FeedArticlePopup/FeedArticlePopup.tsx index 1597138a..0943b814 100644 --- a/src/components/Feed/FeedArticlePopup/FeedArticlePopup.tsx +++ b/src/components/Feed/FeedArticlePopup/FeedArticlePopup.tsx @@ -4,9 +4,9 @@ import { clsx } from 'clsx' import { Show, createSignal } from 'solid-js' import { useLocalize } from '../../../context/localize' +import { Icon } from '../../_shared/Icon' import { Popup } from '../../_shared/Popup' import { SoonChip } from '../../_shared/SoonChip' -import { Icon } from '../../_shared/Icon' import styles from './FeedArticlePopup.module.scss' @@ -39,7 +39,7 @@ export const FeedArticlePopup = (props: Props) => { setHidePopup(true) }} > - +
    {t('Share')}
    @@ -53,7 +53,7 @@ export const FeedArticlePopup = (props: Props) => { setHidePopup(true) }} > - +
    {t('Help to edit')}
    @@ -67,22 +67,22 @@ export const FeedArticlePopup = (props: Props) => { setHidePopup(false) }} > - +
    {t('Invite experts')}
  • From 3319bfe973c2c0b71e908c5cc12350f9b4ff32cd Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Sat, 25 May 2024 19:27:15 +0300 Subject: [PATCH 14/15] Fixed subscribers style --- .../Author/AuthorCard/AuthorCard.module.scss | 16 ++++++- .../Author/AuthorCard/AuthorCard.tsx | 14 +++--- src/components/Topic/Full.module.scss | 8 +++- .../Subscribers/Subscribers.module.scss | 19 +++----- .../_shared/Subscribers/Subscribers.tsx | 47 ++++++++++--------- src/pages/topic.page.tsx | 2 +- 6 files changed, 61 insertions(+), 45 deletions(-) diff --git a/src/components/Author/AuthorCard/AuthorCard.module.scss b/src/components/Author/AuthorCard/AuthorCard.module.scss index 14db38aa..a8e7e0e9 100644 --- a/src/components/Author/AuthorCard/AuthorCard.module.scss +++ b/src/components/Author/AuthorCard/AuthorCard.module.scss @@ -18,9 +18,8 @@ .authorName { @include font-size(4rem); - font-weight: 700; - margin-bottom: 0.2em; + margin-bottom: 1.2rem; } .authorAbout { @@ -432,3 +431,16 @@ .listWrapper { max-height: 70vh; } + +.subscribersContainer { + display: flex; + flex-wrap: wrap; + font-size: 1.4rem; + gap: 1rem; + margin-top: 0; + white-space: nowrap; + + @include media-breakpoint-down(md) { + justify-content: center; + } +} diff --git a/src/components/Author/AuthorCard/AuthorCard.tsx b/src/components/Author/AuthorCard/AuthorCard.tsx index 6a75dab0..8ddbba52 100644 --- a/src/components/Author/AuthorCard/AuthorCard.tsx +++ b/src/components/Author/AuthorCard/AuthorCard.tsx @@ -127,12 +127,14 @@ export const AuthorCard = (props: Props) => {
    0 || props.following?.length > 0}> - +
    + +
    diff --git a/src/components/Topic/Full.module.scss b/src/components/Topic/Full.module.scss index ee8ba0bd..c34ea44e 100644 --- a/src/components/Topic/Full.module.scss +++ b/src/components/Topic/Full.module.scss @@ -44,18 +44,22 @@ } .topicDetails { + align-items: flex-start; display: flex; flex-wrap: wrap; font-size: 1.4rem; justify-content: center; - gap: 4rem; + gap: 1rem; margin-top: 1.5rem; } .topicDetailsItem { + align-items: center; display: flex; + margin-right: 1rem; + white-space: nowrap; } .topicDetailsIcon { display: block; -} \ No newline at end of file +} diff --git a/src/components/_shared/Subscribers/Subscribers.module.scss b/src/components/_shared/Subscribers/Subscribers.module.scss index 6248311c..84494bd3 100644 --- a/src/components/_shared/Subscribers/Subscribers.module.scss +++ b/src/components/_shared/Subscribers/Subscribers.module.scss @@ -1,19 +1,8 @@ -.subscribersContainer { - display: flex; - flex-wrap: wrap; - font-size: 1.4rem; - margin-top: 1.5rem; - - @include media-breakpoint-down(md) { - justify-content: center; - } -} - .subscribers { align-items: center; cursor: pointer; display: inline-flex; - margin: 0 2% 1rem; + margin: 0 1rem 0 0; vertical-align: top; border-bottom: unset !important; @@ -44,7 +33,6 @@ .subscribersCounter { font-weight: 500; - margin-left: 1rem; } &:hover { @@ -55,3 +43,8 @@ } } } + +.subscribersList { + display: flex; + margin-right: 0.6rem; +} diff --git a/src/components/_shared/Subscribers/Subscribers.tsx b/src/components/_shared/Subscribers/Subscribers.tsx index 97a5afc7..fa12a935 100644 --- a/src/components/_shared/Subscribers/Subscribers.tsx +++ b/src/components/_shared/Subscribers/Subscribers.tsx @@ -1,9 +1,9 @@ -import { For, Show } from 'solid-js' +import {For, Show} from 'solid-js' -import { useLocalize } from '../../../context/localize' +import {useLocalize} from '../../../context/localize' -import { Author, Topic } from '../../../graphql/schema/core.gen' -import { Userpic } from '../../Author/Userpic' +import {Author, Topic} from '../../../graphql/schema/core.gen' +import {Userpic} from '../../Author/Userpic' import styles from './Subscribers.module.scss' @@ -15,15 +15,17 @@ type Props = { } export const Subscribers = (props: Props) => { - const { t } = useLocalize() + const {t} = useLocalize() return ( -
    + <> 0}> - - {(f) => } - +
    + + {(f) => } + +
    + ) } diff --git a/src/pages/topic.page.tsx b/src/pages/topic.page.tsx index 4fa3fb49..f139081f 100644 --- a/src/pages/topic.page.tsx +++ b/src/pages/topic.page.tsx @@ -54,7 +54,7 @@ export const TopicPage = (props: PageProps) => { const usePrerenderedData = props.topic?.slug === slug() return ( - + }> Date: Sat, 25 May 2024 19:35:02 +0300 Subject: [PATCH 15/15] Fixed code style --- src/components/Views/Topic.tsx | 9 +------- .../_shared/Subscribers/Subscribers.tsx | 21 +++++++++++-------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/components/Views/Topic.tsx b/src/components/Views/Topic.tsx index 8579a739..7389b3a5 100644 --- a/src/components/Views/Topic.tsx +++ b/src/components/Views/Topic.tsx @@ -1,11 +1,4 @@ -import { - Author, - AuthorsBy, - LoadShoutsOptions, - QueryLoad_Authors_ByArgs, - Shout, - Topic, -} from '../../graphql/schema/core.gen' +import { Author, AuthorsBy, LoadShoutsOptions, Shout, Topic } from '../../graphql/schema/core.gen' import { clsx } from 'clsx' import { For, Show, createEffect, createMemo, createSignal, on, onMount } from 'solid-js' diff --git a/src/components/_shared/Subscribers/Subscribers.tsx b/src/components/_shared/Subscribers/Subscribers.tsx index fa12a935..899f5565 100644 --- a/src/components/_shared/Subscribers/Subscribers.tsx +++ b/src/components/_shared/Subscribers/Subscribers.tsx @@ -1,9 +1,9 @@ -import {For, Show} from 'solid-js' +import { For, Show } from 'solid-js' -import {useLocalize} from '../../../context/localize' +import { useLocalize } from '../../../context/localize' -import {Author, Topic} from '../../../graphql/schema/core.gen' -import {Userpic} from '../../Author/Userpic' +import { Author, Topic } from '../../../graphql/schema/core.gen' +import { Userpic } from '../../Author/Userpic' import styles from './Subscribers.module.scss' @@ -15,7 +15,7 @@ type Props = { } export const Subscribers = (props: Props) => { - const {t} = useLocalize() + const { t } = useLocalize() return ( <> @@ -23,7 +23,7 @@ export const Subscribers = (props: Props) => { 0}>
    - {(f) => } + {(f) => }
    @@ -40,12 +40,15 @@ export const Subscribers = (props: Props) => { {(f) => { if ('name' in f) { - return + return ( + + ) } if ('title' in f) { - return + return ( + + ) } return null