add relay context

This commit is contained in:
hzrd149
2023-04-11 22:37:42 -05:00
parent 66ff04254b
commit dc33622e84
14 changed files with 104 additions and 73 deletions

View File

@@ -14,12 +14,14 @@ import { convertTimestampToDate } from "../helpers/date";
import useSubject from "../hooks/use-subject";
import settings from "../services/settings";
import EventVerificationIcon from "./event-verification-icon";
import { useReadRelayUrls } from "../hooks/use-client-relays";
const EmbeddedNote = ({ note }: { note: NostrEvent }) => {
const account = useCurrentAccount();
const showSignatureVerification = useSubject(settings.showSignatureVerification);
const contacts = useUserContacts(account.pubkey);
const readRelays = useReadRelayUrls();
const contacts = useUserContacts(account.pubkey, readRelays);
const following = contacts?.contacts || [];
return (

View File

@@ -35,6 +35,7 @@ import EventVerificationIcon from "../event-verification-icon";
import { ReplyButton } from "./buttons/reply-button";
import { RepostButton } from "./buttons/repost-button";
import { QuoteRepostButton } from "./buttons/quote-repost-button";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
export type NoteProps = {
event: NostrEvent;
@@ -47,7 +48,8 @@ export const Note = React.memo(({ event, maxHeight, variant = "outline" }: NoteP
const showReactions = useSubject(settings.showReactions);
const showSignatureVerification = useSubject(settings.showSignatureVerification);
const contacts = useUserContacts(account.pubkey);
const readRelays = useReadRelayUrls();
const contacts = useUserContacts(account.pubkey, readRelays);
const following = contacts?.contacts || [];
return (

View File

@@ -1,15 +1,11 @@
import { useMemo } from "react";
import { unique } from "../helpers/array";
import userContactsService from "../services/user-contacts";
import { useReadRelayUrls } from "./use-client-relays";
import useSubject from "./use-subject";
export function useUserContacts(pubkey: string, additionalRelays: string[] = [], alwaysRequest = false) {
const readRelays = useReadRelayUrls(additionalRelays);
export function useUserContacts(pubkey: string, relays: string[], alwaysRequest = false) {
const observable = useMemo(
() => userContactsService.requestContacts(pubkey, readRelays, alwaysRequest),
[pubkey, readRelays, alwaysRequest]
() => userContactsService.requestContacts(pubkey, relays, alwaysRequest),
[pubkey, relays.join("|"), alwaysRequest]
);
const contacts = useSubject(observable);

View File

@@ -2,7 +2,7 @@ import { useMemo } from "react";
import userFollowersService from "../services/user-followers";
import useSubject from "./use-subject";
export function useUserFollowers(pubkey: string, relays: string[] = [], alwaysRequest = false) {
export function useUserFollowers(pubkey: string, relays: string[], alwaysRequest = false) {
const subject = useMemo(
() => userFollowersService.requestFollowers(pubkey, relays, alwaysRequest),
[pubkey, alwaysRequest]

View File

@@ -0,0 +1,24 @@
import React, { useContext } from "react";
import { unique } from "../helpers/array";
import { safeRelayUrl } from "../helpers/url";
export const RelayContext = React.createContext<string[]>([]);
export function useAdditionalRelayContext() {
return useContext(RelayContext) ?? [];
}
export function AdditionalRelayProvider({
relays,
children,
extend = true,
}: {
relays: string[];
children: React.ReactNode;
extend?: boolean;
}) {
const parentRelays = useAdditionalRelayContext();
const safeUrls = (extend ? [...parentRelays, ...relays] : relays).map(safeRelayUrl).filter(Boolean) as string[];
return <RelayContext.Provider value={unique(safeUrls)}>{children}</RelayContext.Provider>;
}

View File

@@ -24,10 +24,10 @@ function mergeNext(subject: Subject<string[] | null>, next: string[]) {
subject.next(arr);
}
function requestFollowers(pubkey: string, additionalRelays: string[] = [], alwaysRequest = false) {
function requestFollowers(pubkey: string, relays: string[], alwaysRequest = false) {
let subject = subjects.getSubject(pubkey);
if (additionalRelays.length) subjects.addRelays(pubkey, additionalRelays);
if (relays.length) subjects.addRelays(pubkey, relays);
db.getAllKeysFromIndex("userFollows", "follows", pubkey).then((cached) => {
mergeNext(subject, cached);
@@ -50,9 +50,6 @@ function flushRequests() {
if (pubkeys.size === 0) return;
const clientRelays = clientRelaysService.getReadUrls();
for (const url of clientRelays) relayUrls.add(url);
const query: NostrQuery = { kinds: [3], "#p": Array.from(pubkeys) };
subscription.setRelays(Array.from(relayUrls));

View File

@@ -1,5 +1,4 @@
import Subject from "../classes/subject";
import { normalizeRelayConfigs } from "../helpers/relay";
import userContactsService from "./user-contacts";
import userRelaysService, { UserRelays } from "./user-relays";

View File

@@ -14,9 +14,9 @@ import RepostNote from "../../components/repost-note";
export default function FollowingTab() {
const account = useCurrentAccount();
const relays = useReadRelayUrls();
const readRelays = useReadRelayUrls();
const { openModal } = useContext(PostModalContext);
const contacts = useUserContacts(account.pubkey);
const contacts = useUserContacts(account.pubkey, readRelays);
const [search, setSearch] = useSearchParams();
const showReplies = search.has("replies");
const onToggle = () => {
@@ -26,7 +26,7 @@ export default function FollowingTab() {
const following = contacts?.contacts || [];
const { events, loading, loadMore } = useTimelineLoader(
`${account.pubkey}-following-posts`,
relays,
readRelays,
{ authors: following, kinds: [1, 6], since: moment().subtract(2, "hour").unix() },
{ pageSize: moment.duration(2, "hour").asSeconds(), enabled: following.length > 0 }
);

View File

@@ -8,7 +8,7 @@ import { Bech32Prefix, normalizeToBech32 } from "../../../helpers/nip19";
import { UserDnsIdentityIcon } from "../../../components/user-dns-identity";
export const UserCard = ({ pubkey, relay }: { pubkey: string; relay?: string }) => {
const metadata = useUserMetadata(pubkey);
const metadata = useUserMetadata(pubkey, relay ? [relay] : []);
return (
<Box borderWidth="1px" borderRadius="lg" pl="3" pr="3" pt="2" pb="2" overflow="hidden">

View File

@@ -5,11 +5,15 @@ import { useUserFollowers } from "../../hooks/use-user-followers";
import { useOutletContext } from "react-router-dom";
import { usePaginatedList } from "../../hooks/use-paginated-list";
import { PaginationControls } from "../../components/pagination-controls";
import { useAdditionalRelayContext } from "../../providers/additional-relay-context";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
const UserFollowersTab = () => {
const { pubkey } = useOutletContext() as { pubkey: string };
const { isOpen, onToggle } = useDisclosure();
const followers = useUserFollowers(pubkey, [], isOpen);
const contextRelays = useAdditionalRelayContext();
const relays = useReadRelayUrls(contextRelays);
const followers = useUserFollowers(pubkey, relays, isOpen);
const pagination = usePaginatedList(followers ?? [], { pageSize: 3 * 10 });

View File

@@ -6,10 +6,12 @@ import { useUserContacts } from "../../hooks/use-user-contacts";
import { useOutletContext } from "react-router-dom";
import { usePaginatedList } from "../../hooks/use-paginated-list";
import { PaginationControls } from "../../components/pagination-controls";
import { useAdditionalRelayContext } from "../../providers/additional-relay-context";
const UserFollowingTab = () => {
const { pubkey } = useOutletContext() as { pubkey: string };
const contacts = useUserContacts(pubkey, [], true);
const contextRelays = useAdditionalRelayContext();
const contacts = useUserContacts(pubkey, contextRelays, true);
const pagination = usePaginatedList(contacts?.contacts ?? [], { pageSize: 3 * 10 });

View File

@@ -1,4 +1,4 @@
import { Flex, Spinner, Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react";
import { Flex, Image, Spinner, Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react";
import { Outlet, useLoaderData, useMatches, useNavigate } from "react-router-dom";
import { useUserMetadata } from "../../hooks/use-user-metadata";
import { getUserDisplayName } from "../../helpers/user-metadata";
@@ -7,6 +7,11 @@ import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip19";
import { useAppTitle } from "../../hooks/use-app-title";
import Header from "./components/header";
import { Suspense } from "react";
import useFallbackUserRelays from "../../hooks/use-fallback-user-relays";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import relayScoreboardService from "../../services/relay-scoreboard";
import { RelayMode } from "../../classes/relay";
import { AdditionalRelayProvider } from "../../providers/additional-relay-context";
const tabs = [
{ label: "Notes", path: "notes" },
@@ -17,10 +22,22 @@ const tabs = [
{ label: "Reports", path: "reports" },
];
function useUserTop4Relays(pubkey: string) {
// get user relays
const userRelays = useFallbackUserRelays(pubkey)
.filter((r) => r.mode & RelayMode.WRITE)
.map((r) => r.url);
// merge the users relays with client relays
const readRelays = useReadRelayUrls();
// find the top 4
return userRelays.length === 0 ? readRelays : relayScoreboardService.getRankedRelays(userRelays).slice(0, 4);
}
const UserView = () => {
const isMobile = useIsMobile();
const navigate = useNavigate();
const { pubkey } = useLoaderData() as { pubkey: string };
const userTopRelays = useUserTop4Relays(pubkey);
const matches = useMatches();
const lastMatch = matches[matches.length - 1];
@@ -33,35 +50,37 @@ const UserView = () => {
useAppTitle(getUserDisplayName(metadata, npub ?? pubkey));
return (
<Flex direction="column" alignItems="stretch" gap="2" overflow={isMobile ? "auto" : "hidden"} height="100%">
{/* {metadata?.banner && <Image src={metadata.banner} />} */}
<Header pubkey={pubkey} />
<Tabs
display="flex"
flexDirection="column"
flexGrow="1"
overflow={isMobile ? undefined : "hidden"}
isLazy
index={activeTab}
onChange={(v) => navigate(tabs[v].path)}
>
<TabList overflowX="auto" overflowY="hidden" flexShrink={0}>
{tabs.map(({ label }) => (
<Tab key={label}>{label}</Tab>
))}
</TabList>
<AdditionalRelayProvider relays={userTopRelays}>
<Flex direction="column" alignItems="stretch" gap="2" overflow={isMobile ? "auto" : "hidden"} height="100%">
{/* {metadata?.banner && <Image src={metadata.banner} mb={-120} />} */}
<Header pubkey={pubkey} />
<Tabs
display="flex"
flexDirection="column"
flexGrow="1"
overflow={isMobile ? undefined : "hidden"}
isLazy
index={activeTab}
onChange={(v) => navigate(tabs[v].path)}
>
<TabList overflowX="auto" overflowY="hidden" flexShrink={0}>
{tabs.map(({ label }) => (
<Tab key={label}>{label}</Tab>
))}
</TabList>
<TabPanels overflow={isMobile ? undefined : "auto"} height="100%">
{tabs.map(({ label }) => (
<TabPanel key={label} pr={0} pl={0}>
<Suspense fallback={<Spinner />}>
<Outlet context={{ pubkey }} />
</Suspense>
</TabPanel>
))}
</TabPanels>
</Tabs>
</Flex>
<TabPanels overflow={isMobile ? undefined : "auto"} height="100%">
{tabs.map(({ label }) => (
<TabPanel key={label} pr={0} pl={0}>
<Suspense fallback={<Spinner />}>
<Outlet context={{ pubkey }} />
</Suspense>
</TabPanel>
))}
</TabPanels>
</Tabs>
</Flex>
</AdditionalRelayProvider>
);
};

View File

@@ -18,33 +18,23 @@ import {
} from "@chakra-ui/react";
import moment from "moment";
import { useOutletContext } from "react-router-dom";
import { RelayMode } from "../../classes/relay";
import { RelayIcon } from "../../components/icons";
import { Note } from "../../components/note";
import RepostNote from "../../components/repost-note";
import { isReply, isRepost, truncatedId } from "../../helpers/nostr-event";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import useFallbackUserRelays from "../../hooks/use-fallback-user-relays";
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
import relayScoreboardService from "../../services/relay-scoreboard";
import { useAdditionalRelayContext } from "../../providers/additional-relay-context";
const UserNotesTab = () => {
const { pubkey } = useOutletContext() as { pubkey: string };
// get user relays
const userRelays = useFallbackUserRelays(pubkey)
.filter((r) => r.mode & RelayMode.WRITE)
.map((r) => r.url);
// merge the users relays with client relays
const readRelays = useReadRelayUrls();
// find the top 4
const relays = userRelays.length === 0 ? readRelays : relayScoreboardService.getRankedRelays(userRelays).slice(0, 4);
const contextRelays = useAdditionalRelayContext();
const { isOpen: showReplies, onToggle: toggleReplies } = useDisclosure();
const { isOpen: hideReposts, onToggle: toggleReposts } = useDisclosure();
const { events, loading, loadMore } = useTimelineLoader(
`${truncatedId(pubkey)}-notes`,
relays,
contextRelays,
{ authors: [pubkey], kinds: [1, 6] },
{ pageSize: moment.duration(2, "day").asSeconds(), startLimit: 20 }
);
@@ -77,7 +67,7 @@ const UserNotesTab = () => {
<PopoverCloseButton />
<PopoverBody>
<UnorderedList>
{relays.map((url) => (
{contextRelays.map((url) => (
<ListItem key={url}>{url}</ListItem>
))}
</UnorderedList>

View File

@@ -2,7 +2,6 @@ import { Box, Button, Flex, Select, Spinner, Text, useDisclosure } from "@chakra
import moment from "moment";
import { useState } from "react";
import { useOutletContext } from "react-router-dom";
import { RelayMode } from "../../classes/relay";
import { ErrorBoundary, ErrorFallback } from "../../components/error-boundary";
import { LightningIcon } from "../../components/icons";
import { NoteLink } from "../../components/note-link";
@@ -12,10 +11,10 @@ import { readablizeSats } from "../../helpers/bolt11";
import { convertTimestampToDate } from "../../helpers/date";
import { truncatedId } from "../../helpers/nostr-event";
import { isProfileZap, isNoteZap, parseZapNote, totalZaps } from "../../helpers/zaps";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import useFallbackUserRelays from "../../hooks/use-fallback-user-relays";
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
import { NostrEvent } from "../../types/nostr-event";
import { useAdditionalRelayContext } from "../../providers/additional-relay-context";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
const Zap = ({ zapEvent }: { zapEvent: NostrEvent }) => {
const { isOpen, onToggle } = useDisclosure();
@@ -66,11 +65,8 @@ const Zap = ({ zapEvent }: { zapEvent: NostrEvent }) => {
const UserZapsTab = () => {
const { pubkey } = useOutletContext() as { pubkey: string };
const [filter, setFilter] = useState("both");
// get user relays
const userRelays = useFallbackUserRelays(pubkey)
.filter((r) => r.mode & RelayMode.WRITE)
.map((r) => r.url);
const relays = useReadRelayUrls(userRelays);
const contextRelays = useAdditionalRelayContext();
const relays = useReadRelayUrls(contextRelays);
const { events, loading, loadMore } = useTimelineLoader(
`${truncatedId(pubkey)}-zaps`,