2024-06-24 17:50:27 +00:00
|
|
|
import {
|
|
|
|
Accessor,
|
|
|
|
JSX,
|
|
|
|
createContext,
|
|
|
|
createEffect,
|
|
|
|
createMemo,
|
|
|
|
createSignal,
|
|
|
|
on,
|
2024-06-26 08:22:05 +00:00
|
|
|
useContext
|
2024-06-24 17:50:27 +00:00
|
|
|
} from 'solid-js'
|
2024-07-04 07:51:15 +00:00
|
|
|
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'
|
2024-07-05 19:40:54 +00:00
|
|
|
import { byStat } from '~/lib/sortby'
|
2024-06-24 17:50:27 +00:00
|
|
|
import { useFeed } from './feed'
|
|
|
|
import { useGraphQL } from './graphql'
|
|
|
|
|
|
|
|
const TOP_AUTHORS_COUNT = 5
|
|
|
|
|
|
|
|
type FilterFunction<Author> = (a: Author) => boolean
|
|
|
|
export type SortFunction<Author> = (a: Author, b: Author) => number
|
|
|
|
|
|
|
|
// Универсальная функция фильтрации и сортировки
|
|
|
|
function filterAndSort<Author>(
|
|
|
|
items: Author[],
|
|
|
|
sortFunction: SortFunction<Author>,
|
2024-06-26 08:22:05 +00:00
|
|
|
filters: FilterFunction<Author>[] = []
|
2024-06-24 17:50:27 +00:00
|
|
|
): Author[] {
|
|
|
|
return items.filter((a: Author) => filters.every((filter) => filter(a))).sort(sortFunction)
|
|
|
|
}
|
|
|
|
|
|
|
|
type AuthorsContextType = {
|
|
|
|
authorsEntities: Accessor<Record<string, Author>>
|
|
|
|
authorsSorted: Accessor<Author[]>
|
|
|
|
addAuthors: (authors: Author[]) => void
|
|
|
|
addAuthor: (author: Author) => void
|
|
|
|
loadAuthor: (slug: string) => Promise<void>
|
|
|
|
loadAuthors: (args: QueryLoad_Authors_ByArgs) => Promise<void>
|
|
|
|
topAuthors: Accessor<Author[]>
|
|
|
|
authorsByTopic: Accessor<{ [topicSlug: string]: Author[] }>
|
2024-07-05 19:40:54 +00:00
|
|
|
setAuthorsSort: (stat: string) => void
|
2024-06-24 17:50:27 +00:00
|
|
|
loadAllAuthors: () => Promise<Author[]>
|
|
|
|
}
|
|
|
|
|
|
|
|
const AuthorsContext = createContext<AuthorsContextType>({} as AuthorsContextType)
|
|
|
|
|
|
|
|
export const useAuthors = () => useContext(AuthorsContext)
|
|
|
|
|
|
|
|
export const AuthorsProvider = (props: { children: JSX.Element }) => {
|
|
|
|
const [authorsEntities, setAuthors] = createSignal<Record<string, Author>>({})
|
|
|
|
const [authorsSorted, setAuthorsSorted] = createSignal<Author[]>([])
|
|
|
|
const [sortBy, setSortBy] = createSignal<SortFunction<Author>>()
|
|
|
|
const { feedByAuthor } = useFeed()
|
|
|
|
const { query } = useGraphQL()
|
2024-07-05 19:40:54 +00:00
|
|
|
const setAuthorsSort = (stat: string) => setSortBy((_) => byStat(stat) as SortFunction<Author>)
|
2024-06-24 17:50:27 +00:00
|
|
|
|
|
|
|
// Эффект для отслеживания изменений сигнала sortBy и обновления authorsSorted
|
|
|
|
createEffect(
|
|
|
|
on(
|
|
|
|
[sortBy, authorsEntities],
|
|
|
|
([sortfn, authorsdict]) => {
|
|
|
|
if (sortfn) {
|
2024-07-05 19:40:54 +00:00
|
|
|
setAuthorsSorted?.([...filterAndSort(Object.values(authorsdict), sortfn)])
|
2024-06-24 17:50:27 +00:00
|
|
|
}
|
|
|
|
},
|
2024-06-26 08:22:05 +00:00
|
|
|
{ defer: true }
|
|
|
|
)
|
2024-06-24 17:50:27 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
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<void> => {
|
|
|
|
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),
|
2024-06-26 08:22:05 +00:00
|
|
|
0
|
|
|
|
)
|
2024-06-24 17:50:27 +00:00
|
|
|
}))
|
|
|
|
|
|
|
|
// Определяем функцию сортировки по рейтингу
|
|
|
|
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<void> => {
|
|
|
|
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<Topic>, _index: number, _array: Maybe<Topic>[]) => {
|
|
|
|
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<Author[]> => {
|
|
|
|
const resp = await query(loadAuthorsAllQuery, {}).toPromise()
|
|
|
|
return resp?.data?.get_authors_all || []
|
|
|
|
}
|
|
|
|
|
|
|
|
const contextValue: AuthorsContextType = {
|
|
|
|
loadAllAuthors,
|
|
|
|
authorsEntities,
|
|
|
|
authorsSorted,
|
|
|
|
addAuthors,
|
|
|
|
addAuthor,
|
|
|
|
loadAuthor,
|
|
|
|
loadAuthors,
|
|
|
|
topAuthors,
|
|
|
|
authorsByTopic,
|
2024-07-05 19:40:54 +00:00
|
|
|
setAuthorsSort
|
2024-06-24 17:50:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return <AuthorsContext.Provider value={contextValue}>{props.children}</AuthorsContext.Provider>
|
|
|
|
}
|