import { apiClient } from '../../utils/apiClient' import { map, MapStore, ReadableAtom, atom, computed } from 'nanostores' import type { Topic } from '../../graphql/types.gen' import { useStore } from '@nanostores/solid' import { byCreated, byTopicStatDesc } from '../../utils/sortby' import { getLogger } from '../../utils/logger' import { createSignal } from 'solid-js' const log = getLogger('topics store') export type TopicsSortBy = 'created' | 'title' | 'authors' | 'shouts' const sortAllByStore = atom('shouts') let topicEntitiesStore: MapStore> let sortedTopicsStore: ReadableAtom let topTopicsStore: ReadableAtom const [getRandomTopics, setRandomTopics] = createSignal() let topicsByAuthorStore: MapStore> const initStore = (initial?: { [topicSlug: string]: Topic }) => { if (topicEntitiesStore) { return } topicEntitiesStore = map>(initial) sortedTopicsStore = computed([topicEntitiesStore, sortAllByStore], (topicEntities, sortBy) => { const topics = Object.values(topicEntities) switch (sortBy) { case 'created': { // log.debug('sorted by created') topics.sort(byCreated) break } 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}`) } return topics }) topTopicsStore = computed(topicEntitiesStore, (topicEntities) => { const topics = Object.values(topicEntities) topics.sort(byTopicStatDesc('shouts')) return topics }) } export const setSortAllTopicsBy = (sortBy: TopicsSortBy) => { if (sortAllByStore.get() !== sortBy) { sortAllByStore.set(sortBy) } } const addTopics = (...args: Topic[][]) => { const allTopics = args.flatMap((topics) => topics || []) const newTopicEntities = allTopics.reduce((acc, topic) => { acc[topic.slug] = topic return acc }, {} as Record) if (!topicEntitiesStore) { initStore(newTopicEntities) } else { topicEntitiesStore.set({ ...topicEntitiesStore.get(), ...newTopicEntities }) } } export const addTopicsByAuthor = (topicsByAuthors: { [authorSlug: string]: Topic[] }) => { const allTopics = Object.values(topicsByAuthors).flat() addTopics(allTopics) if (!topicsByAuthorStore) { topicsByAuthorStore = map>(topicsByAuthors) } 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) } } export const loadAllTopics = async (): Promise => { const topics = await apiClient.getAllTopics() addTopics(topics) } type InitialState = { topics?: Topic[] randomTopics?: Topic[] sortBy?: TopicsSortBy } export const useTopicsStore = (initialState: InitialState = {}) => { const topics = [...(initialState.topics || [])] const randomTopics = [...(initialState.randomTopics || [])] if (initialState.sortBy) { sortAllByStore.set(initialState.sortBy) } addTopics(topics, randomTopics) if (randomTopics) { setRandomTopics(randomTopics) } const getTopicEntities = useStore(topicEntitiesStore) const getSortedTopics = useStore(sortedTopicsStore) const getTopTopics = useStore(topTopicsStore) return { getTopicEntities, getSortedTopics, getRandomTopics, getTopTopics } }