prepare search modal, refactor search fetch, handle intersection, etc.

This commit is contained in:
dog 2023-12-19 16:12:11 +03:00
parent d5fa4ed034
commit cf7aec3e1c
6 changed files with 206 additions and 142 deletions

View File

@ -335,6 +335,7 @@
"This post has not been rated yet": "This post has not been rated yet",
"This way we ll realize that you re a real person and ll take your vote into account. And you ll see how others voted": "This way we ll realize that you re a real person and ll take your vote into account. And you ll see how others voted",
"This way you ll be able to subscribe to authors, interesting topics and customize your feed": "This way you ll be able to subscribe to authors, interesting topics and customize your feed",
"To find publications, art, comments, authors and topics of interest to you, just start typing your query": "To find publications, art, comments, authors and topics of interest to you, just start typing your query",
"To leave a comment please": "To leave a comment please",
"To write a comment, you must": "To write a comment, you must",
"Top authors": "Authors rating",

View File

@ -353,6 +353,7 @@
"This post has not been rated yet": "Эту публикацию еще пока никто не оценил",
"This way we ll realize that you re a real person and ll take your vote into account. And you ll see how others voted": "Так мы поймем, что вы реальный человек, и учтем ваш голос. А вы увидите, как проголосовали другие",
"This way you ll be able to subscribe to authors, interesting topics and customize your feed": "Так вы сможете подписаться на авторов, интересные темы и настроить свою ленту",
"To find publications, art, comments, authors and topics of interest to you, just start typing your query": "Для поиска публикаций, искусства, комментариев, интересных вам авторов и тем, просто начните вводить ваш запрос",
"To leave a comment please": "Чтобы оставить комментарий, необходимо",
"To write a comment, you must": "Чтобы написать комментарий, необходимо",
"Top authors": "Рейтинг авторов",

View File

@ -161,13 +161,13 @@ export const ArticleCard = (props: ArticleCardProps) => {
<a href={`/${props.article.slug || ''}`}>
<div class={styles.shoutCardTitle}>
<span class={styles.shoutCardLinkWrapper}>
<span class={styles.shoutCardLinkContainer}>{title}</span>
<span class={styles.shoutCardLinkContainer} innerHTML={title} />
</span>
</div>
<Show when={!props.settings?.nosubtitle && subtitle}>
<div class={styles.shoutCardSubtitle}>
<span class={styles.shoutCardLinkContainer}>{subtitle}</span>
<span class={styles.shoutCardLinkContainer} innerHTML={subtitle} />
</div>
</Show>
</a>

View File

@ -1,11 +1,14 @@
@mixin searchFilterControl {
@include font-size(1.4rem);
height: 4rem;
padding: 0 2rem;
background: rgb(64 64 64 / 0.5);
border-radius: 10rem;
color: #fff;
@include font-size(1.4rem);
font-weight: 500;
height: 4rem;
padding: 0 2rem;
white-space: nowrap;
&:hover {
@ -17,42 +20,50 @@
}
}
.searchForm {
.searchContainer {
position: relative;
}
.searchInput {
@include font-size(4.8rem);
width: 100%;
padding: 0 0 0.5rem;
.searchField {
background: none;
border: none;
border-bottom: 2px solid #fff;
color: #fff;
@include font-size(4.8rem);
font-weight: bold;
outline: none;
padding: 0 0 0.5rem;
&::placeholder {
color: rgb(255 255 255 / 0.32);
}
&:not(:placeholder-shown) + .submitControl {
display: block;
}
&:not(:placeholder-shown) + .searchButton img {
filter: invert(1);
}
}
.submitControl {
display: none;
filter: invert(1);
height: 3.2rem;
.searchButton {
position: absolute;
right: 0;
top: 2rem;
width: 3.2rem;
height: 3.2rem;
& img {
filter: invert(0.4);
}
}
.searchDescription {
color: rgb(255 255 255 / 0.64);
@include font-size(1.6rem);
color: rgb(255 255 255 / 0.64);
}
.topicsList {
@ -60,6 +71,7 @@
flex-wrap: wrap;
justify-content: center;
gap: 1rem;
margin-top: 9.6rem !important;
}
@ -90,6 +102,7 @@
display: flex;
flex-wrap: wrap;
gap: 1rem;
margin: 6.4rem 0;
}

View File

@ -1,47 +1,103 @@
import { hideModal } from '../../../stores/ui'
import { useLocalize } from '../../../context/localize'
import { Button } from '../../_shared/Button'
import styles from './SearchModal.module.scss'
import { Icon } from '../../_shared/Icon'
import clsx from 'clsx'
import { createSignal, Show, For } from 'solid-js'
import { ArticleCard } from '../../Feed/ArticleCard'
import { Button } from '../../_shared/Button'
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 { useLocalize } from '../../../context/localize'
import styles from './SearchModal.module.scss'
// @@TODO implement search
// @@TODO implement throttling
// @@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 }) =>
`<span>${str.replace(
new RegExp(intersection, 'g'),
`<span class="blackModeIntersection">${intersection}</span>`
)}</span>`
export const SearchModal = () => {
const { t } = useLocalize()
const action = '/search'
const method = 'get'
let msgElement: HTMLTextAreaElement | undefined
let contactElement: HTMLInputElement | undefined
const submit = async () => {
await fetch(action, {
method,
const searchInputRef: { current: HTMLInputElement } = { current: null }
const [isSearching, setIsSearching] = createSignal(false)
const [searchResultsList, setSearchResultsList] = createSignal([])
// const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
const handleSearch = async () => {
const searchValue = searchInputRef.current?.value || ''
if (Boolean(searchValue)) {
await fetch(`https://search.discours.io/search?q=${searchValue}`, {
method: 'GET',
headers: {
accept: 'application/json',
'content-type': 'application/json; charset=utf-8'
},
body: JSON.stringify({ contact: contactElement?.value, message: msgElement?.textContent })
}
})
// hideModal()
.then((data) => data.json())
.then((data) => {
console.log(data)
// if (data.length) {
// const preparedSearchResultsList = [].map((article) => ({
// ...article,
// title: getSearchCoincidences({
// str: article.title,
// intersection: searchInputRef.current?.value || ''
// }),
// subtitle: getSearchCoincidences({
// str: article.subtitle,
// intersection: searchInputRef.current?.value || ''
// })
// }))
// setSearchResultsList(preparedSearchResultsList)
// } else {
// // @@TODO handle no search results notice
// }
})
.catch((error) => {
console.log('search request failed', error)
})
}
}
return (
<form method={method} action={action} onSubmit={submit} class={styles.searchForm}>
<div class={styles.searchContainer}>
<input
type="text"
name="contact"
type="search"
placeholder={t('Site search')}
ref={contactElement}
class={styles.searchField}
ref={(el) => (searchInputRef.current = el)}
class={styles.searchInput}
onInput={handleSearch}
onFocusIn={() => setIsSearching(true)}
onFocusOut={() => setIsSearching(false)}
/>
<button type={submit} class={styles.submitControl}>
<Icon name="search" />
</button>
<p class={styles.searchDescription}>
Для поиска публикаций, искусства, комментариев, интересных вам авторов и&nbsp;тем, просто начните
вводить ваш запрос
</p>
<ul class={clsx('view-switcher', styles.filterSwitcher)}>
<Button class={styles.searchButton} onClick={handleSearch} value={<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'
)}
/>
{/* @@TODO handle switcher */}
{/* <ul class={clsx('view-switcher', styles.filterSwitcher)}>
<li class="view-switcher__item view-switcher__item--selected">
<button type="button">Все</button>
</li>
@ -51,95 +107,83 @@ export const SearchModal = () => {
<li class="view-switcher__item">
<button type="button">Темы</button>
</li>
</ul>
</ul> */}
{/* @@TODO handle filter */}
{/* <Show when={FILTERS.length}>
<div class={styles.filterResults}>
<button type="button" class={styles.filterResultsControl}>
<For each={FILTERS}>
{(filter) => (
<button
type="button"
class={styles.filterResultsControl}
onClick={() => setActiveFilter(filter)}
>
Период времени
</button>
<button type="button" class={styles.filterResultsControl}>
Рейтинг
</button>
<button type="button" class={styles.filterResultsControl}>
Тип постов
</button>
<button type="button" class={styles.filterResultsControl}>
Темы
</button>
<button type="button" class={styles.filterResultsControl}>
Авторы
</button>
<button type="button" class={styles.filterResultsControl}>
Сообщества
</button>
)}
</For>
</div>
</Show> */}
<div class="container-xl">
{/* <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 */}
{/* <div class="container-xl">
<div class="row">
<div class={clsx('col-md-18 offset-md-2', styles.topicsList)}>
{topics.map((topic) => (
<button type="button" class={styles.topTopic}>
За месяц
</button>
<button type="button" class={styles.topTopic}>
#репортажи
</button>
<button type="button" class={styles.topTopic}>
#интервью
</button>
<button type="button" class={styles.topTopic}>
#культура
</button>
<button type="button" class={styles.topTopic}>
#поэзия
</button>
<button type="button" class={styles.topTopic}>
#теории
</button>
<button type="button" class={styles.topTopic}>
#война в украине
</button>
<button type="button" class={styles.topTopic}>
#общество
</button>
<button type="button" class={styles.topTopic}>
#Экспериментальная Музыка
</button>
<button type="button" class={styles.topTopic}>
Рейтинг 300+
</button>
<button type="button" class={styles.topTopic}>
#Протесты
</button>
<button type="button" class={styles.topTopic}>
Музыка
</button>
<button type="button" class={styles.topTopic}>
#За линией Маннергейма
</button>
<button type="button" class={styles.topTopic}>
Тесты
</button>
<button type="button" class={styles.topTopic}>
Коллективные истории
</button>
<button type="button" class={styles.topTopic}>
#личный опыт
</button>
<button type="button" class={styles.topTopic}>
Тоня Самсонова
</button>
<button type="button" class={styles.topTopic}>
#личный опыт
</button>
<button type="button" class={styles.topTopic}>
#Секс
</button>
<button type="button" class={styles.topTopic}>
Молоко Plus
{topic.name}
</button>
))}
</div>
</div>
</div> */}
</div>
</form>
)
}

View File

@ -1036,3 +1036,8 @@ iframe {
.cursorPointer {
cursor: pointer;
}
.blackModeIntersection {
color: var(--default-color);
background: #fef2f2;
}