2023-11-29 20:24:33 +00:00
|
|
|
import type {
|
|
|
|
Author,
|
|
|
|
Shout,
|
|
|
|
LoadShoutsOptions,
|
|
|
|
QueryLoad_Shouts_SearchArgs,
|
|
|
|
} from '../../graphql/schema/core.gen'
|
2023-11-14 15:10:00 +00:00
|
|
|
|
|
|
|
import { createLazyMemo } from '@solid-primitives/memo'
|
|
|
|
import { createSignal } from 'solid-js'
|
|
|
|
|
2023-11-28 13:18:25 +00:00
|
|
|
import { apiClient } from '../../graphql/client/core'
|
2022-09-13 09:59:04 +00:00
|
|
|
import { byStat } from '../../utils/sortby'
|
2023-11-14 15:10:00 +00:00
|
|
|
|
|
|
|
import { addAuthorsByTopic } from './authors'
|
2022-09-22 09:37:49 +00:00
|
|
|
|
2022-09-28 20:16:44 +00:00
|
|
|
const [sortedArticles, setSortedArticles] = createSignal<Shout[]>([])
|
|
|
|
const [articleEntities, setArticleEntities] = createSignal<{ [articleSlug: string]: Shout }>({})
|
2022-09-09 11:53:35 +00:00
|
|
|
|
2022-09-28 20:16:44 +00:00
|
|
|
const [topArticles, setTopArticles] = createSignal<Shout[]>([])
|
|
|
|
const [topMonthArticles, setTopMonthArticles] = createSignal<Shout[]>([])
|
2022-09-22 09:37:49 +00:00
|
|
|
|
2022-09-29 17:37:21 +00:00
|
|
|
const articlesByAuthor = createLazyMemo(() => {
|
2023-11-01 11:17:31 +00:00
|
|
|
return Object.values(articleEntities()).reduce(
|
|
|
|
(acc, article) => {
|
2024-01-25 19:16:38 +00:00
|
|
|
article.authors?.forEach((author) => {
|
2023-11-01 11:17:31 +00:00
|
|
|
if (!acc[author.slug]) {
|
|
|
|
acc[author.slug] = []
|
|
|
|
}
|
|
|
|
acc[author.slug].push(article)
|
|
|
|
})
|
2022-09-13 09:59:04 +00:00
|
|
|
|
2023-11-01 11:17:31 +00:00
|
|
|
return acc
|
|
|
|
},
|
2023-11-14 15:10:00 +00:00
|
|
|
{} as { [authorSlug: string]: Shout[] },
|
2023-11-01 11:17:31 +00:00
|
|
|
)
|
2022-09-28 20:16:44 +00:00
|
|
|
})
|
2022-09-13 09:59:04 +00:00
|
|
|
|
2022-09-29 17:37:21 +00:00
|
|
|
const articlesByTopic = createLazyMemo(() => {
|
2023-11-01 11:17:31 +00:00
|
|
|
return Object.values(articleEntities()).reduce(
|
|
|
|
(acc, article) => {
|
|
|
|
article.topics.forEach((topic) => {
|
|
|
|
if (!acc[topic.slug]) {
|
|
|
|
acc[topic.slug] = []
|
|
|
|
}
|
|
|
|
acc[topic.slug].push(article)
|
|
|
|
})
|
2022-09-13 09:59:04 +00:00
|
|
|
|
2023-11-01 11:17:31 +00:00
|
|
|
return acc
|
|
|
|
},
|
2023-11-14 15:10:00 +00:00
|
|
|
{} as { [authorSlug: string]: Shout[] },
|
2023-11-01 11:17:31 +00:00
|
|
|
)
|
2022-09-28 20:16:44 +00:00
|
|
|
})
|
2022-09-13 09:59:04 +00:00
|
|
|
|
2022-09-29 17:37:21 +00:00
|
|
|
const topViewedArticles = createLazyMemo(() => {
|
2022-09-28 20:16:44 +00:00
|
|
|
const result = Object.values(articleEntities())
|
|
|
|
result.sort(byStat('viewed'))
|
|
|
|
return result
|
|
|
|
})
|
2022-09-13 09:59:04 +00:00
|
|
|
|
2022-09-29 17:37:21 +00:00
|
|
|
const topCommentedArticles = createLazyMemo(() => {
|
2022-09-28 20:16:44 +00:00
|
|
|
const result = Object.values(articleEntities())
|
|
|
|
result.sort(byStat('commented'))
|
|
|
|
return result
|
|
|
|
})
|
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 || [])
|
|
|
|
|
2023-11-01 11:17:31 +00:00
|
|
|
const newArticleEntities = allArticles.reduce(
|
|
|
|
(acc, article) => {
|
|
|
|
acc[article.slug] = article
|
|
|
|
return acc
|
|
|
|
},
|
2023-11-14 15:10:00 +00:00
|
|
|
{} as { [articleSLug: string]: Shout },
|
2023-11-01 11:17:31 +00:00
|
|
|
)
|
2022-09-09 11:53:35 +00:00
|
|
|
|
2022-09-28 20:16:44 +00:00
|
|
|
setArticleEntities((prevArticleEntities) => {
|
|
|
|
return {
|
|
|
|
...prevArticleEntities,
|
2023-11-14 15:10:00 +00:00
|
|
|
...newArticleEntities,
|
2022-09-28 20:16:44 +00:00
|
|
|
}
|
|
|
|
})
|
2022-09-09 11:53:35 +00:00
|
|
|
|
2023-11-01 11:17:31 +00:00
|
|
|
const authorsByTopic = allArticles.reduce(
|
|
|
|
(acc, article) => {
|
|
|
|
const { authors, topics } = article
|
2023-12-25 04:01:52 +00:00
|
|
|
if (topics) {
|
|
|
|
// TODO: check if data can be consistent without topics and authors in article
|
|
|
|
topics.forEach((topic) => {
|
|
|
|
if (!acc[topic.slug]) {
|
|
|
|
acc[topic.slug] = []
|
2023-11-01 11:17:31 +00:00
|
|
|
}
|
2023-12-25 04:01:52 +00:00
|
|
|
|
|
|
|
authors.forEach((author) => {
|
2024-01-13 14:14:35 +00:00
|
|
|
if (!acc[topic.slug].some((a) => a?.slug === author.slug)) {
|
2023-12-25 04:01:52 +00:00
|
|
|
acc[topic.slug].push(author)
|
|
|
|
}
|
|
|
|
})
|
2023-11-01 11:17:31 +00:00
|
|
|
})
|
2023-12-25 04:01:52 +00:00
|
|
|
}
|
2022-09-13 09:59:04 +00:00
|
|
|
|
2023-11-01 11:17:31 +00:00
|
|
|
return acc
|
|
|
|
},
|
2023-11-14 15:10:00 +00:00
|
|
|
{} as { [topicSlug: string]: Author[] },
|
2023-11-01 11:17:31 +00:00
|
|
|
)
|
2022-09-13 09:59:04 +00:00
|
|
|
|
|
|
|
addAuthorsByTopic(authorsByTopic)
|
|
|
|
}
|
|
|
|
|
|
|
|
const addSortedArticles = (articles: Shout[]) => {
|
2022-09-22 09:37:49 +00:00
|
|
|
setSortedArticles((prevSortedArticles) => [...prevSortedArticles, ...articles])
|
2022-09-09 11:53:35 +00:00
|
|
|
}
|
|
|
|
|
2022-11-18 02:23:04 +00:00
|
|
|
export const loadShout = async (slug: string): Promise<void> => {
|
2023-05-08 17:21:06 +00:00
|
|
|
const newArticle = await apiClient.getShoutBySlug(slug)
|
2023-11-04 13:40:55 +00:00
|
|
|
if (!newArticle) {
|
|
|
|
return
|
|
|
|
}
|
2022-11-18 02:23:04 +00:00
|
|
|
addArticles([newArticle])
|
2023-02-28 17:13:14 +00:00
|
|
|
const newArticleIndex = sortedArticles().findIndex((s) => s.id === newArticle.id)
|
|
|
|
if (newArticleIndex >= 0) {
|
|
|
|
const newSortedArticles = [...sortedArticles()]
|
|
|
|
newSortedArticles[newArticleIndex] = newArticle
|
|
|
|
setSortedArticles(newSortedArticles)
|
|
|
|
}
|
2022-11-18 02:23:04 +00:00
|
|
|
}
|
|
|
|
|
2023-02-28 17:13:14 +00:00
|
|
|
export const loadShouts = async (
|
2023-11-14 15:10:00 +00:00
|
|
|
options: LoadShoutsOptions,
|
2023-02-28 17:13:14 +00:00
|
|
|
): Promise<{ hasMore: boolean; newShouts: Shout[] }> => {
|
2023-11-29 08:23:08 +00:00
|
|
|
options.limit += 1
|
2023-11-29 12:37:27 +00:00
|
|
|
const newShouts = await apiClient.getShouts(options)
|
2023-11-29 08:23:08 +00:00
|
|
|
const hasMore = newShouts ?? newShouts.length === options.limit + 1
|
2022-10-28 21:21:47 +00:00
|
|
|
|
|
|
|
if (hasMore) {
|
2023-02-28 17:13:14 +00:00
|
|
|
newShouts.splice(-1)
|
2022-10-28 21:21:47 +00:00
|
|
|
}
|
|
|
|
|
2023-02-28 17:13:14 +00:00
|
|
|
addArticles(newShouts)
|
|
|
|
addSortedArticles(newShouts)
|
2022-10-28 21:21:47 +00:00
|
|
|
|
2023-02-28 17:13:14 +00:00
|
|
|
return { hasMore, newShouts }
|
2022-10-05 15:11:14 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 16:53:01 +00:00
|
|
|
export const loadMyFeed = async (
|
2023-11-14 15:10:00 +00:00
|
|
|
options: LoadShoutsOptions,
|
2023-07-07 16:53:01 +00:00
|
|
|
): Promise<{ hasMore: boolean; newShouts: Shout[] }> => {
|
2023-11-29 08:23:08 +00:00
|
|
|
options.limit += 1
|
2023-11-29 12:37:27 +00:00
|
|
|
const newShouts = await apiClient.getMyFeed(options)
|
2023-11-29 08:23:08 +00:00
|
|
|
const hasMore = newShouts ?? newShouts.length === options.limit + 1
|
2023-07-07 16:53:01 +00:00
|
|
|
|
|
|
|
if (hasMore) {
|
|
|
|
newShouts.splice(-1)
|
|
|
|
}
|
|
|
|
|
|
|
|
addArticles(newShouts)
|
|
|
|
addSortedArticles(newShouts)
|
|
|
|
|
|
|
|
return { hasMore, newShouts }
|
|
|
|
}
|
|
|
|
|
2023-11-29 20:24:33 +00:00
|
|
|
export const loadShoutsSearch = async (
|
|
|
|
options: QueryLoad_Shouts_SearchArgs,
|
|
|
|
): Promise<{ hasMore: boolean; newShouts: Shout[] }> => {
|
|
|
|
options.limit += 1
|
|
|
|
const newShouts = await apiClient.getShoutsSearch(options)
|
|
|
|
const hasMore = newShouts ?? newShouts.length === options.limit + 1
|
|
|
|
|
|
|
|
if (hasMore) {
|
|
|
|
newShouts.splice(-1)
|
|
|
|
}
|
|
|
|
|
|
|
|
addArticles(newShouts)
|
|
|
|
addSortedArticles(newShouts)
|
|
|
|
|
|
|
|
return { hasMore, newShouts }
|
|
|
|
}
|
|
|
|
|
2022-10-05 15:11:14 +00:00
|
|
|
export const resetSortedArticles = () => {
|
|
|
|
setSortedArticles([])
|
|
|
|
}
|
|
|
|
|
2022-09-09 11:53:35 +00:00
|
|
|
type InitialState = {
|
2022-11-15 14:24:50 +00:00
|
|
|
shouts?: Shout[]
|
2024-01-22 11:07:19 +00:00
|
|
|
layout?: string
|
2022-09-09 11:53:35 +00:00
|
|
|
}
|
|
|
|
|
2022-12-01 18:45:35 +00:00
|
|
|
const TOP_MONTH_ARTICLES_COUNT = 10
|
|
|
|
|
|
|
|
export const loadTopMonthArticles = async (): Promise<void> => {
|
2023-11-29 08:23:08 +00:00
|
|
|
const daysago = Date.now() - 30 * 24 * 60 * 60 * 1000
|
|
|
|
const after = Math.floor(daysago / 1000)
|
|
|
|
const options: LoadShoutsOptions = {
|
2022-12-01 18:45:35 +00:00
|
|
|
filters: {
|
2023-11-29 12:19:25 +00:00
|
|
|
published: true,
|
2023-11-29 08:23:08 +00:00
|
|
|
after,
|
2022-12-01 18:45:35 +00:00
|
|
|
},
|
2024-01-23 13:22:18 +00:00
|
|
|
order_by: 'likes_stat',
|
2023-11-14 15:10:00 +00:00
|
|
|
limit: TOP_MONTH_ARTICLES_COUNT,
|
2023-11-29 08:23:08 +00:00
|
|
|
}
|
2023-11-29 12:37:27 +00:00
|
|
|
const articles = await apiClient.getShouts(options)
|
2022-12-01 18:45:35 +00:00
|
|
|
addArticles(articles)
|
|
|
|
setTopMonthArticles(articles)
|
|
|
|
}
|
|
|
|
|
|
|
|
const TOP_ARTICLES_COUNT = 10
|
|
|
|
|
|
|
|
export const loadTopArticles = async (): Promise<void> => {
|
2023-11-29 08:23:08 +00:00
|
|
|
const options: LoadShoutsOptions = {
|
2023-11-29 12:19:25 +00:00
|
|
|
filters: { published: true },
|
2024-01-23 13:22:18 +00:00
|
|
|
order_by: 'likes_stat',
|
2023-11-14 15:10:00 +00:00
|
|
|
limit: TOP_ARTICLES_COUNT,
|
2023-11-29 08:23:08 +00:00
|
|
|
}
|
2023-11-29 12:37:27 +00:00
|
|
|
const articles = await apiClient.getShouts(options)
|
2022-12-01 18:45:35 +00:00
|
|
|
addArticles(articles)
|
|
|
|
setTopArticles(articles)
|
|
|
|
}
|
|
|
|
|
2022-09-23 07:38:48 +00:00
|
|
|
export const useArticlesStore = (initialState: InitialState = {}) => {
|
2022-11-15 14:24:50 +00:00
|
|
|
addArticles([...(initialState.shouts || [])])
|
2022-09-09 12:18:09 +00:00
|
|
|
|
2024-01-22 11:07:19 +00:00
|
|
|
if (initialState.layout) {
|
|
|
|
// eslint-disable-next-line promise/catch-or-return
|
2024-01-22 14:12:54 +00:00
|
|
|
loadShouts({ filters: { layouts: [initialState.layout] }, limit: 10 }).then(({ newShouts }) => {
|
2024-01-22 11:07:19 +00:00
|
|
|
addArticles(newShouts)
|
|
|
|
setSortedArticles(newShouts)
|
|
|
|
})
|
|
|
|
} else if (initialState.shouts) {
|
|
|
|
addArticles([...initialState.shouts])
|
2022-11-15 14:24:50 +00:00
|
|
|
setSortedArticles([...initialState.shouts])
|
2022-09-13 09:59:04 +00:00
|
|
|
}
|
2022-09-09 12:18:09 +00:00
|
|
|
|
2022-09-13 09:59:04 +00:00
|
|
|
return {
|
2022-09-28 20:16:44 +00:00
|
|
|
articleEntities,
|
|
|
|
sortedArticles,
|
|
|
|
articlesByAuthor,
|
2022-11-15 14:24:50 +00:00
|
|
|
articlesByTopic,
|
2022-09-28 20:16:44 +00:00
|
|
|
topMonthArticles,
|
2022-11-15 14:24:50 +00:00
|
|
|
topArticles,
|
2022-09-28 20:16:44 +00:00
|
|
|
topCommentedArticles,
|
2023-11-14 15:10:00 +00:00
|
|
|
topViewedArticles,
|
2022-09-13 09:59:04 +00:00
|
|
|
}
|
2022-09-09 12:18:09 +00:00
|
|
|
}
|