From e63b018efa5b40dd445b46c890506adb68c9870e Mon Sep 17 00:00:00 2001 From: Igor Lobanov Date: Wed, 5 Oct 2022 17:11:14 +0200 Subject: [PATCH] client data fetching --- src/components/Nav/Header.tsx | 2 +- src/components/Pages/AllAuthorsPage.tsx | 18 ++++++++++++- src/components/Pages/AllTopicsPage.tsx | 18 ++++++++++++- src/components/Pages/ArticlePage.tsx | 21 ++++++++------- src/components/Pages/AuthorPage.tsx | 36 ++++++++++++++++++++++++- src/components/Pages/FeedPage.tsx | 21 ++++++++++++++- src/components/Pages/HomePage.tsx | 23 +++++++++++++++- src/components/Pages/SearchPage.tsx | 33 ++++++++++++++++++++++- src/components/Pages/TopicPage.tsx | 36 ++++++++++++++++++++++++- src/components/Views/Author.tsx | 16 +++-------- src/components/Views/Home.tsx | 4 +-- src/components/Views/Topic.tsx | 12 ++++----- src/components/types.ts | 9 ++++--- src/pages/author/[slug]/index.astro | 2 +- src/pages/authors.astro | 2 +- src/pages/feed/index.astro | 2 +- src/pages/index.astro | 2 +- src/pages/topic/[slug].astro | 2 +- src/pages/topics.astro | 2 +- src/stores/zine/articles.ts | 16 +++++++++++ src/stores/zine/authors.ts | 7 +++++ src/stores/zine/topics.ts | 12 +++++++++ src/utils/apiClient.ts | 4 +++ 23 files changed, 254 insertions(+), 46 deletions(-) diff --git a/src/components/Nav/Header.tsx b/src/components/Nav/Header.tsx index c8109da7..0d0a08f2 100644 --- a/src/components/Nav/Header.tsx +++ b/src/components/Nav/Header.tsx @@ -71,7 +71,7 @@ export const Header = (props: Props) => { onMount(() => { let scrollTop = window.scrollY - window.console.log(props.title) + // window.console.log(props.title) const handleScroll = () => { setIsScrollingBottom(window.scrollY > scrollTop) diff --git a/src/components/Pages/AllAuthorsPage.tsx b/src/components/Pages/AllAuthorsPage.tsx index 4e6dee4c..78e95682 100644 --- a/src/components/Pages/AllAuthorsPage.tsx +++ b/src/components/Pages/AllAuthorsPage.tsx @@ -1,11 +1,27 @@ import { MainLayout } from '../Layouts/MainLayout' import { AllAuthorsView } from '../Views/AllAuthors' import type { PageProps } from '../types' +import { createSignal, onMount, Show } from 'solid-js' +import { loadAllAuthors } from '../../stores/zine/authors' +import { t } from '../../utils/intl' export const AllAuthorsPage = (props: PageProps) => { + const [isLoaded, setIsLoaded] = createSignal(Boolean(props.allAuthors)) + + onMount(async () => { + if (isLoaded()) { + return + } + + await loadAllAuthors() + setIsLoaded(true) + }) + return ( - + + + ) } diff --git a/src/components/Pages/AllTopicsPage.tsx b/src/components/Pages/AllTopicsPage.tsx index 745a6524..2e371ba6 100644 --- a/src/components/Pages/AllTopicsPage.tsx +++ b/src/components/Pages/AllTopicsPage.tsx @@ -1,11 +1,27 @@ import { MainLayout } from '../Layouts/MainLayout' import { AllTopicsView } from '../Views/AllTopics' import type { PageProps } from '../types' +import { createSignal, onMount, Show } from 'solid-js' +import { t } from '../../utils/intl' +import { loadAllTopics } from '../../stores/zine/topics' export const AllTopicsPage = (props: PageProps) => { + const [isLoaded, setIsLoaded] = createSignal(Boolean(props.allTopics)) + + onMount(async () => { + if (isLoaded()) { + return + } + + await loadAllTopics() + setIsLoaded(true) + }) + return ( - + + + ) } diff --git a/src/components/Pages/ArticlePage.tsx b/src/components/Pages/ArticlePage.tsx index 74c8a338..cc7fb918 100644 --- a/src/components/Pages/ArticlePage.tsx +++ b/src/components/Pages/ArticlePage.tsx @@ -10,26 +10,29 @@ import { useRouter } from '../../stores/router' export const ArticlePage = (props: PageProps) => { const sortedArticles = props.article ? [props.article] : [] - const { getPage } = useRouter() + const slug = createMemo(() => { + const { getPage } = useRouter() - const page = getPage() + const page = getPage() - if (page.route !== 'article') { - throw new Error('ts guard') - } + if (page.route !== 'article') { + throw new Error('ts guard') + } + + return page.params.slug + }) const { articleEntities } = useArticlesStore({ sortedArticles }) - const article = createMemo(() => articleEntities()[page.params.slug]) + const article = createMemo(() => articleEntities()[slug()]) onMount(() => { - const slug = page.params.slug - const articleValue = articleEntities()[slug] + const articleValue = articleEntities()[slug()] if (!articleValue || !articleValue.body) { - loadArticle({ slug }) + loadArticle({ slug: slug() }) } }) diff --git a/src/components/Pages/AuthorPage.tsx b/src/components/Pages/AuthorPage.tsx index a57c4dad..aa825fd9 100644 --- a/src/components/Pages/AuthorPage.tsx +++ b/src/components/Pages/AuthorPage.tsx @@ -1,11 +1,45 @@ import { MainLayout } from '../Layouts/MainLayout' import { AuthorView } from '../Views/Author' import type { PageProps } from '../types' +import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js' +import { loadArticlesForAuthors, resetSortedArticles } from '../../stores/zine/articles' +import { useRouter } from '../../stores/router' +import { t } from '../../utils/intl' +import { loadAuthor } from '../../stores/zine/authors' export const AuthorPage = (props: PageProps) => { + const [isLoaded, setIsLoaded] = createSignal(Boolean(props.authorArticles) && Boolean(props.author)) + + const slug = createMemo(() => { + const { getPage } = useRouter() + + const page = getPage() + + if (page.route !== 'author') { + throw new Error('ts guard') + } + + return page.params.slug + }) + + onMount(async () => { + if (isLoaded()) { + return + } + + await loadArticlesForAuthors({ authorSlugs: [slug()] }) + await loadAuthor({ slug: slug() }) + + setIsLoaded(true) + }) + + onCleanup(() => resetSortedArticles()) + return ( - + + + ) } diff --git a/src/components/Pages/FeedPage.tsx b/src/components/Pages/FeedPage.tsx index de95f1b6..1e015e69 100644 --- a/src/components/Pages/FeedPage.tsx +++ b/src/components/Pages/FeedPage.tsx @@ -1,11 +1,30 @@ import { MainLayout } from '../Layouts/MainLayout' import { FeedView } from '../Views/Feed' import type { PageProps } from '../types' +import { createSignal, onCleanup, onMount, Show } from 'solid-js' +import { loadRecentArticles, resetSortedArticles } from '../../stores/zine/articles' +import { t } from '../../utils/intl' export const FeedPage = (props: PageProps) => { + const [isLoaded, setIsLoaded] = createSignal(Boolean(props.feedArticles)) + + onMount(async () => { + if (isLoaded()) { + return + } + + await loadRecentArticles({ limit: 50, offset: 0 }) + + setIsLoaded(true) + }) + + onCleanup(() => resetSortedArticles()) + return ( - + + + ) } diff --git a/src/components/Pages/HomePage.tsx b/src/components/Pages/HomePage.tsx index 78f5b3d1..cbfb9d10 100644 --- a/src/components/Pages/HomePage.tsx +++ b/src/components/Pages/HomePage.tsx @@ -1,11 +1,32 @@ import { HomeView } from '../Views/Home' import { MainLayout } from '../Layouts/MainLayout' import type { PageProps } from '../types' +import { createSignal, onCleanup, onMount, Show } from 'solid-js' +import { t } from '../../utils/intl' +import { loadPublishedArticles, resetSortedArticles } from '../../stores/zine/articles' +import { loadRandomTopics } from '../../stores/zine/topics' export const HomePage = (props: PageProps) => { + const [isLoaded, setIsLoaded] = createSignal(Boolean(props.homeArticles) && Boolean(props.randomTopics)) + + onMount(async () => { + if (isLoaded()) { + return + } + + await loadPublishedArticles({ limit: 5, offset: 0 }) + await loadRandomTopics() + + setIsLoaded(true) + }) + + onCleanup(() => resetSortedArticles()) + return ( - + + + ) } diff --git a/src/components/Pages/SearchPage.tsx b/src/components/Pages/SearchPage.tsx index 830c00a9..b7668cf9 100644 --- a/src/components/Pages/SearchPage.tsx +++ b/src/components/Pages/SearchPage.tsx @@ -1,11 +1,42 @@ import { MainLayout } from '../Layouts/MainLayout' import { SearchView } from '../Views/Search' import type { PageProps } from '../types' +import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js' +import { loadSearchResults, resetSortedArticles } from '../../stores/zine/articles' +import { t } from '../../utils/intl' +import { useRouter } from '../../stores/router' export const SearchPage = (props: PageProps) => { + const [isLoaded, setIsLoaded] = createSignal(Boolean(props.searchResults)) + + const q = createMemo(() => { + const { getPage } = useRouter() + + const page = getPage() + + if (page.route !== 'search') { + throw new Error('ts guard') + } + + return page.params.q + }) + + onMount(async () => { + if (isLoaded()) { + return + } + + await loadSearchResults({ query: q(), limit: 50, offset: 0 }) + setIsLoaded(true) + }) + + onCleanup(() => resetSortedArticles()) + return ( - + + + ) } diff --git a/src/components/Pages/TopicPage.tsx b/src/components/Pages/TopicPage.tsx index f6605f81..f974f20e 100644 --- a/src/components/Pages/TopicPage.tsx +++ b/src/components/Pages/TopicPage.tsx @@ -1,11 +1,45 @@ import { MainLayout } from '../Layouts/MainLayout' import { TopicView } from '../Views/Topic' import type { PageProps } from '../types' +import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js' +import { loadArticlesForTopics, resetSortedArticles } from '../../stores/zine/articles' +import { useRouter } from '../../stores/router' +import { t } from '../../utils/intl' +import { loadTopic } from '../../stores/zine/topics' export const TopicPage = (props: PageProps) => { + const [isLoaded, setIsLoaded] = createSignal(Boolean(props.authorArticles) && Boolean(props.author)) + + const slug = createMemo(() => { + const { getPage } = useRouter() + + const page = getPage() + + if (page.route !== 'author') { + throw new Error('ts guard') + } + + return page.params.slug + }) + + onMount(async () => { + if (isLoaded()) { + return + } + + await loadArticlesForTopics({ topicSlugs: [slug()] }) + await loadTopic({ slug: slug() }) + + setIsLoaded(true) + }) + + onCleanup(() => resetSortedArticles()) + return ( - + + + ) } diff --git a/src/components/Views/Author.tsx b/src/components/Views/Author.tsx index 16f43e3e..d4afed8b 100644 --- a/src/components/Views/Author.tsx +++ b/src/components/Views/Author.tsx @@ -16,6 +16,7 @@ import { useRouter } from '../../stores/router' type AuthorProps = { authorArticles: Shout[] author: Author + authorSlug: string // FIXME author topics fro server // topics: Topic[] } @@ -30,18 +31,9 @@ export const AuthorView = (props: AuthorProps) => { }) const { authorEntities } = useAuthorsStore({ authors: [props.author] }) - const author = createMemo(() => authorEntities()[props.author.slug]) + const author = createMemo(() => authorEntities()[props.authorSlug]) const { getSearchParams, changeSearchParam } = useRouter() - //const slug = createMemo(() => author().slug) - /* - const slug = createMemo(() => { - let slug = props?.slug - if (props?.slug.startsWith('@')) slug = slug.replace('@', '') - return slug - }) - */ - const title = createMemo(() => { const m = getSearchParams().by if (m === 'viewed') return t('Top viewed') @@ -101,8 +93,8 @@ export const AuthorView = (props: AuthorProps) => { {/*/>*/} - - + + diff --git a/src/components/Views/Home.tsx b/src/components/Views/Home.tsx index b508a63e..542325c7 100644 --- a/src/components/Views/Home.tsx +++ b/src/components/Views/Home.tsx @@ -1,4 +1,4 @@ -import { createMemo, For, onMount, Show } from 'solid-js' +import { createEffect, createMemo, For, onMount, Show } from 'solid-js' import Banner from '../Discours/Banner' import { NavTopics } from '../Nav/Topics' import { Row5 } from '../Feed/Row5' @@ -103,7 +103,7 @@ export const HomeView = (props: HomeProps) => { }) return ( - + 0}> diff --git a/src/components/Views/Topic.tsx b/src/components/Views/Topic.tsx index 0ae118ce..727c31f5 100644 --- a/src/components/Views/Topic.tsx +++ b/src/components/Views/Topic.tsx @@ -19,6 +19,7 @@ type TopicsPageSearchParams = { interface TopicProps { topic: Topic topicArticles: Shout[] + topicSlug: string } export const TopicView = (props: TopicProps) => { @@ -29,7 +30,7 @@ export const TopicView = (props: TopicProps) => { const { authorsByTopic } = useAuthorsStore() - const topic = createMemo(() => topicEntities()[props.topic.slug]) + const topic = createMemo(() => topicEntities()[props.topicSlug]) /* const slug = createMemo(() => { @@ -40,11 +41,10 @@ export const TopicView = (props: TopicProps) => { */ const title = createMemo(() => { - // FIXME - // const m = getSearchParams().by - // if (m === 'viewed') return t('Top viewed') - // if (m === 'rating') return t('Top rated') - // if (m === 'commented') return t('Top discussed') + const m = getSearchParams().by + if (m === 'viewed') return t('Top viewed') + if (m === 'rating') return t('Top rated') + if (m === 'commented') return t('Top discussed') return t('Top recent') }) diff --git a/src/components/types.ts b/src/components/types.ts index b8de6695..0b2fca98 100644 --- a/src/components/types.ts +++ b/src/components/types.ts @@ -5,11 +5,14 @@ import type { Author, Shout, Topic } from '../graphql/types.gen' export type PageProps = { randomTopics?: Topic[] article?: Shout - articles?: Shout[] + authorArticles?: Shout[] + topicArticles?: Shout[] + homeArticles?: Shout[] + feedArticles?: Shout[] author?: Author - authors?: Author[] + allAuthors?: Author[] topic?: Topic - topics?: Topic[] + allTopics?: Topic[] searchQuery?: string // other types? searchResults?: Shout[] diff --git a/src/pages/author/[slug]/index.astro b/src/pages/author/[slug]/index.astro index 69774b1f..7ad13be2 100644 --- a/src/pages/author/[slug]/index.astro +++ b/src/pages/author/[slug]/index.astro @@ -15,5 +15,5 @@ Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate' --- - + diff --git a/src/pages/authors.astro b/src/pages/authors.astro index 2cb49a90..2b5b1176 100644 --- a/src/pages/authors.astro +++ b/src/pages/authors.astro @@ -11,5 +11,5 @@ initRouter(pathname, search) --- - + diff --git a/src/pages/feed/index.astro b/src/pages/feed/index.astro index a16d6f37..93c40a9c 100644 --- a/src/pages/feed/index.astro +++ b/src/pages/feed/index.astro @@ -12,5 +12,5 @@ const articles = await apiClient.getRecentArticles({ limit: 50 }) --- - + diff --git a/src/pages/index.astro b/src/pages/index.astro index 7066509a..e03b076c 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -15,6 +15,6 @@ Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate' --- - + diff --git a/src/pages/topic/[slug].astro b/src/pages/topic/[slug].astro index 9a4d0ff1..98134a4b 100644 --- a/src/pages/topic/[slug].astro +++ b/src/pages/topic/[slug].astro @@ -16,5 +16,5 @@ Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate' --- - + diff --git a/src/pages/topics.astro b/src/pages/topics.astro index 0ab55341..eda7b67a 100644 --- a/src/pages/topics.astro +++ b/src/pages/topics.astro @@ -11,6 +11,6 @@ initRouter(pathname, search) --- - + diff --git a/src/stores/zine/articles.ts b/src/stores/zine/articles.ts index f3310868..fa2ca139 100644 --- a/src/stores/zine/articles.ts +++ b/src/stores/zine/articles.ts @@ -151,6 +151,22 @@ export const loadPublishedArticles = async ({ addSortedArticles(newArticles) } +export const loadArticlesForAuthors = async ({ authorSlugs }: { authorSlugs: string[] }): Promise => { + const articles = await apiClient.getArticlesForAuthors({ authorSlugs, limit: 50 }) + addArticles(articles) + setSortedArticles(articles) +} + +export const loadArticlesForTopics = async ({ topicSlugs }: { topicSlugs: string[] }): Promise => { + const articles = await apiClient.getArticlesForTopics({ topicSlugs, limit: 50 }) + addArticles(articles) + setSortedArticles(articles) +} + +export const resetSortedArticles = () => { + setSortedArticles([]) +} + export const loadTopMonthArticles = async (): Promise => { const articles = await apiClient.getTopMonthArticles() addArticles(articles) diff --git a/src/stores/zine/authors.ts b/src/stores/zine/authors.ts index 567f70a1..9932eb2c 100644 --- a/src/stores/zine/authors.ts +++ b/src/stores/zine/authors.ts @@ -46,6 +46,13 @@ const addAuthors = (authors: Author[]) => { }) } +export const loadAuthor = async ({ slug }: { slug: string }): Promise => { + // TODO: + const articles = await apiClient.getArticlesForAuthors({ authorSlugs: [slug], limit: 1 }) + const author = articles[0].authors.find((a) => a.slug === slug) + addAuthors([author]) +} + export const addAuthorsByTopic = (newAuthorsByTopic: { [topicSlug: string]: Author[] }) => { const allAuthors = Object.values(newAuthorsByTopic).flat() addAuthors(allAuthors) diff --git a/src/stores/zine/topics.ts b/src/stores/zine/topics.ts index 9fc75672..b9784387 100644 --- a/src/stores/zine/topics.ts +++ b/src/stores/zine/topics.ts @@ -92,6 +92,18 @@ export const loadAllTopics = async (): Promise => { addTopics(topics) } +export const loadRandomTopics = async (): Promise => { + const topics = await apiClient.getRandomTopics({ amount: 12 }) + setRandomTopics(topics) +} + +export const loadTopic = async ({ slug }: { slug: string }): Promise => { + // TODO: + const articles = await apiClient.getArticlesForTopics({ topicSlugs: [slug], limit: 1 }) + const topic = articles[0].topics.find(({ slug: topicSlug }) => topicSlug === slug) + addTopics([topic]) +} + type InitialState = { topics?: Topic[] randomTopics?: Topic[] diff --git a/src/utils/apiClient.ts b/src/utils/apiClient.ts index 4cd6c3ce..be9e29b5 100644 --- a/src/utils/apiClient.ts +++ b/src/utils/apiClient.ts @@ -225,6 +225,10 @@ export const apiClient = { getPublishedArticles: async ({ limit = FEED_SIZE, offset }: { limit?: number; offset?: number }) => { const response = await publicGraphQLClient.query(articlesRecentPublished, { limit, offset }).toPromise() + if (response.error) { + log.error('getPublishedArticles', response.error) + } + return response.data.recentPublished }, getAllTopics: async () => {