From f93b146c318645073cd35b2c237b6c9be490b8b3 Mon Sep 17 00:00:00 2001 From: Gabi Purcaru Date: Sun, 1 Jan 2023 19:36:47 +0000 Subject: [PATCH] Add search functionality (#28) * Add search functionality --- components/Content.tsx | 315 +++++++++++++++++++++++++++-------------- components/Footer.tsx | 16 +-- components/Header.tsx | 6 +- components/Spinner.tsx | 22 +++ 4 files changed, 245 insertions(+), 114 deletions(-) create mode 100644 components/Spinner.tsx diff --git a/components/Content.tsx b/components/Content.tsx index bb5405e..c6c2e81 100644 --- a/components/Content.tsx +++ b/components/Content.tsx @@ -1,4 +1,5 @@ -import React, { useState } from 'react' +import { Spinner } from './Spinner' +import React, { useState, memo, useRef } from 'react' import sanitizeHtml from 'sanitize-html' import debounce from 'debounce' @@ -11,7 +12,11 @@ type AccountDetails = { id: string acct: string followed_by: Set // list of handles + followers_count: number discoverable: boolean + display_name: string + note: string + avatar_static: string } async function usernameToId( @@ -166,6 +171,23 @@ function getNextPage(linkHeader: string | null): string | null { return null } +function matchesSearch(account: AccountDetails, search: string): boolean { + if (/^\s*$/.test(search)) { + return true + } + const sanitizedSearch = search.replace(/^\s+|\s+$/, '').toLocaleLowerCase() + if (account.acct.toLocaleLowerCase().includes(sanitizedSearch)) { + return true + } + if (account.display_name.toLocaleLowerCase().includes(sanitizedSearch)) { + return true + } + if (account.note.toLocaleLowerCase().includes(sanitizedSearch)) { + return true + } + return false +} + export function Content({}) { const [handle, setHandle] = useState('') const [follows, setFollows] = useState>([]) @@ -264,16 +286,10 @@ export function Content({}) { ease-in-out" > Search - {isLoading ? ( - - {/*! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. */} - - - ) : null} + {isLoading ? ( @@ -313,24 +329,7 @@ export function Content({}) { {isDone || follows.length > 0 ? ( -
-
-
-
    - {follows.slice(0, 500).map((account) => ( - - ))} -
-
-
-
+ ) : null} @@ -339,86 +338,96 @@ export function Content({}) { ) } -function AccountDetails({ account, mainDomain }) { - const { - avatar_static, - display_name, - acct, - note, - followers_count, - followed_by, - } = account - let formatter = Intl.NumberFormat('en', { notation: 'compact' }) - let numFollowers = formatter.format(followers_count) +const AccountDetails = memo( + ({ + account, + mainDomain, + }: { + account: AccountDetails + mainDomain: string + }) => { + const { + avatar_static, + display_name, + acct, + note, + followers_count, + followed_by, + } = account + let formatter = Intl.NumberFormat('en', { notation: 'compact' }) + let numFollowers = formatter.format(followers_count) - const [expandedFollowers, setExpandedFollowers] = useState(false) + const [expandedFollowers, setExpandedFollowers] = useState(false) - return ( -
  • -
    -
    - {display_name} -
    -
    -

    - {display_name} -

    -
    - {acct} - | - {numFollowers} followers + return ( +
  • +
    +
    + {/* eslint-disable-next-line @next/next/no-img-element */} + {display_name} +
    +
    +

    + {display_name} +

    +
    + {acct} + | + {numFollowers} followers +
    +
    + +
    + + Followed by{' '} + {followed_by.size < 9 || expandedFollowers ? ( + Array.from(followed_by.values()).map((handle, idx) => ( + + + {handle.replace(/@.+/, '')} + + {idx === followed_by.size - 1 ? '.' : ', '} + + )) + ) : ( + <> + + . + + )} + +
    + -
    - -
    - - Followed by{' '} - {followed_by.size < 9 || expandedFollowers ? ( - Array.from(followed_by.values()).map((handle, idx) => ( - - - {handle.replace(/@.+/, '')} - - {idx === followed_by.size - 1 ? '.' : ', '} - - )) - ) : ( - <> - - . - - )} -
    - - -
  • - ) -} + + ) + } +) +AccountDetails.displayName = 'AccountDetails' function ErrorLog({ errors }: { errors: Array }) { const [expanded, setExpanded] = useState(false) @@ -443,3 +452,99 @@ function ErrorLog({ errors }: { errors: Array }) { ) } + +function Results({ + domain, + follows, +}: { + domain: string + follows: Array +}) { + let [search, setSearch] = useState('') + const [isLoading, setLoading] = useState(false) + const updateSearch = useRef( + debounce((s: string) => { + setLoading(false) + setSearch(s) + }, 1500) + ).current + + follows = follows.filter((acc) => matchesSearch(acc, search)).slice(0, 500) + + return ( +
    +
    +
    + +
    +
    +
    + {follows.length === 0 ? ( +

    + No results found. +

    + ) : null} +
      + {follows.map((account) => ( + + ))} +
    +
    +
    +
    +
    + ) +} + +function SearchInput({ onChange }: { onChange: (s: string) => void }) { + let [search, setSearchInputValue] = useState('') + return ( + { + setSearchInputValue(e.target.value) + onChange(e.target.value) + }} + className=" + form-control + block + w-80 + px-3 + py-1.5 + text-base + font-normal + text-gray-700 + bg-white bg-clip-padding + border border-solid border-gray-300 + rounded + transition + ease-in-out + m-0 + focus:text-gray-900 focus:bg-white focus:border-green-600 focus:outline-none + dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-gray-200 dark:focus:bg-gray-900 dark:focus:text-gray-200" + /> + ) +} diff --git a/components/Footer.tsx b/components/Footer.tsx index 00b5284..cc66046 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -17,15 +17,15 @@ export default function Footer({}) { @gabipurcaru@mastodon.online .
    - - Privacy | {' '} - - Donate + + + Privacy + {' '} + |{' '} + + Donate - + Built with{' '} diff --git a/components/Header.tsx b/components/Header.tsx index 206f26b..3f13d45 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -1,7 +1,11 @@ import Link from 'next/link' import React from 'react' -export default function Header({ selected }: { selected: 'home' | 'donate' | 'privacy' }) { +export default function Header({ + selected, +}: { + selected: 'home' | 'donate' | 'privacy' +}) { return (