From 1f73120b2e984e7eb53669cca7605dd0537f7fa2 Mon Sep 17 00:00:00 2001 From: hzrd149 Date: Tue, 17 Oct 2023 11:53:43 -0500 Subject: [PATCH] Add option to search notes in search view --- .changeset/smooth-kings-obey.md | 5 +++ src/views/search/index.tsx | 79 +++++++++++++++++++++++++++------ 2 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 .changeset/smooth-kings-obey.md diff --git a/.changeset/smooth-kings-obey.md b/.changeset/smooth-kings-obey.md new file mode 100644 index 000000000..89b196a34 --- /dev/null +++ b/.changeset/smooth-kings-obey.md @@ -0,0 +1,5 @@ +--- +"nostrudel": minor +--- + +Add option to search notes in search view diff --git a/src/views/search/index.tsx b/src/views/search/index.tsx index c20aa08c0..0a3f5b5e9 100644 --- a/src/views/search/index.tsx +++ b/src/views/search/index.tsx @@ -1,31 +1,34 @@ import { useCallback, useEffect, useMemo, useState } from "react"; -import { Box, Button, Flex, IconButton, Input, Link, Text, useDisclosure } from "@chakra-ui/react"; +import { Box, Button, ButtonGroup, Flex, IconButton, Input, Link, Text, useDisclosure } from "@chakra-ui/react"; import { Kind } from "nostr-tools"; import { useSearchParams, useNavigate } from "react-router-dom"; import { useAsync } from "react-use"; -import { CopyToClipboardIcon, QrCodeIcon } from "../../components/icons"; -import QrScannerModal from "../../components/qr-scanner-modal"; +import { SEARCH_RELAYS } from "../../const"; +import { NostrEvent } from "../../types/nostr-event"; import { safeDecode } from "../../helpers/nip19"; import { getMatchHashtag } from "../../helpers/regexp"; +import { parseKind0Event } from "../../helpers/user-metadata"; +import { readablizeSats } from "../../helpers/bolt11"; +import { searchParamsToJson } from "../../helpers/url"; +import { EmbedableContent, embedUrls } from "../../helpers/embeds"; +import { CopyToClipboardIcon, NotesIcon, QrCodeIcon } from "../../components/icons"; +import QrScannerModal from "../../components/qr-scanner-modal"; import RelaySelectionButton from "../../components/relay-selection/relay-selection-button"; import RelaySelectionProvider, { useRelaySelectionRelays } from "../../providers/relay-selection-provider"; import useTimelineLoader from "../../hooks/use-timeline-loader"; import useSubject from "../../hooks/use-subject"; import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback"; import IntersectionObserverProvider from "../../providers/intersection-observer"; -import { NostrEvent } from "../../types/nostr-event"; -import { parseKind0Event } from "../../helpers/user-metadata"; import { UserAvatar } from "../../components/user-avatar"; -import { UserDnsIdentityIcon } from "../../components/user-dns-identity-icon"; import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status"; -import { EmbedableContent, embedUrls } from "../../helpers/embeds"; +import { UserDnsIdentityIcon } from "../../components/user-dns-identity-icon"; import { embedNostrLinks, renderGenericUrl } from "../../components/embed-types"; import { UserLink } from "../../components/user-link"; import trustedUserStatsService, { NostrBandUserStats } from "../../services/trusted-user-stats"; -import { readablizeSats } from "../../helpers/bolt11"; import VerticalPageLayout from "../../components/vertical-page-layout"; -import { SEARCH_RELAYS } from "../../const"; +import User01 from "../../components/icons/user-01"; +import GenericNoteTimeline from "../../components/timeline-page/generic-note-timeline"; function ProfileResult({ profile }: { profile: NostrEvent }) { const metadata = parseKind0Event(profile); @@ -57,11 +60,11 @@ function ProfileResult({ profile }: { profile: NostrEvent }) { ); } -function SearchResults({ search }: { search: string }) { +function ProfileSearchResults({ search }: { search: string }) { const searchRelays = useRelaySelectionRelays(); const timeline = useTimelineLoader( - `${search}-search`, + `${search}-profile-search`, searchRelays, { search: search || "", kinds: [Kind.Metadata] }, { enabled: !!search }, @@ -98,12 +101,39 @@ function SearchResults({ search }: { search: string }) { ); } +function NoteSearchResults({ search }: { search: string }) { + const searchRelays = useRelaySelectionRelays(); + + const timeline = useTimelineLoader( + `${search}-note-search`, + searchRelays, + { search: search || "", kinds: [Kind.Text] }, + { enabled: !!search }, + ); + + const callback = useTimelineCurserIntersectionCallback(timeline); + + return ( + + + + ); +} + export function SearchPage() { const navigate = useNavigate(); const qrScannerModal = useDisclosure(); const [searchParams, setSearchParams] = useSearchParams(); + const mergeSearchParams = useCallback( + (params: Record) => { + setSearchParams((p) => ({ ...searchParamsToJson(p), ...params }), { replace: true }); + }, + [setSearchParams], + ); + const [searchInput, setSearchInput] = useState(searchParams.get("q") ?? ""); + const type = searchParams.get("type") ?? "users"; const search = searchParams.get("q"); // update the input value when search changes @@ -121,11 +151,11 @@ export function SearchPage() { const hashTagMatch = getMatchHashtag().exec(cleanText); if (hashTagMatch) { - navigate({ pathname: "/t/" + hashTagMatch[2].toLocaleLowerCase() }); + navigate({ pathname: "/t/" + hashTagMatch[2].toLocaleLowerCase() }, { replace: true }); return; } - setSearchParams({ q: cleanText }, { replace: true }); + mergeSearchParams({ q: cleanText }); }; const readClipboard = useCallback(async () => { @@ -138,6 +168,8 @@ export function SearchPage() { handleSearchText(searchInput); }; + const SearchResults = type === "users" ? ProfileSearchResults : NoteSearchResults; + return ( @@ -152,10 +184,29 @@ export function SearchPage() { setSearchInput(e.target.value)} /> - + + + + + + + + {search ? (