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)} />
-
+
+
+ }
+ colorScheme={type === "users" ? "primary" : undefined}
+ onClick={() => mergeSearchParams({ type: "users" })}
+ >
+ Users
+
+ }
+ colorScheme={type === "notes" ? "primary" : undefined}
+ onClick={() => mergeSearchParams({ type: "notes" })}
+ >
+ Notes
+
+
+
+
+
{search ? (