2022-09-09 11:53:35 +00:00
|
|
|
import { apiClient } from '../../utils/apiClient'
|
2022-09-22 09:37:49 +00:00
|
|
|
import { map, MapStore, ReadableAtom, atom, computed } from 'nanostores'
|
2022-09-09 11:53:35 +00:00
|
|
|
import type { Topic } from '../../graphql/types.gen'
|
|
|
|
import { useStore } from '@nanostores/solid'
|
2022-09-22 09:37:49 +00:00
|
|
|
import { byCreated, byTopicStatDesc } from '../../utils/sortby'
|
|
|
|
import { getLogger } from '../../utils/logger'
|
|
|
|
import { createSignal } from 'solid-js'
|
2022-09-09 11:53:35 +00:00
|
|
|
|
2022-09-22 09:37:49 +00:00
|
|
|
const log = getLogger('topics store')
|
2022-09-09 11:53:35 +00:00
|
|
|
|
2022-09-22 09:37:49 +00:00
|
|
|
export type TopicsSortBy = 'created' | 'title' | 'authors' | 'shouts'
|
|
|
|
|
|
|
|
const sortAllByStore = atom<TopicsSortBy>('shouts')
|
2022-09-09 11:53:35 +00:00
|
|
|
|
2022-09-16 07:45:56 +00:00
|
|
|
let topicEntitiesStore: MapStore<Record<string, Topic>>
|
2022-09-09 11:53:35 +00:00
|
|
|
let sortedTopicsStore: ReadableAtom<Topic[]>
|
2022-09-13 09:59:04 +00:00
|
|
|
let topTopicsStore: ReadableAtom<Topic[]>
|
2022-09-22 09:37:49 +00:00
|
|
|
|
|
|
|
const [getRandomTopics, setRandomTopics] = createSignal<Topic[]>()
|
2022-09-16 07:45:56 +00:00
|
|
|
let topicsByAuthorStore: MapStore<Record<string, Topic[]>>
|
2022-09-09 11:53:35 +00:00
|
|
|
|
2022-09-22 09:37:49 +00:00
|
|
|
const initStore = (initial?: { [topicSlug: string]: Topic }) => {
|
2022-09-09 11:53:35 +00:00
|
|
|
if (topicEntitiesStore) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-09-16 07:45:56 +00:00
|
|
|
topicEntitiesStore = map<Record<string, Topic>>(initial)
|
2022-09-09 11:53:35 +00:00
|
|
|
|
2022-09-13 13:38:26 +00:00
|
|
|
sortedTopicsStore = computed([topicEntitiesStore, sortAllByStore], (topicEntities, sortBy) => {
|
2022-09-09 11:53:35 +00:00
|
|
|
const topics = Object.values(topicEntities)
|
|
|
|
switch (sortBy) {
|
|
|
|
case 'created': {
|
2022-09-22 09:37:49 +00:00
|
|
|
// log.debug('sorted by created')
|
2022-09-09 11:53:35 +00:00
|
|
|
topics.sort(byCreated)
|
|
|
|
break
|
|
|
|
}
|
2022-09-22 09:37:49 +00:00
|
|
|
case 'shouts':
|
|
|
|
case 'authors':
|
|
|
|
// log.debug(`sorted by ${sortBy}`)
|
|
|
|
topics.sort(byTopicStatDesc(sortBy))
|
|
|
|
break
|
|
|
|
case 'title':
|
|
|
|
// log.debug('sorted by title')
|
|
|
|
topics.sort((a, b) => a.title.localeCompare(b.title))
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
log.error(`Unknown sort: ${sortBy}`)
|
2022-09-09 11:53:35 +00:00
|
|
|
}
|
|
|
|
return topics
|
|
|
|
})
|
2022-09-13 09:59:04 +00:00
|
|
|
|
|
|
|
topTopicsStore = computed(topicEntitiesStore, (topicEntities) => {
|
|
|
|
const topics = Object.values(topicEntities)
|
2022-09-22 09:37:49 +00:00
|
|
|
topics.sort(byTopicStatDesc('shouts'))
|
2022-09-13 09:59:04 +00:00
|
|
|
return topics
|
|
|
|
})
|
2022-09-09 11:53:35 +00:00
|
|
|
}
|
|
|
|
|
2022-09-22 09:37:49 +00:00
|
|
|
export const setSortAllTopicsBy = (sortBy: TopicsSortBy) => {
|
|
|
|
if (sortAllByStore.get() !== sortBy) {
|
|
|
|
sortAllByStore.set(sortBy)
|
|
|
|
}
|
2022-09-13 13:38:26 +00:00
|
|
|
}
|
|
|
|
|
2022-09-13 09:59:04 +00:00
|
|
|
const addTopics = (...args: Topic[][]) => {
|
|
|
|
const allTopics = args.flatMap((topics) => topics || [])
|
|
|
|
|
|
|
|
const newTopicEntities = allTopics.reduce((acc, topic) => {
|
2022-09-09 11:53:35 +00:00
|
|
|
acc[topic.slug] = topic
|
|
|
|
return acc
|
|
|
|
}, {} as Record<string, Topic>)
|
|
|
|
|
|
|
|
if (!topicEntitiesStore) {
|
|
|
|
initStore(newTopicEntities)
|
|
|
|
} else {
|
|
|
|
topicEntitiesStore.set({
|
|
|
|
...topicEntitiesStore.get(),
|
|
|
|
...newTopicEntities
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-13 09:59:04 +00:00
|
|
|
export const addTopicsByAuthor = (topicsByAuthors: { [authorSlug: string]: Topic[] }) => {
|
|
|
|
const allTopics = Object.values(topicsByAuthors).flat()
|
|
|
|
addTopics(allTopics)
|
|
|
|
|
|
|
|
if (!topicsByAuthorStore) {
|
2022-09-16 07:45:56 +00:00
|
|
|
topicsByAuthorStore = map<Record<string, Topic[]>>(topicsByAuthors)
|
2022-09-13 09:59:04 +00:00
|
|
|
} else {
|
|
|
|
const newState = Object.entries(topicsByAuthors).reduce((acc, [authorSlug, topics]) => {
|
|
|
|
if (!acc[authorSlug]) {
|
|
|
|
acc[authorSlug] = []
|
|
|
|
}
|
|
|
|
|
|
|
|
topics.forEach((topic) => {
|
|
|
|
if (!acc[authorSlug].some((t) => t.slug === topic.slug)) {
|
|
|
|
acc[authorSlug].push(topic)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
return acc
|
|
|
|
}, topicsByAuthorStore.get())
|
|
|
|
|
|
|
|
topicsByAuthorStore.set(newState)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-09 11:53:35 +00:00
|
|
|
export const loadAllTopics = async (): Promise<void> => {
|
|
|
|
const topics = await apiClient.getAllTopics()
|
|
|
|
addTopics(topics)
|
|
|
|
}
|
|
|
|
|
|
|
|
type InitialState = {
|
|
|
|
topics?: Topic[]
|
|
|
|
randomTopics?: Topic[]
|
2022-09-22 09:37:49 +00:00
|
|
|
sortBy?: TopicsSortBy
|
2022-09-09 11:53:35 +00:00
|
|
|
}
|
|
|
|
|
2022-09-22 09:37:49 +00:00
|
|
|
export const useTopicsStore = ({ topics, randomTopics, sortBy }: InitialState = {}) => {
|
|
|
|
if (sortBy) {
|
|
|
|
sortAllByStore.set(sortBy)
|
2022-09-16 07:45:56 +00:00
|
|
|
}
|
2022-09-22 09:37:49 +00:00
|
|
|
|
|
|
|
addTopics(topics, randomTopics)
|
|
|
|
|
2022-09-16 07:45:56 +00:00
|
|
|
if (randomTopics) {
|
2022-09-22 09:37:49 +00:00
|
|
|
setRandomTopics(randomTopics)
|
2022-09-13 09:59:04 +00:00
|
|
|
}
|
2022-09-09 11:53:35 +00:00
|
|
|
|
|
|
|
const getTopicEntities = useStore(topicEntitiesStore)
|
2022-09-22 09:37:49 +00:00
|
|
|
|
2022-09-09 11:53:35 +00:00
|
|
|
const getSortedTopics = useStore(sortedTopicsStore)
|
2022-09-22 09:37:49 +00:00
|
|
|
|
2022-09-13 09:59:04 +00:00
|
|
|
const getTopTopics = useStore(topTopicsStore)
|
2022-09-13 08:05:11 +00:00
|
|
|
|
2022-09-22 09:37:49 +00:00
|
|
|
return { getTopicEntities, getSortedTopics, getRandomTopics, getTopTopics }
|
2022-09-09 11:53:35 +00:00
|
|
|
}
|