mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-06-14 10:51:25 +02:00
Add explanations to relay views
This commit is contained in:
parent
a38710e630
commit
ad6e51ed98
5
.changeset/calm-impalas-battle.md
Normal file
5
.changeset/calm-impalas-battle.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"nostrudel": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add explanations to relay views
|
12
src/app.tsx
12
src/app.tsx
@ -69,6 +69,12 @@ import CommunityTrendingView from "./views/community/views/trending";
|
|||||||
import RelaysView from "./views/relays";
|
import RelaysView from "./views/relays";
|
||||||
import RelayView from "./views/relays/relay";
|
import RelayView from "./views/relays/relay";
|
||||||
import BrowseRelaySetsView from "./views/relays/browse-sets";
|
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 UserDMsTab from "./views/user/dms";
|
||||||
import DMTimelineView from "./views/tools/dm-timeline";
|
import DMTimelineView from "./views/tools/dm-timeline";
|
||||||
import LoginNostrConnectView from "./views/signin/nostr-connect";
|
import LoginNostrConnectView from "./views/signin/nostr-connect";
|
||||||
@ -82,10 +88,6 @@ import LaunchpadView from "./views/launchpad";
|
|||||||
import VideosView from "./views/videos";
|
import VideosView from "./views/videos";
|
||||||
import VideoDetailsView from "./views/videos/video";
|
import VideoDetailsView from "./views/videos/video";
|
||||||
import BookmarksView from "./views/bookmarks";
|
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 LoginNostrAddressView from "./views/signin/address";
|
||||||
import LoginNostrAddressCreate from "./views/signin/address/create";
|
import LoginNostrAddressCreate from "./views/signin/address/create";
|
||||||
const TracksView = lazy(() => import("./views/tracks"));
|
const TracksView = lazy(() => import("./views/tracks"));
|
||||||
@ -267,6 +269,8 @@ const router = createHashRouter([
|
|||||||
{ path: "app", element: <AppRelays /> },
|
{ path: "app", element: <AppRelays /> },
|
||||||
{ path: "cache", element: <CacheRelayView /> },
|
{ path: "cache", element: <CacheRelayView /> },
|
||||||
{ path: "mailboxes", element: <MailboxesView /> },
|
{ path: "mailboxes", element: <MailboxesView /> },
|
||||||
|
{ path: "nip05", element: <NIP05RelaysView /> },
|
||||||
|
{ path: "contacts", element: <ContactListRelaysView /> },
|
||||||
{ path: "sets", element: <BrowseRelaySetsView /> },
|
{ path: "sets", element: <BrowseRelaySetsView /> },
|
||||||
{ path: ":id", element: <RelaySetView /> },
|
{ path: ":id", element: <RelaySetView /> },
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
import { relaysFromContactsEvent } from "../helpers/nostr/contacts";
|
||||||
import { getRelaysFromMailbox } from "../helpers/nostr/mailbox";
|
import { getRelaysFromMailbox } from "../helpers/nostr/mailbox";
|
||||||
|
import { safeJson } from "../helpers/parse";
|
||||||
import { safeRelayUrl } from "../helpers/relay";
|
import { safeRelayUrl } from "../helpers/relay";
|
||||||
import relayPoolService from "../services/relay-pool";
|
import relayPoolService from "../services/relay-pool";
|
||||||
import { NostrEvent } from "../types/nostr-event";
|
import { NostrEvent } from "../types/nostr-event";
|
||||||
@ -39,4 +41,12 @@ export default class RelaySet extends Set<string> {
|
|||||||
.map((r) => r.url),
|
.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),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
19
src/helpers/nip07.ts
Normal file
19
src/helpers/nip07.ts
Normal file
@ -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 };
|
||||||
|
}
|
21
src/helpers/nostr/contacts.ts
Normal file
21
src/helpers/nostr/contacts.ts
Normal file
@ -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<string, { read: boolean; write: boolean }>;
|
||||||
|
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;
|
||||||
|
}
|
25
src/hooks/use-user-contact-relays.ts
Normal file
25
src/hooks/use-user-contact-relays.ts
Normal file
@ -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<string>,
|
||||||
|
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]);
|
||||||
|
}
|
7
src/hooks/use-user-dns-identity.ts
Normal file
7
src/hooks/use-user-dns-identity.ts
Normal file
@ -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);
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import { MouseEventHandler, PropsWithChildren, useCallback } from "react";
|
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 { useReadRelays } from "../../hooks/use-client-relays";
|
||||||
import clientRelaysService, { recommendedReadRelays, recommendedWriteRelays } from "../../services/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"))
|
if (readRelays.size === 0 && !offline && !location.pathname.startsWith("/relays"))
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" maxW="md" mx="auto" h="full" alignItems="center" justifyContent="center" gap="4">
|
<Flex direction="column" maxW="md" mx="auto" h="full" alignItems="center" justifyContent="center" gap="4">
|
||||||
<Heading size="md">Looks like you don't have any relays setup</Heading>
|
<Box w="full">
|
||||||
|
<Heading size="md" textAlign="center">
|
||||||
|
Setup App Relays
|
||||||
|
</Heading>
|
||||||
|
<Text fontStyle="italic">
|
||||||
|
App Relays are stored locally and are used to fetch your timeline and other users notes
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
<RelaySetCard label="Recommended Relays" read={recommendedReadRelays} write={recommendedWriteRelays} />
|
<RelaySetCard label="Recommended Relays" read={recommendedReadRelays} write={recommendedWriteRelays} />
|
||||||
<RelaySetCard label="Japanese relays" read={JapaneseRelays} write={JapaneseRelays} />
|
<RelaySetCard label="Japanese relays" read={JapaneseRelays} write={JapaneseRelays} />
|
||||||
<Card w="full" variant="outline">
|
<Card w="full" variant="outline">
|
||||||
@ -63,7 +71,14 @@ export default function RequireReadRelays({ children }: PropsWithChildren) {
|
|||||||
<AddRelayForm onSubmit={(url) => clientRelaysService.addRelay(url, RelayMode.ALL)} w="full" />
|
<AddRelayForm onSubmit={(url) => clientRelaysService.addRelay(url, RelayMode.ALL)} w="full" />
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
<Button onClick={() => offlineMode.next(true)}>Offline mode</Button>
|
<ButtonGroup>
|
||||||
|
<Button as={RouterLink} to="/relays/app" variant="outline" colorScheme="primary">
|
||||||
|
Custom Relays
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => offlineMode.next(true)} variant="outline">
|
||||||
|
Offline mode
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ export const recommendedReadRelays = new RelaySet(
|
|||||||
"wss://relay.snort.social/",
|
"wss://relay.snort.social/",
|
||||||
"wss://nos.lol/",
|
"wss://nos.lol/",
|
||||||
"wss://purplerelay.com/",
|
"wss://purplerelay.com/",
|
||||||
"wss://eden.nostr.land/",
|
"wss://nostr.land/",
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
export const recommendedWriteRelays = new RelaySet(
|
export const recommendedWriteRelays = new RelaySet(
|
||||||
@ -63,6 +63,7 @@ class ClientRelayService {
|
|||||||
setRelaysFromRelaySet(event: NostrEvent) {
|
setRelaysFromRelaySet(event: NostrEvent) {
|
||||||
this.writeRelays.next(RelaySet.fromNIP65Event(event, RelayMode.WRITE));
|
this.writeRelays.next(RelaySet.fromNIP65Event(event, RelayMode.WRITE));
|
||||||
this.readRelays.next(RelaySet.fromNIP65Event(event, RelayMode.READ));
|
this.readRelays.next(RelaySet.fromNIP65Event(event, RelayMode.READ));
|
||||||
|
this.saveRelays();
|
||||||
}
|
}
|
||||||
|
|
||||||
saveRelays() {
|
saveRelays() {
|
||||||
|
@ -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<string, string | undefined>;
|
|
||||||
created_at: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
type RelayJson = Record<string, { read: boolean; write: boolean }>;
|
|
||||||
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<string, string>,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
pubkey: event.pubkey,
|
|
||||||
relays,
|
|
||||||
inbox,
|
|
||||||
outbox,
|
|
||||||
contacts: pubkeys,
|
|
||||||
contactRelay,
|
|
||||||
created_at: event.created_at,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class UserContactsService {
|
|
||||||
private subjects = new SuperMap<string, Subject<UserContacts>>(() => new Subject<UserContacts>());
|
|
||||||
getSubject(pubkey: string) {
|
|
||||||
return this.subjects.get(pubkey);
|
|
||||||
}
|
|
||||||
requestContacts(pubkey: string, relays: Iterable<string>, 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;
|
|
@ -1,10 +1,11 @@
|
|||||||
|
import { kinds } from "nostr-tools";
|
||||||
import { COMMON_CONTACT_RELAY } from "../const";
|
import { COMMON_CONTACT_RELAY } from "../const";
|
||||||
import { logger } from "../helpers/debug";
|
import { logger } from "../helpers/debug";
|
||||||
import accountService from "./account";
|
import accountService from "./account";
|
||||||
import clientRelaysService from "./client-relays";
|
import clientRelaysService from "./client-relays";
|
||||||
import { offlineMode } from "./offline-mode";
|
import { offlineMode } from "./offline-mode";
|
||||||
|
import replaceableEventLoaderService from "./replaceable-event-requester";
|
||||||
import userAppSettings from "./settings/user-app-settings";
|
import userAppSettings from "./settings/user-app-settings";
|
||||||
import userContactsService from "./user-contacts";
|
|
||||||
import userMailboxesService from "./user-mailboxes";
|
import userMailboxesService from "./user-mailboxes";
|
||||||
import userMetadataService from "./user-metadata";
|
import userMetadataService from "./user-metadata";
|
||||||
|
|
||||||
@ -14,9 +15,15 @@ function loadContactsList() {
|
|||||||
const account = accountService.current.value!;
|
const account = accountService.current.value!;
|
||||||
|
|
||||||
log("Loading contacts list");
|
log("Loading contacts list");
|
||||||
userContactsService.requestContacts(account.pubkey, [...clientRelaysService.readRelays.value, COMMON_CONTACT_RELAY], {
|
replaceableEventLoaderService.requestEvent(
|
||||||
alwaysRequest: true,
|
[...clientRelaysService.readRelays.value, COMMON_CONTACT_RELAY],
|
||||||
});
|
kinds.Contacts,
|
||||||
|
account.pubkey,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
alwaysRequest: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadEvents() {
|
function downloadEvents() {
|
||||||
|
@ -3,10 +3,10 @@ import { kinds } from "nostr-tools";
|
|||||||
import { NostrEvent } from "../types/nostr-event";
|
import { NostrEvent } from "../types/nostr-event";
|
||||||
import SuperMap from "../classes/super-map";
|
import SuperMap from "../classes/super-map";
|
||||||
import Subject from "../classes/subject";
|
import Subject from "../classes/subject";
|
||||||
import userContactsService from "./user-contacts";
|
|
||||||
import replaceableEventLoaderService, { createCoordinate, RequestOptions } from "./replaceable-event-requester";
|
import replaceableEventLoaderService, { createCoordinate, RequestOptions } from "./replaceable-event-requester";
|
||||||
import RelaySet from "../classes/relay-set";
|
import RelaySet from "../classes/relay-set";
|
||||||
import { RelayMode } from "../classes/relay";
|
import { RelayMode } from "../classes/relay";
|
||||||
|
import { relaysFromContactsEvent } from "../helpers/nostr/contacts";
|
||||||
|
|
||||||
export type UserMailboxes = {
|
export type UserMailboxes = {
|
||||||
pubkey: string;
|
pubkey: string;
|
||||||
@ -39,17 +39,18 @@ class UserMailboxesService {
|
|||||||
sub.connectWithHandler(requestSub, (event, next) => next(nip65ToUserMailboxes(event)));
|
sub.connectWithHandler(requestSub, (event, next) => next(nip65ToUserMailboxes(event)));
|
||||||
|
|
||||||
// also fetch the relays from the users contacts
|
// also fetch the relays from the users contacts
|
||||||
const contactsSub = userContactsService.requestContacts(pubkey, relays, opts);
|
const contactsSub = replaceableEventLoaderService.requestEvent(relays, kinds.Contacts, pubkey, undefined, opts);
|
||||||
sub.connectWithHandler(contactsSub, (contacts, next, value) => {
|
sub.connectWithHandler(contactsSub, (event, next, value) => {
|
||||||
// NOTE: only use relays from contact list if the user dose not have a NIP-65 relay list
|
// 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({
|
next({
|
||||||
pubkey: contacts.pubkey,
|
pubkey: event.pubkey,
|
||||||
event: null,
|
event: null,
|
||||||
relays: contacts.relays,
|
relays: RelaySet.fromContactsEvent(event),
|
||||||
inbox: contacts.inbox,
|
inbox: RelaySet.fromContactsEvent(event, RelayMode.READ),
|
||||||
outbox: contacts.outbox,
|
outbox: RelaySet.fromContactsEvent(event, RelayMode.WRITE),
|
||||||
created_at: contacts.created_at,
|
created_at: event.created_at,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -34,7 +34,6 @@ function LaunchpadPage() {
|
|||||||
<Flex gap="4" w="full">
|
<Flex gap="4" w="full">
|
||||||
<Button colorScheme="primary" size="lg" onClick={() => openModal()} variant="outline">
|
<Button colorScheme="primary" size="lg" onClick={() => openModal()} variant="outline">
|
||||||
New Note
|
New Note
|
||||||
<KeyboardShortcut letter="n" ml="auto" onPress={(e) => openModal()} />
|
|
||||||
</Button>
|
</Button>
|
||||||
<SearchForm flex={1} />
|
<SearchForm flex={1} />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -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 useSubject from "../../../hooks/use-subject";
|
||||||
import { offlineMode } from "../../../services/offline-mode";
|
import { offlineMode } from "../../../services/offline-mode";
|
||||||
import WifiOff from "../../../components/icons/wifi-off";
|
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 useCurrentAccount from "../../../hooks/use-current-account";
|
||||||
import RelayControl from "./relay-control";
|
import RelayControl from "./relay-control";
|
||||||
import SelectRelaySet from "./select-relay-set";
|
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() {
|
export default function AppRelays() {
|
||||||
const account = useCurrentAccount();
|
const account = useCurrentAccount();
|
||||||
const readRelays = useReadRelays();
|
const readRelays = useReadRelays();
|
||||||
const writeRelays = useWriteRelays();
|
const writeRelays = useWriteRelays();
|
||||||
const offline = useSubject(offlineMode);
|
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]);
|
const sorted = useMemo(() => RelaySet.from(readRelays, writeRelays).urls.sort(), [readRelays, writeRelays]);
|
||||||
|
|
||||||
@ -27,7 +35,9 @@ export default function AppRelays() {
|
|||||||
<Flex gap="2" direction="column" overflow="auto hidden" flex={1}>
|
<Flex gap="2" direction="column" overflow="auto hidden" flex={1}>
|
||||||
<Flex gap="2" alignItems="center">
|
<Flex gap="2" alignItems="center">
|
||||||
<BackButton hideFrom="lg" size="sm" />
|
<BackButton hideFrom="lg" size="sm" />
|
||||||
<Heading size="lg">App Relays</Heading>
|
<Heading size="lg" px={{ base: 0, lg: "2" }}>
|
||||||
|
App Relays
|
||||||
|
</Heading>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => offlineMode.next(!offline)}
|
onClick={() => offlineMode.next(!offline)}
|
||||||
leftIcon={offline ? <WifiOff /> : <Wifi />}
|
leftIcon={offline ? <WifiOff /> : <Wifi />}
|
||||||
@ -38,6 +48,10 @@ export default function AppRelays() {
|
|||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
<Text fontStyle="italic" px="2" mt="-2">
|
||||||
|
These relays are stored locally and are used for everything in the app
|
||||||
|
</Text>
|
||||||
|
|
||||||
{sorted.map((url) => (
|
{sorted.map((url) => (
|
||||||
<RelayControl key={url} url={url} />
|
<RelayControl key={url} url={url} />
|
||||||
))}
|
))}
|
||||||
@ -47,6 +61,60 @@ export default function AppRelays() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{writeRelays.size === 0 && (
|
||||||
|
<Text color="yellow.500">
|
||||||
|
<WarningIcon /> There are write relays set, any note you create might not be saved
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Heading size="md" mt="2">
|
||||||
|
Import from:
|
||||||
|
</Heading>
|
||||||
|
<Flex wrap="wrap" gap="2">
|
||||||
|
{window.nostr && (
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
const { read, write } = await getRelaysFromExt();
|
||||||
|
clientRelaysService.readRelays.next(read);
|
||||||
|
clientRelaysService.writeRelays.next(write);
|
||||||
|
clientRelaysService.saveRelays();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Extension
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{nip65 && (
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
clientRelaysService.setRelaysFromRelaySet(nip65);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
NIP-65 (Mailboxes)
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{nip05 && (
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
clientRelaysService.readRelays.next(RelaySet.from(nip05.relays));
|
||||||
|
clientRelaysService.writeRelays.next(RelaySet.from(nip05.relays));
|
||||||
|
clientRelaysService.saveRelays();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
NIP-05
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{contactRelays && (
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
clientRelaysService.readRelays.next(contactRelays.inbox);
|
||||||
|
clientRelaysService.writeRelays.next(contactRelays.outbox);
|
||||||
|
clientRelaysService.saveRelays();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Contact List (Legacy)
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
{/* {account && (
|
{/* {account && (
|
||||||
<>
|
<>
|
||||||
<Heading size="md" mt="2">
|
<Heading size="md" mt="2">
|
||||||
|
3
src/views/relays/cache/index.tsx
vendored
3
src/views/relays/cache/index.tsx
vendored
@ -100,6 +100,9 @@ export default function CacheRelayView() {
|
|||||||
<BackButton hideFrom="lg" size="sm" />
|
<BackButton hideFrom="lg" size="sm" />
|
||||||
<Heading size="lg">Cache Relay</Heading>
|
<Heading size="lg">Cache Relay</Heading>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
<Text fontStyle="italic" mt="-2" px={{ base: "2", lg: 0 }}>
|
||||||
|
The cache relay is used to cache event locally so they can be loaded quickly
|
||||||
|
</Text>
|
||||||
<InternalRelay />
|
<InternalRelay />
|
||||||
<NostrRelayTray />
|
<NostrRelayTray />
|
||||||
{window.CACHE_RELAY_ENABLED && <HostedRelay />}
|
{window.CACHE_RELAY_ENABLED && <HostedRelay />}
|
||||||
|
114
src/views/relays/contact-list/index.tsx
Normal file
114
src/views/relays/contact-list/index.tsx
Normal file
@ -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 (
|
||||||
|
<Flex gap="2" alignItems="center">
|
||||||
|
<RelayFavicon relay={url} size="sm" />
|
||||||
|
<Link as={RouterLink} to={`/r/${encodeURIComponent(url)}`} isTruncated>
|
||||||
|
{url}
|
||||||
|
</Link>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 <Spinner />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex gap="2" direction="column" overflow="auto hidden" flex={1} px={{ base: "2", lg: 0 }}>
|
||||||
|
<Flex gap="2" alignItems="center">
|
||||||
|
<BackButton hideFrom="lg" size="sm" />
|
||||||
|
<Heading size="lg">Contact List Relays</Heading>
|
||||||
|
{relays && (
|
||||||
|
<Button
|
||||||
|
colorScheme="red"
|
||||||
|
onClick={clearRelays}
|
||||||
|
isLoading={loading}
|
||||||
|
ml="auto"
|
||||||
|
size="sm"
|
||||||
|
isDisabled={account?.readonly}
|
||||||
|
>
|
||||||
|
Clear Relays
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Text fontStyle="italic" mt="-2">
|
||||||
|
Some apps store relays in your contacts list (kind-3)
|
||||||
|
<br />
|
||||||
|
noStrudel dose not use these relays, instead it uses your{" "}
|
||||||
|
<Link as={RouterLink} to="/relays/mailboxes" color="blue.500">
|
||||||
|
Mailbox Relays
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{relays === null ? (
|
||||||
|
<Text color="green.500" fontSize="lg" mt="4">
|
||||||
|
<CheckIcon /> You don't have any relays stored in your contact list
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Heading size="md" mt="2">
|
||||||
|
Read Relays
|
||||||
|
</Heading>
|
||||||
|
{relays.inbox.urls.map((relay) => (
|
||||||
|
<Flex key={relay} gap="2" alignItems="center" overflow="hidden">
|
||||||
|
<RelayFavicon relay={relay} size="xs" />
|
||||||
|
<Link as={RouterLink} to={`/r/${encodeURIComponent(relay)}`} isTruncated>
|
||||||
|
{relay}
|
||||||
|
</Link>
|
||||||
|
</Flex>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<Heading size="md" mt="2">
|
||||||
|
Write Relays
|
||||||
|
</Heading>
|
||||||
|
{relays.outbox.urls.map((relay) => (
|
||||||
|
<Flex key={relay} gap="2" alignItems="center" overflow="hidden">
|
||||||
|
<RelayFavicon relay={relay} size="xs" />
|
||||||
|
<Link as={RouterLink} to={`/r/${encodeURIComponent(relay)}`} isTruncated>
|
||||||
|
{relay}
|
||||||
|
</Link>
|
||||||
|
</Flex>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
@ -8,13 +8,18 @@ import { getListName } from "../../helpers/nostr/lists";
|
|||||||
import { getEventCoordinate } from "../../helpers/nostr/events";
|
import { getEventCoordinate } from "../../helpers/nostr/events";
|
||||||
import { useBreakpointValue } from "../../providers/global/breakpoint-provider";
|
import { useBreakpointValue } from "../../providers/global/breakpoint-provider";
|
||||||
import Database01 from "../../components/icons/database-01";
|
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 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() {
|
export default function RelaysView() {
|
||||||
const account = useCurrentAccount();
|
const account = useCurrentAccount();
|
||||||
const relaySets = useUserRelaySets(account?.pubkey, undefined);
|
const relaySets = useUserRelaySets(account?.pubkey, undefined);
|
||||||
const vertical = useBreakpointValue({ base: true, lg: false });
|
const vertical = useBreakpointValue({ base: true, lg: false });
|
||||||
|
const nip05 = useUserDNSIdentity(account?.pubkey);
|
||||||
|
const kind3Relays = useUserContactRelays(account?.pubkey);
|
||||||
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
@ -54,6 +59,26 @@ export default function RelaysView() {
|
|||||||
Mailboxes
|
Mailboxes
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
{nip05 && (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
as={RouterLink}
|
||||||
|
to="/relays/nip05"
|
||||||
|
leftIcon={<AtIcon boxSize={6} />}
|
||||||
|
colorScheme={location.pathname === "/relays/nip05" ? "primary" : undefined}
|
||||||
|
>
|
||||||
|
NIP-05 Relays
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
as={RouterLink}
|
||||||
|
to="/relays/contacts"
|
||||||
|
leftIcon={<UserSquare boxSize={6} />}
|
||||||
|
colorScheme={location.pathname === "/relays/contacts" ? "primary" : undefined}
|
||||||
|
>
|
||||||
|
Contact List Relays
|
||||||
|
</Button>
|
||||||
{/* {account && (
|
{/* {account && (
|
||||||
<>
|
<>
|
||||||
<Heading size="sm" mt="2">
|
<Heading size="sm" mt="2">
|
||||||
|
@ -17,7 +17,7 @@ import VerticalPageLayout from "../../../components/vertical-page-layout";
|
|||||||
import RequireCurrentAccount from "../../../providers/route/require-current-account";
|
import RequireCurrentAccount from "../../../providers/route/require-current-account";
|
||||||
import useUserMailboxes from "../../../hooks/use-user-mailboxes";
|
import useUserMailboxes from "../../../hooks/use-user-mailboxes";
|
||||||
import useCurrentAccount from "../../../hooks/use-current-account";
|
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 { RelayUrlInput } from "../../../components/relay-url-input";
|
||||||
import { RelayFavicon } from "../../../components/relay-favicon";
|
import { RelayFavicon } from "../../../components/relay-favicon";
|
||||||
import { RelayMode } from "../../../classes/relay";
|
import { RelayMode } from "../../../classes/relay";
|
||||||
@ -30,6 +30,7 @@ import { safeRelayUrl } from "../../../helpers/relay";
|
|||||||
import { usePublishEvent } from "../../../providers/global/publish-provider";
|
import { usePublishEvent } from "../../../providers/global/publish-provider";
|
||||||
import { COMMON_CONTACT_RELAY } from "../../../const";
|
import { COMMON_CONTACT_RELAY } from "../../../const";
|
||||||
import BackButton from "../../../components/back-button";
|
import BackButton from "../../../components/back-button";
|
||||||
|
import AddRelayForm from "../app/add-relay-form";
|
||||||
|
|
||||||
function RelayLine({ relay, mode, list }: { relay: string; mode: RelayMode; list?: NostrEvent }) {
|
function RelayLine({ relay, mode, list }: { relay: string; mode: RelayMode; list?: NostrEvent }) {
|
||||||
const publish = usePublishEvent();
|
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 (
|
|
||||||
<Flex as="form" display="flex" gap="2" onSubmit={submit} flex={1}>
|
|
||||||
<RelayUrlInput {...register("url")} placeholder="wss://relay.example.com" size="sm" borderRadius="md" />
|
|
||||||
<Button type="submit" colorScheme="primary" size="sm">
|
|
||||||
Add
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function MailboxesPage() {
|
function MailboxesPage() {
|
||||||
const account = useCurrentAccount()!;
|
const account = useCurrentAccount()!;
|
||||||
const publish = usePublishEvent();
|
const publish = usePublishEvent();
|
||||||
@ -95,41 +72,46 @@ function MailboxesPage() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" gap="2">
|
<Flex gap="2" direction="column" overflow="auto hidden" flex={1} px="2">
|
||||||
<Flex gap="2" alignItems="center">
|
<Flex gap="2" alignItems="center">
|
||||||
<BackButton hideFrom="lg" size="sm" />
|
<BackButton hideFrom="lg" size="sm" />
|
||||||
<Heading size="lg">Mailboxes</Heading>
|
<Heading size="lg">Mailboxes</Heading>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Card maxW="lg">
|
<Text fontStyle="italic" mt="-2">
|
||||||
<CardHeader p="4" pb="2" display="flex" gap="2" alignItems="center">
|
Mailbox relays are a way for other users to find your events, or send you events. they are defined in{" "}
|
||||||
<InboxIcon boxSize={5} />
|
<Link
|
||||||
<Heading size="md">Inbox</Heading>
|
color="blue.500"
|
||||||
</CardHeader>
|
isExternal
|
||||||
<CardBody px="4" py="0" display="flex" flexDirection="column" gap="2">
|
href={`https://github.com/nostr-protocol/nips/blob/master/65.md`}
|
||||||
<Text fontStyle="italic">Other users will send DMs and notes to these relays to notify you</Text>
|
textDecoration="underline"
|
||||||
{inbox?.urls
|
>
|
||||||
.sort()
|
NIP-65
|
||||||
.map((url) => <RelayLine key={url} relay={url} mode={RelayMode.READ} list={event ?? undefined} />)}
|
</Link>
|
||||||
</CardBody>
|
</Text>
|
||||||
<CardFooter display="flex" gap="2" p="4">
|
|
||||||
<AddRelayForm onSubmit={(r) => addRelay(r, RelayMode.READ)} />
|
<Flex gap="2" mt="2">
|
||||||
</CardFooter>
|
<InboxIcon boxSize={5} />
|
||||||
</Card>
|
<Heading size="md">Inbox</Heading>
|
||||||
<Card maxW="lg">
|
</Flex>
|
||||||
<CardHeader p="4" pb="2" display="flex" gap="2" alignItems="center">
|
<Text fontStyle="italic" mt="-2">
|
||||||
<InboxIcon boxSize={5} />
|
These relays are used by other users to send DMs and notes to you
|
||||||
<Heading size="md">Outbox</Heading>
|
</Text>
|
||||||
</CardHeader>
|
{inbox?.urls
|
||||||
<CardBody px="4" py="0" display="flex" flexDirection="column" gap="1">
|
.sort()
|
||||||
<Text fontStyle="italic">Always publish to these relays so your followers can find your notes</Text>
|
.map((url) => <RelayLine key={url} relay={url} mode={RelayMode.READ} list={event ?? undefined} />)}
|
||||||
{outbox?.urls
|
<AddRelayForm onSubmit={(r) => addRelay(r, RelayMode.READ)} />
|
||||||
.sort()
|
|
||||||
.map((url) => <RelayLine key={url} relay={url} mode={RelayMode.WRITE} list={event ?? undefined} />)}
|
<Flex gap="2" mt="4">
|
||||||
</CardBody>
|
<OutboxIcon boxSize={5} />
|
||||||
<CardFooter display="flex" gap="2" p="4">
|
<Heading size="md">Outbox</Heading>
|
||||||
<AddRelayForm onSubmit={(r) => addRelay(r, RelayMode.WRITE)} />
|
</Flex>
|
||||||
</CardFooter>
|
<Text fontStyle="italic" mt="-2">
|
||||||
</Card>
|
noStrudel will always publish to these relays so other users can find your notes
|
||||||
|
</Text>
|
||||||
|
{outbox?.urls
|
||||||
|
.sort()
|
||||||
|
.map((url) => <RelayLine key={url} relay={url} mode={RelayMode.WRITE} list={event ?? undefined} />)}
|
||||||
|
<AddRelayForm onSubmit={(r) => addRelay(r, RelayMode.WRITE)} />
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
48
src/views/relays/nip05/index.tsx
Normal file
48
src/views/relays/nip05/index.tsx
Normal file
@ -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 (
|
||||||
|
<Flex gap="2" alignItems="center">
|
||||||
|
<RelayFavicon relay={url} size="sm" />
|
||||||
|
<Link as={RouterLink} to={`/r/${encodeURIComponent(url)}`} isTruncated>
|
||||||
|
{url}
|
||||||
|
</Link>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NIP05RelaysView() {
|
||||||
|
const account = useCurrentAccount();
|
||||||
|
const nip05 = useUserDNSIdentity(account?.pubkey);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex gap="2" direction="column" overflow="auto hidden" flex={1} px={{ base: "2", lg: 0 }}>
|
||||||
|
<Flex gap="2" alignItems="center">
|
||||||
|
<BackButton hideFrom="lg" size="sm" />
|
||||||
|
<Heading size="lg">NIP-05 Relays</Heading>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Text fontStyle="italic" mt="-2">
|
||||||
|
These relays cant be modified by noStrudel, they must be set manually on your{" "}
|
||||||
|
<Code>/.well-known/nostr.json</Code> file or by your identity provider
|
||||||
|
<br />
|
||||||
|
<Link
|
||||||
|
href="https://nostr.how/en/guides/get-verified#self-hosted"
|
||||||
|
isExternal
|
||||||
|
color="blue.500"
|
||||||
|
fontStyle="initial"
|
||||||
|
>
|
||||||
|
Read more about nip-05
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{nip05?.relays.map((url) => <RelayItem key={url} url={url} />)}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
@ -25,6 +25,8 @@ import accountService from "../../services/account";
|
|||||||
import serialPortService from "../../services/serial-port";
|
import serialPortService from "../../services/serial-port";
|
||||||
import amberSignerService from "../../services/amber-signer";
|
import amberSignerService from "../../services/amber-signer";
|
||||||
import { AtIcon } from "../../components/icons";
|
import { AtIcon } from "../../components/icons";
|
||||||
|
import { getRelaysFromExt } from "../../helpers/nip07";
|
||||||
|
import { safeRelayUrls } from "../../helpers/relay";
|
||||||
|
|
||||||
export default function LoginStartView() {
|
export default function LoginStartView() {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@ -40,16 +42,15 @@ export default function LoginStartView() {
|
|||||||
const pubkey = await window.nostr.getPublicKey();
|
const pubkey = await window.nostr.getPublicKey();
|
||||||
|
|
||||||
if (!accountService.hasAccount(pubkey)) {
|
if (!accountService.hasAccount(pubkey)) {
|
||||||
let relays: string[] = [];
|
let relays = (await getRelaysFromExt()).read.urls;
|
||||||
const extRelays = (await window.nostr.getRelays?.()) ?? [];
|
|
||||||
if (Array.isArray(extRelays)) {
|
|
||||||
relays = extRelays;
|
|
||||||
} else {
|
|
||||||
relays = Object.keys(extRelays).filter((url) => extRelays[url].read);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (relays.length === 0) {
|
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 });
|
accountService.addAccount({ pubkey, relays, type: "extension", readonly: false });
|
||||||
|
Loading…
x
Reference in New Issue
Block a user