add relay management drawer

This commit is contained in:
hzrd149 2024-01-20 18:15:55 +00:00
parent 91f4c7c92e
commit 238a3be17e
75 changed files with 327 additions and 325 deletions

View File

@ -1,7 +1,7 @@
import relayScoreboardService from "../services/relay-scoreboard";
import { RawIncomingNostrEvent, NostrEvent, CountResponse } from "../types/nostr-event";
import { NostrOutgoingMessage } from "../types/nostr-query";
import { Subject } from "./subject";
import { PersistentSubject, Subject } from "./subject";
export type IncomingEvent = {
type: "EVENT";
@ -43,6 +43,7 @@ const CONNECTION_TIMEOUT = 1000 * 30;
export default class Relay {
url: string;
status = new PersistentSubject<number>(WebSocket.CLOSED);
onOpen = new Subject<Relay>(undefined, false);
onClose = new Subject<Relay>(undefined, false);
onEvent = new Subject<IncomingEvent>(undefined, false);
@ -88,6 +89,7 @@ export default class Relay {
this.ws.onopen = () => {
window.clearTimeout(connectionTimeout);
this.onOpen.next(this);
this.status.next(this.ws!.readyState);
this.ejectTimer = relayScoreboardService.relayEjectTime.get(this.url).createTimer();
if (this.connectionTimer) {
@ -99,6 +101,7 @@ export default class Relay {
};
this.ws.onclose = () => {
this.onClose.next(this);
this.status.next(this.ws!.readyState);
if (!this.intentionalClose && this.ejectTimer) {
this.ejectTimer();

View File

@ -8,14 +8,14 @@ import { NostrEvent } from "../../../types/nostr-event";
import useChannelMetadata from "../../../hooks/use-channel-metadata";
import HoverLinkOverlay from "../../hover-link-overlay";
import singleEventService from "../../../services/single-event";
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
import { useReadRelays } from "../../../hooks/use-client-relays";
export default function EmbeddedChannel({
channel,
additionalRelays,
...props
}: Omit<CardProps, "children"> & { channel: NostrEvent; additionalRelays?: string[] }) {
const readRelays = useReadRelayUrls(additionalRelays);
const readRelays = useReadRelays(additionalRelays);
const { metadata } = useChannelMetadata(channel.id, readRelays);
if (!channel || !metadata) return null;

View File

@ -5,12 +5,12 @@ import { NostrEvent } from "../../types/nostr-event";
import UserAvatarLink from "../user-avatar-link";
import UserLink from "../user-link";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import useSubject from "../../hooks/use-subject";
import Timestamp from "../timestamp";
export default function RepostDetails({ event }: { event: NostrEvent }) {
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const timeline = useTimelineLoader(`${event.id}-reposts`, readRelays, {
kinds: [kinds.Repost, kinds.GenericRepost],
"#e": [event.id],

View File

@ -12,7 +12,7 @@ import UserAvatar from "../user-avatar";
import UserLink from "../user-link";
import { GhostIcon } from "../icons";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import TimelineLoader from "../../classes/timeline-loader";
import { NostrEvent } from "../../types/nostr-event";
import { getSharableEventAddress } from "../../helpers/nip19";
@ -96,7 +96,7 @@ export default function GhostToolbar() {
const account = useCurrentAccount()!;
const isGhost = useSubject(accountService.isGhost);
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const [since] = useState(dayjs().subtract(6, "hours").unix());
const timeline = useTimelineLoader(`${account.pubkey}-ghost`, readRelays, { since, authors: [account.pubkey] });

View File

@ -30,7 +30,7 @@ import { kinds } from "nostr-tools";
import { ChevronDownIcon, ChevronUpIcon, UploadImageIcon } from "../icons";
import NostrPublishAction from "../../classes/nostr-publish-action";
import { useWriteRelayUrls } from "../../hooks/use-client-relays";
import { useWriteRelays } from "../../hooks/use-client-relays";
import { useSigningContext } from "../../providers/global/signing-provider";
import { NoteContents } from "../note/text-note-contents";
import { PublishDetails } from "../publish-details";
@ -87,7 +87,7 @@ export default function PostModal({
const { noteDifficulty } = useAppSettings();
const { requestSignature } = useSigningContext();
const additionalRelays = useAdditionalRelayContext();
const writeRelays = useWriteRelayUrls(additionalRelays);
const writeRelays = useWriteRelays(additionalRelays);
const [miningTarget, setMiningTarget] = useState(0);
const [publishAction, setPublishAction] = useState<NostrPublishAction>();
const emojis = useContextEmojis();

View File

@ -0,0 +1,134 @@
import {
Button,
Drawer,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerHeader,
DrawerOverlay,
DrawerProps,
Flex,
Heading,
IconButton,
Switch,
Text,
} from "@chakra-ui/react";
import { useReadRelays, useWriteRelays } from "../../hooks/use-client-relays";
import { useMemo } from "react";
import relayPoolService from "../../services/relay-pool";
import useSubject from "../../hooks/use-subject";
import { RelayUrlInput } from "../relay-url-input";
import { useForm } from "react-hook-form";
import clientRelaysService from "../../services/client-relays";
import { RelayMode } from "../../classes/relay";
import RelaySet from "../../classes/relay-set";
import { CloseIcon } from "@chakra-ui/icons";
import Circle from "../icons/circle";
import { safeRelayUrl } from "../../helpers/relay";
import UploadCloud01 from "../icons/upload-cloud-01";
import { RelayFavicon } from "../relay-favicon";
function RelayControl({ url }: { url: string }) {
const relay = useMemo(() => relayPoolService.requestRelay(url, false), [url]);
const status = useSubject(relay.status);
const writeRelays = useSubject(clientRelaysService.writeRelays);
let color = "gray";
switch (status) {
case WebSocket.OPEN:
color = "green";
break;
case WebSocket.CONNECTING:
color = "yellow";
break;
case WebSocket.CLOSED:
color = "red";
break;
}
const onChange = () => {
if (writeRelays.has(url)) clientRelaysService.removeRelay(url, RelayMode.WRITE);
else clientRelaysService.addRelay(url, RelayMode.WRITE);
};
return (
<Flex gap="2" alignItems="center">
<RelayFavicon relay={url} size="xs" outline="2px solid" outlineColor={color} />
<Text fontFamily="monospace" fontSize="md" flexGrow={1} isTruncated title={url}>
{url}
</Text>
<IconButton
aria-label="Toggle Write"
icon={<UploadCloud01 />}
size="sm"
variant={writeRelays.has(url) ? "solid" : "ghost"}
colorScheme={writeRelays.has(url) ? "green" : "gray"}
onClick={onChange}
title="Toggle Write"
/>
<IconButton
aria-label="Remove Relay"
icon={<CloseIcon />}
size="sm"
colorScheme="red"
onClick={() => clientRelaysService.removeRelay(url, RelayMode.ALL)}
/>
</Flex>
);
}
function AddRelayForm() {
const { register, handleSubmit, reset } = useForm({
defaultValues: {
url: "",
},
});
const submit = handleSubmit((values) => {
const url = safeRelayUrl(values.url);
if (!url) return;
clientRelaysService.addRelay(url, RelayMode.READ);
reset();
});
return (
<Flex as="form" display="flex" gap="2" onSubmit={submit}>
<RelayUrlInput {...register("url")} placeholder="wss://relay.example.com" />
<Button type="submit">Add</Button>
</Flex>
);
}
export default function RelayManagementDrawer({ isOpen, onClose, ...props }: Omit<DrawerProps, "children">) {
const readRelays = useReadRelays();
const writeRelays = useWriteRelays();
const sorted = useMemo(() => RelaySet.from(readRelays, writeRelays).urls.sort(), [readRelays, writeRelays]);
const others = Array.from(relayPoolService.relays.values())
.filter((r) => !r.closed && !sorted.includes(r.url))
.map((r) => r.url)
.sort();
return (
<Drawer isOpen={isOpen} placement="right" onClose={onClose} size="md" {...props}>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader px="4" py="2">
Relays
</DrawerHeader>
<DrawerBody px={{ base: 2, md: 4 }} pb="2" pt="0" display="flex" gap="2" flexDir="column">
<AddRelayForm />
{sorted.map((url) => (
<RelayControl key={url} url={url} />
))}
<Heading size="sm">Other Relays</Heading>
{others.map((url) => (
<RelayControl key={url} url={url} />
))}
</DrawerBody>
</DrawerContent>
</Drawer>
);
}

View File

@ -2,7 +2,7 @@ import { Button, ButtonProps, useDisclosure } from "@chakra-ui/react";
import { RelayIcon } from "../icons";
import { useRelaySelectionContext } from "../../providers/local/relay-selection-provider";
import RelaySelectionModal from "./relay-selection-modal";
import RelayManagementDrawer from "../relay-management-drawer";
export default function RelaySelectionButton({ ...props }: ButtonProps) {
const relaysModal = useDisclosure();
@ -13,9 +13,7 @@ export default function RelaySelectionButton({ ...props }: ButtonProps) {
<Button leftIcon={<RelayIcon />} onClick={relaysModal.onOpen} {...props}>
{relays.length} {relays.length === 1 ? "Relay" : "Relays"}
</Button>
{relaysModal.isOpen && (
<RelaySelectionModal selected={relays} onSubmit={setSelected} onClose={relaysModal.onClose} />
)}
<RelayManagementDrawer isOpen={relaysModal.isOpen} onClose={relaysModal.onClose} />
</>
);
}

View File

@ -15,7 +15,7 @@ import {
useToast,
} from "@chakra-ui/react";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import { RelayFavicon } from "../relay-favicon";
import { RelayUrlInput } from "../relay-url-input";
import { unique } from "../../helpers/array";
@ -61,7 +61,7 @@ export default function RelaySelectionModal({
onClose: () => void;
}) {
const [newSelected, setSelected] = useState<string[]>(selected);
const relays = useReadRelayUrls([...selected, ...newSelected, ...Array.from(manuallyAddedRelays)]);
const relays = useReadRelays([...selected, ...newSelected, ...Array.from(manuallyAddedRelays)]);
return (
<Modal isOpen={true} onClose={onClose}>

View File

@ -1,105 +1,27 @@
import { forwardRef, useState } from "react";
import {
Button,
Flex,
IconButton,
Input,
InputGroup,
InputLeftElement,
InputProps,
InputRightElement,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalOverlay,
ModalProps,
useDisclosure,
} from "@chakra-ui/react";
import { forwardRef } from "react";
import { Input, InputProps } from "@chakra-ui/react";
import { useAsync } from "react-use";
import { unique } from "../helpers/array";
import { RelayIcon, SearchIcon } from "./icons";
function RelayPickerModal({
onSelect,
onClose,
...props
}: { onSelect: (relay: string) => void } & Omit<ModalProps, "children">) {
const [search, setSearch] = useState("");
const { value: onlineRelays } = useAsync(async () =>
fetch("https://api.nostr.watch/v1/online").then((res) => res.json() as Promise<string[]>),
);
const relayList = unique(onlineRelays ?? []);
const filteredRelays = search ? relayList.filter((url) => url.includes(search)) : relayList;
return (
<Modal onClose={onClose} {...props}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Pick Relay</ModalHeader>
<ModalCloseButton />
<ModalBody pt="0" px="4" pb="4">
<InputGroup mb="2">
<InputLeftElement pointerEvents="none" children={<SearchIcon />} />
<Input
type="search"
placeholder="Search"
name="relay-search"
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</InputGroup>
<Flex gap="2" direction="column">
{filteredRelays.map((url) => (
<Button
value={url}
onClick={() => {
onSelect(url);
onClose();
}}
variant="outline"
size="sm"
>
{url}
</Button>
))}
</Flex>
</ModalBody>
</ModalContent>
</Modal>
);
}
export type RelayUrlInputProps = Omit<InputProps, "type">;
export const RelayUrlInput = forwardRef(
({ onChange, ...props }: Omit<RelayUrlInputProps, "onChange"> & { onChange: (url: string) => void }, ref) => {
const { isOpen, onClose, onOpen } = useDisclosure();
const { value: relaysJson } = useAsync(async () =>
fetch("https://api.nostr.watch/v1/online").then((res) => res.json() as Promise<string[]>),
);
const relaySuggestions = unique(relaysJson ?? []);
export const RelayUrlInput = forwardRef(({ ...props }: Omit<InputProps, "type">, ref) => {
const { value: relaysJson } = useAsync(async () =>
fetch("https://api.nostr.watch/v1/online").then((res) => res.json() as Promise<string[]>),
);
const relaySuggestions = unique(relaysJson ?? []);
return (
<>
<InputGroup>
<Input ref={ref} list="relay-suggestions" type="url" onChange={(e) => onChange(e.target.value)} {...props} />
<datalist id="relay-suggestions">
{relaySuggestions.map((url) => (
<option key={url} value={url}>
{url}
</option>
))}
</datalist>
<InputRightElement>
<IconButton icon={<RelayIcon />} aria-label="Pick from list" size="sm" onClick={onOpen} />
</InputRightElement>
</InputGroup>
<RelayPickerModal onClose={onClose} isOpen={isOpen} onSelect={(url) => onChange(url)} size="2xl" />
</>
);
},
);
return (
<>
<Input ref={ref} list="relay-suggestions" type="url" {...props} />
<datalist id="relay-suggestions">
{relaySuggestions.map((url) => (
<option key={url} value={url}>
{url}
</option>
))}
</datalist>
</>
);
});

View File

@ -1,12 +1,12 @@
import clientRelaysService from "../services/client-relays";
import useSubject from "./use-subject";
export function useReadRelayUrls(additional?: Iterable<string>) {
export function useReadRelays(additional?: Iterable<string>) {
const set = useSubject(clientRelaysService.readRelays);
if (additional) return set.clone().merge(additional);
return set;
}
export function useWriteRelayUrls(additional?: Iterable<string>) {
export function useWriteRelays(additional?: Iterable<string>) {
const set = useSubject(clientRelaysService.writeRelays);
if (additional) return set.clone().merge(additional);
return set;

View File

@ -1,10 +1,10 @@
import { useMemo } from "react";
import eventReactionsService from "../services/event-reactions";
import { useReadRelayUrls } from "./use-client-relays";
import { useReadRelays } from "./use-client-relays";
import useSubject from "./use-subject";
export default function useEventReactions(eventId: string, additionalRelays?: Iterable<string>, alwaysRequest = true) {
const relays = useReadRelayUrls(additionalRelays);
const relays = useReadRelays(additionalRelays);
const subject = useMemo(
() => eventReactionsService.requestReactions(eventId, relays, alwaysRequest),

View File

@ -1,12 +1,12 @@
import { useMemo } from "react";
import eventZapsService from "../services/event-zaps";
import { useReadRelayUrls } from "./use-client-relays";
import { useReadRelays } from "./use-client-relays";
import useSubject from "./use-subject";
import { parseZapEvent } from "../helpers/nostr/zaps";
export default function useEventZaps(eventUID: string, additionalRelays?: Iterable<string>, alwaysRequest = true) {
const readRelays = useReadRelayUrls(additionalRelays);
const readRelays = useReadRelays(additionalRelays);
const subject = useMemo(
() => eventZapsService.requestZaps(eventUID, readRelays, alwaysRequest),

View File

@ -1,6 +1,6 @@
import { useEffect, useMemo, useState } from "react";
import eventReactionsService from "../services/event-reactions";
import { useReadRelayUrls } from "./use-client-relays";
import { useReadRelays } from "./use-client-relays";
import { NostrEvent } from "../types/nostr-event";
import Subject from "../classes/subject";
@ -9,7 +9,7 @@ export default function useEventsReactions(
additionalRelays?: Iterable<string>,
alwaysRequest = true,
) {
const readRelays = useReadRelayUrls(additionalRelays);
const readRelays = useReadRelays(additionalRelays);
// get subjects
const subjects = useMemo(() => {

View File

@ -1,6 +1,6 @@
import { useMemo } from "react";
import { useReadRelayUrls } from "./use-client-relays";
import { useReadRelays } from "./use-client-relays";
import replaceableEventLoaderService, { RequestOptions } from "../services/replaceable-event-requester";
import { CustomAddressPointer, parseCoordinate } from "../helpers/nostr/events";
import useSubject from "./use-subject";
@ -10,7 +10,7 @@ export default function useReplaceableEvent(
additionalRelays?: Iterable<string>,
opts: RequestOptions = {},
) {
const readRelays = useReadRelayUrls(additionalRelays);
const readRelays = useReadRelays(additionalRelays);
const sub = useMemo(() => {
const parsed = typeof cord === "string" ? parseCoordinate(cord) : cord;
if (!parsed) return;

View File

@ -1,6 +1,6 @@
import { useMemo } from "react";
import { useReadRelayUrls } from "./use-client-relays";
import { useReadRelays } from "./use-client-relays";
import replaceableEventLoaderService, { RequestOptions } from "../services/replaceable-event-requester";
import { CustomAddressPointer, parseCoordinate } from "../helpers/nostr/events";
import Subject from "../classes/subject";
@ -12,7 +12,7 @@ export default function useReplaceableEvents(
additionalRelays?: Iterable<string>,
opts: RequestOptions = {},
) {
const readRelays = useReadRelayUrls(additionalRelays);
const readRelays = useReadRelays(additionalRelays);
const subs = useMemo(() => {
if (!coordinates) return undefined;
const subs: Subject<NostrEvent>[] = [];

View File

@ -1,10 +1,10 @@
import singleEventService from "../services/single-event";
import { useReadRelayUrls } from "./use-client-relays";
import { useReadRelays } from "./use-client-relays";
import { useMemo } from "react";
import useSubject from "./use-subject";
export default function useSingleEvent(id?: string, additionalRelays?: Iterable<string>) {
const readRelays = useReadRelayUrls(additionalRelays);
const readRelays = useReadRelays(additionalRelays);
const subject = useMemo(() => {
if (id) return singleEventService.requestEvent(id, readRelays);
}, [id, readRelays.urls.join("|")]);

View File

@ -1,11 +1,11 @@
import { useMemo } from "react";
import singleEventService from "../services/single-event";
import { useReadRelayUrls } from "./use-client-relays";
import { useReadRelays } from "./use-client-relays";
import useSubjects from "./use-subjects";
export default function useSingleEvents(ids?: string[], additionalRelays?: Iterable<string>) {
const readRelays = useReadRelayUrls(additionalRelays);
const readRelays = useReadRelays(additionalRelays);
const subjects = useMemo(() => {
return ids?.map((id) => singleEventService.requestEvent(id, readRelays)) ?? [];
}, [ids, readRelays.urls.join("|")]);

View File

@ -3,13 +3,13 @@ import { useEffect, useState } from "react";
import { GOAL_KIND } from "../helpers/nostr/goal";
import { ParsedStream, getATag } from "../helpers/nostr/stream";
import { NostrEvent } from "../types/nostr-event";
import { useReadRelayUrls } from "./use-client-relays";
import { useReadRelays } from "./use-client-relays";
import NostrRequest from "../classes/nostr-request";
import useSingleEvent from "./use-single-event";
export default function useStreamGoal(stream: ParsedStream) {
const [goal, setGoal] = useState<NostrEvent>();
const readRelays = useReadRelayUrls(stream.relays);
const readRelays = useReadRelays(stream.relays);
const streamGoal = useSingleEvent(stream.goal);

View File

@ -1,13 +1,13 @@
import { useCallback } from "react";
import { NOTE_LIST_KIND, PEOPLE_LIST_KIND, isJunkList } from "../helpers/nostr/lists";
import { useReadRelayUrls } from "./use-client-relays";
import { useReadRelays } from "./use-client-relays";
import useSubject from "./use-subject";
import useTimelineLoader from "./use-timeline-loader";
import { NostrEvent } from "../types/nostr-event";
export default function useUserLists(pubkey?: string, additionalRelays?: Iterable<string>) {
const readRelays = useReadRelayUrls(additionalRelays);
const readRelays = useReadRelays(additionalRelays);
const eventFilter = useCallback((event: NostrEvent) => {
return !isJunkList(event);
}, []);

View File

@ -1,18 +1,18 @@
import RelaySet from "../classes/relay-set";
import { RequestOptions } from "../services/replaceable-event-requester";
import userMailboxesService from "../services/user-mailboxes";
import { useReadRelayUrls } from "./use-client-relays";
import { useReadRelays } from "./use-client-relays";
import useSubject from "./use-subject";
export default function useUserMailboxes(pubkey: string, opts?: RequestOptions) {
const readRelays = useReadRelayUrls();
const sub = userMailboxesService.requestMailboxes(pubkey, readRelays, opts);
export default function useUserMailboxes(pubkey?: string, opts?: RequestOptions) {
const readRelays = useReadRelays();
const sub = pubkey ? userMailboxesService.requestMailboxes(pubkey, readRelays, opts) : undefined;
const value = useSubject(sub);
return value;
}
export function useUserInbox(pubkey: string, opts?: RequestOptions) {
export function useUserInbox(pubkey?: string, opts?: RequestOptions) {
return useUserMailboxes(pubkey, opts)?.inbox ?? new RelaySet();
}
export function useUserOutbox(pubkey: string, opts?: RequestOptions) {
export function useUserOutbox(pubkey?: string, opts?: RequestOptions) {
return useUserMailboxes(pubkey, opts)?.outbox ?? new RelaySet();
}

View File

@ -1,12 +1,12 @@
import { useMemo } from "react";
import userMetadataService from "../services/user-metadata";
import { useReadRelayUrls } from "./use-client-relays";
import { useReadRelays } from "./use-client-relays";
import useSubject from "./use-subject";
import { RequestOptions } from "../services/replaceable-event-requester";
import { COMMON_CONTACT_RELAY } from "../const";
export function useUserMetadata(pubkey: string, additionalRelays: Iterable<string> = [], opts: RequestOptions = {}) {
const relays = useReadRelayUrls([...additionalRelays, COMMON_CONTACT_RELAY]);
const relays = useReadRelays([...additionalRelays, COMMON_CONTACT_RELAY]);
const subject = useMemo(() => userMetadataService.requestMetadata(pubkey, relays, opts), [pubkey, relays]);
const metadata = useSubject(subject);

View File

@ -4,13 +4,13 @@ import { kinds } from "nostr-tools";
import { getPubkeysFromList } from "../helpers/nostr/lists";
import useUserContactList from "./use-user-contact-list";
import replaceableEventLoaderService from "../services/replaceable-event-requester";
import { useReadRelayUrls } from "./use-client-relays";
import { useReadRelays } from "./use-client-relays";
import useSubjects from "./use-subjects";
import userMetadataService from "../services/user-metadata";
import { Kind0ParsedContent } from "../helpers/user-metadata";
export function useUsersMetadata(pubkeys: string[], additionalRelays?: Iterable<string>) {
const readRelays = useReadRelayUrls(additionalRelays);
const readRelays = useReadRelays(additionalRelays);
const metadataSubjects = useMemo(() => {
return pubkeys.map((pubkey) => userMetadataService.requestMetadata(pubkey, readRelays));
}, [pubkeys]);
@ -28,7 +28,7 @@ export function useUsersMetadata(pubkeys: string[], additionalRelays?: Iterable<
}
export default function useUserNetwork(pubkey: string, additionalRelays?: Iterable<string>) {
const readRelays = useReadRelayUrls(additionalRelays);
const readRelays = useReadRelays(additionalRelays);
const contacts = useUserContactList(pubkey);
const contactsPubkeys = contacts ? getPubkeysFromList(contacts) : [];

View File

@ -2,14 +2,14 @@ import { useMemo } from "react";
import userMailboxesService from "../services/user-mailboxes";
import useSubject from "./use-subject";
import { useReadRelayUrls } from "./use-client-relays";
import { useReadRelays } from "./use-client-relays";
import { RequestOptions } from "../services/replaceable-event-requester";
import { COMMON_CONTACT_RELAY } from "../const";
import RelaySet from "../classes/relay-set";
/** @deprecated */
export function useUserRelays(pubkey: string, additionalRelays: Iterable<string> = [], opts: RequestOptions = {}) {
const readRelays = useReadRelayUrls([...additionalRelays, COMMON_CONTACT_RELAY]);
const readRelays = useReadRelays([...additionalRelays, COMMON_CONTACT_RELAY]);
const subject = useMemo(
() => userMailboxesService.requestMailboxes(pubkey, readRelays, opts),
[pubkey, readRelays.urls.join("|")],

View File

@ -1,7 +1,7 @@
import { PropsWithChildren, createContext, useCallback, useContext, useMemo } from "react";
import { kinds } from "nostr-tools";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import useCurrentAccount from "../../hooks/use-current-account";
import TimelineLoader from "../../classes/timeline-loader";
import { NostrEvent } from "../../types/nostr-event";
@ -23,7 +23,7 @@ export function useDMTimeline() {
export default function DMTimelineProvider({ children }: PropsWithChildren) {
const account = useCurrentAccount();
const inbox = useReadRelayUrls();
const inbox = useReadRelays();
const userMuteFilter = useClientSideMuteFilter();
const eventFilter = useCallback(

View File

@ -1,7 +1,7 @@
import { PropsWithChildren, createContext, useCallback, useContext, useMemo } from "react";
import { kinds } from "nostr-tools";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import useCurrentAccount from "../../hooks/use-current-account";
import TimelineLoader from "../../classes/timeline-loader";
import { NostrEvent } from "../../types/nostr-event";
@ -24,7 +24,7 @@ export function useNotificationTimeline() {
export default function NotificationTimelineProvider({ children }: PropsWithChildren) {
const account = useCurrentAccount();
const inbox = useReadRelayUrls();
const inbox = useReadRelays();
const userMuteFilter = useClientSideMuteFilter();
const eventFilter = useCallback(

View File

@ -1,7 +1,7 @@
import { PropsWithChildren, createContext, useCallback, useContext, useMemo } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import { unique } from "../../helpers/array";
type RelaySelectionContextType = {
@ -34,7 +34,7 @@ export default function RelaySelectionProvider({
const navigate = useNavigate();
const location = useLocation();
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const relays = useMemo(() => {
if (location.state?.relays) return location.state.relays as string[];
if (overrideDefault) return Array.from(overrideDefault);

View File

@ -34,7 +34,7 @@ import NostrPublishAction from "../../classes/nostr-publish-action";
import { Tag } from "../../types/nostr-event";
import deleteEventService from "../../services/delete-events";
import { EmbedEvent } from "../../components/embed-event";
import { useWriteRelayUrls } from "../../hooks/use-client-relays";
import { useWriteRelays } from "../../hooks/use-client-relays";
type DeleteEventContextType = {
isLoading: boolean;
@ -59,7 +59,7 @@ export default function DeleteEventProvider({ children }: PropsWithChildren) {
const [reason, setReason] = useState("");
const eventRelays = useEventRelays(event && getEventUID(event));
const writeRelays = useWriteRelayUrls(eventRelays);
const writeRelays = useWriteRelays(eventRelays);
const deleteEvent = useCallback((event: Event) => {
setEvent(event);

View File

@ -1,44 +1,34 @@
import accountService from "./account";
import { RelayMode } from "../classes/relay";
import userMailboxesService, { UserMailboxes } from "./user-mailboxes";
import { PersistentSubject, Subject } from "../classes/subject";
import userMailboxesService from "./user-mailboxes";
import { PersistentSubject } from "../classes/subject";
import { logger } from "../helpers/debug";
import appSettings from "./settings/app-settings";
import RelaySet from "../classes/relay-set";
import { safeUrl } from "../helpers/parse";
export type RelayDirectory = Record<string, { read: boolean; write: boolean }>;
// const userRelaysToRelayConfig: Connection<ParsedUserRelays, RelayConfig[], RelayConfig[] | undefined> = (
// userRelays,
// next,
// ) => next(userRelays.relays);
class ClientRelayService {
readRelays = new PersistentSubject(new RelaySet());
writeRelays = new PersistentSubject(new RelaySet());
// outbox = new PersistentSubject(new RelaySet());
// inbox = new PersistentSubject(new RelaySet());
log = logger.extend("ClientRelays");
constructor() {
accountService.loading.subscribe(this.handleAccountChange, this);
accountService.current.subscribe(this.handleAccountChange, this);
const cachedRead = localStorage.getItem("read-relays")?.split(",");
if (cachedRead) this.readRelays.next(RelaySet.from(cachedRead));
appSettings.subscribe(this.handleSettingsChange, this);
// set the read and write relays
// this.relays.subscribe((relays) => {
// this.log("Got new relay list", relays);
// this.outbox.next(relays.filter((r) => r.mode & RelayMode.WRITE));
// this.inbox.next(relays.filter((r) => r.mode & RelayMode.READ));
// });
const cachedWrite = localStorage.getItem("write-relays")?.split(",");
if (cachedWrite) this.writeRelays.next(RelaySet.from(cachedWrite));
}
addRelay(url: string, mode: RelayMode) {
if (mode & RelayMode.WRITE) this.writeRelays.next(this.writeRelays.value.clone().add(url));
if (mode & RelayMode.READ) this.readRelays.next(this.readRelays.value.clone().add(url));
if (mode & RelayMode.WRITE && !this.writeRelays.value.has(url))
this.writeRelays.next(this.writeRelays.value.clone().add(url));
if (mode & RelayMode.READ && !this.readRelays.value.has(url))
this.readRelays.next(this.readRelays.value.clone().add(url));
this.saveRelays();
}
removeRelay(url: string, mode: RelayMode) {
if (mode & RelayMode.WRITE) {
@ -51,67 +41,23 @@ class ClientRelayService {
next.delete(url);
this.readRelays.next(next);
}
this.saveRelays();
}
private handleSettingsChange() {
this.readRelays.next(RelaySet.from(appSettings.value.defaultRelays));
this.writeRelays.next(new RelaySet());
saveRelays() {
localStorage.setItem("read-relays", this.readRelays.value.urls.join(","));
localStorage.setItem("write-relays", this.writeRelays.value.urls.join(","));
}
private userRelaySub: Subject<UserMailboxes> | undefined;
private handleAccountChange() {
// skip if account is loading
if (accountService.loading.value) return;
// disconnect the relay list subject
// if (this.userRelaySub) {
// this.relays.disconnect(this.userRelaySub);
// this.userRelaySub.unsubscribe(this.handleUserRelays, this);
// this.userRelaySub = undefined;
// }
const account = accountService.current.value;
if (!account) return;
// connect the relay subject with the account relay subject
// this.userRelaySub = userMailboxesService.requestMailboxes(account.pubkey, [COMMON_CONTACT_RELAY]);
// this.userRelaySub.subscribe(this.handleUserRelays, this);
// this.relays.connectWithHandler(this.userRelaySub, userRelaysToRelayConfig);
// load the relays from cache
// if (!userRelaysService.getRelays(account.pubkey).value) {
// this.log("Load users relay list from cache");
// userRelaysService.loadFromCache(account.pubkey).then(() => {
// if (this.relays.value.length === 0) {
// const bootstrapRelays = account.relays ?? [COMMON_CONTACT_RELAY];
// this.log("Loading relay list from bootstrap relays", bootstrapRelays);
// userRelaysService.requestRelays(account.pubkey, bootstrapRelays, { alwaysRequest: true });
// }
// });
// }
// double check for new relay notes
// setTimeout(() => {
// if (this.relays.value.length === 0) return;
// this.log("Requesting latest relay list from relays");
// userRelaysService.requestRelays(account.pubkey, this.getOutboxURLs(), { alwaysRequest: true });
// }, 5000);
}
// private handleUserRelays(userRelays: UserMailboxes) {
// if (userRelays.pubkey === accountService.current.value?.pubkey) {
// this.inbox.next(userRelays.mailboxes.filter(RelayMode.READ));
// this.outbox.next(userRelays.mailboxes.filter(RelayMode.WRITE));
// }
// }
/** @deprecated */
get outbox() {
const account = accountService.current.value;
if (account) return userMailboxesService.getMailboxes(account.pubkey).value?.outbox ?? this.writeRelays.value;
return this.writeRelays.value;
}
/** @deprecated */
get inbox() {
const account = accountService.current.value;
if (account) return userMailboxesService.getMailboxes(account.pubkey).value?.inbox ?? this.readRelays.value;

View File

@ -22,7 +22,7 @@ import { EventRelays } from "../../components/note/note-relays";
import { getBadgeAwardPubkeys, getBadgeDescription, getBadgeImage, getBadgeName } from "../../helpers/nostr/badges";
import BadgeMenu from "./components/badge-menu";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
import useSubject from "../../hooks/use-subject";
@ -85,7 +85,7 @@ function BadgeDetailsPage({ badge }: { badge: NostrEvent }) {
const image = getBadgeImage(badge);
const description = getBadgeDescription(badge);
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const coordinate = getEventCoordinate(badge);
const awardsTimeline = useTimelineLoader(`${coordinate}-awards`, readRelays, {
"#a": [coordinate],

View File

@ -4,7 +4,7 @@ import { kinds } from "nostr-tools";
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
import useSubject from "../../hooks/use-subject";
@ -15,7 +15,7 @@ import VerticalPageLayout from "../../components/vertical-page-layout";
function BadgesBrowsePage() {
const { filter, listId } = usePeopleListContext();
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const timeline = useTimelineLoader(
`${listId}-badges`,
readRelays,

View File

@ -8,7 +8,7 @@ import VerticalPageLayout from "../../components/vertical-page-layout";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import useSubject from "../../hooks/use-subject";
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
@ -27,7 +27,7 @@ function BadgesPage() {
},
[muteFilter],
);
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const timeline = useTimelineLoader(
`${listId}-lists`,
readRelays,

View File

@ -23,7 +23,7 @@ import HoverLinkOverlay from "../../../components/hover-link-overlay";
import UserAvatarLink from "../../../components/user-avatar-link";
import UserLink from "../../../components/user-link";
import useSingleEvent from "../../../hooks/use-single-event";
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
import { useReadRelays } from "../../../hooks/use-client-relays";
import singleEventService from "../../../services/single-event";
export default function ChannelCard({
@ -31,7 +31,7 @@ export default function ChannelCard({
additionalRelays,
...props
}: Omit<CardProps, "children"> & { channel: NostrEvent; additionalRelays?: string[] }) {
const readRelays = useReadRelayUrls(additionalRelays);
const readRelays = useReadRelays(additionalRelays);
const { metadata } = useChannelMetadata(channel.id, readRelays);
const ref = useRef<HTMLDivElement | null>(null);

View File

@ -7,7 +7,7 @@ import { PointerCommunityCard } from "./components/community-card";
import VerticalPageLayout from "../../components/vertical-page-layout";
import { COMMUNITY_DEFINITION_KIND } from "../../helpers/nostr/communities";
import { ErrorBoundary } from "../../components/error-boundary";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import useSubjects from "../../hooks/use-subjects";
import replaceableEventLoaderService from "../../services/replaceable-event-requester";
import { COMMUNITIES_LIST_KIND, getCoordinatesFromList } from "../../helpers/nostr/lists";
@ -18,7 +18,7 @@ import UserAvatarLink from "../../components/user-avatar-link";
import { AddressPointer } from "nostr-tools/lib/types/nip19";
export function useUsersJoinedCommunitiesLists(pubkeys: string[], additionalRelays?: Iterable<string>) {
const readRelays = useReadRelayUrls(additionalRelays);
const readRelays = useReadRelays(additionalRelays);
const communityListsSubjects = useMemo(() => {
return pubkeys.map((pubkey) =>
replaceableEventLoaderService.requestEvent(readRelays, COMMUNITIES_LIST_KIND, pubkey),

View File

@ -41,7 +41,7 @@ import NostrPublishAction from "../../classes/nostr-publish-action";
import clientRelaysService from "../../services/client-relays";
import replaceableEventLoaderService, { createCoordinate } from "../../services/replaceable-event-requester";
import { getImageSize } from "../../helpers/image";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import useSubject from "../../hooks/use-subject";
import useUserMuteFilter from "../../hooks/use-user-mute-filter";
@ -60,7 +60,7 @@ function CommunitiesHomePage() {
const account = useCurrentAccount()!;
const createModal = useDisclosure();
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const { pointers: communityCoordinates } = useUserCommunitiesList(account.pubkey, readRelays, {
alwaysRequest: true,
});

View File

@ -23,7 +23,7 @@ import Hourglass03 from "../../components/icons/hourglass-03";
import VerticalCommunityDetails from "./components/vertical-community-details";
import { useBreakpointValue } from "../../providers/global/breakpoint-provider";
import HorizontalCommunityDetails from "./components/horizonal-community-details";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { getEventCoordinate, getEventUID } from "../../helpers/nostr/events";
import { WritingIcon } from "../../components/icons";
@ -50,7 +50,7 @@ export default function CommunityHomePage({ community }: { community: NostrEvent
const verticalLayout = useBreakpointValue({ base: true, xl: false });
const communityRelays = getCommunityRelays(community);
const readRelays = useReadRelayUrls(communityRelays);
const readRelays = useReadRelays(communityRelays);
const timeline = useTimelineLoader(`${getEventUID(community)}-timeline`, readRelays, {
kinds: [kinds.ShortTextNote, kinds.Repost, kinds.GenericRepost, COMMUNITY_APPROVAL_KIND],
"#a": [communityCoordinate],

View File

@ -14,7 +14,7 @@ import {
import { NostrEvent } from "../../../types/nostr-event";
import useTimelineLoader from "../../../hooks/use-timeline-loader";
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
import { useReadRelays } from "../../../hooks/use-client-relays";
import { getCommunityRelays } from "../../../helpers/nostr/communities";
import { getEventCoordinate } from "../../../helpers/nostr/events";
import { COMMUNITIES_LIST_KIND } from "../../../helpers/nostr/lists";
@ -44,7 +44,7 @@ export default function CommunityMembersModal({
...props
}: Omit<ModalProps, "children"> & { community: NostrEvent }) {
const communityCoordinate = getEventCoordinate(community);
const readRelays = useReadRelayUrls(getCommunityRelays(community));
const readRelays = useReadRelays(getCommunityRelays(community));
const timeline = useTimelineLoader(`${communityCoordinate}-members`, readRelays, [
{ "#a": [communityCoordinate], kinds: [COMMUNITIES_LIST_KIND] },
]);

View File

@ -27,7 +27,7 @@ import { getEventUID, parseHardcodedNoteContent } from "../../../helpers/nostr/e
import UserLink from "../../../components/user-link";
import UserAvatarLink from "../../../components/user-avatar-link";
import useUserMuteFilter from "../../../hooks/use-user-mute-filter";
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
import { useReadRelays } from "../../../hooks/use-client-relays";
import useSingleEvent from "../../../hooks/use-single-event";
import CommunityPostMenu from "./community-post-menu";
@ -129,7 +129,7 @@ export function CommunityRepostPost({
const encodedRepost = parseHardcodedNoteContent(event);
const [_, eventId, relay] = event.tags.find(isETag) ?? [];
const readRelays = useReadRelayUrls(relay ? [relay] : []);
const readRelays = useReadRelays(relay ? [relay] : []);
const loadedRepost = useSingleEvent(eventId, readRelays);
const repost = encodedRepost || loadedRepost;

View File

@ -2,7 +2,7 @@ import { useCallback } from "react";
import { Navigate, useParams } from "react-router-dom";
import { Heading, SimpleGrid } from "@chakra-ui/react";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import { COMMUNITY_DEFINITION_KIND, validateCommunity } from "../../helpers/nostr/communities";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { NostrEvent } from "../../types/nostr-event";
@ -23,7 +23,7 @@ export default function CommunityFindByNameView() {
return <Navigate to={`/c/${decoded.data.identifier}/${decoded.data.pubkey}`} replace />;
}
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const eventFilter = useCallback((event: NostrEvent) => {
return validateCommunity(event);
}, []);

View File

@ -24,7 +24,7 @@ import NostrPublishAction from "../../../classes/nostr-publish-action";
import CommunityPost from "../components/community-post";
import { RouterContext } from "../community-home";
import useUserMuteFilter from "../../../hooks/use-user-mute-filter";
import { useWriteRelayUrls } from "../../../hooks/use-client-relays";
import { useWriteRelays } from "../../../hooks/use-client-relays";
type PendingProps = {
event: NostrEvent;
@ -40,7 +40,7 @@ function ModPendingPost({ event, community, approvals }: PendingProps) {
useRegisterIntersectionEntity(ref, getEventUID(event));
const communityRelays = getCommunityRelays(community);
const writeRelays = useWriteRelayUrls(communityRelays);
const writeRelays = useWriteRelays(communityRelays);
const [loading, setLoading] = useState(false);
const approve = useCallback(async () => {
setLoading(true);

View File

@ -27,7 +27,7 @@ import NostrPublishAction from "../../../classes/nostr-publish-action";
import { useSigningContext } from "../../../providers/global/signing-provider";
import { DraftNostrEvent } from "../../../types/nostr-event";
import clientRelaysService from "../../../services/client-relays";
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
import { useReadRelays } from "../../../hooks/use-client-relays";
import { DVMAvatarLink } from "./dvm-avatar";
import DVMLink from "./dvm-name";
import { AddressPointer } from "nostr-tools/lib/types/nip19";
@ -38,7 +38,7 @@ function NextPageButton({ chain, pointer }: { pointer: AddressPointer; chain: Ch
const toast = useToast();
const { requestSignature } = useSigningContext();
const dvmRelays = useUserMailboxes(pointer.pubkey)?.relays;
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const lastJob = chain[chain.length - 1];
const requestNextPage = async () => {

View File

@ -30,7 +30,7 @@ import clientRelaysService from "../../services/client-relays";
import VerticalPageLayout from "../../components/vertical-page-layout";
import useSubject from "../../hooks/use-subject";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import { useSigningContext } from "../../providers/global/signing-provider";
import useCurrentAccount from "../../hooks/use-current-account";
import RequireCurrentAccount from "../../providers/route/require-current-account";
@ -52,7 +52,7 @@ function DVMFeedPage({ pointer }: { pointer: AddressPointer }) {
const debugModal = useDisclosure();
const dvmRelays = useUserMailboxes(pointer.pubkey)?.relays;
const readRelays = useReadRelayUrls(dvmRelays);
const readRelays = useReadRelays(dvmRelays);
const timeline = useTimelineLoader(`${pointer.kind}:${pointer.pubkey}:${pointer.identifier}-jobs`, readRelays, [
{ authors: [account.pubkey], "#p": [pointer.pubkey], kinds: [DVM_CONTENT_DISCOVERY_JOB_KIND], since },
{

View File

@ -4,7 +4,7 @@ import VerticalPageLayout from "../../components/vertical-page-layout";
import DVMCard from "./components/dvm-card";
import { DVM_CONTENT_DISCOVERY_JOB_KIND } from "../../helpers/nostr/dvm";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import useSubject from "../../hooks/use-subject";
import RequireCurrentAccount from "../../providers/route/require-current-account";
import { getEventCoordinate } from "../../helpers/nostr/events";
@ -12,7 +12,7 @@ import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
function DVMFeedHomePage() {
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const timeline = useTimelineLoader("content-discovery-dvms", readRelays, {
kinds: [31990],
"#k": [String(DVM_CONTENT_DISCOVERY_JOB_KIND)],

View File

@ -4,7 +4,7 @@ import { Flex, SimpleGrid, Switch, useDisclosure } from "@chakra-ui/react";
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import { NostrEvent } from "../../types/nostr-event";
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
@ -25,7 +25,7 @@ function EmojiPacksBrowsePage() {
},
[showEmpty.isOpen],
);
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const timeline = useTimelineLoader(
`${listId}-browse-emoji-packs`,
readRelays,

View File

@ -4,7 +4,7 @@ import { Link as RouterLink } from "react-router-dom";
import useCurrentAccount from "../../hooks/use-current-account";
import { ExternalLinkIcon } from "../../components/icons";
import { getEventCoordinate, getEventUID } from "../../helpers/nostr/events";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { EMOJI_PACK_KIND, getPackCordsFromFavorites } from "../../helpers/nostr/emoji-packs";
import useSubject from "../../hooks/use-subject";
@ -18,7 +18,7 @@ function UserEmojiPackMangerPage() {
const account = useCurrentAccount()!;
const favoritePacks = useFavoriteEmojiPacks(account.pubkey);
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const timeline = useTimelineLoader(
`${account.pubkey}-emoji-packs`,
readRelays,

View File

@ -5,7 +5,7 @@ import dayjs from "dayjs";
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
import useSubject from "../../hooks/use-subject";
@ -20,7 +20,7 @@ function GoalsBrowsePage() {
const { filter, listId } = usePeopleListContext();
const showClosed = useDisclosure();
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const eventFilter = useCallback(
(event: NostrEvent) => {
const closed = getGoalClosedDate(event);

View File

@ -4,7 +4,7 @@ import ZapModal from "../../../components/event-zap-modal";
import eventZapsService from "../../../services/event-zaps";
import { getEventUID } from "../../../helpers/nostr/events";
import { getGoalRelays } from "../../../helpers/nostr/goal";
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
import { useReadRelays } from "../../../hooks/use-client-relays";
export default function GoalZapButton({
goal,
@ -12,7 +12,7 @@ export default function GoalZapButton({
}: Omit<ButtonProps, "children" | "onClick"> & { goal: NostrEvent }) {
const modal = useDisclosure();
const readRelays = useReadRelayUrls(getGoalRelays(goal));
const readRelays = useReadRelays(getGoalRelays(goal));
const onZapped = async () => {
modal.onClose();
setTimeout(() => {

View File

@ -4,7 +4,7 @@ import { Navigate, Link as RouterLink } from "react-router-dom";
import useCurrentAccount from "../../hooks/use-current-account";
import { ExternalLinkIcon } from "../../components/icons";
import { getEventUID } from "../../helpers/nostr/events";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import useSubject from "../../hooks/use-subject";
import GoalCard from "./components/goal-card";
@ -14,7 +14,7 @@ import VerticalPageLayout from "../../components/vertical-page-layout";
function UserGoalsManagerPage() {
const account = useCurrentAccount()!;
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const timeline = useTimelineLoader(
`${account.pubkey}-goals`,
readRelays,

View File

@ -3,7 +3,7 @@ import { Flex, Select, SimpleGrid, Switch, useDisclosure } from "@chakra-ui/reac
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import {
MUTE_LIST_KIND,
NOTE_LIST_KIND,
@ -42,7 +42,7 @@ function BrowseListPage() {
},
[showEmpty.isOpen, showMute.isOpen, listKind],
);
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const timeline = useTimelineLoader(
`${listId}-lists`,
readRelays,

View File

@ -1,11 +1,11 @@
import { Text } from "@chakra-ui/react";
import { Note } from "../../../components/note";
import { NoteLink } from "../../../components/note-link";
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
import { useReadRelays } from "../../../hooks/use-client-relays";
import useSingleEvent from "../../../hooks/use-single-event";
export default function NoteCard({ id, relay }: { id: string; relay?: string }) {
const readRelays = useReadRelayUrls(relay ? [relay] : []);
const readRelays = useReadRelays(relay ? [relay] : []);
const event = useSingleEvent(id, readRelays);
return event ? (

View File

@ -10,7 +10,7 @@ import "leaflet.locatecontrol";
import useSubject from "../../hooks/use-subject";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import { debounce } from "../../helpers/function";
import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status";
@ -119,7 +119,7 @@ export default function MapView() {
const [cells, setCells] = useState<string[]>([]);
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const timeline = useTimelineLoader(
"geo-events",
readRelays,

View File

@ -18,7 +18,7 @@ import NostrPublishAction from "../../classes/nostr-publish-action";
import { ExternalLinkIcon } from "../../components/icons";
import { isLNURL } from "../../helpers/lnurl";
import { Kind0ParsedContent } from "../../helpers/user-metadata";
import { useReadRelayUrls, useWriteRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays, useWriteRelays } from "../../hooks/use-client-relays";
import useCurrentAccount from "../../hooks/use-current-account";
import { useUserMetadata } from "../../hooks/use-user-metadata";
import dnsIdentityService from "../../services/dns-identity";
@ -189,8 +189,8 @@ const MetadataForm = ({ defaultValues, onSubmit }: MetadataFormProps) => {
};
export const ProfileEditView = () => {
const writeRelays = useWriteRelayUrls([COMMON_CONTACT_RELAY]);
const readRelays = useReadRelayUrls();
const writeRelays = useWriteRelays([COMMON_CONTACT_RELAY]);
const readRelays = useReadRelays();
const toast = useToast();
const account = useCurrentAccount()!;
const metadata = useUserMetadata(account.pubkey, readRelays, { alwaysRequest: true });

View File

@ -170,7 +170,6 @@ export default function RelayCard({ url, ...props }: { url: string } & Omit<Card
{/* <RelayModeAction url={url} /> */}
<RelayShareButton relay={url} ml="auto" size="sm" />
<RelayDebugButton url={url} size="sm" title="Show raw NIP-11 metadata" />
</CardFooter>
</Card>
</>

View File

@ -11,7 +11,7 @@ import { RelayMode } from "../../classes/relay";
import { ErrorBoundary } from "../../components/error-boundary";
import VerticalPageLayout from "../../components/vertical-page-layout";
import { isValidRelayURL } from "../../helpers/relay";
import { useReadRelayUrls, useWriteRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays, useWriteRelays } from "../../hooks/use-client-relays";
export default function RelaysView() {
const [search, setSearch] = useState("");
@ -19,8 +19,8 @@ export default function RelaysView() {
const isSearching = deboundedSearch.length > 2;
const addRelayModal = useDisclosure();
const readRelays = useReadRelayUrls();
const writeRelays = useWriteRelayUrls();
const readRelays = useReadRelays();
const writeRelays = useWriteRelays();
const discoveredRelays = relayPoolService
.getRelays()
.filter((r) => !readRelays.has(r.url) && !writeRelays.has(r.url))

View File

@ -16,7 +16,7 @@ import { Link as RouterLink, useNavigate } from "react-router-dom";
import VerticalPageLayout from "../../components/vertical-page-layout";
import { getPubkeysFromList } from "../../helpers/nostr/lists";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import useCurrentAccount from "../../hooks/use-current-account";
import useSubjects from "../../hooks/use-subjects";
import useUserContactList from "../../hooks/use-user-contact-list";
@ -29,7 +29,7 @@ import UserAvatar from "../../components/user-avatar";
import { RelayMetadata, RelayPaidTag } from "./components/relay-card";
function usePopularContactsRelays(list?: NostrEvent) {
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const subs = list
? getPubkeysFromList(list).map((p) => userMailboxesService.requestMailboxes(p.pubkey, readRelays))
: [];

View File

@ -2,7 +2,7 @@ import { Button, Flex, FlexProps, Heading, Textarea, useToast } from "@chakra-ui
import { useForm } from "react-hook-form";
import dayjs from "dayjs";
import { useWriteRelayUrls } from "../../../hooks/use-client-relays";
import { useWriteRelays } from "../../../hooks/use-client-relays";
import StarRating from "../../../components/star-rating";
import { DraftNostrEvent } from "../../../types/nostr-event";
import { RELAY_REVIEW_LABEL, RELAY_REVIEW_LABEL_NAMESPACE, REVIEW_KIND } from "../../../helpers/nostr/reviews";
@ -16,7 +16,7 @@ export default function RelayReviewForm({
}: { onClose: () => void; relay: string } & Omit<FlexProps, "children">) {
const toast = useToast();
const { requestSignature } = useSigningContext();
const writeRelays = useWriteRelayUrls();
const writeRelays = useWriteRelays();
const { register, getValues, watch, handleSubmit, setValue } = useForm({
defaultValues: {
quality: 0.6,

View File

@ -1,7 +1,7 @@
import { Flex } from "@chakra-ui/react";
import { RELAY_REVIEW_LABEL } from "../../../helpers/nostr/reviews";
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
import { useReadRelays } from "../../../hooks/use-client-relays";
import useSubject from "../../../hooks/use-subject";
import useTimelineLoader from "../../../hooks/use-timeline-loader";
import RelayReviewNote from "../components/relay-review-note";
@ -10,7 +10,7 @@ import { usePeopleListContext } from "../../../providers/local/people-list-provi
export default function RelayReviews({ relay }: { relay: string }) {
useAppTitle(`${relay} - Reviews`);
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const { filter } = usePeopleListContext();
const timeline = useTimelineLoader(

View File

@ -1,7 +1,7 @@
import { Button, Flex, Heading } from "@chakra-ui/react";
import { useNavigate } from "react-router-dom";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import useSubject from "../../hooks/use-subject";
import RelayReviewNote from "./components/relay-review-note";
@ -14,7 +14,7 @@ import { ChevronLeftIcon } from "../../components/icons";
function RelayReviewsPage() {
const navigate = useNavigate();
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const { filter } = usePeopleListContext();
const timeline = useTimelineLoader(

View File

@ -1,7 +1,7 @@
import { useMemo } from "react";
import { Card, CardBody, CardHeader, CardProps, Heading, Image, LinkBox, LinkOverlay } from "@chakra-ui/react";
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
import { useReadRelays } from "../../../hooks/use-client-relays";
import { useRelaySelectionRelays } from "../../../providers/local/relay-selection-provider";
import replaceableEventLoaderService from "../../../services/replaceable-event-requester";
import useSubject from "../../../hooks/use-subject";
@ -25,7 +25,7 @@ function useStreamerCardsCords(pubkey: string, relays: Iterable<string>) {
function StreamerCard({ cord, relay, ...props }: { cord: string; relay?: string } & CardProps) {
const contextRelays = useRelaySelectionRelays();
const readRelays = useReadRelayUrls(relay ? [...contextRelays, relay] : contextRelays);
const readRelays = useReadRelays(relay ? [...contextRelays, relay] : contextRelays);
const card = useReplaceableEvent(cord, readRelays);
if (!card || card.kind !== STREAMER_CARD_TYPE) return null;
@ -62,7 +62,7 @@ function StreamerCard({ cord, relay, ...props }: { cord: string; relay?: string
export default function StreamerCards({ pubkey, ...props }: Omit<CardProps, "children"> & { pubkey: string }) {
const contextRelays = useRelaySelectionRelays();
const readRelays = useReadRelayUrls(contextRelays);
const readRelays = useReadRelays(contextRelays);
const cardCords = useStreamerCardsCords(pubkey, readRelays);

View File

@ -13,7 +13,7 @@ import useTimelineLoader from "../../../hooks/use-timeline-loader";
import RequireCurrentAccount from "../../../providers/route/require-current-account";
import useCurrentAccount from "../../../hooks/use-current-account";
import { getEventUID } from "../../../helpers/nostr/events";
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
import { useReadRelays } from "../../../hooks/use-client-relays";
import { ChevronLeftIcon } from "../../../components/icons";
import RelaySelectionProvider from "../../../providers/local/relay-selection-provider";
import UsersCard from "./users-card";
@ -70,7 +70,7 @@ function StreamModerationDashboard({ stream }: { stream: ParsedStream }) {
function StreamModerationPage() {
const navigate = useNavigate();
const account = useCurrentAccount()!;
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const timeline = useTimelineLoader(account.pubkey + "-streams", readRelays, [
{

View File

@ -21,7 +21,7 @@ import { nip19 } from "nostr-tools";
import { Global, css } from "@emotion/react";
import { ParsedStream, STREAM_KIND, parseStreamEvent } from "../../../helpers/nostr/stream";
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
import { useReadRelays } from "../../../hooks/use-client-relays";
import { unique } from "../../../helpers/array";
import { LiveVideoPlayer } from "../../../components/live-video-player";
import StreamChat, { ChatDisplayMode } from "./stream-chat";
@ -243,7 +243,7 @@ export default function StreamView() {
if (!naddr) return <Navigate replace to="/streams" />;
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const [streamRelays, setStreamRelays] = useState<string[]>([]);
const subject = useMemo(() => {

View File

@ -18,7 +18,7 @@ import {
} from "../../../helpers/nostr/post";
import useCurrentAccount from "../../../hooks/use-current-account";
import { useSigningContext } from "../../../providers/global/signing-provider";
import { useWriteRelayUrls } from "../../../hooks/use-client-relays";
import { useWriteRelays } from "../../../hooks/use-client-relays";
import NostrPublishAction from "../../../classes/nostr-publish-action";
import MagicTextArea, { RefType } from "../../../components/magic-textarea";
import { useContextEmojis } from "../../../providers/global/emoji-provider";
@ -39,7 +39,7 @@ export default function ReplyForm({ item, onCancel, onSubmitted, replyKind = kin
const account = useCurrentAccount();
const emojis = useContextEmojis();
const { requestSignature } = useSigningContext();
const writeRelays = useWriteRelayUrls();
const writeRelays = useWriteRelays();
const threadMembers = useMemo(() => getThreadMembers(item, account?.pubkey), [item, account?.pubkey]);
const { setValue, getValues, watch, handleSubmit } = useForm({

View File

@ -6,7 +6,7 @@ import { nip19 } from "nostr-tools";
import Note from "../../components/note";
import { ThreadPost } from "./components/thread-post";
import VerticalPageLayout from "../../components/vertical-page-layout";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import { ThreadItem, buildThread } from "../../helpers/thread";
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
@ -101,7 +101,7 @@ function ThreadPage({
export default function ThreadView() {
const pointer = useParamsEventPointer("id");
const readRelays = useReadRelayUrls(pointer.relays);
const readRelays = useReadRelays(pointer.relays);
const focusedEvent = useSingleEvent(pointer.id, pointer.relays);
const { rootPointer, events, timeline } = useThreadTimelineLoader(focusedEvent, readRelays);

View File

@ -6,7 +6,7 @@ import VerticalPageLayout from "../../components/vertical-page-layout";
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
import IntersectionObserverProvider, {
useRegisterIntersectionEntity,
@ -41,7 +41,7 @@ export function DMTimelinePage() {
},
[clientMuteFilter],
);
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const timeline = useTimelineLoader(
`${listId ?? "global"}-dm-feed`,
readRelays,

View File

@ -18,7 +18,7 @@ import RequireCurrentAccount from "../../providers/route/require-current-account
import { useUsersMetadata } from "../../hooks/use-user-network";
import { MUTE_LIST_KIND, getPubkeysFromList, isPubkeyInList } from "../../helpers/nostr/lists";
import useUserContactList from "../../hooks/use-user-contact-list";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import replaceableEventLoaderService from "../../services/replaceable-event-requester";
import useSubjects from "../../hooks/use-subjects";
import { useUserMetadata } from "../../hooks/use-user-metadata";
@ -26,7 +26,7 @@ import { useNavigate } from "react-router-dom";
import { ChevronLeftIcon } from "../../components/icons";
export function useUsersMuteLists(pubkeys: string[], additionalRelays?: Iterable<string>) {
const readRelays = useReadRelayUrls(additionalRelays);
const readRelays = useReadRelays(additionalRelays);
const muteListSubjects = useMemo(() => {
return pubkeys.map((pubkey) => replaceableEventLoaderService.requestEvent(readRelays, MUTE_LIST_KIND, pubkey));
}, [pubkeys]);

View File

@ -3,7 +3,7 @@ import { Button, Flex, Select, useToast } from "@chakra-ui/react";
import dayjs from "dayjs";
import codes from "iso-language-codes";
import { useReadRelayUrls } from "../../../../hooks/use-client-relays";
import { useReadRelays } from "../../../../hooks/use-client-relays";
import useTimelineLoader from "../../../../hooks/use-timeline-loader";
import { getEventUID } from "../../../../helpers/nostr/events";
import {
@ -28,7 +28,7 @@ export default function NoteTextToSpeechPage({ note }: { note: NostrEvent }) {
const account = useCurrentAccount();
const [lang, setLang] = useState(navigator.language.split("-")[0] ?? "en");
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const requestReading = useCallback(async () => {
try {
const top8Relays = relayScoreboardService.getRankedRelays(readRelays).slice(0, 8);

View File

@ -18,7 +18,7 @@ import codes from "iso-language-codes";
import { DraftNostrEvent, NostrEvent } from "../../../../types/nostr-event";
import useTimelineLoader from "../../../../hooks/use-timeline-loader";
import { getEventUID } from "../../../../helpers/nostr/events";
import { useReadRelayUrls } from "../../../../hooks/use-client-relays";
import { useReadRelays } from "../../../../hooks/use-client-relays";
import useSubject from "../../../../hooks/use-subject";
import { useSigningContext } from "../../../../providers/global/signing-provider";
import relayScoreboardService from "../../../../services/relay-scoreboard";
@ -40,7 +40,7 @@ export function NoteTranslationsPage({ note }: { note: NostrEvent }) {
const toast = useToast();
const [lang, setLang] = useState(navigator.language.split("-")[0] ?? "en");
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const requestTranslation = useCallback(async () => {
try {
const top8Relays = relayScoreboardService.getRankedRelays(readRelays).slice(0, 8);

View File

@ -9,7 +9,7 @@ import VerticalPageLayout from "../../components/vertical-page-layout";
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
import IntersectionObserverProvider, {
useRegisterIntersectionEntity,
@ -77,7 +77,7 @@ export function UnknownTimelinePage() {
},
[clientMuteFilter],
);
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const timeline = useTimelineLoader(`${listId ?? "global"}-unknown-feed`, readRelays, filter, { eventFilter });
const events = useSubject(timeline.timeline);

View File

@ -2,7 +2,7 @@ import { memo, useMemo, useRef, useState } from "react";
import { NostrEvent } from "../../../types/nostr-event";
import { TORRENT_COMMENT_KIND } from "../../../helpers/nostr/torrents";
import { useReadRelayUrls } from "../../../hooks/use-client-relays";
import { useReadRelays } from "../../../hooks/use-client-relays";
import useThreadTimelineLoader from "../../../hooks/use-thread-timeline-loader";
import { ThreadItem, buildThread, countReplies } from "../../../helpers/thread";
import { useTimelineCurserIntersectionCallback } from "../../../hooks/use-timeline-cursor-intersection-callback";
@ -157,7 +157,7 @@ export const ThreadPost = memo(({ post, level = -1 }: { post: ThreadItem; level?
});
export default function TorrentComments({ torrent }: { torrent: NostrEvent }) {
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const { timeline, events } = useThreadTimelineLoader(torrent, readRelays, TORRENT_COMMENT_KIND);
const thread = useMemo(() => buildThread(events), [events]);

View File

@ -2,7 +2,7 @@ import { Flex, SimpleGrid } from "@chakra-ui/react";
import { useOutletContext } from "react-router-dom";
import { Event, kinds } from "nostr-tools";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import useSubject from "../../hooks/use-subject";
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
@ -29,7 +29,7 @@ function FollowerItem({ event }: { event: Event }) {
export default function UserFollowersTab() {
const { pubkey } = useOutletContext() as { pubkey: string };
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const timeline = useTimelineLoader(`${pubkey}-followers`, readRelays, {
"#p": [pubkey],

View File

@ -31,7 +31,7 @@ import { Outlet, useMatches, useNavigate } from "react-router-dom";
import { useUserMetadata } from "../../hooks/use-user-metadata";
import { getUserDisplayName } from "../../helpers/user-metadata";
import { useAppTitle } from "../../hooks/use-app-title";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import relayScoreboardService from "../../services/relay-scoreboard";
import { AdditionalRelayProvider } from "../../providers/local/additional-relay-context";
import { unique } from "../../helpers/array";
@ -68,7 +68,7 @@ const tabs = [
function useUserBestOutbox(pubkey: string, count: number = 4) {
const mailbox = useUserMailboxes(pubkey);
const relays = useReadRelayUrls(mailbox?.outbox);
const relays = useReadRelays(mailbox?.outbox);
const sorted = relayScoreboardService.getRankedRelays(relays);
return !count ? sorted : sorted.slice(0, count);
}

View File

@ -5,7 +5,7 @@ import { Link as RouterLink, useOutletContext } from "react-router-dom";
import UserAvatarLink from "../../components/user-avatar-link";
import UserLink from "../../components/user-link";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import { MUTE_LIST_KIND, PEOPLE_LIST_KIND, getListName, getPubkeysFromList } from "../../helpers/nostr/lists";
import useSubject from "../../hooks/use-subject";
import IntersectionObserverProvider, {
@ -45,7 +45,7 @@ const User = memo(({ pubkey, lists }: { pubkey: string; lists: NostrEvent[] }) =
export default function UserMutedByTab() {
const { pubkey } = useOutletContext() as { pubkey: string };
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const timeline = useTimelineLoader(`${pubkey}-muted-by`, readRelays, [
{ kinds: [MUTE_LIST_KIND], "#p": [pubkey] },
{ kinds: [PEOPLE_LIST_KIND], "#d": ["mute"], "#p": [pubkey] },

View File

@ -6,7 +6,7 @@ import { nip25 } from "nostr-tools";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { NostrEvent } from "../../types/nostr-event";
import { useAdditionalRelayContext } from "../../providers/local/additional-relay-context";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status";
import useSubject from "../../hooks/use-subject";
import IntersectionObserverProvider, {
@ -49,7 +49,7 @@ const Reaction = ({ reaction: reaction }: { reaction: NostrEvent }) => {
export default function UserReactionsTab() {
const { pubkey } = useOutletContext() as { pubkey: string };
const contextRelays = useAdditionalRelayContext();
const readRelays = useReadRelayUrls(contextRelays);
const readRelays = useReadRelays(contextRelays);
const timeline = useTimelineLoader(`${pubkey}-likes`, readRelays, { authors: [pubkey], kinds: [7] });

View File

@ -2,7 +2,7 @@ import { useOutletContext, Link as RouterLink } from "react-router-dom";
import { Button, Flex, Heading, Spacer, StackDivider, Tag, VStack } from "@chakra-ui/react";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import useSubject from "../../hooks/use-subject";
import { NostrEvent } from "../../types/nostr-event";
import RelayReviewNote from "../relays/components/relay-review-note";
@ -58,7 +58,7 @@ const UserRelaysTab = () => {
const { pubkey } = useOutletContext() as { pubkey: string };
const mailboxes = useUserMailboxes(pubkey);
const readRelays = useReadRelayUrls(mailboxes?.outbox);
const readRelays = useReadRelays(mailboxes?.outbox);
const timeline = useTimelineLoader(`${pubkey}-relay-reviews`, readRelays, {
authors: [pubkey],
kinds: [1985],

View File

@ -13,7 +13,7 @@ import { isProfileZap, isNoteZap, parseZapEvent, totalZaps } from "../../helpers
import useTimelineLoader from "../../hooks/use-timeline-loader";
import { NostrEvent, isATag, isETag, isPTag } from "../../types/nostr-event";
import { useAdditionalRelayContext } from "../../providers/local/additional-relay-context";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import TimelineActionAndStatus from "../../components/timeline-page/timeline-action-and-status";
import useSubject from "../../hooks/use-subject";
import IntersectionObserverProvider, {
@ -83,7 +83,7 @@ const UserZapsTab = () => {
const { pubkey } = useOutletContext() as { pubkey: string };
const [filter, setFilter] = useState("both");
const contextRelays = useAdditionalRelayContext();
const relays = useReadRelayUrls(contextRelays);
const relays = useReadRelays(contextRelays);
const eventFilter = useCallback(
(event: NostrEvent) => {

View File

@ -21,7 +21,7 @@ import SimpleLikeButton from "../../components/event-reactions/simple-like-butto
import SimpleDislikeButton from "../../components/event-reactions/simple-dislike-button";
import { ErrorBoundary } from "../../components/error-boundary";
import QuoteRepostButton from "../../components/note/components/quote-repost-button";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useReadRelays } from "../../hooks/use-client-relays";
import useTimelineLoader from "../../hooks/use-timeline-loader";
import useSubject from "../../hooks/use-subject";
import VideoCard from "./components/video-card";
@ -31,7 +31,7 @@ import { useBreakpointValue } from "../../providers/global/breakpoint-provider";
import SimpleBookmarkButton from "../../components/simple-bookmark-button";
function VideoRecommendations({ video }: { video: NostrEvent }) {
const readRelays = useReadRelayUrls();
const readRelays = useReadRelays();
const timeline = useTimelineLoader(video.pubkey + "-videos", readRelays, {
authors: [video.pubkey],
kinds: [FLARE_VIDEO_KIND],