loadwrapper-search

This commit is contained in:
Untone 2024-10-06 20:02:55 +03:00
parent 47dd3b21f4
commit d585f40956

View File

@ -2,7 +2,8 @@ import { For, Show, createResource, createSignal, onCleanup } from 'solid-js'
import { debounce } from 'throttle-debounce' import { debounce } from 'throttle-debounce'
import { Button } from '~/components/_shared/Button' import { Button } from '~/components/_shared/Button'
import { Icon } from '~/components/_shared/Icon' import { Icon } from '~/components/_shared/Icon'
import { useFeed } from '~/context/feed' import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
import { SHOUTS_PER_PAGE, useFeed } from '~/context/feed'
import { useLocalize } from '~/context/localize' import { useLocalize } from '~/context/localize'
import type { Shout } from '~/graphql/schema/core.gen' import type { Shout } from '~/graphql/schema/core.gen'
import { restoreScrollPosition, saveScrollPosition } from '~/utils/scroll' import { restoreScrollPosition, saveScrollPosition } from '~/utils/scroll'
@ -13,9 +14,7 @@ import { SearchResultItem } from './SearchResultItem'
import styles from './SearchModal.module.scss' import styles from './SearchModal.module.scss'
// @@TODO handle empty article options after backend support (subtitle, cover, etc.) // @@TODO handle empty article options after backend support (subtitle, cover, etc.)
// @@TODO implement load more
// @@TODO implement FILTERS & TOPICS // @@TODO implement FILTERS & TOPICS
// @@TODO use save/restoreScrollPosition if needed
const getSearchCoincidences = ({ str, intersection }: { str: string; intersection: string }) => const getSearchCoincidences = ({ str, intersection }: { str: string; intersection: string }) =>
`<span>${str.replaceAll( `<span>${str.replaceAll(
@ -48,34 +47,30 @@ export const SearchModal = () => {
const [inputValue, setInputValue] = createSignal('') const [inputValue, setInputValue] = createSignal('')
const [isLoading, setIsLoading] = createSignal(false) const [isLoading, setIsLoading] = createSignal(false)
const [offset, setOffset] = createSignal<number>(0) const [offset, setOffset] = createSignal<number>(0)
const fetchSearchResults = async () => {
setIsLoading(true)
saveScrollPosition()
const { hasMore, newShouts } = await loadShoutsSearch({
limit: FEED_PAGE_SIZE,
text: inputValue(),
offset: offset()
})
setIsLoading(false)
setOffset(newShouts.length)
setIsLoadMoreButtonVisible(hasMore)
return newShouts
}
const [searchResultsList, { refetch: loadSearchResults, mutate: setSearchResultsList }] = createResource< const [searchResultsList, { refetch: loadSearchResults, mutate: setSearchResultsList }] = createResource<
Shout[] Shout[]
>( >(fetchSearchResults, { ssrLoadFrom: 'initial', initialValue: [] })
async () => {
setIsLoading(true)
saveScrollPosition()
const { hasMore, newShouts } = await loadShoutsSearch({
limit: FEED_PAGE_SIZE,
text: inputValue(),
offset: offset()
})
setIsLoading(false)
setOffset(newShouts.length)
setIsLoadMoreButtonVisible(hasMore)
return newShouts
},
{
ssrLoadFrom: 'initial',
initialValue: []
}
)
let searchEl: HTMLInputElement const [searchEl, setSearchEl] = createSignal<HTMLInputElement | undefined>()
const debouncedLoadMore = debounce(500, loadSearchResults) const debouncedLoadMore = debounce(500, loadSearchResults)
const handleQueryInput = async () => { const handleQueryInput = async () => {
setInputValue(searchEl.value) setInputValue(searchEl()?.value ?? '')
if (searchEl.value?.length > 2) { if ((searchEl()?.value?.length || 0) > 2) {
await debouncedLoadMore() await debouncedLoadMore()
} else { } else {
setIsLoading(false) setIsLoading(false)
@ -101,6 +96,11 @@ export const SearchModal = () => {
// console.debug('[SearchModal] cleanup debouncing search') // console.debug('[SearchModal] cleanup debouncing search')
}) })
const loadMoreResults = async () => {
const result = await fetchSearchResults()
return result as LoadMoreItems
}
return ( return (
<div class={styles.searchContainer}> <div class={styles.searchContainer}>
<input <input
@ -109,7 +109,7 @@ export const SearchModal = () => {
class={styles.searchInput} class={styles.searchInput}
onInput={handleQueryInput} onInput={handleQueryInput}
onKeyDown={enterQuery} onKeyDown={enterQuery}
ref={(el: HTMLInputElement) => (searchEl = el)} ref={setSearchEl}
/> />
<Button <Button
@ -127,28 +127,26 @@ export const SearchModal = () => {
<Show when={!isLoading()}> <Show when={!isLoading()}>
<Show when={searchResultsList()}> <Show when={searchResultsList()}>
<For each={prepareSearchResults(searchResultsList(), inputValue())}> <LoadMoreWrapper
{(article: Shout) => ( loadFunction={loadMoreResults}
<div> pageSize={SHOUTS_PER_PAGE}
<SearchResultItem hidden={!isLoadMoreButtonVisible()}
article={article} >
settings={{ <For each={prepareSearchResults(searchResultsList(), inputValue())}>
isFloorImportant: true, {(article: Shout) => (
isSingle: true, <div>
nodate: true <SearchResultItem
}} article={article}
/> settings={{
</div> isFloorImportant: true,
)} isSingle: true,
</For> nodate: true
}}
<Show when={isLoadMoreButtonVisible()}> />
<p class="load-more-container"> </div>
<button class="button" onClick={loadSearchResults}> )}
{t('Load more')} </For>
</button> </LoadMoreWrapper>
</p>
</Show>
</Show> </Show>
<Show when={Array.isArray(searchResultsList()) && searchResultsList().length === 0}> <Show when={Array.isArray(searchResultsList()) && searchResultsList().length === 0}>