mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-19 03:51:34 +02:00
add relay context
This commit is contained in:
@@ -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 (
|
||||
|
@@ -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 (
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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]
|
||||
|
24
src/providers/additional-relay-context.tsx
Normal file
24
src/providers/additional-relay-context.tsx
Normal 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>;
|
||||
}
|
@@ -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));
|
||||
|
@@ -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";
|
||||
|
||||
|
@@ -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 }
|
||||
);
|
||||
|
@@ -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">
|
||||
|
@@ -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 });
|
||||
|
||||
|
@@ -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 });
|
||||
|
||||
|
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -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`,
|
||||
|
Reference in New Issue
Block a user