One request for random topics (#428)

This commit is contained in:
Ilya Y 2024-03-18 14:07:28 +03:00 committed by GitHub
parent 896c180dd1
commit 6812ecd187
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 99 additions and 31 deletions

6
package-lock.json generated
View File

@ -11,6 +11,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"form-data": "4.0.0", "form-data": "4.0.0",
"idb": "8.0.0",
"mailgun.js": "10.1.0" "mailgun.js": "10.1.0"
}, },
"devDependencies": { "devDependencies": {
@ -7231,6 +7232,11 @@
"node": ">=0.10.0" "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": { "node_modules/ieee754": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",

View File

@ -29,6 +29,7 @@
}, },
"dependencies": { "dependencies": {
"form-data": "4.0.0", "form-data": "4.0.0",
"idb": "8.0.0",
"mailgun.js": "10.1.0" "mailgun.js": "10.1.0"
}, },
"devDependencies": { "devDependencies": {

View File

@ -14,6 +14,7 @@ import { MediaQueryProvider } from '../context/mediaQuery'
import { NotificationsProvider } from '../context/notifications' import { NotificationsProvider } from '../context/notifications'
import { SessionProvider } from '../context/session' import { SessionProvider } from '../context/session'
import { SnackbarProvider } from '../context/snackbar' import { SnackbarProvider } from '../context/snackbar'
import { TopicsProvider } from '../context/topics'
import { DiscussionRulesPage } from '../pages/about/discussionRules.page' import { DiscussionRulesPage } from '../pages/about/discussionRules.page'
import { DogmaPage } from '../pages/about/dogma.page' import { DogmaPage } from '../pages/about/dogma.page'
import { GuidePage } from '../pages/about/guide.page' import { GuidePage } from '../pages/about/guide.page'
@ -116,21 +117,23 @@ export const App = (props: Props) => {
<LocalizeProvider> <LocalizeProvider>
<MediaQueryProvider> <MediaQueryProvider>
<SnackbarProvider> <SnackbarProvider>
<ConfirmProvider> <TopicsProvider>
<SessionProvider onStateChangeCallback={console.log}> <ConfirmProvider>
<FollowingProvider> <SessionProvider onStateChangeCallback={console.log}>
<ConnectProvider> <FollowingProvider>
<NotificationsProvider> <ConnectProvider>
<EditorProvider> <NotificationsProvider>
<InboxProvider> <EditorProvider>
<Dynamic component={pageComponent()} {...props} /> <InboxProvider>
</InboxProvider> <Dynamic component={pageComponent()} {...props} />
</EditorProvider> </InboxProvider>
</NotificationsProvider> </EditorProvider>
</ConnectProvider> </NotificationsProvider>
</FollowingProvider> </ConnectProvider>
</SessionProvider> </FollowingProvider>
</ConfirmProvider> </SessionProvider>
</ConfirmProvider>
</TopicsProvider>
</SnackbarProvider> </SnackbarProvider>
</MediaQueryProvider> </MediaQueryProvider>
</LocalizeProvider> </LocalizeProvider>

View File

@ -6,12 +6,10 @@ import { For, Show, createEffect, createSignal, onCleanup, onMount } from 'solid
import { useLocalize } from '../../../context/localize' import { useLocalize } from '../../../context/localize'
import { useSession } from '../../../context/session' import { useSession } from '../../../context/session'
import { apiClient } from '../../../graphql/client/core'
import { ROUTES, router, useRouter } from '../../../stores/router' import { ROUTES, router, useRouter } from '../../../stores/router'
import { useModalStore } from '../../../stores/ui' import { useModalStore } from '../../../stores/ui'
import { getDescription } from '../../../utils/meta' import { getDescription } from '../../../utils/meta'
import { SharePopup, getShareUrl } from '../../Article/SharePopup' import { SharePopup, getShareUrl } from '../../Article/SharePopup'
import { RANDOM_TOPICS_COUNT } from '../../Views/Home'
import { Icon } from '../../_shared/Icon' import { Icon } from '../../_shared/Icon'
import { Subscribe } from '../../_shared/Subscribe' import { Subscribe } from '../../_shared/Subscribe'
import { AuthModal } from '../AuthModal' import { AuthModal } from '../AuthModal'
@ -23,6 +21,8 @@ import { Snackbar } from '../Snackbar'
import { Link } from './Link' import { Link } from './Link'
import { useTopics } from '../../../context/topics'
import { getRandomTopicsFromArray } from '../../../utils/getRandomTopicsFromArray'
import styles from './Header.module.scss' import styles from './Header.module.scss'
type Props = { type Props = {
@ -48,6 +48,7 @@ export const Header = (props: Props) => {
const { page } = useRouter() const { page } = useRouter()
const { requireAuthentication } = useSession() const { requireAuthentication } = useSession()
const { searchParams } = useRouter<HeaderSearchParams>() const { searchParams } = useRouter<HeaderSearchParams>()
const { topics } = useTopics()
const [randomTopics, setRandomTopics] = createSignal([]) const [randomTopics, setRandomTopics] = createSignal([])
const [getIsScrollingBottom, setIsScrollingBottom] = createSignal(false) const [getIsScrollingBottom, setIsScrollingBottom] = createSignal(false)
const [getIsScrolled, setIsScrolled] = createSignal(false) const [getIsScrolled, setIsScrolled] = createSignal(false)
@ -58,6 +59,7 @@ export const Header = (props: Props) => {
const [isTopicsVisible, setIsTopicsVisible] = createSignal(false) const [isTopicsVisible, setIsTopicsVisible] = createSignal(false)
const [isZineVisible, setIsZineVisible] = createSignal(false) const [isZineVisible, setIsZineVisible] = createSignal(false)
const [isFeedVisible, setIsFeedVisible] = createSignal(false) const [isFeedVisible, setIsFeedVisible] = createSignal(false)
const toggleFixed = () => setFixed(!fixed()) const toggleFixed = () => setFixed(!fixed())
const tag = (topic: Topic) => const tag = (topic: Topic) =>
@ -65,6 +67,10 @@ export const Header = (props: Props) => {
let windowScrollTop = 0 let windowScrollTop = 0
createEffect(() => {
setRandomTopics(getRandomTopicsFromArray(topics()))
})
createEffect(() => { createEffect(() => {
const mainContent = document.querySelector<HTMLDivElement>('.main-content') const mainContent = document.querySelector<HTMLDivElement>('.main-content')
@ -141,11 +147,6 @@ export const Header = (props: Props) => {
}, time) }, time)
} }
onMount(async () => {
const topics = await apiClient.getRandomTopics({ amount: RANDOM_TOPICS_COUNT })
setRandomTopics(topics)
})
const handleToggleMenuByLink = (event: MouseEvent, route: keyof typeof ROUTES) => { const handleToggleMenuByLink = (event: MouseEvent, route: keyof typeof ROUTES) => {
if (!fixed()) { if (!fixed()) {
return return

View File

@ -66,15 +66,6 @@ export const HomeView = (props: Props) => {
}) })
setIsLoadMoreButtonVisible(hasMore) 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 () => { const loadMore = async () => {

59
src/context/topics.tsx Normal file
View File

@ -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<Topic[]>
}
const TopicsContext = createContext<TopicsContextType>()
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<Topic[]>([])
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 <TopicsContext.Provider value={value}>{props.children}</TopicsContext.Provider>
}

View File

@ -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)
}