From cf7aec3e1c4f66699adbfb0368f73b957a3e9ffd Mon Sep 17 00:00:00 2001 From: dog Date: Tue, 19 Dec 2023 16:12:11 +0300 Subject: [PATCH 1/9] prepare search modal, refactor search fetch, handle intersection, etc. --- public/locales/en/translation.json | 1 + public/locales/ru/translation.json | 1 + .../Feed/ArticleCard/ArticleCard.tsx | 4 +- .../Nav/SearchModal/SearchModal.module.scss | 61 ++-- .../Nav/SearchModal/SearchModal.tsx | 276 ++++++++++-------- src/styles/app.scss | 5 + 6 files changed, 206 insertions(+), 142 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 8cfdc076..61dfd54f 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -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", diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index 1c379fac..9075a05e 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -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": "Рейтинг авторов", diff --git a/src/components/Feed/ArticleCard/ArticleCard.tsx b/src/components/Feed/ArticleCard/ArticleCard.tsx index 97f1b8ee..9c0eda98 100644 --- a/src/components/Feed/ArticleCard/ArticleCard.tsx +++ b/src/components/Feed/ArticleCard/ArticleCard.tsx @@ -161,13 +161,13 @@ export const ArticleCard = (props: ArticleCardProps) => {
- {title} +
- {subtitle} +
diff --git a/src/components/Nav/SearchModal/SearchModal.module.scss b/src/components/Nav/SearchModal/SearchModal.module.scss index 54c4114b..e0dc9801 100644 --- a/src/components/Nav/SearchModal/SearchModal.module.scss +++ b/src/components/Nav/SearchModal/SearchModal.module.scss @@ -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; +} - .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; +.searchInput { + @include font-size(4.8rem); - &::placeholder { - color: rgb(255 255 255 / 0.32); - } + width: 100%; - &:not(:placeholder-shown) + .submitControl { - display: block; - } + padding: 0 0 0.5rem; + + background: none; + border: none; + border-bottom: 2px solid #fff; + color: #fff; + font-weight: bold; + outline: none; + + &::placeholder { + color: rgb(255 255 255 / 0.32); + } + + &: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; } diff --git a/src/components/Nav/SearchModal/SearchModal.tsx b/src/components/Nav/SearchModal/SearchModal.tsx index 6c2e1c7e..456a88d3 100644 --- a/src/components/Nav/SearchModal/SearchModal.tsx +++ b/src/components/Nav/SearchModal/SearchModal.tsx @@ -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 }) => + `${str.replace( + new RegExp(intersection, 'g'), + `${intersection}` + )}` 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, - headers: { - accept: 'application/json', - 'content-type': 'application/json; charset=utf-8' - }, - body: JSON.stringify({ contact: contactElement?.value, message: msgElement?.textContent }) - }) - // hideModal() + 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' + } + }) + .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 ( -
+
(searchInputRef.current = el)} + class={styles.searchInput} + onInput={handleSearch} + onFocusIn={() => setIsSearching(true)} + onFocusOut={() => setIsSearching(false)} /> - -

- Для поиска публикаций, искусства, комментариев, интересных вам авторов и тем, просто начните - вводить ваш запрос -

-
    + @@ -51,95 +107,83 @@ export const SearchModal = () => {
  • -
+ */} -
- - - - - - -
+ {/* @@TODO handle filter */} + {/* +
+ + {(filter) => ( + + )} + +
+
*/} -
+ {/* */} + + {/* */} + + {(article: Shout) => ( + + )} + + + {/* @@TODO handle load more */} + {/* +

+ +

+
*/} +
+ + {/* @@TODO handle topics */} + {/*
- - - - - - - - - - - - - - - - - - - - + {topics.map((topic) => ( + + ))}
-
- +
*/} +
) } diff --git a/src/styles/app.scss b/src/styles/app.scss index 44b7e5a0..c861b448 100644 --- a/src/styles/app.scss +++ b/src/styles/app.scss @@ -1036,3 +1036,8 @@ iframe { .cursorPointer { cursor: pointer; } + +.blackModeIntersection { + color: var(--default-color); + background: #fef2f2; +} From f520de9d52dcaf4593567d1d57a9d171b2f55571 Mon Sep 17 00:00:00 2001 From: dog Date: Wed, 20 Dec 2023 16:19:31 +0300 Subject: [PATCH 2/9] add search loader & refactor results rendering --- public/locales/en/translation.json | 1 + public/locales/ru/translation.json | 1 + .../Nav/SearchModal/SearchModal.module.scss | 21 ++ .../Nav/SearchModal/SearchModal.tsx | 182 +++++++++--------- src/utils/config.ts | 3 + 5 files changed, 117 insertions(+), 91 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 61dfd54f..5ed8ca8d 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -363,6 +363,7 @@ "Video": "Video", "Video format not supported": "Video format not supported", "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 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.", diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index 9075a05e..c5d4348c 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -381,6 +381,7 @@ "Video": "Видео", "Video format not supported": "Тип видео не поддерживается", "Views": "Просмотры", + "We couldn't find anything for your request": "Мы не смогли ничего найти по вашему запросу", "We can't find you, check email or": "Не можем вас найти, проверьте адрес электронной почты или", "We know you, please try to login": "Такой адрес почты уже зарегистрирован, попробуйте залогиниться", "We've sent you a message with a link to enter our website.": "Мы выслали вам письмо с ссылкой на почту. Перейдите по ссылке в письме, чтобы войти на сайт.", diff --git a/src/components/Nav/SearchModal/SearchModal.module.scss b/src/components/Nav/SearchModal/SearchModal.module.scss index e0dc9801..0a7e40ec 100644 --- a/src/components/Nav/SearchModal/SearchModal.module.scss +++ b/src/components/Nav/SearchModal/SearchModal.module.scss @@ -109,3 +109,24 @@ .filterResultsControl { @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); + } +} diff --git a/src/components/Nav/SearchModal/SearchModal.tsx b/src/components/Nav/SearchModal/SearchModal.tsx index 456a88d3..e756edef 100644 --- a/src/components/Nav/SearchModal/SearchModal.tsx +++ b/src/components/Nav/SearchModal/SearchModal.tsx @@ -5,22 +5,16 @@ 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 { searchUrl } from '../../../utils/config' + import { useLocalize } from '../../../context/localize' import styles from './SearchModal.module.scss' -// @@TODO implement search -// @@TODO implement throttling - +// @@TODO handle founded shouts rendering (cors) // @@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 }) => `${str.replace( @@ -33,15 +27,17 @@ export const SearchModal = () => { const searchInputRef: { current: HTMLInputElement } = { current: null } - const [isSearching, setIsSearching] = createSignal(false) - const [searchResultsList, setSearchResultsList] = createSignal([]) - // const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) + const [searchResultsList, setSearchResultsList] = createSignal<[] | null>([]) + const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) + const [isLoading, setIsLoading] = createSignal(false) const handleSearch = async () => { const searchValue = searchInputRef.current?.value || '' if (Boolean(searchValue)) { - await fetch(`https://search.discours.io/search?q=${searchValue}`, { + setIsLoading(true) + + await fetch(`${searchUrl}=${searchValue}`, { method: 'GET', headers: { accept: 'application/json', @@ -50,10 +46,8 @@ export const SearchModal = () => { }) .then((data) => data.json()) .then((data) => { - console.log(data) - - // if (data.length) { - // const preparedSearchResultsList = [].map((article) => ({ + // if (data.what) { + // const preparedSearchResultsList = data.what.map((article) => ({ // ...article, // title: getSearchCoincidences({ // str: article.title, @@ -62,19 +56,27 @@ export const SearchModal = () => { // subtitle: getSearchCoincidences({ // str: article.subtitle, // intersection: searchInputRef.current?.value || '' - // }) + // }), // })) + // // setSearchResultsList(preparedSearchResultsList) + // + // @@TODO handle setIsLoadMoreButtonVisible() // } else { - // // @@TODO handle no search results notice + // setSearchResultsList(null) // } }) .catch((error) => { console.log('search request failed', error) }) + .finally(() => { + setIsLoading(false) + }) } } + const loadMore = () => {} + return (
{ ref={(el) => (searchInputRef.current = el)} class={styles.searchInput} onInput={handleSearch} - onFocusIn={() => setIsSearching(true)} - onFocusOut={() => setIsSearching(false)} /> - - -
  • - -
  • -
  • - -
  • - */} + {/* */} + + + {/* */} + + {(article: Shout) => ( + + )} + + + +

    + +

    +
    +
    + + +

    + + {/* @@TODO handle filter */} {/* @@ -119,71 +161,29 @@ export const SearchModal = () => { class={styles.filterResultsControl} onClick={() => setActiveFilter(filter)} > - Период времени + {filter.name} )}

    */} - {/* */} - - {/* */} - - {(article: Shout) => ( - - )} - - - {/* @@TODO handle load more */} - {/* -

    - -

    -
    */} -
    - {/* @@TODO handle topics */} - {/*
    -
    -
    - {topics.map((topic) => ( - - ))} + {/* +
    +
    +
    + + {(topic) => ( + + )} + +
    -
    */} + */}
    ) } diff --git a/src/utils/config.ts b/src/utils/config.ts index 3039e213..e83e9735 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -7,3 +7,6 @@ const defaultThumborUrl = 'https://images.discours.io' export const thumborUrl = import.meta.env.PUBLIC_THUMBOR_URL || defaultThumborUrl 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 From c78d5b33374d94ab7aa1b288f3f111f35f9d81bd Mon Sep 17 00:00:00 2001 From: dog Date: Tue, 9 Jan 2024 16:37:35 +0300 Subject: [PATCH 3/9] render search results & refactor search modal --- src/components/Nav/Modal/Modal.module.scss | 7 ++ .../Nav/SearchModal/SearchModal.module.scss | 6 + .../Nav/SearchModal/SearchModal.tsx | 117 ++++++++---------- 3 files changed, 68 insertions(+), 62 deletions(-) diff --git a/src/components/Nav/Modal/Modal.module.scss b/src/components/Nav/Modal/Modal.module.scss index 8e99ddc6..3b34b8e3 100644 --- a/src/components/Nav/Modal/Modal.module.scss +++ b/src/components/Nav/Modal/Modal.module.scss @@ -88,6 +88,13 @@ position: relative; text-align: left; + &::-webkit-scrollbar { + display: none; + } + + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + @include media-breakpoint-up(sm) { padding: 5rem; } diff --git a/src/components/Nav/SearchModal/SearchModal.module.scss b/src/components/Nav/SearchModal/SearchModal.module.scss index 0a7e40ec..1dbc5039 100644 --- a/src/components/Nav/SearchModal/SearchModal.module.scss +++ b/src/components/Nav/SearchModal/SearchModal.module.scss @@ -45,6 +45,11 @@ &:not(:placeholder-shown) + .searchButton img { filter: invert(1); } + + &::-moz-selection, + &::selection { + color: #2638d9; + } } .searchButton { @@ -61,6 +66,7 @@ } .searchDescription { + margin-bottom: 44px; @include font-size(1.6rem); color: rgb(255 255 255 / 0.64); diff --git a/src/components/Nav/SearchModal/SearchModal.tsx b/src/components/Nav/SearchModal/SearchModal.tsx index e756edef..d31ce669 100644 --- a/src/components/Nav/SearchModal/SearchModal.tsx +++ b/src/components/Nav/SearchModal/SearchModal.tsx @@ -1,4 +1,3 @@ -import clsx from 'clsx' import { createSignal, Show, For } from 'solid-js' import { ArticleCard } from '../../Feed/ArticleCard' @@ -10,16 +9,18 @@ import type { Shout } from '../../../graphql/types.gen' import { searchUrl } from '../../../utils/config' import { useLocalize } from '../../../context/localize' +import { hideModal } from '../../../stores/ui' import styles from './SearchModal.module.scss' -// @@TODO handle founded shouts rendering (cors) -// @@TODO implement load more (await ...({ filters: { .. }, limit: .., offset: .. })) +// @@TODO handle empty article options after backend support (subtitle, cover, etc.) +// @@TODO implement load more +// @@TODO implement FILTERS & TOPICS -const getSearchCoincidences = ({ str, intersection }) => +const getSearchCoincidences = ({ str, intersection }: { str: string; intersection: string }) => `${str.replace( - new RegExp(intersection, 'g'), - `${intersection}` + new RegExp(intersection, 'gi'), + (casePreservedMatch) => `${casePreservedMatch}` )}` export const SearchModal = () => { @@ -28,8 +29,8 @@ export const SearchModal = () => { const searchInputRef: { current: HTMLInputElement } = { current: null } const [searchResultsList, setSearchResultsList] = createSignal<[] | null>([]) - const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) const [isLoading, setIsLoading] = createSignal(false) + // const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) const handleSearch = async () => { const searchValue = searchInputRef.current?.value || '' @@ -46,25 +47,34 @@ export const SearchModal = () => { }) .then((data) => data.json()) .then((data) => { - // if (data.what) { - // const preparedSearchResultsList = data.what.map((article) => ({ - // ...article, - // title: getSearchCoincidences({ - // str: article.title, - // intersection: searchInputRef.current?.value || '' - // }), - // subtitle: getSearchCoincidences({ - // str: article.subtitle, - // intersection: searchInputRef.current?.value || '' - // }), - // })) - // - // setSearchResultsList(preparedSearchResultsList) - // - // @@TODO handle setIsLoadMoreButtonVisible() - // } else { - // setSearchResultsList(null) - // } + if (data.length) { + const preparedSearchResultsList = data.map((article, index) => ({ + ...article, + body: '', + cover: '', + createdAt: '', + id: index, + slug: article.slug, + authors: [], + topics: [], + title: article.title + ? getSearchCoincidences({ + str: article.title, + intersection: searchInputRef.current?.value || '' + }) + : '', + subtitle: article.subtitle + ? getSearchCoincidences({ + str: article.subtitle, + intersection: searchInputRef.current?.value || '' + }) + : '' + })) + + setSearchResultsList(preparedSearchResultsList) + } else { + setSearchResultsList(null) + } }) .catch((error) => { console.log('search request failed', error) @@ -75,7 +85,9 @@ export const SearchModal = () => { } } - const loadMore = () => {} + const handleArticleClick = () => { + hideModal() + } return (
    @@ -100,50 +112,31 @@ export const SearchModal = () => { )} /> - {/* */} - - - {/* */} - + + + {(article: Shout) => ( - +
    + +
    )}
    - + {/*

    -
    +
    */}
    From 6752d35491368e9d8182e1b9c9f52a25d962902c Mon Sep 17 00:00:00 2001 From: dog Date: Sun, 21 Jan 2024 15:57:03 +0300 Subject: [PATCH 4/9] refactor by review comments --- package.json | 3 +- .../Feed/ArticleCard/ArticleCard.tsx | 19 ++- src/components/Nav/HeaderAuth.tsx | 4 +- .../Nav/SearchModal/SearchModal.tsx | 109 ++++++++---------- .../Nav/SearchModal/SearchResultItem.tsx | 33 ++++++ src/utils/apiClient.ts | 15 +++ src/utils/config.ts | 2 +- 7 files changed, 118 insertions(+), 67 deletions(-) create mode 100644 src/components/Nav/SearchModal/SearchResultItem.tsx diff --git a/package.json b/package.json index 988290eb..7d39da8d 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "i18next-icu": "2.3.0", "intl-messageformat": "10.5.3", "just-throttle": "4.2.0", - "mailgun.js": "8.2.1" + "mailgun.js": "8.2.1", + "sanitize-html": "2.11.0" }, "devDependencies": { "@babel/core": "7.21.8", diff --git a/src/components/Feed/ArticleCard/ArticleCard.tsx b/src/components/Feed/ArticleCard/ArticleCard.tsx index 9c0eda98..7c381e96 100644 --- a/src/components/Feed/ArticleCard/ArticleCard.tsx +++ b/src/components/Feed/ArticleCard/ArticleCard.tsx @@ -1,4 +1,6 @@ import { createMemo, createSignal, For, Show } from 'solid-js' +import sanitizeHtml from 'sanitize-html' + import type { Shout } from '../../../graphql/types.gen' import { capitalize } from '../../../utils/capitalize' import { Icon } from '../../_shared/Icon' @@ -70,6 +72,14 @@ const getTitleAndSubtitle = ( return { title, subtitle } } +const sanitizeString = (html) => + sanitizeHtml(html, { + allowedTags: ['span'], + allowedAttributes: { + span: ['class'] + } + }) + export const ArticleCard = (props: ArticleCardProps) => { const { t, lang, formatDate } = useLocalize() const { user } = useSession() @@ -161,13 +171,13 @@ export const ArticleCard = (props: ArticleCardProps) => {
    - +
    - +
    @@ -191,7 +201,10 @@ export const ArticleCard = (props: ArticleCardProps) => {
    -
    +
    diff --git a/src/components/Nav/HeaderAuth.tsx b/src/components/Nav/HeaderAuth.tsx index 1678391e..49542fb9 100644 --- a/src/components/Nav/HeaderAuth.tsx +++ b/src/components/Nav/HeaderAuth.tsx @@ -124,10 +124,10 @@ export const HeaderAuth = (props: Props) => {
    - +
    diff --git a/src/components/Nav/SearchModal/SearchModal.tsx b/src/components/Nav/SearchModal/SearchModal.tsx index d31ce669..d118dcc0 100644 --- a/src/components/Nav/SearchModal/SearchModal.tsx +++ b/src/components/Nav/SearchModal/SearchModal.tsx @@ -1,15 +1,13 @@ -import { createSignal, Show, For } from 'solid-js' +import { createSignal, Show, For, JSX } from 'solid-js' -import { ArticleCard } from '../../Feed/ArticleCard' import { Button } from '../../_shared/Button' import { Icon } from '../../_shared/Icon' +import { SearchResultItem } from './SearchResultItem' +import { apiClient } from '../../../utils/apiClient' import type { Shout } from '../../../graphql/types.gen' -import { searchUrl } from '../../../utils/config' - import { useLocalize } from '../../../context/localize' -import { hideModal } from '../../../stores/ui' import styles from './SearchModal.module.scss' @@ -26,77 +24,68 @@ const getSearchCoincidences = ({ str, intersection }: { str: string; intersectio export const SearchModal = () => { const { t } = useLocalize() - const searchInputRef: { current: HTMLInputElement } = { current: null } - + const [inputValue, setInputValue] = createSignal('') const [searchResultsList, setSearchResultsList] = createSignal<[] | null>([]) const [isLoading, setIsLoading] = createSignal(false) // const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) const handleSearch = async () => { - const searchValue = searchInputRef.current?.value || '' + const searchValue = inputValue() || '' - if (Boolean(searchValue)) { + if (Boolean(searchValue) && searchValue.length > 2) { setIsLoading(true) - await fetch(`${searchUrl}=${searchValue}`, { - method: 'GET', - headers: { - accept: 'application/json', - 'content-type': 'application/json; charset=utf-8' + try { + const response = await apiClient.getSearchResults(searchValue) + const searchResult = await response.json() + + if (searchResult.length) { + const preparedSearchResultsList = searchResult.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 + }) + : '' + })) + + setSearchResultsList(preparedSearchResultsList) + } else { + setSearchResultsList(null) } - }) - .then((data) => data.json()) - .then((data) => { - if (data.length) { - const preparedSearchResultsList = data.map((article, index) => ({ - ...article, - body: '', - cover: '', - createdAt: '', - id: index, - slug: article.slug, - authors: [], - topics: [], - title: article.title - ? getSearchCoincidences({ - str: article.title, - intersection: searchInputRef.current?.value || '' - }) - : '', - subtitle: article.subtitle - ? getSearchCoincidences({ - str: article.subtitle, - intersection: searchInputRef.current?.value || '' - }) - : '' - })) - - setSearchResultsList(preparedSearchResultsList) - } else { - setSearchResultsList(null) - } - }) - .catch((error) => { - console.log('search request failed', error) - }) - .finally(() => { - setIsLoading(false) - }) + } catch (error) { + console.log('search request failed', error) + } finally { + setIsLoading(false) + } } } - const handleArticleClick = () => { - hideModal() - } - return (
    (searchInputRef.current = el)} class={styles.searchInput} - onInput={handleSearch} + onInput={(event) => { + setInputValue(event.target.value) + + handleSearch() + }} />
    -
    +
    diff --git a/src/components/Nav/SearchModal/SearchModal.tsx b/src/components/Nav/SearchModal/SearchModal.tsx index 227d2641..8223171e 100644 --- a/src/components/Nav/SearchModal/SearchModal.tsx +++ b/src/components/Nav/SearchModal/SearchModal.tsx @@ -1,4 +1,4 @@ -import { createSignal, Show, For, JSX } from 'solid-js' +import { createSignal, Show, For } from 'solid-js' import { Button } from '../../_shared/Button' import { Icon } from '../../_shared/Icon'