webapp/src/stores/zine/articles.ts

229 lines
6.3 KiB
TypeScript
Raw Normal View History

2022-09-13 09:59:04 +00:00
import { atom, computed, ReadableAtom } from 'nanostores'
import type { Author, Shout, Topic } from '../../graphql/types.gen'
2022-09-09 11:53:35 +00:00
import type { WritableAtom } from 'nanostores'
import { useStore } from '@nanostores/solid'
import { apiClient } from '../../utils/apiClient'
2022-09-13 09:59:04 +00:00
import { addAuthorsByTopic } from './authors'
import { addTopicsByAuthor } from './topics'
import { byStat } from '../../utils/sortby'
2022-09-09 11:53:35 +00:00
2022-09-13 09:59:04 +00:00
let articleEntitiesStore: WritableAtom<{ [articleSlug: string]: Shout }>
2022-09-09 11:53:35 +00:00
let sortedArticlesStore: WritableAtom<Shout[]>
2022-09-13 09:59:04 +00:00
let topRatedArticlesStore: WritableAtom<Shout[]>
let topRatedMonthArticlesStore: WritableAtom<Shout[]>
let articlesByAuthorsStore: ReadableAtom<{ [authorSlug: string]: Shout[] }>
let articlesByTopicsStore: ReadableAtom<{ [topicSlug: string]: Shout[] }>
let topViewedArticlesStore: ReadableAtom<Shout[]>
let topCommentedArticlesStore: ReadableAtom<Shout[]>
2022-09-09 11:53:35 +00:00
const initStore = (initial?: Record<string, Shout>) => {
if (articleEntitiesStore) {
return
}
articleEntitiesStore = atom<Record<string, Shout>>(initial)
2022-09-13 09:59:04 +00:00
articlesByAuthorsStore = computed(articleEntitiesStore, (articleEntities) => {
return Object.values(articleEntities).reduce((acc, article) => {
article.authors.forEach((author) => {
if (!acc[author.slug]) {
acc[author.slug] = []
}
acc[author.slug].push(article)
})
return acc
}, {} as { [authorSlug: string]: Shout[] })
})
articlesByTopicsStore = computed(articleEntitiesStore, (articleEntities) => {
return Object.values(articleEntities).reduce((acc, article) => {
article.topics.forEach((topic) => {
if (!acc[topic.slug]) {
acc[topic.slug] = []
}
acc[topic.slug].push(article)
})
return acc
}, {} as { [authorSlug: string]: Shout[] })
})
topViewedArticlesStore = computed(articleEntitiesStore, (articleEntities) => {
const sortedArticles = Object.values(articleEntities)
sortedArticles.sort(byStat('viewed'))
return sortedArticles
})
topCommentedArticlesStore = computed(articleEntitiesStore, (articleEntities) => {
const sortedArticles = Object.values(articleEntities)
sortedArticles.sort(byStat('commented'))
return sortedArticles
})
2022-09-09 11:53:35 +00:00
}
// eslint-disable-next-line sonarjs/cognitive-complexity
2022-09-13 09:59:04 +00:00
const addArticles = (...args: Shout[][]) => {
const allArticles = args.flatMap((articles) => articles || [])
const newArticleEntities = allArticles.reduce((acc, article) => {
2022-09-09 11:53:35 +00:00
acc[article.slug] = article
return acc
}, {} as Record<string, Shout>)
if (!articleEntitiesStore) {
initStore(newArticleEntities)
} else {
articleEntitiesStore.set({
...articleEntitiesStore.get(),
...newArticleEntities
})
}
2022-09-13 09:59:04 +00:00
const authorsByTopic = allArticles.reduce((acc, article) => {
const { authors, topics } = article
topics.forEach((topic) => {
if (!acc[topic.slug]) {
acc[topic.slug] = []
}
2022-09-09 11:53:35 +00:00
2022-09-13 09:59:04 +00:00
authors.forEach((author) => {
if (!acc[topic.slug].some((a) => a.slug === author.slug)) {
acc[topic.slug].push(author)
}
2022-09-09 11:53:35 +00:00
})
2022-09-13 09:59:04 +00:00
})
return acc
}, {} as { [topicSlug: string]: Author[] })
addAuthorsByTopic(authorsByTopic)
const topicsByAuthor = allArticles.reduce((acc, article) => {
const { authors, topics } = article
authors.forEach((author) => {
if (!acc[author.slug]) {
acc[author.slug] = []
}
topics.forEach((topic) => {
if (!acc[author.slug].some((t) => t.slug === topic.slug)) {
acc[author.slug].push(topic)
}
2022-09-09 11:53:35 +00:00
})
})
2022-09-13 09:59:04 +00:00
return acc
}, {} as { [authorSlug: string]: Topic[] })
addTopicsByAuthor(topicsByAuthor)
}
const addSortedArticles = (articles: Shout[]) => {
if (!sortedArticlesStore) {
sortedArticlesStore = atom(articles)
return
2022-09-09 11:53:35 +00:00
}
2022-09-13 09:59:04 +00:00
if (articles) {
sortedArticlesStore.set([...sortedArticlesStore.get(), ...articles])
2022-09-09 11:53:35 +00:00
}
}
2022-09-14 11:27:10 +00:00
export const loadRecentArticles = async ({
limit,
offset
}: {
limit?: number
offset?: number
}): Promise<void> => {
const newArticles = await apiClient.getRecentArticles({ limit, offset })
2022-09-09 12:18:09 +00:00
addArticles(newArticles)
2022-09-13 09:59:04 +00:00
addSortedArticles(newArticles)
2022-09-09 12:18:09 +00:00
}
2022-09-14 11:27:10 +00:00
export const loadPublishedArticles = async ({
limit,
offset
}: {
limit?: number
offset?: number
}): Promise<void> => {
const newArticles = await apiClient.getPublishedArticles({ limit, offset })
2022-09-09 11:53:35 +00:00
addArticles(newArticles)
2022-09-13 09:59:04 +00:00
addSortedArticles(newArticles)
}
2022-09-14 11:27:10 +00:00
export const loadSearchResults = async ({
query,
limit,
offset
}: {
query: string
limit?: number
offset?: number
}): Promise<void> => {
const newArticles = await apiClient.getSearchResults({ query, limit, offset })
2022-09-13 09:59:04 +00:00
addArticles(newArticles)
addSortedArticles(newArticles)
2022-09-09 11:53:35 +00:00
}
2022-09-14 11:28:43 +00:00
export const incrementView = async ({ articleSlug }: { articleSlug: string }): Promise<void> => {
await apiClient.incrementView({ articleSlug })
}
export const loadArticle = async ({ slug }: { slug: string }): Promise<Shout> => {
return await apiClient.getArticle({ slug })
}
2022-09-09 11:53:35 +00:00
type InitialState = {
sortedArticles?: Shout[]
2022-09-13 09:59:04 +00:00
topRatedArticles?: Shout[]
topRatedMonthArticles?: Shout[]
2022-09-09 11:53:35 +00:00
}
2022-09-13 09:59:04 +00:00
export const useArticlesStore = ({
sortedArticles,
topRatedArticles,
topRatedMonthArticles
}: InitialState = {}) => {
addArticles(sortedArticles, topRatedArticles, topRatedMonthArticles)
addSortedArticles(sortedArticles)
2022-09-09 11:53:35 +00:00
2022-09-13 09:59:04 +00:00
if (!topRatedArticlesStore) {
topRatedArticlesStore = atom(topRatedArticles)
} else {
topRatedArticlesStore.set(topRatedArticles)
}
2022-09-09 12:18:09 +00:00
2022-09-13 09:59:04 +00:00
if (!topRatedMonthArticlesStore) {
topRatedMonthArticlesStore = atom(topRatedMonthArticles)
} else {
topRatedMonthArticlesStore.set(topRatedMonthArticles)
}
2022-09-09 12:18:09 +00:00
2022-09-13 09:59:04 +00:00
const getArticleEntities = useStore(articleEntitiesStore)
const getSortedArticles = useStore(sortedArticlesStore)
const getTopRatedArticles = useStore(topRatedArticlesStore)
const getTopRatedMonthArticles = useStore(topRatedMonthArticlesStore)
const getArticlesByAuthor = useStore(articlesByAuthorsStore)
const getArticlesByTopic = useStore(articlesByTopicsStore)
// TODO: get from server
const getTopViewedArticles = useStore(topViewedArticlesStore)
// TODO: get from server
const getTopCommentedArticles = useStore(topCommentedArticlesStore)
return {
getArticleEntities,
getSortedArticles,
getArticlesByTopic,
getArticlesByAuthor,
getTopRatedArticles,
getTopViewedArticles,
getTopCommentedArticles,
getTopRatedMonthArticles
}
2022-09-09 12:18:09 +00:00
}