add search loader & refactor results rendering

This commit is contained in:
dog 2023-12-20 16:19:31 +03:00
parent cf7aec3e1c
commit f520de9d52
5 changed files with 117 additions and 91 deletions

View File

@ -363,6 +363,7 @@
"Video": "Video", "Video": "Video",
"Video format not supported": "Video format not supported", "Video format not supported": "Video format not supported",
"Views": "Views", "Views": "Views",
"We couldn't find anything for your request": "We couldn’t find anything for your request",
"We can't find you, check email or": "We can't find you, check email or", "We can't find you, check email or": "We can't find you, check email or",
"We know you, please try to login": "This email address is already registered, please try to login", "We know you, please try to login": "This email address is already registered, please try to login",
"We've sent you a message with a link to enter our website.": "We've sent you an email with a link to your email. Follow the link in the email to enter our website.", "We've sent you a message with a link to enter our website.": "We've sent you an email with a link to your email. Follow the link in the email to enter our website.",

View File

@ -381,6 +381,7 @@
"Video": "Видео", "Video": "Видео",
"Video format not supported": "Тип видео не поддерживается", "Video format not supported": "Тип видео не поддерживается",
"Views": "Просмотры", "Views": "Просмотры",
"We couldn't find anything for your request": "Мы не смогли ничего найти по вашему запросу",
"We can't find you, check email or": "Не можем вас найти, проверьте адрес электронной почты или", "We can't find you, check email or": "Не можем вас найти, проверьте адрес электронной почты или",
"We know you, please try to login": "Такой адрес почты уже зарегистрирован, попробуйте залогиниться", "We know you, please try to login": "Такой адрес почты уже зарегистрирован, попробуйте залогиниться",
"We've sent you a message with a link to enter our website.": "Мы выслали вам письмо с ссылкой на почту. Перейдите по ссылке в письме, чтобы войти на сайт.", "We've sent you a message with a link to enter our website.": "Мы выслали вам письмо с ссылкой на почту. Перейдите по ссылке в письме, чтобы войти на сайт.",

View File

@ -109,3 +109,24 @@
.filterResultsControl { .filterResultsControl {
@include searchFilterControl; @include searchFilterControl;
} }
.searchLoader {
width: 28px;
height: 28px;
border: 5px solid #fff;
border-bottom-color: transparent;
border-radius: 50%;
animation: rotation 1s linear infinite;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

View File

@ -5,22 +5,16 @@ import { ArticleCard } from '../../Feed/ArticleCard'
import { Button } from '../../_shared/Button' import { Button } from '../../_shared/Button'
import { Icon } from '../../_shared/Icon' import { Icon } from '../../_shared/Icon'
// import { PRERENDERED_ARTICLES_COUNT } from '../../Views/Home'
// import { restoreScrollPosition, saveScrollPosition } from '../../../utils/scroll'
import type { Shout } from '../../../graphql/types.gen' import type { Shout } from '../../../graphql/types.gen'
import { searchUrl } from '../../../utils/config'
import { useLocalize } from '../../../context/localize' import { useLocalize } from '../../../context/localize'
import styles from './SearchModal.module.scss' import styles from './SearchModal.module.scss'
// @@TODO implement search // @@TODO handle founded shouts rendering (cors)
// @@TODO implement throttling
// @@TODO implement load more (await ...({ filters: { .. }, limit: .., offset: .. })) // @@TODO implement load more (await ...({ filters: { .. }, limit: .., offset: .. }))
// @@TODO implement modal hiding on article click
// @@TODO search url as const
// @@TODO refactor switcher, filters, topics
const getSearchCoincidences = ({ str, intersection }) => const getSearchCoincidences = ({ str, intersection }) =>
`<span>${str.replace( `<span>${str.replace(
@ -33,15 +27,17 @@ export const SearchModal = () => {
const searchInputRef: { current: HTMLInputElement } = { current: null } const searchInputRef: { current: HTMLInputElement } = { current: null }
const [isSearching, setIsSearching] = createSignal(false) const [searchResultsList, setSearchResultsList] = createSignal<[] | null>([])
const [searchResultsList, setSearchResultsList] = createSignal([]) const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
// const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) const [isLoading, setIsLoading] = createSignal(false)
const handleSearch = async () => { const handleSearch = async () => {
const searchValue = searchInputRef.current?.value || '' const searchValue = searchInputRef.current?.value || ''
if (Boolean(searchValue)) { if (Boolean(searchValue)) {
await fetch(`https://search.discours.io/search?q=${searchValue}`, { setIsLoading(true)
await fetch(`${searchUrl}=${searchValue}`, {
method: 'GET', method: 'GET',
headers: { headers: {
accept: 'application/json', accept: 'application/json',
@ -50,10 +46,8 @@ export const SearchModal = () => {
}) })
.then((data) => data.json()) .then((data) => data.json())
.then((data) => { .then((data) => {
console.log(data) // if (data.what) {
// const preparedSearchResultsList = data.what.map((article) => ({
// if (data.length) {
// const preparedSearchResultsList = [].map((article) => ({
// ...article, // ...article,
// title: getSearchCoincidences({ // title: getSearchCoincidences({
// str: article.title, // str: article.title,
@ -62,19 +56,27 @@ export const SearchModal = () => {
// subtitle: getSearchCoincidences({ // subtitle: getSearchCoincidences({
// str: article.subtitle, // str: article.subtitle,
// intersection: searchInputRef.current?.value || '' // intersection: searchInputRef.current?.value || ''
// }) // }),
// })) // }))
//
// setSearchResultsList(preparedSearchResultsList) // setSearchResultsList(preparedSearchResultsList)
//
// @@TODO handle setIsLoadMoreButtonVisible()
// } else { // } else {
// // @@TODO handle no search results notice // setSearchResultsList(null)
// } // }
}) })
.catch((error) => { .catch((error) => {
console.log('search request failed', error) console.log('search request failed', error)
}) })
.finally(() => {
setIsLoading(false)
})
} }
} }
const loadMore = () => {}
return ( return (
<div class={styles.searchContainer}> <div class={styles.searchContainer}>
<input <input
@ -83,11 +85,13 @@ export const SearchModal = () => {
ref={(el) => (searchInputRef.current = el)} ref={(el) => (searchInputRef.current = el)}
class={styles.searchInput} class={styles.searchInput}
onInput={handleSearch} onInput={handleSearch}
onFocusIn={() => setIsSearching(true)}
onFocusOut={() => setIsSearching(false)}
/> />
<Button class={styles.searchButton} onClick={handleSearch} value={<Icon name="search" />} /> <Button
class={styles.searchButton}
onClick={handleSearch}
value={isLoading() ? <div class={styles.searchLoader} /> : <Icon name="search" />}
/>
<p <p
class={styles.searchDescription} class={styles.searchDescription}
@ -96,18 +100,56 @@ export const SearchModal = () => {
)} )}
/> />
{/* @@TODO handle switcher */} {/* <Show when={!isLoading()}> */}
{/* <ul class={clsx('view-switcher', styles.filterSwitcher)}> <Show when={false}>
<li class="view-switcher__item view-switcher__item--selected"> <Show when={searchResultsList().length}>
<button type="button">Все</button> {/* <For each={searchResultsList()}> */}
</li> <For
<li class="view-switcher__item"> each={[
<button type="button">Публикации</button> {
</li> body: 'body',
<li class="view-switcher__item"> cover: 'production/image/bbad6b10-9b44-11ee-bdef-5758f9198f7d.png',
<button type="button">Темы</button> createdAt: '12',
</li> id: 12,
</ul> */} slug: '/about',
authors: [
{
id: 1,
name: 'author',
slug: '/'
}
],
title: 'asas',
subtitle: 'asas',
topics: []
}
]}
>
{(article: Shout) => (
<ArticleCard
article={article}
settings={{
isFloorImportant: true,
isSingle: true,
nodate: true
}}
/>
)}
</For>
<Show when={isLoadMoreButtonVisible()}>
<p class="load-more-container">
<button class="button" onClick={loadMore}>
{t('Load more')}
</button>
</p>
</Show>
</Show>
<Show when={!searchResultsList()}>
<p class={styles.searchDescription} innerHTML={t("We couldn't find anything for your request")} />
</Show>
</Show>
{/* @@TODO handle filter */} {/* @@TODO handle filter */}
{/* <Show when={FILTERS.length}> {/* <Show when={FILTERS.length}>
@ -119,71 +161,29 @@ export const SearchModal = () => {
class={styles.filterResultsControl} class={styles.filterResultsControl}
onClick={() => setActiveFilter(filter)} onClick={() => setActiveFilter(filter)}
> >
Период времени {filter.name}
</button> </button>
)} )}
</For> </For>
</div> </div>
</Show> */} </Show> */}
{/* <Show when={searchResultsList().length}> */}
<Show when={true}>
{/* <For each={searchResultsList()}> */}
<For
each={[
{
body: 'body',
cover: 'production/image/bbad6b10-9b44-11ee-bdef-5758f9198f7d.png',
createdAt: '12',
id: 12,
slug: '/',
authors: [
{
id: 1,
name: 'author',
slug: '/'
}
],
title: '',
subtitle: '',
topics: []
}
]}
>
{(article: Shout) => (
<ArticleCard
article={article}
settings={{
isFloorImportant: true,
isSingle: true,
nodate: true
}}
/>
)}
</For>
{/* @@TODO handle load more */}
{/* <Show when={isLoadMoreButtonVisible()}>
<p class="load-more-container">
<button class="button" onClick={loadMore}>
{t('Load more')}
</button>
</p>
</Show> */}
</Show>
{/* @@TODO handle topics */} {/* @@TODO handle topics */}
{/* <div class="container-xl"> {/* <Show when={TOPICS.length}>
<div class="row"> <div class="container-xl">
<div class={clsx('col-md-18 offset-md-2', styles.topicsList)}> <div class="row">
{topics.map((topic) => ( <div class={clsx('col-md-18 offset-md-2', styles.topicsList)}>
<button type="button" class={styles.topTopic}> <For each={TOPICS}>
{topic.name} {(topic) => (
</button> <button type="button" class={styles.topTopic} onClick={() => setActiveTopic(topic)}>
))} {topic.name}
</button>
)}
</For>
</div>
</div> </div>
</div> </div>
</div> */} </Show> */}
</div> </div>
) )
} }

View File

@ -7,3 +7,6 @@ const defaultThumborUrl = 'https://images.discours.io'
export const thumborUrl = import.meta.env.PUBLIC_THUMBOR_URL || defaultThumborUrl export const thumborUrl = import.meta.env.PUBLIC_THUMBOR_URL || defaultThumborUrl
export const SENTRY_DSN = import.meta.env.PUBLIC_SENTRY_DSN || '' export const SENTRY_DSN = import.meta.env.PUBLIC_SENTRY_DSN || ''
const defaultSearchUrl = 'https://search.discours.io/search?q'
export const searchUrl = import.meta.env.PUBLIC_SEARCH_URL || defaultSearchUrl