From 24e594138f4b49a4fe24ede36c0a386c4a5d224c Mon Sep 17 00:00:00 2001 From: Untone Date: Sat, 13 Jul 2024 13:32:27 +0300 Subject: [PATCH] topic-!-routing --- src/components/Views/Topic.tsx | 8 +- .../{(author-or-post).tsx => [...tab].tsx} | 73 ++++++++++++------- .../topic/{[slug].tsx => [slug]/[...tab].tsx} | 10 ++- 3 files changed, 55 insertions(+), 36 deletions(-) rename src/routes/[slug]/{(author-or-post).tsx => [...tab].tsx} (65%) rename src/routes/topic/{[slug].tsx => [slug]/[...tab].tsx} (88%) diff --git a/src/components/Views/Topic.tsx b/src/components/Views/Topic.tsx index b9116c6f..920421c4 100644 --- a/src/components/Views/Topic.tsx +++ b/src/components/Views/Topic.tsx @@ -20,15 +20,15 @@ import { FullTopic } from '../Topic/Full' import { Loading } from '../_shared/Loading' import { ArticleCardSwiper } from '../_shared/SolidSwiper/ArticleCardSwiper' -type TopicsPageSearchParams = { - by: 'comments' | '' | 'recent' | 'viewed' | 'rating' | 'commented' -} +// FIXME: should be 'last_comment' and 'comments_stat' or just one? +export type TopicFeedSortBy = 'comments' | '' | 'recent' | 'viewed' | 'rating' | 'commented' interface Props { topic: Topic shouts: Shout[] topicSlug: string followers?: Author[] + selectedTab?: TopicFeedSortBy } export const PRERENDERED_ARTICLES_COUNT = 28 @@ -39,7 +39,7 @@ export const TopicView = (props: Props) => { const { feedByTopic, addFeed } = useFeed() const { topicEntities } = useTopics() const { authorsByTopic } = useAuthors() - const [searchParams, changeSearchParams] = useSearchParams() + const [searchParams, changeSearchParams] = useSearchParams<{by: TopicFeedSortBy}>() const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) const [favoriteTopArticles, setFavoriteTopArticles] = createSignal([]) const [reactedTopMonthArticles, setReactedTopMonthArticles] = createSignal([]) diff --git a/src/routes/[slug]/(author-or-post).tsx b/src/routes/[slug]/[...tab].tsx similarity index 65% rename from src/routes/[slug]/(author-or-post).tsx rename to src/routes/[slug]/[...tab].tsx index 7b85f217..4df13c5a 100644 --- a/src/routes/[slug]/(author-or-post).tsx +++ b/src/routes/[slug]/[...tab].tsx @@ -5,7 +5,6 @@ import { Show, Suspense, createEffect, - createMemo, createSignal, on, onMount @@ -15,13 +14,14 @@ import { Loading } from '~/components/_shared/Loading' import { gaIdentity } from '~/config' import { useLocalize } from '~/context/localize' import { getShout } from '~/graphql/api/public' -import type { Author, Reaction, Shout } from '~/graphql/schema/core.gen' +import type { Author, Reaction, Shout, Topic } from '~/graphql/schema/core.gen' import { initGA, loadGAScript } from '~/utils/ga' import { descFromBody, keywordsFromTopics } from '~/utils/meta' import { FullArticle } from '../../components/Article/FullArticle' import { PageLayout } from '../../components/_shared/PageLayout' import { ReactionsProvider } from '../../context/reactions' import AuthorPage, { AuthorPageProps } from '../author/[slug]/[...tab]' +import TopicPage, { TopicPageProps } from '../topic/[slug]/[...tab]' const fetchShout = async (slug: string): Promise => { const shoutLoader = getShout({ slug }) @@ -35,29 +35,16 @@ export const route: RouteDefinition = { }) } -type SlugPageProps = { article?: Shout; comments?: Reaction[]; votes?: Reaction[]; author?: Author } - -export default (props: RouteSectionProps) => { - if (props.params.slug.startsWith('@')) { - console.debug('[slug] @ found, render as author page') - const patchedProps = { - ...props, - params: { - ...props.params, - slug: props.params.slug.slice(1, props.params.slug.length) - } - } as RouteSectionProps - return AuthorPage(patchedProps) - } +type ArticlePageProps = { article?: Shout; comments?: Reaction[]; votes?: Reaction[]; author?: Author } +export const ArticlePage = (props: RouteSectionProps) => { const loc = useLocation() const { t } = useLocalize() const [scrollToComments, setScrollToComments] = createSignal(false) - const article = createAsync(async () => props.data.article || (await fetchShout(props.params.slug))) - const titleSuffix = createMemo(() => (article()?.title ? ` :: ${article()?.title || ''}` : '')) + const data = createAsync(async () => props.data?.article || await fetchShout(props.params.slug)) onMount(async () => { - if (gaIdentity && article()?.id) { + if (gaIdentity && data()?.id) { try { await loadGAScript(gaIdentity) initGA(gaIdentity) @@ -69,7 +56,7 @@ export default (props: RouteSectionProps) => { createEffect( on( - article, + data, (a?: Shout) => { if (!a?.id) return window?.gtag?.('event', 'page_view', { @@ -86,7 +73,7 @@ export default (props: RouteSectionProps) => { }> }> @@ -95,16 +82,16 @@ export default (props: RouteSectionProps) => { } > setScrollToComments(value)} > - + @@ -112,3 +99,33 @@ export default (props: RouteSectionProps) => { ) } + +type SlugPageProps = { article?: Shout; comments?: Reaction[]; votes?: Reaction[]; author?: Author, topics: Topic[] } + +export default (props: RouteSectionProps) => { + if (props.params.slug.startsWith('@')) { + console.debug('[slug] starts with @, render as author page') + const patchedProps = { + ...props, + params: { + ...props.params, + slug: props.params.slug.slice(1, props.params.slug.length) + } + } as RouteSectionProps + return + } + + if (props.params.slug.startsWith('!')) { + console.debug('[slug] starts with !, render as topic page') + const patchedProps = { + ...props, + params: { + ...props.params, + slug: props.params.slug.slice(1, props.params.slug.length) + } + } as RouteSectionProps + return + } + + return +} diff --git a/src/routes/topic/[slug].tsx b/src/routes/topic/[slug]/[...tab].tsx similarity index 88% rename from src/routes/topic/[slug].tsx rename to src/routes/topic/[slug]/[...tab].tsx index ebc22868..62510216 100644 --- a/src/routes/topic/[slug].tsx +++ b/src/routes/topic/[slug]/[...tab].tsx @@ -2,16 +2,16 @@ import { RouteSectionProps, createAsync } from '@solidjs/router' import { HttpStatusCode } from '@solidjs/start' import { Show, Suspense, createEffect, createMemo, createSignal } from 'solid-js' import { FourOuFourView } from '~/components/Views/FourOuFour' -import { TopicView } from '~/components/Views/Topic' +import { TopicFeedSortBy, TopicView } from '~/components/Views/Topic' import { Loading } from '~/components/_shared/Loading' import { PageLayout } from '~/components/_shared/PageLayout' import { useLocalize } from '~/context/localize' import { useTopics } from '~/context/topics' import { loadShouts, loadTopics } from '~/graphql/api/public' -import { LoadShoutsOptions, Shout, Topic } from '~/graphql/schema/core.gen' +import { Author, LoadShoutsOptions, Shout, Topic } from '~/graphql/schema/core.gen' import { getImageUrl } from '~/lib/getImageUrl' import { descFromBody } from '~/utils/meta' -import { SHOUTS_PER_PAGE } from '../(main)' +import { SHOUTS_PER_PAGE } from '../../(main)' const fetchTopicShouts = async (slug: string, offset?: number) => { const opts: LoadShoutsOptions = { filters: { topic: slug }, limit: SHOUTS_PER_PAGE, offset } @@ -34,8 +34,9 @@ export const route = { } } } +export type TopicPageProps = { articles?: Shout[]; topics: Topic[], authors?: Author[] } -export default (props: RouteSectionProps<{ articles: Shout[]; topics: Topic[] }>) => { +export default function TopicPage(props: RouteSectionProps) { const { t } = useLocalize() const { addTopics } = useTopics() const [loadingError, setLoadingError] = createSignal(false) @@ -104,6 +105,7 @@ export default (props: RouteSectionProps<{ articles: Shout[]; topics: Topic[] }> topic={topic() as Topic} topicSlug={props.params.slug} shouts={articles() as Shout[]} + selectedTab={props.params.tab as TopicFeedSortBy} />