diff --git a/src/components/Nav/HeaderAuth.tsx b/src/components/Nav/HeaderAuth.tsx index fb8ee22a..d8ca5f6b 100644 --- a/src/components/Nav/HeaderAuth.tsx +++ b/src/components/Nav/HeaderAuth.tsx @@ -128,7 +128,7 @@ export const HeaderAuth = (props: Props) => {
- + diff --git a/src/components/Nav/SearchModal/SearchModal.tsx b/src/components/Nav/SearchModal/SearchModal.tsx index 00b7aa46..5e3ddaca 100644 --- a/src/components/Nav/SearchModal/SearchModal.tsx +++ b/src/components/Nav/SearchModal/SearchModal.tsx @@ -1,9 +1,10 @@ import type { Shout } from '../../../graphql/schema/core.gen' -import { createSignal, Show, For } from 'solid-js' +import { createResource, createSignal, For, onCleanup, Show } from 'solid-js' +import { debounce } from 'throttle-debounce' import { useLocalize } from '../../../context/localize' -import { loadShoutsSearch, useArticlesStore } from '../../../stores/zine/articles' +import { loadShoutsSearch } from '../../../stores/zine/articles' import { restoreScrollPosition, saveScrollPosition } from '../../../utils/scroll' import { byScore } from '../../../utils/sortby' import { Button } from '../../_shared/Button' @@ -17,6 +18,7 @@ import styles from './SearchModal.module.scss' // @@TODO handle empty article options after backend support (subtitle, cover, etc.) // @@TODO implement load more // @@TODO implement FILTERS & TOPICS +// @@TODO use save/restoreScrollPosition if needed const getSearchCoincidences = ({ str, intersection }: { str: string; intersection: string }) => `${str.replaceAll( @@ -24,16 +26,16 @@ const getSearchCoincidences = ({ str, intersection }: { str: string; intersectio (casePreservedMatch) => `${casePreservedMatch}`, )}` -const prepareSearchResults = (list, searchValue) => - list.map((article, index) => ({ +const prepareSearchResults = (list: Shout[], searchValue: string) => + list.sort(byScore()).map((article, index) => ({ ...article, - body: '', - cover: '', - createdAt: '', + body: article.body, + cover: article.cover, + created_at: article.created_at, id: index, slug: article.slug, - authors: [], - topics: [], + authors: article.authors, + topics: article.topics, title: article.title ? getSearchCoincidences({ str: article.title, @@ -50,53 +52,76 @@ const prepareSearchResults = (list, searchValue) => export const SearchModal = () => { const { t } = useLocalize() - const { sortedArticles } = useArticlesStore() const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) - const [offset, setOffset] = createSignal(0) const [inputValue, setInputValue] = createSignal('') - //const [searchResultsList, setSearchResultsList] = createSignal<[] | null>([]) const [isLoading, setIsLoading] = createSignal(false) - // const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) - - let searchEl: HTMLInputElement - const handleQueryChange = async (_ev) => { - setInputValue(searchEl.value) - - if (inputValue() && inputValue().length > 2) await loadMore() - } - - const loadMore = async () => { - setIsLoading(true) - saveScrollPosition() - if (inputValue() && inputValue().length > 2) { - console.log(inputValue()) - const { hasMore } = await loadShoutsSearch({ + const [offset, setOffset] = createSignal(0) + const [searchResultsList, { refetch: loadSearchResults, mutate: setSearchResultsList }] = createResource< + Shout[] | null + >( + async () => { + setIsLoading(true) + const { hasMore, newShouts } = await loadShoutsSearch({ + limit: FEED_PAGE_SIZE, text: inputValue(), offset: offset(), - limit: FEED_PAGE_SIZE, }) + setIsLoading(false) + setOffset(newShouts.length) setIsLoadMoreButtonVisible(hasMore) - setOffset(offset() + FEED_PAGE_SIZE) + return newShouts + }, + { + ssrLoadFrom: 'initial', + initialValue: null, + }, + ) + + let searchEl: HTMLInputElement + const debouncedLoadMore = debounce(500, loadSearchResults) + + const handleQueryInput = async () => { + setInputValue(searchEl.value) + if (searchEl.value?.length > 2) { + await debouncedLoadMore() } else { - console.warn('[SaerchView] no query found') + setIsLoading(false) + setSearchResultsList(null) + } + } + + const enterQuery = async (ev: KeyboardEvent) => { + setIsLoading(true) + if (ev.key === 'Enter' && inputValue().length > 2) { + await debouncedLoadMore() + } else { + setIsLoading(false) + setSearchResultsList(null) } restoreScrollPosition() setIsLoading(false) } + // Cleanup the debounce timer when the component unmounts + onCleanup(() => { + debouncedLoadMore.cancel() + // console.debug('[SearchModal] cleanup debouncing search') + }) + return (

- +

diff --git a/src/components/ProfileSettings/ProfileSettings.tsx b/src/components/ProfileSettings/ProfileSettings.tsx index 348db613..490c289a 100644 --- a/src/components/ProfileSettings/ProfileSettings.tsx +++ b/src/components/ProfileSettings/ProfileSettings.tsx @@ -190,7 +190,7 @@ export const ProfileSettings = () => {

{t('Userpic')}