diff --git a/biome.json b/biome.json index d279c479..30d953a8 100644 --- a/biome.json +++ b/biome.json @@ -99,4 +99,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/components/App.tsx b/src/components/App.tsx index 57b218f1..5a362864 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -40,16 +40,12 @@ import { InboxPage } from '../pages/inbox.page' import { HomePage } from '../pages/index.page' import { ProfileSecurityPage } from '../pages/profile/profileSecurity.page' import { ProfileSettingsPage } from '../pages/profile/profileSettings.page' -//TODO: ProfileSubscriptionsPage - garbage code? import { ProfileSubscriptionsPage } from '../pages/profile/profileSubscriptions.page' import { SearchPage } from '../pages/search.page' import { TopicPage } from '../pages/topic.page' import { ROUTES, useRouter } from '../stores/router' import { MODALS, showModal } from '../stores/ui' -// TODO: lazy load -// const SomePage = lazy(() => import('./Pages/SomePage')) - const pagesMap: Record> = { author: AuthorPage, authorComments: AuthorPage, diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx index 25001f38..5d878580 100644 --- a/src/components/Article/FullArticle.tsx +++ b/src/components/Article/FullArticle.tsx @@ -75,7 +75,7 @@ export const FullArticle = (props: Props) => { const [isReactionsLoaded, setIsReactionsLoaded] = createSignal(false) const [isActionPopupActive, setIsActionPopupActive] = createSignal(false) const { t, formatDate, lang } = useLocalize() - const { author, session, isAuthenticated, requireAuthentication } = useSession() + const { author, session, requireAuthentication } = useSession() const formattedDate = createMemo(() => formatDate(new Date(props.article.published_at * 1000))) @@ -561,7 +561,7 @@ export const FullArticle = (props: Props) => { /> - +
diff --git a/src/components/AuthGuard/AuthGuard.tsx b/src/components/AuthGuard/AuthGuard.tsx index 6ba70476..96f3e502 100644 --- a/src/components/AuthGuard/AuthGuard.tsx +++ b/src/components/AuthGuard/AuthGuard.tsx @@ -12,7 +12,7 @@ type Props = { } export const AuthGuard = (props: Props) => { - const { isAuthenticated, isSessionLoaded } = useSession() + const { author, isSessionLoaded } = useSession() const { changeSearchParams } = useRouter() createEffect(() => { @@ -20,7 +20,7 @@ export const AuthGuard = (props: Props) => { return } if (isSessionLoaded()) { - if (isAuthenticated()) { + if (author()?.id) { hideModal() } else { changeSearchParams( @@ -37,5 +37,5 @@ export const AuthGuard = (props: Props) => { } }) - return {props.children} + return {props.children} } diff --git a/src/components/Author/AuthorBadge/AuthorBadge.tsx b/src/components/Author/AuthorBadge/AuthorBadge.tsx index 730b42c2..665f300f 100644 --- a/src/components/Author/AuthorBadge/AuthorBadge.tsx +++ b/src/components/Author/AuthorBadge/AuthorBadge.tsx @@ -119,6 +119,9 @@ export const AuthorBadge = (props: Props) => { 0}>
{t('PublicationsWithCount', { count: props.author.stat?.shouts ?? 0 })}
+ 0}> +
{t('CommentsWithCount', { count: props.author.stat?.comments ?? 0 })}
+
0}>
{t('FollowersWithCount', { count: props.author.stat?.followers ?? 0 })}
diff --git a/src/components/Discours/Hero.tsx b/src/components/Discours/Hero.tsx index 105300cc..872db08c 100644 --- a/src/components/Discours/Hero.tsx +++ b/src/components/Discours/Hero.tsx @@ -1,7 +1,7 @@ import { useLocalize } from '../../context/localize' import { useRouter } from '../../stores/router' import { showModal } from '../../stores/ui' -import { AuthModalSearchParams } from '../Nav/AuthModal/types' +import type { AuthModalSearchParams } from '../Nav/AuthModal/types' import styles from './Hero.module.scss' diff --git a/src/components/Editor/AudioUploader/AudioUploader.tsx b/src/components/Editor/AudioUploader/AudioUploader.tsx index 329dac3e..b5cc4954 100644 --- a/src/components/Editor/AudioUploader/AudioUploader.tsx +++ b/src/components/Editor/AudioUploader/AudioUploader.tsx @@ -7,6 +7,7 @@ import { composeMediaItems } from '../../../utils/composeMediaItems' import { AudioPlayer } from '../../Article/AudioPlayer' import { DropArea } from '../../_shared/DropArea' +// import { Buffer } from 'node:buffer' import styles from './AudioUploader.module.scss' window.Buffer = Buffer diff --git a/src/components/Editor/EditorFloatingMenu/EditorFloatingMenu.tsx b/src/components/Editor/EditorFloatingMenu/EditorFloatingMenu.tsx index 5c349d72..95c3dfcb 100644 --- a/src/components/Editor/EditorFloatingMenu/EditorFloatingMenu.tsx +++ b/src/components/Editor/EditorFloatingMenu/EditorFloatingMenu.tsx @@ -30,8 +30,10 @@ const embedData = (data) => { // biome-ignore lint/style/useForOf: for (let i = 0; i < attributes.length; i++) { - const attribute = attributes[i] - result[attribute.name] = attribute.value + const attribute = attributes.item(i) + if (attribute) { + result[attribute.name] = attribute.value + } } return result diff --git a/src/components/Editor/Panel/Panel.tsx b/src/components/Editor/Panel/Panel.tsx index fe96f1e7..aaf7bd60 100644 --- a/src/components/Editor/Panel/Panel.tsx +++ b/src/components/Editor/Panel/Panel.tsx @@ -23,8 +23,16 @@ type Props = { export const Panel = (props: Props) => { const { t } = useLocalize() - const { isEditorPanelVisible, wordCounter, editorRef, form, toggleEditorPanel, saveShout, publishShout } = - useEditorContext() + const { + isEditorPanelVisible, + wordCounter, + editorRef, + form, + toggleEditorPanel, + saveShout, + saveDraft, + publishShout, + } = useEditorContext() const containerRef: { current: HTMLElement } = { current: null } const [isShortcutsVisible, setIsShortcutsVisible] = createSignal(false) @@ -43,7 +51,12 @@ export const Panel = (props: Props) => { }) const handleSaveClick = () => { - saveShout(form) + const hasTopics = form.selectedTopics?.length > 0 + if (hasTopics) { + saveShout(form) + } else { + saveDraft(form) + } } const html = useEditorHTML(() => editorRef.current()) diff --git a/src/components/Nav/AuthModal/LoginForm.tsx b/src/components/Nav/AuthModal/LoginForm.tsx index 6b4b8d3f..83d89917 100644 --- a/src/components/Nav/AuthModal/LoginForm.tsx +++ b/src/components/Nav/AuthModal/LoginForm.tsx @@ -31,7 +31,7 @@ export const LoginForm = () => { const [isSubmitting, setIsSubmitting] = createSignal(false) const [password, setPassword] = createSignal('') const [validationErrors, setValidationErrors] = createSignal({}) - + // FIXME: use signal or remove const [_isLinkSent, setIsLinkSent] = createSignal(false) const authFormRef: { current: HTMLFormElement } = { current: null } const { showSnackbar } = useSnackbar() diff --git a/src/components/Nav/AuthModal/RegisterForm.tsx b/src/components/Nav/AuthModal/RegisterForm.tsx index 58ac511a..c58459aa 100644 --- a/src/components/Nav/AuthModal/RegisterForm.tsx +++ b/src/components/Nav/AuthModal/RegisterForm.tsx @@ -32,6 +32,7 @@ export const RegisterForm = () => { const { changeSearchParams } = useRouter() const { t } = useLocalize() const { signUp, isRegistered, resendVerifyEmail } = useSession() + // FIXME: use submit error data or remove signal const [_submitError, setSubmitError] = createSignal('') const [fullName, setFullName] = createSignal('') const [password, setPassword] = createSignal('') diff --git a/src/components/Nav/Header/Header.tsx b/src/components/Nav/Header/Header.tsx index 360d5a25..f1342f1d 100644 --- a/src/components/Nav/Header/Header.tsx +++ b/src/components/Nav/Header/Header.tsx @@ -59,7 +59,7 @@ export const Header = (props: Props) => { const [isTopicsVisible, setIsTopicsVisible] = createSignal(false) const [isZineVisible, setIsZineVisible] = createSignal(false) const [isFeedVisible, setIsFeedVisible] = createSignal(false) - const { isAuthenticated } = useSession() + const { session } = useSession() const toggleFixed = () => setFixed(!fixed()) @@ -335,7 +335,7 @@ export const Header = (props: Props) => {
{ const { t } = useLocalize() const { page } = useRouter() - const { session, author, isAuthenticated, isSessionLoaded } = useSession() + const { session, author, isSessionLoaded } = useSession() const { unreadNotificationsCount, showNotificationsPanel } = useNotifications() - const { form, toggleEditorPanel, saveShout, publishShout } = useEditorContext() + const { form, toggleEditorPanel, saveShout, saveDraft, publishShout } = useEditorContext() const handleBellIconClick = (event: Event) => { event.preventDefault() - if (!isAuthenticated()) { + if (!author()?.id) { showModal('auth') return } @@ -48,19 +48,22 @@ export const HeaderAuth = (props: Props) => { } const isEditorPage = createMemo(() => page().route === 'edit' || page().route === 'editSettings') - const isNotificationsVisible = createMemo(() => isAuthenticated() && !isEditorPage()) - const isSaveButtonVisible = createMemo(() => isAuthenticated() && isEditorPage()) + const isNotificationsVisible = createMemo(() => author()?.id && !isEditorPage()) + const isSaveButtonVisible = createMemo(() => author()?.id && isEditorPage()) const isCreatePostButtonVisible = createMemo(() => !isEditorPage()) - const isAuthenticatedControlsVisible = createMemo( - () => isAuthenticated() && session()?.user?.email_verified, - ) + const isAuthenticatedControlsVisible = createMemo(() => author()?.id && session()?.user?.email_verified) const handleBurgerButtonClick = () => { toggleEditorPanel() } - const _handleSaveButtonClick = () => { - saveShout(form) + const handleSaveClick = () => { + const hasTopics = form.selectedTopics?.length > 0 + if (hasTopics) { + saveShout(form) + } else { + saveDraft(form) + } } const [width, setWidth] = createSignal(0) @@ -106,14 +109,8 @@ export const HeaderAuth = (props: Props) => {
- -
+ + - -
+ +
{t('Create post')} @@ -239,7 +230,7 @@ export const HeaderAuth = (props: Props) => { + - + { props.setIsProfilePopupVisible(isVisible) diff --git a/src/components/NotificationsPanel/NotificationsPanel.tsx b/src/components/NotificationsPanel/NotificationsPanel.tsx index 8d98292a..45c4cd3f 100644 --- a/src/components/NotificationsPanel/NotificationsPanel.tsx +++ b/src/components/NotificationsPanel/NotificationsPanel.tsx @@ -46,7 +46,7 @@ const isEarlier = (date: Date) => { export const NotificationsPanel = (props: Props) => { const [isLoading, setIsLoading] = createSignal(false) - const { isAuthenticated } = useSession() + const { author } = useSession() const { t } = useLocalize() const { after, @@ -150,16 +150,13 @@ export const NotificationsPanel = (props: Props) => { }) createEffect( - on( - () => isAuthenticated(), - async () => { - if (isAuthenticated()) { - setIsLoading(true) - await loadNextPage() - setIsLoading(false) - } - }, - ), + on(author, async (a) => { + if (a?.id) { + setIsLoading(true) + await loadNextPage() + setIsLoading(false) + } + }), ) return ( diff --git a/src/components/Topic/Card.tsx b/src/components/Topic/Card.tsx index ca61e6a9..525c6b29 100644 --- a/src/components/Topic/Card.tsx +++ b/src/components/Topic/Card.tsx @@ -49,11 +49,9 @@ export const TopicCard = (props: TopicProps) => { const handleFollowClick = () => { requireAuthentication(() => { - if (isSubscribed()) { - unfollow(FollowingEntity.Topic, props.topic.slug) - } else { - follow(FollowingEntity.Topic, props.topic.slug) - } + isSubscribed() + ? unfollow(FollowingEntity.Topic, props.topic.slug) + : follow(FollowingEntity.Topic, props.topic.slug) }, 'subscribe') } diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx index a523f6e4..ff307500 100644 --- a/src/components/Views/Author/Author.tsx +++ b/src/components/Views/Author/Author.tsx @@ -42,7 +42,6 @@ export const AuthorView = (props: Props) => { const { followers: myFollowers } = useFollowing() const { session } = useSession() const { sortedArticles } = useArticlesStore({ shouts: props.shouts }) - // const { authorEntities } = useAuthorsStore({ authors: [props.author] }) const { page: getPage, searchParams } = useRouter() const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) const [isBioExpanded, setIsBioExpanded] = createSignal(false) @@ -87,7 +86,7 @@ export const AuthorView = (props: Props) => { setFollowing([...(authors || []), ...(topics || [])]) setFollowers(followersResult || []) - console.info('[components.Author] data loaded') + console.debug('[components.Author] following data loaded', subscriptionsResult) } catch (error) { console.error('[components.Author] fetch error', error) } diff --git a/src/components/Views/DraftsView/DraftsView.tsx b/src/components/Views/DraftsView/DraftsView.tsx index 193b4bc4..acbc30db 100644 --- a/src/components/Views/DraftsView/DraftsView.tsx +++ b/src/components/Views/DraftsView/DraftsView.tsx @@ -1,6 +1,6 @@ import { openPage } from '@nanostores/router' import { clsx } from 'clsx' -import { For, Show, createEffect, createSignal } from 'solid-js' +import { For, Show, createEffect, createSignal, on } from 'solid-js' import { useEditorContext } from '../../../context/editor' import { useSession } from '../../../context/session' @@ -9,22 +9,24 @@ import { Shout } from '../../../graphql/schema/core.gen' import { router } from '../../../stores/router' import { Draft } from '../../Draft' +import { Loading } from '../../_shared/Loading' import styles from './DraftsView.module.scss' export const DraftsView = () => { - const { isAuthenticated, isSessionLoaded } = useSession() + const { session } = useSession() const [drafts, setDrafts] = createSignal([]) - const loadDrafts = async () => { - if (apiClient.private) { - const loadedDrafts = await apiClient.getDrafts() - setDrafts(loadedDrafts.reverse() || []) - } - } - - createEffect(() => { - if (isSessionLoaded()) loadDrafts() - }) + createEffect( + on( + () => session(), + async (s) => { + if (s) { + const loadedDrafts = await apiClient.getDrafts() + setDrafts(loadedDrafts.reverse() || []) + } + }, + ), + ) const { publishShoutById, deleteShout } = useEditorContext() @@ -44,22 +46,20 @@ export const DraftsView = () => { return (
- + }>
- - - {(draft) => ( - - )} - - + + {(draft) => ( + + )} +
diff --git a/src/components/Views/EditView/EditView.tsx b/src/components/Views/EditView/EditView.tsx index 83da483d..1e8650b1 100644 --- a/src/components/Views/EditView/EditView.tsx +++ b/src/components/Views/EditView/EditView.tsx @@ -2,6 +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 { ShoutForm, useEditorContext } from '../../../context/editor' import { useLocalize } from '../../../context/localize' @@ -41,7 +42,9 @@ export const EMPTY_TOPIC: Topic = { slug: '', } +const THROTTLING_INTERVAL = 2000 const AUTO_SAVE_INTERVAL = 5000 +const AUTO_SAVE_DELAY = 5000 const handleScrollTopButtonClick = (e) => { e.preventDefault() window.scrollTo({ @@ -65,12 +68,14 @@ export const EditView = (props: Props) => { } = useEditorContext() const shoutTopics = props.shout.topics || [] - // TODO: проверить сохранение черновика в local storage (не работает) const draft = getDraftFromLocalStorage(props.shout.id) + if (draft) { - setForm(Object.keys(draft).length !== 0 ? draft : { shoutId: props.shout.id }) + const draftForm = Object.keys(draft).length !== 0 ? draft : { shoutId: props.shout.id } + setForm(draftForm) + console.debug('draft from localstorage: ', draftForm) } else { - setForm({ + const draftForm = { slug: props.shout.slug, shoutId: props.shout.id, title: props.shout.title, @@ -83,7 +88,9 @@ export const EditView = (props: Props) => { coverImageUrl: props.shout.cover, media: props.shout.media, layout: props.shout.layout, - }) + } + setForm(draftForm) + console.debug('draft from props data: ', draftForm) } const subtitleInput: { current: HTMLTextAreaElement } = { current: null } @@ -106,9 +113,6 @@ export const EditView = (props: Props) => { onCleanup(() => { window.removeEventListener('scroll', handleScroll) }) - }) - - onMount(() => { // eslint-disable-next-line unicorn/consistent-function-scoping const handleBeforeUnload = (event) => { if (!deepEqual(prevForm, form)) { @@ -180,42 +184,39 @@ export const EditView = (props: Props) => { let autoSaveTimeOutId: number | string | NodeJS.Timeout - //TODO: add throttle + const autoSave = async () => { + const hasChanges = !deepEqual(form, prevForm) + const hasTopic = Boolean(form.mainTopic) + if (hasChanges || hasTopic) { + console.debug('saving draft', form) + setSaving(true) + saveDraftToLocalStorage(form) + await saveDraft(form) + setPrevForm(clone(form)) + setTimeout(() => setSaving(false), AUTO_SAVE_DELAY) + } + } + + // Throttle the autoSave function + const throttledAutoSave = throttle(THROTTLING_INTERVAL, autoSave) + const autoSaveRecursive = () => { - autoSaveTimeOutId = setTimeout(async () => { - const hasChanges = !deepEqual(form, prevForm) - if (hasChanges) { - setSaving(true) - if (props.shout?.published_at) { - saveDraftToLocalStorage(form) - } else { - await saveDraft(form) - } - setPrevForm(clone(form)) - setTimeout(() => { - setSaving(false) - }, 2000) - } + autoSaveTimeOutId = setTimeout(() => { + throttledAutoSave() autoSaveRecursive() }, AUTO_SAVE_INTERVAL) } - const stopAutoSave = () => { - clearTimeout(autoSaveTimeOutId) - } - onMount(() => { autoSaveRecursive() - }) - - onCleanup(() => { - stopAutoSave() + onCleanup(() => clearTimeout(autoSaveTimeOutId)) }) const showSubtitleInput = () => { setIsSubtitleVisible(true) subtitleInput.current.focus() } + const showLeadInput = () => { setIsLeadVisible(true) } diff --git a/src/components/Views/Feed/Feed.tsx b/src/components/Views/Feed/Feed.tsx index e457cafc..e5c196e9 100644 --- a/src/components/Views/Feed/Feed.tsx +++ b/src/components/Views/Feed/Feed.tsx @@ -49,7 +49,7 @@ type VisibilityItem = { } type FeedSearchParams = { - by: 'publish_date' | 'likes' | 'comments' + by: 'publish_date' | 'likes' | 'last_comment' period: FeedPeriod visibility: VisibilityMode } @@ -258,10 +258,10 @@ export const FeedView = (props: Props) => {
  • - changeSearchParams({ by: 'comments' })}> + changeSearchParams({ by: 'last_comment' })}> {t('Most commented')}
  • diff --git a/src/context/notifications.tsx b/src/context/notifications.tsx index b25ca037..7233f426 100644 --- a/src/context/notifications.tsx +++ b/src/context/notifications.tsx @@ -40,11 +40,11 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => { const [unreadNotificationsCount, setUnreadNotificationsCount] = createSignal(0) const [totalNotificationsCount, setTotalNotificationsCount] = createSignal(0) const [notificationEntities, setNotificationEntities] = createStore>({}) - const { isAuthenticated } = useSession() + const { author } = useSession() const { addHandler } = useConnect() const loadNotificationsGrouped = async (options: { after: number; limit?: number; offset?: number }) => { - if (isAuthenticated() && notifierClient?.private) { + if (author()?.id && notifierClient?.private) { const notificationsResult = await notifierClient.getNotifications(options) const groups = notificationsResult?.notifications || [] const total = notificationsResult?.total || 0 @@ -74,7 +74,7 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => { onMount(() => { addHandler((data: SSEMessage) => { - if (data.entity === 'reaction' && isAuthenticated()) { + if (data.entity === 'reaction' && author()?.id) { console.info('[context.notifications] event', data) loadNotificationsGrouped({ after: after(), limit: Math.max(PAGE_SIZE, loadedNotificationsCount()) }) } @@ -91,14 +91,14 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => { } const markSeenAll = async () => { - if (isAuthenticated() && notifierClient.private) { + if (author()?.id && notifierClient.private) { await notifierClient.markSeenAfter({ after: after() }) await loadNotificationsGrouped({ after: after(), limit: loadedNotificationsCount() }) } } const markSeen = async (notification_id: number) => { - if (isAuthenticated() && notifierClient.private) { + if (author()?.id && notifierClient.private) { await notifierClient.markSeen(notification_id) await loadNotificationsGrouped({ after: after(), limit: loadedNotificationsCount() }) } diff --git a/src/context/session.tsx b/src/context/session.tsx index 8fbd38e5..8ebb803a 100644 --- a/src/context/session.tsx +++ b/src/context/session.tsx @@ -48,7 +48,6 @@ export type SessionContextType = { author: Resource authError: Accessor isSessionLoaded: Accessor - isAuthenticated: Accessor loadSession: () => AuthToken | Promise setSession: (token: AuthToken | null) => void // setSession loadAuthor: (info?: unknown) => Author | Promise @@ -271,12 +270,9 @@ export const SessionProvider = (props: { // callback state updater createEffect( - on( - () => props.onStateChangeCallback, - () => { - props.onStateChangeCallback(session()) - }, - ), + on([() => props.onStateChangeCallback, session], ([_, ses]) => { + ses?.user?.id && props.onStateChangeCallback(ses) + }), ) const [authCallback, setAuthCallback] = createSignal<() => void>(noop) @@ -378,9 +374,6 @@ export const SessionProvider = (props: { console.warn(error) } } - - const isAuthenticated = createMemo(() => Boolean(author())) - const actions = { loadSession, requireAuthentication, @@ -405,7 +398,6 @@ export const SessionProvider = (props: { isSessionLoaded, author, ...actions, - isAuthenticated, resendVerifyEmail, } diff --git a/src/pages/edit.page.tsx b/src/pages/edit.page.tsx index ca6cd445..6b33a92f 100644 --- a/src/pages/edit.page.tsx +++ b/src/pages/edit.page.tsx @@ -1,9 +1,10 @@ -import { Show, Suspense, createMemo, createSignal, lazy, onMount } from 'solid-js' +import { Show, Suspense, createEffect, createMemo, createSignal, lazy, on, onMount } from 'solid-js' import { AuthGuard } from '../components/AuthGuard' import { Loading } from '../components/_shared/Loading' import { PageLayout } from '../components/_shared/PageLayout' 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' @@ -14,67 +15,70 @@ import { LayoutType } from './types' const EditView = lazy(() => import('../components/Views/EditView/EditView')) -export const EditPage = () => { - const snackbar = useSnackbar() - const { t } = useLocalize() +const getContentTypeTitle = (layout: LayoutType) => { + switch (layout) { + case 'audio': + return 'Publish Album' + case 'image': + return 'Create gallery' + case 'video': + return 'Create video' + case 'literature': + return 'New literary work' + default: + return 'Write an article' + } +} - const [shout, setShout] = createSignal(null) - const loadMyShout = async (shout_id: number) => { - if (shout_id) { - const { shout: loadedShout, error } = await apiClient.getMyShout(shout_id) - console.log(loadedShout) - if (error) { - await snackbar?.showSnackbar({ type: 'error', body: t('This content is not published yet') }) - redirectPage(router, 'drafts') - } else { - setShout(loadedShout) - } - } +export const EditPage = () => { + const { t } = useLocalize() + const { session } = useSession() + const snackbar = useSnackbar() + + const fail = async (error: string) => { + console.error(error) + await snackbar?.showSnackbar({ type: 'error', body: t(error) }) + redirectPage(router, 'drafts') } - onMount(async () => { - const shout_id = window.location.pathname.split('/').pop() - if (shout_id) { - try { - await loadMyShout(Number.parseInt(shout_id, 10)) - } catch (e) { - console.error(e) - } - } + 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([session, shout, shoutId], async ([ses, sh, shid]) => { + if (ses?.user && !sh && shid) { + const { shout: loadedShout, error } = await apiClient.getMyShout(shid) + if (error) { + fail(error) + } else { + setShout(loadedShout) + } + } + }), + ) + const title = createMemo(() => { if (!shout()) { return t('Create post') } - - switch (shout().layout as LayoutType) { - case 'audio': { - return t('Publish Album') - } - case 'image': { - return t('Create gallery') - } - case 'video': { - return t('Create video') - } - case 'literature': { - return t('New literary work') - } - default: { - return t('Write an article') - } - } + return t(getContentTypeTitle(shout()?.layout as LayoutType)) }) return ( - - }> - - - + }> + }> + + + ) diff --git a/src/stores/router.ts b/src/stores/router.ts index 3b2e49c8..46a5efd1 100644 --- a/src/stores/router.ts +++ b/src/stores/router.ts @@ -69,6 +69,7 @@ const checkOpenOnClient = (link: HTMLAnchorElement, event) => { ) } +// TODO: use scrollToHash or remove const _scrollToHash = (hash: string) => { let selector = hash diff --git a/src/stores/zine/authors.ts b/src/stores/zine/authors.ts index 7098ad2b..677af2a2 100644 --- a/src/stores/zine/authors.ts +++ b/src/stores/zine/authors.ts @@ -6,7 +6,7 @@ import { Author, QueryLoad_Authors_ByArgs } from '../../graphql/schema/core.gen' export type AuthorsSortBy = 'shouts' | 'name' | 'followers' type SortedAuthorsSetter = (prev: Author[]) => Author[] - +// FIXME: use signal or remove const [_sortAllBy, setSortAllBy] = createSignal('name') export const setAuthorsSort = (sortBy: AuthorsSortBy) => setSortAllBy(sortBy) diff --git a/src/utils/getRandomTopicsFromArray.ts b/src/utils/getRandomTopicsFromArray.ts index ced616c0..fb1a9328 100644 --- a/src/utils/getRandomTopicsFromArray.ts +++ b/src/utils/getRandomTopicsFromArray.ts @@ -2,6 +2,7 @@ import { RANDOM_TOPICS_COUNT } from '../components/Views/Home' import { Topic } from '../graphql/schema/core.gen' export const getRandomTopicsFromArray = (topics: Topic[], count: number = RANDOM_TOPICS_COUNT): Topic[] => { + if (!Array.isArray(topics)) return [] const shuffledTopics = [...topics].sort(() => 0.5 - Math.random()) return shuffledTopics.slice(0, count) }