2024-01-25 18:19:59 +00:00
|
|
|
import type { Shout } from '../../../graphql/schema/core.gen'
|
|
|
|
|
2024-01-25 15:06:26 +00:00
|
|
|
import { createSignal, Show, For } from 'solid-js'
|
2023-12-26 10:05:15 +00:00
|
|
|
|
2024-01-25 18:19:59 +00:00
|
|
|
import { useLocalize } from '../../../context/localize'
|
2024-01-25 15:06:26 +00:00
|
|
|
import { Button } from '../../_shared/Button'
|
2023-11-02 19:21:51 +00:00
|
|
|
import { Icon } from '../../_shared/Icon'
|
2024-01-25 18:19:59 +00:00
|
|
|
import { FEED_PAGE_SIZE } from '../../Views/Feed/Feed'
|
2024-01-25 15:06:26 +00:00
|
|
|
|
2024-01-25 18:19:59 +00:00
|
|
|
import { SearchResultItem } from './SearchResultItem'
|
2023-11-30 08:50:29 +00:00
|
|
|
|
|
|
|
import styles from './SearchModal.module.scss'
|
2024-01-25 19:16:38 +00:00
|
|
|
import { restoreScrollPosition, saveScrollPosition } from '../../../utils/scroll'
|
|
|
|
import { loadShoutsSearch, useArticlesStore } from '../../../stores/zine/articles'
|
|
|
|
import { byScore } from '../../../utils/sortby'
|
2023-11-02 19:21:51 +00:00
|
|
|
|
2024-01-25 15:06:26 +00:00
|
|
|
// @@TODO handle empty article options after backend support (subtitle, cover, etc.)
|
|
|
|
// @@TODO implement load more
|
|
|
|
// @@TODO implement FILTERS & TOPICS
|
|
|
|
|
|
|
|
const getSearchCoincidences = ({ str, intersection }: { str: string; intersection: string }) =>
|
2024-01-25 18:19:59 +00:00
|
|
|
`<span>${str.replaceAll(
|
2024-01-25 15:06:26 +00:00
|
|
|
new RegExp(intersection, 'gi'),
|
|
|
|
(casePreservedMatch) => `<span class="blackModeIntersection">${casePreservedMatch}</span>`,
|
|
|
|
)}</span>`
|
|
|
|
|
|
|
|
const prepareSearchResults = (list, searchValue) =>
|
|
|
|
list.map((article, index) => ({
|
|
|
|
...article,
|
|
|
|
body: '',
|
|
|
|
cover: '',
|
|
|
|
createdAt: '',
|
|
|
|
id: index,
|
|
|
|
slug: article.slug,
|
|
|
|
authors: [],
|
|
|
|
topics: [],
|
|
|
|
title: article.title
|
|
|
|
? getSearchCoincidences({
|
|
|
|
str: article.title,
|
|
|
|
intersection: searchValue,
|
|
|
|
})
|
|
|
|
: '',
|
|
|
|
subtitle: article.subtitle
|
|
|
|
? getSearchCoincidences({
|
|
|
|
str: article.subtitle,
|
|
|
|
intersection: searchValue,
|
|
|
|
})
|
|
|
|
: '',
|
|
|
|
}))
|
|
|
|
|
2023-11-02 19:21:51 +00:00
|
|
|
export const SearchModal = () => {
|
|
|
|
const { t } = useLocalize()
|
2024-01-25 19:16:38 +00:00
|
|
|
const { sortedArticles } = useArticlesStore()
|
|
|
|
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
|
|
|
const [offset, setOffset] = createSignal(0)
|
2024-01-25 15:06:26 +00:00
|
|
|
const [inputValue, setInputValue] = createSignal('')
|
2024-01-25 19:16:38 +00:00
|
|
|
//const [searchResultsList, setSearchResultsList] = createSignal<[] | null>([])
|
2024-01-25 15:06:26 +00:00
|
|
|
const [isLoading, setIsLoading] = createSignal(false)
|
|
|
|
// const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
|
|
|
|
2024-01-25 19:16:38 +00:00
|
|
|
let searchEl: HTMLInputElement
|
|
|
|
const handleQueryChange = async (_ev) => {
|
|
|
|
setInputValue(searchEl.value)
|
2024-01-25 15:06:26 +00:00
|
|
|
|
2024-01-25 19:16:38 +00:00
|
|
|
if (inputValue() && inputValue().length > 2) await loadMore()
|
|
|
|
}
|
2024-01-25 15:06:26 +00:00
|
|
|
|
2024-01-25 19:16:38 +00:00
|
|
|
const loadMore = async () => {
|
|
|
|
setIsLoading(true)
|
|
|
|
saveScrollPosition()
|
|
|
|
if (inputValue() && inputValue().length > 2) {
|
|
|
|
console.log(inputValue())
|
|
|
|
const { hasMore } = await loadShoutsSearch({
|
|
|
|
text: inputValue(),
|
|
|
|
offset: offset(),
|
|
|
|
limit: FEED_PAGE_SIZE,
|
|
|
|
})
|
|
|
|
setIsLoadMoreButtonVisible(hasMore)
|
|
|
|
setOffset(offset() + FEED_PAGE_SIZE)
|
|
|
|
} else {
|
|
|
|
console.warn('[SaerchView] no query found')
|
2024-01-25 15:06:26 +00:00
|
|
|
}
|
2024-01-25 19:16:38 +00:00
|
|
|
restoreScrollPosition()
|
|
|
|
setIsLoading(false)
|
2023-11-02 19:21:51 +00:00
|
|
|
}
|
2024-01-25 15:06:26 +00:00
|
|
|
|
2023-11-02 19:21:51 +00:00
|
|
|
return (
|
2024-01-25 15:06:26 +00:00
|
|
|
<div class={styles.searchContainer}>
|
2023-11-02 19:21:51 +00:00
|
|
|
<input
|
2024-01-25 15:06:26 +00:00
|
|
|
type="search"
|
2023-11-02 19:21:51 +00:00
|
|
|
placeholder={t('Site search')}
|
2024-01-25 15:06:26 +00:00
|
|
|
class={styles.searchInput}
|
2024-01-25 19:16:38 +00:00
|
|
|
onInput={handleQueryChange}
|
|
|
|
ref={searchEl}
|
2024-01-25 15:06:26 +00:00
|
|
|
/>
|
|
|
|
|
|
|
|
<Button
|
|
|
|
class={styles.searchButton}
|
2024-01-25 19:16:38 +00:00
|
|
|
onClick={loadMore}
|
2024-01-25 15:06:26 +00:00
|
|
|
value={isLoading() ? <div class={styles.searchLoader} /> : <Icon name="search" />}
|
|
|
|
/>
|
|
|
|
|
|
|
|
<p
|
|
|
|
class={styles.searchDescription}
|
|
|
|
innerHTML={t(
|
|
|
|
'To find publications, art, comments, authors and topics of interest to you, just start typing your query',
|
|
|
|
)}
|
2023-11-02 19:21:51 +00:00
|
|
|
/>
|
2024-01-25 15:06:26 +00:00
|
|
|
|
|
|
|
<Show when={!isLoading()}>
|
2024-01-25 19:16:38 +00:00
|
|
|
<Show when={sortedArticles()}>
|
|
|
|
<For each={sortedArticles().sort(byScore())}>
|
2024-01-25 15:06:26 +00:00
|
|
|
{(article: Shout) => (
|
|
|
|
<div>
|
|
|
|
<SearchResultItem
|
|
|
|
article={article}
|
|
|
|
settings={{
|
|
|
|
isFloorImportant: true,
|
|
|
|
isSingle: true,
|
|
|
|
nodate: true,
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</For>
|
|
|
|
|
2024-01-25 19:16:38 +00:00
|
|
|
<Show when={isLoadMoreButtonVisible()}>
|
2024-01-25 15:06:26 +00:00
|
|
|
<p class="load-more-container">
|
|
|
|
<button class="button" onClick={loadMore}>
|
|
|
|
{t('Load more')}
|
|
|
|
</button>
|
|
|
|
</p>
|
2024-01-25 19:16:38 +00:00
|
|
|
</Show>
|
2024-01-25 15:06:26 +00:00
|
|
|
</Show>
|
|
|
|
|
2024-01-25 19:16:38 +00:00
|
|
|
<Show when={!sortedArticles()}>
|
2024-01-25 15:06:26 +00:00
|
|
|
<p class={styles.searchDescription} innerHTML={t("We couldn't find anything for your request")} />
|
|
|
|
</Show>
|
|
|
|
</Show>
|
|
|
|
|
|
|
|
{/* @@TODO handle filter */}
|
|
|
|
{/* <Show when={FILTERS.length}>
|
|
|
|
<div class={styles.filterResults}>
|
|
|
|
<For each={FILTERS}>
|
|
|
|
{(filter) => (
|
|
|
|
<button
|
|
|
|
type="button"
|
|
|
|
class={styles.filterResultsControl}
|
|
|
|
onClick={() => setActiveFilter(filter)}
|
|
|
|
>
|
|
|
|
{filter.name}
|
|
|
|
</button>
|
|
|
|
)}
|
|
|
|
</For>
|
|
|
|
</div>
|
|
|
|
</Show> */}
|
|
|
|
|
|
|
|
{/* @@TODO handle topics */}
|
|
|
|
{/* <Show when={TOPICS.length}>
|
|
|
|
<div class="container-xl">
|
|
|
|
<div class="row">
|
|
|
|
<div class={clsx('col-md-18 offset-md-2', styles.topicsList)}>
|
|
|
|
<For each={TOPICS}>
|
|
|
|
{(topic) => (
|
|
|
|
<button type="button" class={styles.topTopic} onClick={() => setActiveTopic(topic)}>
|
|
|
|
{topic.name}
|
|
|
|
</button>
|
|
|
|
)}
|
|
|
|
</For>
|
|
|
|
</div>
|
2023-11-02 19:21:51 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2024-01-25 15:06:26 +00:00
|
|
|
</Show> */}
|
|
|
|
</div>
|
2023-11-02 19:21:51 +00:00
|
|
|
)
|
|
|
|
}
|