parent
fe9fd37d9d
commit
73e1f575f8
|
@ -1,5 +1,5 @@
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import { For, Show, createEffect, createSignal } from 'solid-js'
|
import { For, Show, createEffect, createSignal, on, onMount } from 'solid-js'
|
||||||
import { useFollowing } from '../../context/following'
|
import { useFollowing } from '../../context/following'
|
||||||
import { useLocalize } from '../../context/localize'
|
import { useLocalize } from '../../context/localize'
|
||||||
import { apiClient } from '../../graphql/client/core'
|
import { apiClient } from '../../graphql/client/core'
|
||||||
|
@ -11,24 +11,29 @@ import styles from './AuthorsList.module.scss'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
class?: string
|
class?: string
|
||||||
query: 'shouts' | 'followers'
|
query: 'shouts' | 'authors'
|
||||||
|
searchQuery?: string
|
||||||
|
allAuthorsLength?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const PAGE_SIZE = 20
|
const PAGE_SIZE = 20
|
||||||
export const AuthorsList = (props: Props) => {
|
export const AuthorsList = (props: Props) => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const { isOwnerSubscribed } = useFollowing()
|
const { isOwnerSubscribed } = useFollowing()
|
||||||
|
const { authorsByShouts, authorsByFollowers } = useAuthorsStore()
|
||||||
const [loading, setLoading] = createSignal(false)
|
const [loading, setLoading] = createSignal(false)
|
||||||
const [currentPage, setCurrentPage] = createSignal({ shouts: 0, followers: 0 })
|
const [currentPage, setCurrentPage] = createSignal({ shouts: 0, followers: 0 })
|
||||||
const { authorsByShouts, authorsByFollowers } = useAuthorsStore()
|
const [allLoaded, setAllLoaded] = createSignal(false)
|
||||||
|
|
||||||
const fetchAuthors = async (queryType: 'shouts' | 'followers', page: number) => {
|
const fetchAuthors = async (queryType: 'shouts' | 'authors', page: number) => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
|
||||||
|
console.log('!!! AAA:')
|
||||||
const offset = PAGE_SIZE * page
|
const offset = PAGE_SIZE * page
|
||||||
const result = await apiClient.loadAuthorsBy({
|
const result = await apiClient.loadAuthorsBy({
|
||||||
by: { order: queryType },
|
by: { order: queryType },
|
||||||
limit: PAGE_SIZE,
|
limit: PAGE_SIZE,
|
||||||
offset: offset,
|
offset,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (queryType === 'shouts') {
|
if (queryType === 'shouts') {
|
||||||
|
@ -41,25 +46,38 @@ export const AuthorsList = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadMoreAuthors = () => {
|
const loadMoreAuthors = () => {
|
||||||
const queryType = props.query
|
const nextPage = currentPage()[props.query] + 1
|
||||||
const nextPage = currentPage()[queryType] + 1
|
fetchAuthors(props.query, nextPage).then(() =>
|
||||||
fetchAuthors(queryType, nextPage).then(() =>
|
setCurrentPage({ ...currentPage(), [props.query]: nextPage }),
|
||||||
setCurrentPage({ ...currentPage(), [queryType]: nextPage }),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(
|
||||||
const queryType = props.query
|
on(
|
||||||
if (
|
() => props.query,
|
||||||
currentPage()[queryType] === 0 &&
|
(query) => {
|
||||||
(authorsByShouts().length === 0 || authorsByFollowers().length === 0)
|
const authorsList = query === 'shouts' ? authorsByShouts() : authorsByFollowers()
|
||||||
) {
|
if (authorsList.length === 0 || currentPage()[query] === 0) {
|
||||||
loadMoreAuthors()
|
setCurrentPage((prev) => ({ ...prev, [query]: 0 }))
|
||||||
|
fetchAuthors(query, 0).then(() => setCurrentPage((prev) => ({ ...prev, [query]: 1 })))
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
const authorsList = () => (props.query === 'shouts' ? authorsByShouts() : authorsByFollowers())
|
const authorsList = () => (props.query === 'shouts' ? authorsByShouts() : authorsByFollowers())
|
||||||
|
|
||||||
|
// TODO: do it with backend
|
||||||
|
// createEffect(() => {
|
||||||
|
// if (props.searchQuery) {
|
||||||
|
// // search logic
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
setAllLoaded(authorsByShouts().length === authorsList.length)
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={clsx(styles.AuthorsList, props.class)}>
|
<div class={clsx(styles.AuthorsList, props.class)}>
|
||||||
<For each={authorsList()}>
|
<For each={authorsList()}>
|
||||||
|
@ -77,14 +95,18 @@ export const AuthorsList = (props: Props) => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-20 col-xl-18">
|
||||||
<div class={styles.action}>
|
<div class={styles.action}>
|
||||||
<Show when={!loading()}>
|
<Show when={!loading() && authorsList().length > 0 && !allLoaded()}>
|
||||||
<Button value={t('Load more')} onClick={loadMoreAuthors} />
|
<Button value={t('Load more')} onClick={loadMoreAuthors} />
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={loading()}>
|
<Show when={loading() && !allLoaded()}>
|
||||||
<InlineLoader />
|
<InlineLoader />
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ type Props = {
|
||||||
|
|
||||||
export const AllAuthors = (props: Props) => {
|
export const AllAuthors = (props: Props) => {
|
||||||
const { t, lang } = useLocalize()
|
const { t, lang } = useLocalize()
|
||||||
|
const [searchQuery, setSearchQuery] = createSignal('')
|
||||||
const ALPHABET =
|
const ALPHABET =
|
||||||
lang() === 'ru' ? [...'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ@'] : [...'ABCDEFGHIJKLMNOPQRSTUVWXYZ@']
|
lang() === 'ru' ? [...'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ@'] : [...'ABCDEFGHIJKLMNOPQRSTUVWXYZ@']
|
||||||
const { searchParams, changeSearchParams } = useRouter<AllAuthorsPageSearchParams>()
|
const { searchParams, changeSearchParams } = useRouter<AllAuthorsPageSearchParams>()
|
||||||
|
@ -36,27 +37,22 @@ export const AllAuthors = (props: Props) => {
|
||||||
sortBy: searchParams().by || 'name',
|
sortBy: searchParams().by || 'name',
|
||||||
})
|
})
|
||||||
|
|
||||||
const [searchQuery, setSearchQuery] = createSignal('')
|
const filteredAuthors = createMemo(() => {
|
||||||
|
const query = searchQuery().toLowerCase()
|
||||||
createEffect(() => {
|
return sortedAuthors().filter((author) => {
|
||||||
let by = searchParams().by
|
return author.name.toLowerCase().includes(query) // Предполагаем, что у автора есть свойство name
|
||||||
if (by) {
|
})
|
||||||
setAuthorsSort(by)
|
|
||||||
} else {
|
|
||||||
by = 'name'
|
|
||||||
changeSearchParams({ by })
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const byLetter = createMemo<{ [letter: string]: Author[] }>(() => {
|
const byLetterFiltered = createMemo<{ [letter: string]: Author[] }>(() => {
|
||||||
return sortedAuthors().reduce(
|
return filteredAuthors().reduce(
|
||||||
(acc, author) => authorLetterReduce(acc, author, lang()),
|
(acc, author) => authorLetterReduce(acc, author, lang()),
|
||||||
{} as { [letter: string]: Author[] },
|
{} as { [letter: string]: Author[] },
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const sortedKeys = createMemo<string[]>(() => {
|
const sortedKeys = createMemo<string[]>(() => {
|
||||||
const keys = Object.keys(byLetter())
|
const keys = Object.keys(byLetterFiltered())
|
||||||
keys.sort()
|
keys.sort()
|
||||||
keys.push(keys.shift())
|
keys.push(keys.shift())
|
||||||
return keys
|
return keys
|
||||||
|
@ -106,7 +102,7 @@ export const AllAuthors = (props: Props) => {
|
||||||
>
|
>
|
||||||
<a href="/authors?by=name">{t('By name')}</a>
|
<a href="/authors?by=name">{t('By name')}</a>
|
||||||
</li>
|
</li>
|
||||||
<Show when={searchParams().by !== 'name'}>
|
<Show when={searchParams().by === 'name'}>
|
||||||
<li class="view-switcher__search">
|
<li class="view-switcher__search">
|
||||||
<SearchField onChange={(value) => setSearchQuery(value)} />
|
<SearchField onChange={(value) => setSearchQuery(value)} />
|
||||||
</li>
|
</li>
|
||||||
|
@ -122,7 +118,7 @@ export const AllAuthors = (props: Props) => {
|
||||||
<For each={ALPHABET}>
|
<For each={ALPHABET}>
|
||||||
{(letter, index) => (
|
{(letter, index) => (
|
||||||
<li>
|
<li>
|
||||||
<Show when={letter in byLetter()} fallback={letter}>
|
<Show when={letter in byLetterFiltered()} fallback={letter}>
|
||||||
<a
|
<a
|
||||||
href={`/authors?by=name#letter-${index()}`}
|
href={`/authors?by=name#letter-${index()}`}
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
|
@ -147,7 +143,7 @@ export const AllAuthors = (props: Props) => {
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-20">
|
<div class="col-lg-20">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<For each={byLetter()[letter]}>
|
<For each={byLetterFiltered()[letter]}>
|
||||||
{(author) => (
|
{(author) => (
|
||||||
<div class={clsx(styles.topic, 'topic col-sm-12 col-md-8')}>
|
<div class={clsx(styles.topic, 'topic col-sm-12 col-md-8')}>
|
||||||
<div class="topic-title">
|
<div class="topic-title">
|
||||||
|
@ -167,8 +163,12 @@ export const AllAuthors = (props: Props) => {
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={searchParams().by !== 'name' && props.isLoaded} fallback={<Loading />}>
|
<Show when={searchParams().by !== 'name' && props.isLoaded}>
|
||||||
<AuthorsList query={searchParams().by === 'shouts' ? 'shouts' : 'followers'} />
|
<AuthorsList
|
||||||
|
allAuthorsLength={sortedAuthors()?.length}
|
||||||
|
searchQuery={searchQuery()}
|
||||||
|
query={searchParams().by === 'shouts' ? 'shouts' : 'authors'}
|
||||||
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { createSignal } from 'solid-js'
|
||||||
|
|
||||||
import { apiClient } from '../../graphql/client/core'
|
import { apiClient } from '../../graphql/client/core'
|
||||||
import { Author, QueryLoad_Authors_ByArgs } from '../../graphql/schema/core.gen'
|
import { Author, QueryLoad_Authors_ByArgs } from '../../graphql/schema/core.gen'
|
||||||
import { byStat } from '../../utils/sortby'
|
|
||||||
|
|
||||||
export type AuthorsSortBy = 'shouts' | 'name' | 'followers'
|
export type AuthorsSortBy = 'shouts' | 'name' | 'followers'
|
||||||
type SortedAuthorsSetter = (prev: Author[]) => Author[]
|
type SortedAuthorsSetter = (prev: Author[]) => Author[]
|
||||||
|
@ -21,19 +20,7 @@ export const setAuthorsByShouts = (authors: SortedAuthorsSetter) => setSortedAut
|
||||||
export const setAuthorsByFollowers = (authors: SortedAuthorsSetter) => setSortedAuthorsByFollowers(authors)
|
export const setAuthorsByFollowers = (authors: SortedAuthorsSetter) => setSortedAuthorsByFollowers(authors)
|
||||||
|
|
||||||
const sortedAuthors = createLazyMemo(() => {
|
const sortedAuthors = createLazyMemo(() => {
|
||||||
const authors = Object.values(authorEntities())
|
return Object.values(authorEntities())
|
||||||
switch (sortAllBy()) {
|
|
||||||
case 'followers': {
|
|
||||||
return authors.sort(byStat('followers'))
|
|
||||||
}
|
|
||||||
case 'shouts': {
|
|
||||||
return authors.sort(byStat('shouts'))
|
|
||||||
}
|
|
||||||
case 'name': {
|
|
||||||
return authors.sort((a, b) => a.name.localeCompare(b.name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return authors
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const addAuthors = (authors: Author[]) => {
|
export const addAuthors = (authors: Author[]) => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user