From 6752d35491368e9d8182e1b9c9f52a25d962902c Mon Sep 17 00:00:00 2001 From: dog Date: Sun, 21 Jan 2024 15:57:03 +0300 Subject: [PATCH] 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() + }} />