2024-05-06 23:44:25 +00:00
|
|
|
import { createLazyMemo } from '@solid-primitives/memo'
|
2024-03-18 11:07:28 +00:00
|
|
|
import { openDB } from 'idb'
|
2024-05-06 23:44:25 +00:00
|
|
|
import { Accessor, JSX, createContext, createMemo, createSignal, onMount, useContext } from 'solid-js'
|
2024-03-18 11:07:28 +00:00
|
|
|
import { apiClient } from '../graphql/client/core'
|
|
|
|
import { Topic } from '../graphql/schema/core.gen'
|
2024-05-06 23:44:25 +00:00
|
|
|
import { useRouter } from '../stores/router'
|
|
|
|
import { byTopicStatDesc } from '../utils/sortby'
|
2024-03-18 11:07:28 +00:00
|
|
|
|
|
|
|
type TopicsContextType = {
|
2024-05-06 23:44:25 +00:00
|
|
|
topicEntities: Accessor<{ [topicSlug: string]: Topic }>
|
|
|
|
sortedTopics: Accessor<Topic[]>
|
|
|
|
randomTopics: Accessor<Topic[]>
|
|
|
|
topTopics: Accessor<Topic[]>
|
|
|
|
setTopicsSort: (sortBy: string) => void
|
|
|
|
addTopics: (topics: Topic[]) => void
|
2024-03-18 11:07:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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' })
|
|
|
|
}
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-05-05 16:13:48 +00:00
|
|
|
const getTopicsFromIndexedDB = (db) => {
|
2024-03-18 11:07:28 +00:00
|
|
|
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 }) => {
|
2024-05-06 23:44:25 +00:00
|
|
|
const [topicEntities, setTopicEntities] = createSignal<{ [topicSlug: string]: Topic }>({})
|
|
|
|
const [sortAllBy, setSortAllBy] = createSignal<'shouts' | 'followers' | 'authors' | 'title'>('shouts')
|
2024-03-18 11:07:28 +00:00
|
|
|
const [randomTopics, setRandomTopics] = createSignal<Topic[]>([])
|
|
|
|
|
2024-05-06 23:44:25 +00:00
|
|
|
const sortedTopics = createLazyMemo<Topic[]>(() => {
|
|
|
|
const topics = Object.values(topicEntities())
|
|
|
|
const { changeSearchParams } = useRouter()
|
|
|
|
switch (sortAllBy()) {
|
|
|
|
case 'followers': {
|
|
|
|
topics.sort(byTopicStatDesc('followers'))
|
|
|
|
break
|
|
|
|
}
|
|
|
|
case 'shouts': {
|
|
|
|
topics.sort(byTopicStatDesc('shouts'))
|
|
|
|
break
|
|
|
|
}
|
|
|
|
case 'authors': {
|
|
|
|
topics.sort(byTopicStatDesc('authors'))
|
|
|
|
break
|
|
|
|
}
|
|
|
|
case 'title': {
|
|
|
|
topics.sort((a, b) => a.title.localeCompare(b.title))
|
|
|
|
break
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
topics.sort(byTopicStatDesc('shouts'))
|
|
|
|
changeSearchParams({ by: 'shouts' })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return topics
|
|
|
|
})
|
|
|
|
|
|
|
|
const topTopics = createMemo(() => {
|
|
|
|
const topics = Object.values(topicEntities())
|
|
|
|
topics.sort(byTopicStatDesc('shouts'))
|
|
|
|
return topics
|
|
|
|
})
|
|
|
|
|
|
|
|
const addTopics = (...args: Topic[][]) => {
|
|
|
|
const allTopics = args.flatMap((topics) => (topics || []).filter(Boolean))
|
|
|
|
|
|
|
|
const newTopicEntities = allTopics.reduce(
|
|
|
|
(acc, topic) => {
|
|
|
|
acc[topic.slug] = topic
|
|
|
|
return acc
|
|
|
|
},
|
|
|
|
{} as Record<string, Topic>,
|
|
|
|
)
|
|
|
|
|
|
|
|
setTopicEntities((prevTopicEntities) => {
|
|
|
|
return {
|
|
|
|
...prevTopicEntities,
|
|
|
|
...newTopicEntities,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-03-18 11:07:28 +00:00
|
|
|
onMount(async () => {
|
|
|
|
const db = await setupIndexedDB()
|
2024-05-06 23:44:25 +00:00
|
|
|
let topics = await getTopicsFromIndexedDB(db)
|
2024-03-18 11:07:28 +00:00
|
|
|
|
2024-05-06 23:44:25 +00:00
|
|
|
if (topics.length < 100) {
|
2024-03-18 11:07:28 +00:00
|
|
|
topics = await apiClient.getAllTopics()
|
|
|
|
await saveTopicsToIndexedDB(db, topics)
|
|
|
|
}
|
2024-05-06 23:44:25 +00:00
|
|
|
addTopics(topics)
|
2024-03-18 11:07:28 +00:00
|
|
|
setRandomTopics(topics)
|
|
|
|
})
|
|
|
|
|
2024-05-06 23:44:25 +00:00
|
|
|
const value: TopicsContextType = {
|
|
|
|
setTopicsSort: setSortAllBy,
|
|
|
|
topicEntities,
|
|
|
|
sortedTopics,
|
|
|
|
randomTopics,
|
|
|
|
topTopics,
|
|
|
|
addTopics,
|
|
|
|
}
|
2024-03-18 11:07:28 +00:00
|
|
|
|
|
|
|
return <TopicsContext.Provider value={value}>{props.children}</TopicsContext.Provider>
|
|
|
|
}
|