import { Accessor, JSX, createContext, createEffect, createMemo, createSignal, on, useContext } from 'solid-js' import loadAuthorByQuery from '~/graphql/query/core/author-by' import loadAuthorsAllQuery from '~/graphql/query/core/authors-all' import loadAuthorsByQuery from '~/graphql/query/core/authors-load-by' import { Author, Maybe, QueryLoad_Authors_ByArgs, Shout, Topic } from '~/graphql/schema/core.gen' import { byStat } from '~/lib/sortby' import { useFeed } from './feed' import { useGraphQL } from './graphql' const TOP_AUTHORS_COUNT = 5 type FilterFunction = (a: Author) => boolean export type SortFunction = (a: Author, b: Author) => number // Универсальная функция фильтрации и сортировки function filterAndSort( items: Author[], sortFunction: SortFunction, filters: FilterFunction[] = [] ): Author[] { return items.filter((a: Author) => filters.every((filter) => filter(a))).sort(sortFunction) } type AuthorsContextType = { authorsEntities: Accessor> authorsSorted: Accessor addAuthors: (authors: Author[]) => void addAuthor: (author: Author) => void loadAuthor: (slug: string) => Promise loadAuthors: (args: QueryLoad_Authors_ByArgs) => Promise topAuthors: Accessor authorsByTopic: Accessor<{ [topicSlug: string]: Author[] }> setAuthorsSort: (stat: string) => void loadAllAuthors: () => Promise } const AuthorsContext = createContext({} as AuthorsContextType) export const useAuthors = () => useContext(AuthorsContext) export const AuthorsProvider = (props: { children: JSX.Element }) => { const [authorsEntities, setAuthors] = createSignal>({}) const [authorsSorted, setAuthorsSorted] = createSignal([]) const [sortBy, setSortBy] = createSignal>() const { feedByAuthor } = useFeed() const { query } = useGraphQL() const setAuthorsSort = (stat: string) => setSortBy((_) => byStat(stat) as SortFunction) // Эффект для отслеживания изменений сигнала sortBy и обновления authorsSorted createEffect( on( [sortBy, authorsEntities], ([sortfn, authorsdict]) => { if (sortfn) { setAuthorsSorted?.([...filterAndSort(Object.values(authorsdict), sortfn)]) } }, { defer: true } ) ) const addAuthors = (newAuthors: Author[]) => { setAuthors((prevAuthors) => { const updatedAuthors = { ...prevAuthors } newAuthors.forEach((author) => { updatedAuthors[author.slug] = author }) return updatedAuthors }) } const addAuthor = (newAuthor: Author) => { setAuthors((prevAuthors) => { const updatedAuthors = { ...prevAuthors } updatedAuthors[newAuthor.slug] = newAuthor return updatedAuthors }) } const loadAuthor = async (slug: string): Promise => { try { const resp = await query(loadAuthorByQuery, { slug }).toPromise() if (resp) { const author = resp.data.get_author if (author?.id) addAuthor(author) } } catch (error) { console.error('Error loading author:', error) throw error } } const topAuthors = createMemo(() => { const articlesByAuthorMap = feedByAuthor?.() || {} // Получаем всех авторов const authors = Object.keys(articlesByAuthorMap).map((authorSlug) => ({ slug: authorSlug, rating: articlesByAuthorMap[authorSlug].reduce( (acc: number, article: Shout) => acc + (article.stat?.rating || 0), 0 ) })) // Определяем функцию сортировки по рейтингу const sortByRating: SortFunction<{ slug: string; rating: number }> = (a, b) => b.rating - a.rating // Фильтруем и сортируем авторов const sortedTopAuthors = filterAndSort(authors, sortByRating) .slice(0, TOP_AUTHORS_COUNT) .map((author) => authorsEntities()[author.slug]) .filter(Boolean) return sortedTopAuthors }) const loadAuthors = async (args: QueryLoad_Authors_ByArgs): Promise => { try { const resp = await query(loadAuthorsByQuery, { ...args }).toPromise() if (resp) { const author = resp.data.get_author if (author?.id) addAuthor(author) } } catch (error) { console.error('Error loading author:', error) throw error } } const authorsByTopic = createMemo(() => { const articlesByAuthorMap = feedByAuthor?.() || {} const result: { [topicSlug: string]: Author[] } = {} Object.values(articlesByAuthorMap).forEach((articles) => { articles.forEach((article) => { const { authors, topics } = article if (topics) { topics.forEach((topic: Maybe, _index: number, _array: Maybe[]) => { if (topic) { if (!result[topic.slug]) { result[topic.slug] = [] } if (authors) { authors.forEach((author) => { if (!result[topic.slug].some((a) => a.slug === author?.slug)) { result[topic.slug].push(author as Author) } }) } } }) } }) }) return result }) const loadAllAuthors = async (): Promise => { const resp = await query(loadAuthorsAllQuery, {}).toPromise() return resp?.data?.get_authors_all || [] } const contextValue: AuthorsContextType = { loadAllAuthors, authorsEntities, authorsSorted, addAuthors, addAuthor, loadAuthor, loadAuthors, topAuthors, authorsByTopic, setAuthorsSort } return {props.children} }