From 6812ecd1873d33a981af474d6099f218362bb5ba Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Mon, 18 Mar 2024 14:07:28 +0300 Subject: [PATCH] One request for random topics (#428) --- package-lock.json | 6 +++ package.json | 1 + src/components/App.tsx | 33 ++++++++------- src/components/Nav/Header/Header.tsx | 15 +++---- src/components/Views/Home.tsx | 9 ---- src/context/topics.tsx | 59 +++++++++++++++++++++++++++ src/utils/getRandomTopicsFromArray.ts | 7 ++++ 7 files changed, 99 insertions(+), 31 deletions(-) create mode 100644 src/context/topics.tsx create mode 100644 src/utils/getRandomTopicsFromArray.ts diff --git a/package-lock.json b/package-lock.json index e8a84240..5c76496f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "license": "MIT", "dependencies": { "form-data": "4.0.0", + "idb": "8.0.0", "mailgun.js": "10.1.0" }, "devDependencies": { @@ -7231,6 +7232,11 @@ "node": ">=0.10.0" } }, + "node_modules/idb": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/idb/-/idb-8.0.0.tgz", + "integrity": "sha512-l//qvlAKGmQO31Qn7xdzagVPPaHTxXx199MhrAFuVBTPqydcPYBWjkrbv4Y0ktB+GmWOiwHl237UUOrLmQxLvw==" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", diff --git a/package.json b/package.json index 9a277dbc..5e79d11f 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ }, "dependencies": { "form-data": "4.0.0", + "idb": "8.0.0", "mailgun.js": "10.1.0" }, "devDependencies": { diff --git a/src/components/App.tsx b/src/components/App.tsx index c595ec15..15ce90bf 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -14,6 +14,7 @@ import { MediaQueryProvider } from '../context/mediaQuery' import { NotificationsProvider } from '../context/notifications' import { SessionProvider } from '../context/session' import { SnackbarProvider } from '../context/snackbar' +import { TopicsProvider } from '../context/topics' import { DiscussionRulesPage } from '../pages/about/discussionRules.page' import { DogmaPage } from '../pages/about/dogma.page' import { GuidePage } from '../pages/about/guide.page' @@ -116,21 +117,23 @@ export const App = (props: Props) => { - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/src/components/Nav/Header/Header.tsx b/src/components/Nav/Header/Header.tsx index dfbb2e71..7c0258e1 100644 --- a/src/components/Nav/Header/Header.tsx +++ b/src/components/Nav/Header/Header.tsx @@ -6,12 +6,10 @@ import { For, Show, createEffect, createSignal, onCleanup, onMount } from 'solid import { useLocalize } from '../../../context/localize' import { useSession } from '../../../context/session' -import { apiClient } from '../../../graphql/client/core' import { ROUTES, router, useRouter } from '../../../stores/router' import { useModalStore } from '../../../stores/ui' import { getDescription } from '../../../utils/meta' import { SharePopup, getShareUrl } from '../../Article/SharePopup' -import { RANDOM_TOPICS_COUNT } from '../../Views/Home' import { Icon } from '../../_shared/Icon' import { Subscribe } from '../../_shared/Subscribe' import { AuthModal } from '../AuthModal' @@ -23,6 +21,8 @@ import { Snackbar } from '../Snackbar' import { Link } from './Link' +import { useTopics } from '../../../context/topics' +import { getRandomTopicsFromArray } from '../../../utils/getRandomTopicsFromArray' import styles from './Header.module.scss' type Props = { @@ -48,6 +48,7 @@ export const Header = (props: Props) => { const { page } = useRouter() const { requireAuthentication } = useSession() const { searchParams } = useRouter() + const { topics } = useTopics() const [randomTopics, setRandomTopics] = createSignal([]) const [getIsScrollingBottom, setIsScrollingBottom] = createSignal(false) const [getIsScrolled, setIsScrolled] = createSignal(false) @@ -58,6 +59,7 @@ export const Header = (props: Props) => { const [isTopicsVisible, setIsTopicsVisible] = createSignal(false) const [isZineVisible, setIsZineVisible] = createSignal(false) const [isFeedVisible, setIsFeedVisible] = createSignal(false) + const toggleFixed = () => setFixed(!fixed()) const tag = (topic: Topic) => @@ -65,6 +67,10 @@ export const Header = (props: Props) => { let windowScrollTop = 0 + createEffect(() => { + setRandomTopics(getRandomTopicsFromArray(topics())) + }) + createEffect(() => { const mainContent = document.querySelector('.main-content') @@ -141,11 +147,6 @@ export const Header = (props: Props) => { }, time) } - onMount(async () => { - const topics = await apiClient.getRandomTopics({ amount: RANDOM_TOPICS_COUNT }) - setRandomTopics(topics) - }) - const handleToggleMenuByLink = (event: MouseEvent, route: keyof typeof ROUTES) => { if (!fixed()) { return diff --git a/src/components/Views/Home.tsx b/src/components/Views/Home.tsx index 5161f75e..25e15a31 100644 --- a/src/components/Views/Home.tsx +++ b/src/components/Views/Home.tsx @@ -66,15 +66,6 @@ export const HomeView = (props: Props) => { }) setIsLoadMoreButtonVisible(hasMore) } - - const result = await apiClient.getRandomTopicShouts(RANDOM_TOPIC_SHOUTS_COUNT) - if (!result || result.error) console.warn('[apiClient.getRandomTopicShouts] failed') - batch(() => { - if (!result?.error) { - if (result?.topic) setRandomTopic(result.topic) - if (result?.shouts) setRandomTopicArticles(result.shouts) - } - }) }) const loadMore = async () => { diff --git a/src/context/topics.tsx b/src/context/topics.tsx new file mode 100644 index 00000000..4fe8d5f6 --- /dev/null +++ b/src/context/topics.tsx @@ -0,0 +1,59 @@ +import { openDB } from 'idb' +import { Accessor, JSX, createContext, createSignal, onMount, useContext } from 'solid-js' +import { apiClient } from '../graphql/client/core' +import { Topic } from '../graphql/schema/core.gen' + +type TopicsContextType = { + topics: Accessor +} + +const TopicsContext = createContext() +export function useTopics() { + return useContext(TopicsContext) +} + +const DB_NAME = 'discourseAppDB' +const DB_VERSION = 1 +const STORE_NAME = 'topics' +const setupIndexedDB = async () => { + return await openDB(DB_NAME, DB_VERSION, { + upgrade(db) { + if (!db.objectStoreNames.contains(STORE_NAME)) { + db.createObjectStore(STORE_NAME, { keyPath: 'id' }) + } + }, + }) +} + +const getTopicsFromIndexedDB = async (db) => { + const tx = db.transaction(STORE_NAME, 'readonly') + const store = tx.objectStore(STORE_NAME) + return store.getAll() +} +const saveTopicsToIndexedDB = async (db, topics) => { + const tx = db.transaction(STORE_NAME, 'readwrite') + const store = tx.objectStore(STORE_NAME) + for (const topic of topics) { + await store.put(topic) + } + await tx.done +} + +export const TopicsProvider = (props: { children: JSX.Element }) => { + const [randomTopics, setRandomTopics] = createSignal([]) + + onMount(async () => { + const db = await setupIndexedDB() + let topics = await getTopicsFromIndexedDB(db) + + if (topics.length === 0) { + topics = await apiClient.getAllTopics() + await saveTopicsToIndexedDB(db, topics) + } + setRandomTopics(topics) + }) + + const value: TopicsContextType = { topics: randomTopics } + + return {props.children} +} diff --git a/src/utils/getRandomTopicsFromArray.ts b/src/utils/getRandomTopicsFromArray.ts new file mode 100644 index 00000000..ced616c0 --- /dev/null +++ b/src/utils/getRandomTopicsFromArray.ts @@ -0,0 +1,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[] => { + const shuffledTopics = [...topics].sort(() => 0.5 - Math.random()) + return shuffledTopics.slice(0, count) +}