From ad6e51ed98e317a4258ea6b339c6d71468897ce6 Mon Sep 17 00:00:00 2001 From: hzrd149 Date: Mon, 5 Feb 2024 11:52:50 +0000 Subject: [PATCH 1/2] Add explanations to relay views --- .changeset/calm-impalas-battle.md | 5 + src/app.tsx | 12 ++- src/classes/relay-set.ts | 10 ++ src/helpers/nip07.ts | 19 ++++ src/helpers/nostr/contacts.ts | 21 ++++ src/hooks/use-user-contact-relays.ts | 25 +++++ src/hooks/use-user-dns-identity.ts | 7 ++ src/providers/route/require-read-relays.tsx | 21 +++- src/services/client-relays.ts | 3 +- src/services/user-contacts.ts | 87 --------------- src/services/user-event-sync.ts | 15 ++- src/services/user-mailboxes.ts | 19 ++-- src/views/launchpad/index.tsx | 1 - src/views/relays/app/index.tsx | 74 ++++++++++++- src/views/relays/cache/index.tsx | 3 + src/views/relays/contact-list/index.tsx | 114 ++++++++++++++++++++ src/views/relays/index.tsx | 27 ++++- src/views/relays/mailboxes/index.tsx | 94 +++++++--------- src/views/relays/nip05/index.tsx | 48 +++++++++ src/views/signin/start.tsx | 17 +-- 20 files changed, 445 insertions(+), 177 deletions(-) create mode 100644 .changeset/calm-impalas-battle.md create mode 100644 src/helpers/nip07.ts create mode 100644 src/helpers/nostr/contacts.ts create mode 100644 src/hooks/use-user-contact-relays.ts create mode 100644 src/hooks/use-user-dns-identity.ts delete mode 100644 src/services/user-contacts.ts create mode 100644 src/views/relays/contact-list/index.tsx create mode 100644 src/views/relays/nip05/index.tsx diff --git a/.changeset/calm-impalas-battle.md b/.changeset/calm-impalas-battle.md new file mode 100644 index 000000000..08d27f5d1 --- /dev/null +++ b/.changeset/calm-impalas-battle.md @@ -0,0 +1,5 @@ +--- +"nostrudel": patch +--- + +Add explanations to relay views diff --git a/src/app.tsx b/src/app.tsx index 9dae6c00a..9c8c0cc8d 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -69,6 +69,12 @@ import CommunityTrendingView from "./views/community/views/trending"; import RelaysView from "./views/relays"; import RelayView from "./views/relays/relay"; import BrowseRelaySetsView from "./views/relays/browse-sets"; +import CacheRelayView from "./views/relays/cache"; +import RelaySetView from "./views/relays/relay-set"; +import AppRelays from "./views/relays/app"; +import MailboxesView from "./views/relays/mailboxes"; +import NIP05RelaysView from "./views/relays/nip05"; +import ContactListRelaysView from "./views/relays/contact-list"; import UserDMsTab from "./views/user/dms"; import DMTimelineView from "./views/tools/dm-timeline"; import LoginNostrConnectView from "./views/signin/nostr-connect"; @@ -82,10 +88,6 @@ import LaunchpadView from "./views/launchpad"; import VideosView from "./views/videos"; import VideoDetailsView from "./views/videos/video"; import BookmarksView from "./views/bookmarks"; -import CacheRelayView from "./views/relays/cache"; -import RelaySetView from "./views/relays/relay-set"; -import AppRelays from "./views/relays/app"; -import MailboxesView from "./views/relays/mailboxes"; import LoginNostrAddressView from "./views/signin/address"; import LoginNostrAddressCreate from "./views/signin/address/create"; const TracksView = lazy(() => import("./views/tracks")); @@ -267,6 +269,8 @@ const router = createHashRouter([ { path: "app", element: }, { path: "cache", element: }, { path: "mailboxes", element: }, + { path: "nip05", element: }, + { path: "contacts", element: }, { path: "sets", element: }, { path: ":id", element: }, ], diff --git a/src/classes/relay-set.ts b/src/classes/relay-set.ts index 751b52b11..0f1d9914f 100644 --- a/src/classes/relay-set.ts +++ b/src/classes/relay-set.ts @@ -1,4 +1,6 @@ +import { relaysFromContactsEvent } from "../helpers/nostr/contacts"; import { getRelaysFromMailbox } from "../helpers/nostr/mailbox"; +import { safeJson } from "../helpers/parse"; import { safeRelayUrl } from "../helpers/relay"; import relayPoolService from "../services/relay-pool"; import { NostrEvent } from "../types/nostr-event"; @@ -39,4 +41,12 @@ export default class RelaySet extends Set { .map((r) => r.url), ); } + + static fromContactsEvent(contacts: NostrEvent, mode: RelayMode = RelayMode.ALL) { + return new RelaySet( + relaysFromContactsEvent(contacts) + .filter((r) => r.mode & mode) + .map((r) => r.url), + ); + } } diff --git a/src/helpers/nip07.ts b/src/helpers/nip07.ts new file mode 100644 index 000000000..c7f8aa0aa --- /dev/null +++ b/src/helpers/nip07.ts @@ -0,0 +1,19 @@ +import RelaySet from "../classes/relay-set"; +import { safeRelayUrls } from "./relay"; + +export async function getRelaysFromExt() { + if (!window.nostr) throw new Error("Missing extension"); + const read = new RelaySet(); + const write = new RelaySet(); + const extRelays = (await window.nostr.getRelays?.()) ?? []; + if (Array.isArray(extRelays)) { + const safeUrls = safeRelayUrls(extRelays); + read.merge(safeUrls); + write.merge(safeUrls); + } else { + read.merge(safeRelayUrls(Object.keys(extRelays).filter((url) => extRelays[url].read))); + write.merge(safeRelayUrls(Object.keys(extRelays).filter((url) => extRelays[url].write))); + } + + return { read, write }; +} diff --git a/src/helpers/nostr/contacts.ts b/src/helpers/nostr/contacts.ts new file mode 100644 index 000000000..aba924e1e --- /dev/null +++ b/src/helpers/nostr/contacts.ts @@ -0,0 +1,21 @@ +import { NostrEvent } from "nostr-tools"; +import { RelayMode } from "../../classes/relay"; +import { safeJson } from "../parse"; +import { safeRelayUrl } from "../relay"; + +type RelayJson = Record; +export function relaysFromContactsEvent(event: NostrEvent) { + const relayJson = safeJson(event.content, {}) as RelayJson; + + const relays: { url: string; mode: RelayMode }[] = []; + for (const [url, opts] of Object.entries(relayJson)) { + const safeUrl = safeRelayUrl(url); + if (!safeUrl) continue; + let mode = RelayMode.NONE; + if (opts.write) mode = mode | RelayMode.WRITE; + if (opts.read) mode = mode | RelayMode.READ; + if (mode === RelayMode.NONE) mode = RelayMode.ALL; + relays.push({ url: safeUrl, mode }); + } + return relays; +} diff --git a/src/hooks/use-user-contact-relays.ts b/src/hooks/use-user-contact-relays.ts new file mode 100644 index 000000000..a3d731139 --- /dev/null +++ b/src/hooks/use-user-contact-relays.ts @@ -0,0 +1,25 @@ +import { useMemo } from "react"; +import { RequestOptions } from "../services/replaceable-event-requester"; +import RelaySet from "../classes/relay-set"; +import useUserContactList from "./use-user-contact-list"; +import { RelayMode } from "../classes/relay"; +import { relaysFromContactsEvent } from "../helpers/nostr/contacts"; + +export default function useUserContactRelays( + pubkey?: string, + additionalRelays?: Iterable, + opts: RequestOptions = {}, +) { + const contacts = useUserContactList(pubkey, additionalRelays, opts); + + return useMemo(() => { + if (!contacts) return undefined; + if (contacts.content.length === 0) return null; + + const relays = relaysFromContactsEvent(contacts); + const inbox = new RelaySet(relays.filter((r) => r.mode & RelayMode.READ).map((r) => r.url)); + const outbox = new RelaySet(relays.filter((r) => r.mode & RelayMode.WRITE).map((r) => r.url)); + + return { inbox, outbox }; + }, [contacts]); +} diff --git a/src/hooks/use-user-dns-identity.ts b/src/hooks/use-user-dns-identity.ts new file mode 100644 index 000000000..fcbf90a9a --- /dev/null +++ b/src/hooks/use-user-dns-identity.ts @@ -0,0 +1,7 @@ +import { useDnsIdentity } from "./use-dns-identity"; +import { useUserMetadata } from "./use-user-metadata"; + +export function useUserDNSIdentity(pubkey?: string) { + const metadata = useUserMetadata(pubkey); + return useDnsIdentity(metadata?.nip05); +} diff --git a/src/providers/route/require-read-relays.tsx b/src/providers/route/require-read-relays.tsx index 655a330a2..41d6aa8c6 100644 --- a/src/providers/route/require-read-relays.tsx +++ b/src/providers/route/require-read-relays.tsx @@ -1,5 +1,6 @@ import { MouseEventHandler, PropsWithChildren, useCallback } from "react"; -import { Button, Card, CardBody, CardHeader, Flex, Heading } from "@chakra-ui/react"; +import { Box, Button, ButtonGroup, Card, CardBody, CardHeader, Flex, Heading, Text } from "@chakra-ui/react"; +import { Link as RouterLink } from "react-router-dom"; import { useReadRelays } from "../../hooks/use-client-relays"; import clientRelaysService, { recommendedReadRelays, recommendedWriteRelays } from "../../services/client-relays"; @@ -52,7 +53,14 @@ export default function RequireReadRelays({ children }: PropsWithChildren) { if (readRelays.size === 0 && !offline && !location.pathname.startsWith("/relays")) return ( - Looks like you don't have any relays setup + + + Setup App Relays + + + App Relays are stored locally and are used to fetch your timeline and other users notes + + @@ -63,7 +71,14 @@ export default function RequireReadRelays({ children }: PropsWithChildren) { clientRelaysService.addRelay(url, RelayMode.ALL)} w="full" /> - + + + + ); diff --git a/src/services/client-relays.ts b/src/services/client-relays.ts index 48c412851..a44fba0a5 100644 --- a/src/services/client-relays.ts +++ b/src/services/client-relays.ts @@ -16,7 +16,7 @@ export const recommendedReadRelays = new RelaySet( "wss://relay.snort.social/", "wss://nos.lol/", "wss://purplerelay.com/", - "wss://eden.nostr.land/", + "wss://nostr.land/", ]), ); export const recommendedWriteRelays = new RelaySet( @@ -63,6 +63,7 @@ class ClientRelayService { setRelaysFromRelaySet(event: NostrEvent) { this.writeRelays.next(RelaySet.fromNIP65Event(event, RelayMode.WRITE)); this.readRelays.next(RelaySet.fromNIP65Event(event, RelayMode.READ)); + this.saveRelays(); } saveRelays() { diff --git a/src/services/user-contacts.ts b/src/services/user-contacts.ts deleted file mode 100644 index 2b8c714a0..000000000 --- a/src/services/user-contacts.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { kinds } from "nostr-tools"; - -import { isPTag, NostrEvent } from "../types/nostr-event"; -import { safeJson } from "../helpers/parse"; -import SuperMap from "../classes/super-map"; -import Subject from "../classes/subject"; -import replaceableEventLoaderService, { RequestOptions } from "./replaceable-event-requester"; -import RelaySet from "../classes/relay-set"; - -export type UserContacts = { - pubkey: string; - relays: RelaySet; - inbox: RelaySet; - outbox: RelaySet; - contacts: string[]; - contactRelay: Record; - created_at: number; -}; - -type RelayJson = Record; -function relayJsonToMailboxes(relayJson: RelayJson) { - const relays = new RelaySet(); - const inbox = new RelaySet(); - const outbox = new RelaySet(); - for (const [url, opts] of Object.entries(relayJson)) { - relays.add(url); - if (opts.write) outbox.add(url); - if (opts.read) inbox.add(url); - } - return { relays, inbox, outbox }; -} - -function parseContacts(event: NostrEvent): UserContacts { - const relayJson = safeJson(event.content, {}) as RelayJson; - const { relays, inbox, outbox } = relayJsonToMailboxes(relayJson); - - const pubkeys = event.tags.filter(isPTag).map((tag) => tag[1]); - const contactRelay = event.tags.filter(isPTag).reduce( - (dir, tag) => { - if (tag[2]) { - dir[tag[1]] = tag[2]; - } - return dir; - }, - {} as Record, - ); - - return { - pubkey: event.pubkey, - relays, - inbox, - outbox, - contacts: pubkeys, - contactRelay, - created_at: event.created_at, - }; -} - -class UserContactsService { - private subjects = new SuperMap>(() => new Subject()); - getSubject(pubkey: string) { - return this.subjects.get(pubkey); - } - requestContacts(pubkey: string, relays: Iterable, opts?: RequestOptions) { - const sub = this.subjects.get(pubkey); - - const requestSub = replaceableEventLoaderService.requestEvent(relays, kinds.Contacts, pubkey, undefined, opts); - - sub.connectWithHandler(requestSub, (event, next) => next(parseContacts(event))); - - return sub; - } - - /** @deprecated */ - receiveEvent(event: NostrEvent) { - replaceableEventLoaderService.handleEvent(event); - } -} - -const userContactsService = new UserContactsService(); - -if (import.meta.env.DEV) { - // @ts-ignore - window.userContactsService = userContactsService; -} - -export default userContactsService; diff --git a/src/services/user-event-sync.ts b/src/services/user-event-sync.ts index 466af89c9..3debdfa36 100644 --- a/src/services/user-event-sync.ts +++ b/src/services/user-event-sync.ts @@ -1,10 +1,11 @@ +import { kinds } from "nostr-tools"; import { COMMON_CONTACT_RELAY } from "../const"; import { logger } from "../helpers/debug"; import accountService from "./account"; import clientRelaysService from "./client-relays"; import { offlineMode } from "./offline-mode"; +import replaceableEventLoaderService from "./replaceable-event-requester"; import userAppSettings from "./settings/user-app-settings"; -import userContactsService from "./user-contacts"; import userMailboxesService from "./user-mailboxes"; import userMetadataService from "./user-metadata"; @@ -14,9 +15,15 @@ function loadContactsList() { const account = accountService.current.value!; log("Loading contacts list"); - userContactsService.requestContacts(account.pubkey, [...clientRelaysService.readRelays.value, COMMON_CONTACT_RELAY], { - alwaysRequest: true, - }); + replaceableEventLoaderService.requestEvent( + [...clientRelaysService.readRelays.value, COMMON_CONTACT_RELAY], + kinds.Contacts, + account.pubkey, + undefined, + { + alwaysRequest: true, + }, + ); } function downloadEvents() { diff --git a/src/services/user-mailboxes.ts b/src/services/user-mailboxes.ts index 3e31becc0..61c99174d 100644 --- a/src/services/user-mailboxes.ts +++ b/src/services/user-mailboxes.ts @@ -3,10 +3,10 @@ import { kinds } from "nostr-tools"; import { NostrEvent } from "../types/nostr-event"; import SuperMap from "../classes/super-map"; import Subject from "../classes/subject"; -import userContactsService from "./user-contacts"; import replaceableEventLoaderService, { createCoordinate, RequestOptions } from "./replaceable-event-requester"; import RelaySet from "../classes/relay-set"; import { RelayMode } from "../classes/relay"; +import { relaysFromContactsEvent } from "../helpers/nostr/contacts"; export type UserMailboxes = { pubkey: string; @@ -39,17 +39,18 @@ class UserMailboxesService { sub.connectWithHandler(requestSub, (event, next) => next(nip65ToUserMailboxes(event))); // also fetch the relays from the users contacts - const contactsSub = userContactsService.requestContacts(pubkey, relays, opts); - sub.connectWithHandler(contactsSub, (contacts, next, value) => { + const contactsSub = replaceableEventLoaderService.requestEvent(relays, kinds.Contacts, pubkey, undefined, opts); + sub.connectWithHandler(contactsSub, (event, next, value) => { // NOTE: only use relays from contact list if the user dose not have a NIP-65 relay list - if (contacts.relays.size > 0 && !value) { + const relays = relaysFromContactsEvent(event); + if (relays.length > 0 && !value) { next({ - pubkey: contacts.pubkey, + pubkey: event.pubkey, event: null, - relays: contacts.relays, - inbox: contacts.inbox, - outbox: contacts.outbox, - created_at: contacts.created_at, + relays: RelaySet.fromContactsEvent(event), + inbox: RelaySet.fromContactsEvent(event, RelayMode.READ), + outbox: RelaySet.fromContactsEvent(event, RelayMode.WRITE), + created_at: event.created_at, }); } }); diff --git a/src/views/launchpad/index.tsx b/src/views/launchpad/index.tsx index a6b7caef7..6497def82 100644 --- a/src/views/launchpad/index.tsx +++ b/src/views/launchpad/index.tsx @@ -34,7 +34,6 @@ function LaunchpadPage() { diff --git a/src/views/relays/app/index.tsx b/src/views/relays/app/index.tsx index 0c6fb5c32..e7f1ad8b9 100644 --- a/src/views/relays/app/index.tsx +++ b/src/views/relays/app/index.tsx @@ -1,6 +1,6 @@ -import { useMemo } from "react"; +import { useCallback, useMemo } from "react"; -import { Button, Flex, Heading } from "@chakra-ui/react"; +import { Button, ButtonGroup, Flex, Heading, Text } from "@chakra-ui/react"; import useSubject from "../../../hooks/use-subject"; import { offlineMode } from "../../../services/offline-mode"; import WifiOff from "../../../components/icons/wifi-off"; @@ -14,12 +14,20 @@ import { useReadRelays, useWriteRelays } from "../../../hooks/use-client-relays" import useCurrentAccount from "../../../hooks/use-current-account"; import RelayControl from "./relay-control"; import SelectRelaySet from "./select-relay-set"; +import useUserMailboxes from "../../../hooks/use-user-mailboxes"; +import { getRelaysFromExt } from "../../../helpers/nip07"; +import { useUserDNSIdentity } from "../../../hooks/use-user-dns-identity"; +import useUserContactRelays from "../../../hooks/use-user-contact-relays"; +import { WarningIcon } from "@chakra-ui/icons"; export default function AppRelays() { const account = useCurrentAccount(); const readRelays = useReadRelays(); const writeRelays = useWriteRelays(); const offline = useSubject(offlineMode); + const { event: nip65 } = useUserMailboxes(account?.pubkey) ?? {}; + const nip05 = useUserDNSIdentity(account?.pubkey); + const contactRelays = useUserContactRelays(account?.pubkey); const sorted = useMemo(() => RelaySet.from(readRelays, writeRelays).urls.sort(), [readRelays, writeRelays]); @@ -27,7 +35,9 @@ export default function AppRelays() { - App Relays + + App Relays + + + These relays are stored locally and are used for everything in the app + + {sorted.map((url) => ( ))} @@ -47,6 +61,60 @@ export default function AppRelays() { }} /> + {writeRelays.size === 0 && ( + + There are write relays set, any note you create might not be saved + + )} + + + Import from: + + + {window.nostr && ( + + )} + {nip65 && ( + + )} + {nip05 && ( + + )} + {contactRelays && ( + + )} + {/* {account && ( <> diff --git a/src/views/relays/cache/index.tsx b/src/views/relays/cache/index.tsx index 6d9b1c63c..3cc885e01 100644 --- a/src/views/relays/cache/index.tsx +++ b/src/views/relays/cache/index.tsx @@ -100,6 +100,9 @@ export default function CacheRelayView() { Cache Relay + + The cache relay is used to cache event locally so they can be loaded quickly + {window.CACHE_RELAY_ENABLED && } diff --git a/src/views/relays/contact-list/index.tsx b/src/views/relays/contact-list/index.tsx new file mode 100644 index 000000000..077e7de12 --- /dev/null +++ b/src/views/relays/contact-list/index.tsx @@ -0,0 +1,114 @@ +import { Button, Code, Flex, Heading, Link, Spinner, Text } from "@chakra-ui/react"; +import BackButton from "../../../components/back-button"; +import useCurrentAccount from "../../../hooks/use-current-account"; +import { Link as RouterLink } from "react-router-dom"; + +import { RelayFavicon } from "../../../components/relay-favicon"; +import useUserContactRelays from "../../../hooks/use-user-contact-relays"; +import { CheckIcon, InboxIcon, OutboxIcon } from "../../../components/icons"; +import { useCallback, useState } from "react"; +import useCacheForm from "../../../hooks/use-cache-form"; +import useUserContactList from "../../../hooks/use-user-contact-list"; +import { cloneEvent } from "../../../helpers/nostr/events"; +import { EventTemplate } from "nostr-tools"; +import dayjs from "dayjs"; +import { usePublishEvent } from "../../../providers/global/publish-provider"; + +function RelayItem({ url }: { url: string }) { + return ( + + + + {url} + + + ); +} + +export default function ContactListRelaysView() { + const account = useCurrentAccount(); + const contacts = useUserContactList(account?.pubkey); + const relays = useUserContactRelays(account?.pubkey); + const publish = usePublishEvent(); + + const [loading, setLoading] = useState(false); + const clearRelays = useCallback(async () => { + if (!contacts) return; + if (confirm("Are you use you want to remove these relays? Other nostr apps might be effected") !== true) return; + + const draft: EventTemplate = { + kind: contacts.kind, + content: "", + tags: contacts.tags, + created_at: dayjs().unix(), + }; + + setLoading(true); + await publish("Clear Relays", draft); + setLoading(false); + }, [setLoading, contacts, publish]); + + if (relays === undefined) return ; + + return ( + + + + Contact List Relays + {relays && ( + + )} + + + + Some apps store relays in your contacts list (kind-3) +
+ noStrudel dose not use these relays, instead it uses your{" "} + + Mailbox Relays + +
+ + {relays === null ? ( + + You don't have any relays stored in your contact list + + ) : ( + <> + + Read Relays + + {relays.inbox.urls.map((relay) => ( + + + + {relay} + + + ))} + + + Write Relays + + {relays.outbox.urls.map((relay) => ( + + + + {relay} + + + ))} + + )} +
+ ); +} diff --git a/src/views/relays/index.tsx b/src/views/relays/index.tsx index 8f5d3eacc..5451e0df5 100644 --- a/src/views/relays/index.tsx +++ b/src/views/relays/index.tsx @@ -8,13 +8,18 @@ import { getListName } from "../../helpers/nostr/lists"; import { getEventCoordinate } from "../../helpers/nostr/events"; import { useBreakpointValue } from "../../providers/global/breakpoint-provider"; import Database01 from "../../components/icons/database-01"; -import { RelayIcon } from "../../components/icons"; +import { AtIcon, RelayIcon } from "../../components/icons"; import Mail02 from "../../components/icons/mail-02"; +import { useUserDNSIdentity } from "../../hooks/use-user-dns-identity"; +import useUserContactRelays from "../../hooks/use-user-contact-relays"; +import UserSquare from "../../components/icons/user-square"; export default function RelaysView() { const account = useCurrentAccount(); const relaySets = useUserRelaySets(account?.pubkey, undefined); const vertical = useBreakpointValue({ base: true, lg: false }); + const nip05 = useUserDNSIdentity(account?.pubkey); + const kind3Relays = useUserContactRelays(account?.pubkey); const location = useLocation(); @@ -54,6 +59,26 @@ export default function RelaysView() { Mailboxes )} + {nip05 && ( + + )} + {/* {account && ( <> diff --git a/src/views/relays/mailboxes/index.tsx b/src/views/relays/mailboxes/index.tsx index aa185c6c4..e788f527a 100644 --- a/src/views/relays/mailboxes/index.tsx +++ b/src/views/relays/mailboxes/index.tsx @@ -17,7 +17,7 @@ import VerticalPageLayout from "../../../components/vertical-page-layout"; import RequireCurrentAccount from "../../../providers/route/require-current-account"; import useUserMailboxes from "../../../hooks/use-user-mailboxes"; import useCurrentAccount from "../../../hooks/use-current-account"; -import { InboxIcon } from "../../../components/icons"; +import { InboxIcon, OutboxIcon } from "../../../components/icons"; import { RelayUrlInput } from "../../../components/relay-url-input"; import { RelayFavicon } from "../../../components/relay-favicon"; import { RelayMode } from "../../../classes/relay"; @@ -30,6 +30,7 @@ import { safeRelayUrl } from "../../../helpers/relay"; import { usePublishEvent } from "../../../providers/global/publish-provider"; import { COMMON_CONTACT_RELAY } from "../../../const"; import BackButton from "../../../components/back-button"; +import AddRelayForm from "../app/add-relay-form"; function RelayLine({ relay, mode, list }: { relay: string; mode: RelayMode; list?: NostrEvent }) { const publish = usePublishEvent(); @@ -57,30 +58,6 @@ function RelayLine({ relay, mode, list }: { relay: string; mode: RelayMode; list ); } -function AddRelayForm({ onSubmit }: { onSubmit: (url: string) => void }) { - const { register, handleSubmit, reset } = useForm({ - defaultValues: { - url: "", - }, - }); - - const submit = handleSubmit(async (values) => { - const url = safeRelayUrl(values.url); - if (!url) return; - await onSubmit(url); - reset(); - }); - - return ( - - - - - ); -} - function MailboxesPage() { const account = useCurrentAccount()!; const publish = usePublishEvent(); @@ -95,41 +72,46 @@ function MailboxesPage() { ); return ( - + Mailboxes - - - - Inbox - - - Other users will send DMs and notes to these relays to notify you - {inbox?.urls - .sort() - .map((url) => )} - - - addRelay(r, RelayMode.READ)} /> - - - - - - Outbox - - - Always publish to these relays so your followers can find your notes - {outbox?.urls - .sort() - .map((url) => )} - - - addRelay(r, RelayMode.WRITE)} /> - - + + Mailbox relays are a way for other users to find your events, or send you events. they are defined in{" "} + + NIP-65 + + + + + + Inbox + + + These relays are used by other users to send DMs and notes to you + + {inbox?.urls + .sort() + .map((url) => )} + addRelay(r, RelayMode.READ)} /> + + + + Outbox + + + noStrudel will always publish to these relays so other users can find your notes + + {outbox?.urls + .sort() + .map((url) => )} + addRelay(r, RelayMode.WRITE)} /> ); } diff --git a/src/views/relays/nip05/index.tsx b/src/views/relays/nip05/index.tsx new file mode 100644 index 000000000..aee931a5f --- /dev/null +++ b/src/views/relays/nip05/index.tsx @@ -0,0 +1,48 @@ +import { Code, Flex, Heading, Link, Text } from "@chakra-ui/react"; +import BackButton from "../../../components/back-button"; +import useCurrentAccount from "../../../hooks/use-current-account"; +import { useUserDNSIdentity } from "../../../hooks/use-user-dns-identity"; +import { Link as RouterLink } from "react-router-dom"; + +import { RelayFavicon } from "../../../components/relay-favicon"; + +function RelayItem({ url }: { url: string }) { + return ( + + + + {url} + + + ); +} + +export default function NIP05RelaysView() { + const account = useCurrentAccount(); + const nip05 = useUserDNSIdentity(account?.pubkey); + + return ( + + + + NIP-05 Relays + + + + These relays cant be modified by noStrudel, they must be set manually on your{" "} + /.well-known/nostr.json file or by your identity provider +
+ + Read more about nip-05 + +
+ + {nip05?.relays.map((url) => )} +
+ ); +} diff --git a/src/views/signin/start.tsx b/src/views/signin/start.tsx index a57ba14f8..4ebaa9cd8 100644 --- a/src/views/signin/start.tsx +++ b/src/views/signin/start.tsx @@ -25,6 +25,8 @@ import accountService from "../../services/account"; import serialPortService from "../../services/serial-port"; import amberSignerService from "../../services/amber-signer"; import { AtIcon } from "../../components/icons"; +import { getRelaysFromExt } from "../../helpers/nip07"; +import { safeRelayUrls } from "../../helpers/relay"; export default function LoginStartView() { const location = useLocation(); @@ -40,16 +42,15 @@ export default function LoginStartView() { const pubkey = await window.nostr.getPublicKey(); if (!accountService.hasAccount(pubkey)) { - let relays: string[] = []; - const extRelays = (await window.nostr.getRelays?.()) ?? []; - if (Array.isArray(extRelays)) { - relays = extRelays; - } else { - relays = Object.keys(extRelays).filter((url) => extRelays[url].read); - } + let relays = (await getRelaysFromExt()).read.urls; if (relays.length === 0) { - relays = ["wss://relay.damus.io", "wss://relay.snort.social", "wss://nostr.wine", COMMON_CONTACT_RELAY]; + relays = safeRelayUrls([ + "wss://relay.damus.io/", + "wss://relay.snort.social/", + "wss://nostr.wine/", + COMMON_CONTACT_RELAY, + ]); } accountService.addAccount({ pubkey, relays, type: "extension", readonly: false }); From 1c8f68f66fc50d0db7e78d82346119e6cbee21b5 Mon Sep 17 00:00:00 2001 From: hzrd149 Date: Mon, 5 Feb 2024 12:01:13 +0000 Subject: [PATCH 2/2] allow users to type the letter n --- src/components/keyboard-shortcut.tsx | 4 +++- src/views/launchpad/index.tsx | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/keyboard-shortcut.tsx b/src/components/keyboard-shortcut.tsx index 269380652..22445a35c 100644 --- a/src/components/keyboard-shortcut.tsx +++ b/src/components/keyboard-shortcut.tsx @@ -17,7 +17,9 @@ export default function KeyboardShortcut({ (e) => (requireMeta ? e.ctrlKey || e.metaKey : true) && e.key === letter, (e) => { // ignore if the user is focused on an input - if (document.activeElement instanceof HTMLInputElement) return; + if (document.activeElement instanceof HTMLInputElement || document.activeElement instanceof HTMLTextAreaElement) { + return; + } if (onPress) { e.preventDefault(); diff --git a/src/views/launchpad/index.tsx b/src/views/launchpad/index.tsx index 6497def82..0b942fe05 100644 --- a/src/views/launchpad/index.tsx +++ b/src/views/launchpad/index.tsx @@ -34,6 +34,7 @@ function LaunchpadPage() {