mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-23 06:54:08 +02:00
cleanup fetching user relays
This commit is contained in:
parent
4506c82bd0
commit
d4aef8f8d6
5
.changeset/fluffy-pillows-juggle.md
Normal file
5
.changeset/fluffy-pillows-juggle.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"nostrudel": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
cleanup fetching user relays
|
@ -19,23 +19,23 @@ export class CachedPubkeyEventRequester extends PubkeyEventRequester {
|
|||||||
requestEvent(pubkey: string, relays: string[], alwaysRequest = false) {
|
requestEvent(pubkey: string, relays: string[], alwaysRequest = false) {
|
||||||
const sub = this.getSubject(pubkey);
|
const sub = this.getSubject(pubkey);
|
||||||
|
|
||||||
if (!sub.value || alwaysRequest) {
|
if (!sub.value) {
|
||||||
// only call this.readCache once per pubkey
|
// only call this.readCache once per pubkey
|
||||||
|
if (!this.readCacheDedupe.has(pubkey)) {
|
||||||
const promise = this.readCacheDedupe.get(pubkey) || this.readCache(pubkey);
|
const promise = this.readCacheDedupe.get(pubkey) || this.readCache(pubkey);
|
||||||
this.readCacheDedupe.set(pubkey, promise);
|
this.readCacheDedupe.set(pubkey, promise);
|
||||||
|
|
||||||
promise.then((cached) => {
|
promise.then((cached) => {
|
||||||
this.readCacheDedupe.delete(pubkey);
|
this.readCacheDedupe.delete(pubkey);
|
||||||
|
|
||||||
if (cached && (!sub.value || cached.created_at > sub.value.created_at)) {
|
if (cached) this.handleEvent(cached);
|
||||||
sub.next(cached);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sub.value || alwaysRequest) {
|
if (!sub.value || alwaysRequest) super.requestEvent(pubkey, relays);
|
||||||
super.requestEvent(pubkey, relays, alwaysRequest);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else if (alwaysRequest) {
|
||||||
|
super.requestEvent(pubkey, relays);
|
||||||
|
}
|
||||||
|
|
||||||
return sub;
|
return sub;
|
||||||
}
|
}
|
||||||
|
@ -53,10 +53,10 @@ class PubkeyEventRequestSubscription {
|
|||||||
return this.subjects.get(pubkey);
|
return this.subjects.get(pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
requestEvent(pubkey: string, alwaysRequest = false) {
|
requestEvent(pubkey: string) {
|
||||||
const sub = this.subjects.get(pubkey);
|
const sub = this.subjects.get(pubkey);
|
||||||
|
|
||||||
if (!sub.value || alwaysRequest) {
|
if (!sub.value) {
|
||||||
this.requestNext.add(pubkey);
|
this.requestNext.add(pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,11 +129,11 @@ export class PubkeyEventRequester {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private connected = new WeakSet<any>();
|
private connected = new WeakSet<any>();
|
||||||
requestEvent(pubkey: string, relays: string[], alwaysRequest = false) {
|
requestEvent(pubkey: string, relays: string[]) {
|
||||||
const sub = this.subjects.get(pubkey);
|
const sub = this.subjects.get(pubkey);
|
||||||
|
|
||||||
for (const relay of relays) {
|
for (const relay of relays) {
|
||||||
const relaySub = this.subscriptions.get(relay).requestEvent(pubkey, alwaysRequest);
|
const relaySub = this.subscriptions.get(relay).requestEvent(pubkey);
|
||||||
|
|
||||||
if (!this.connected.has(relaySub)) {
|
if (!this.connected.has(relaySub)) {
|
||||||
relaySub.subscribe((event) => event && this.handleEvent(event));
|
relaySub.subscribe((event) => event && this.handleEvent(event));
|
||||||
|
@ -5,10 +5,14 @@ import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip19";
|
|||||||
import { useUserMetadata } from "../../hooks/use-user-metadata";
|
import { useUserMetadata } from "../../hooks/use-user-metadata";
|
||||||
import RawValue from "./raw-value";
|
import RawValue from "./raw-value";
|
||||||
import RawJson from "./raw-json";
|
import RawJson from "./raw-json";
|
||||||
|
import { useSharableProfileId } from "../../hooks/use-shareable-profile-id";
|
||||||
|
import userRelaysService from "../../services/user-relays";
|
||||||
|
|
||||||
export default function UserDebugModal({ pubkey, ...props }: { pubkey: string } & Omit<ModalProps, "children">) {
|
export default function UserDebugModal({ pubkey, ...props }: { pubkey: string } & Omit<ModalProps, "children">) {
|
||||||
const npub = useMemo(() => normalizeToBech32(pubkey, Bech32Prefix.Pubkey), [pubkey]);
|
const npub = useMemo(() => normalizeToBech32(pubkey, Bech32Prefix.Pubkey), [pubkey]);
|
||||||
const metadata = useUserMetadata(pubkey);
|
const metadata = useUserMetadata(pubkey);
|
||||||
|
const nprofile = useSharableProfileId(pubkey);
|
||||||
|
const relays = userRelaysService.requester.getSubject(pubkey).value;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal {...props}>
|
<Modal {...props}>
|
||||||
@ -18,8 +22,10 @@ export default function UserDebugModal({ pubkey, ...props }: { pubkey: string }
|
|||||||
<ModalBody overflow="auto" p="4">
|
<ModalBody overflow="auto" p="4">
|
||||||
<Flex gap="2" direction="column">
|
<Flex gap="2" direction="column">
|
||||||
<RawValue heading="Hex pubkey" value={pubkey} />
|
<RawValue heading="Hex pubkey" value={pubkey} />
|
||||||
{npub && <RawValue heading="Encoded pubkey (NIP-19)" value={npub} />}
|
{npub && <RawValue heading="npub" value={npub} />}
|
||||||
<RawJson heading="Metadata (kind 0)" json={metadata} />
|
<RawValue heading="nprofile" value={nprofile} />
|
||||||
|
<RawJson heading="Parsed Metadata (kind 0)" json={metadata} />
|
||||||
|
{relays && <RawJson heading="Relay List (kind 10002)" json={relays} />}
|
||||||
</Flex>
|
</Flex>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
import { useMemo } from "react";
|
|
||||||
import { normalizeRelayConfigs } from "../helpers/relay";
|
|
||||||
import userRelaysFallbackService from "../services/user-relays-fallback";
|
|
||||||
import { useReadRelayUrls } from "./use-client-relays";
|
|
||||||
import useSubject from "./use-subject";
|
|
||||||
|
|
||||||
export default function useFallbackUserRelays(pubkey: string, additionalRelays: string[] = [], alwaysFetch = false) {
|
|
||||||
const readRelays = useReadRelayUrls(additionalRelays);
|
|
||||||
|
|
||||||
const observable = useMemo(
|
|
||||||
() => userRelaysFallbackService.requestRelays(pubkey, readRelays, alwaysFetch),
|
|
||||||
[pubkey, readRelays.join("|"), alwaysFetch]
|
|
||||||
);
|
|
||||||
const userRelays = useSubject(observable);
|
|
||||||
|
|
||||||
return userRelays ? normalizeRelayConfigs(userRelays.relays) : [];
|
|
||||||
}
|
|
@ -1,11 +1,11 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import useFallbackUserRelays from "./use-fallback-user-relays";
|
|
||||||
import relayScoreboardService from "../services/relay-scoreboard";
|
import relayScoreboardService from "../services/relay-scoreboard";
|
||||||
import { RelayMode } from "../classes/relay";
|
import { RelayMode } from "../classes/relay";
|
||||||
import { nip19 } from "nostr-tools";
|
import { nip19 } from "nostr-tools";
|
||||||
|
import { useUserRelays } from "./use-user-relays";
|
||||||
|
|
||||||
export function useSharableProfileId(pubkey: string) {
|
export function useSharableProfileId(pubkey: string) {
|
||||||
const userRelays = useFallbackUserRelays(pubkey);
|
const userRelays = useUserRelays(pubkey);
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const writeUrls = userRelays.filter((r) => r.mode & RelayMode.WRITE).map((r) => r.url);
|
const writeUrls = userRelays.filter((r) => r.mode & RelayMode.WRITE).map((r) => r.url);
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import userRelaysService from "../services/user-relays";
|
import userRelaysService from "../services/user-relays";
|
||||||
import { useReadRelayUrls } from "./use-client-relays";
|
|
||||||
import useSubject from "./use-subject";
|
import useSubject from "./use-subject";
|
||||||
|
import { useReadRelayUrls } from "./use-client-relays";
|
||||||
|
|
||||||
export function useUserRelays(pubkey: string, additionalRelays: string[] = [], alwaysRequest = false) {
|
export function useUserRelays(pubkey: string, additionalRelays: string[] = [], alwaysRequest = false) {
|
||||||
const readRelays = useReadRelayUrls(additionalRelays);
|
const relays = useReadRelayUrls(additionalRelays);
|
||||||
|
const subject = useMemo(
|
||||||
const observable = useMemo(
|
() => userRelaysService.requestRelays(pubkey, relays, alwaysRequest),
|
||||||
() => userRelaysService.requestRelays(pubkey, readRelays, alwaysRequest),
|
[pubkey, relays.join("|"), alwaysRequest]
|
||||||
[pubkey, readRelays.join("|"), alwaysRequest]
|
|
||||||
);
|
);
|
||||||
const userRelays = useSubject(observable);
|
const userRelays = useSubject(subject);
|
||||||
|
|
||||||
return userRelays;
|
return userRelays?.relays ?? [];
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { unique } from "../helpers/array";
|
|||||||
import { DraftNostrEvent, RTag } from "../types/nostr-event";
|
import { DraftNostrEvent, RTag } from "../types/nostr-event";
|
||||||
import accountService from "./account";
|
import accountService from "./account";
|
||||||
import { RelayConfig, RelayMode } from "../classes/relay";
|
import { RelayConfig, RelayMode } from "../classes/relay";
|
||||||
import userRelaysService, { UserRelays } from "./user-relays";
|
import userRelaysService, { ParsedUserRelays } from "./user-relays";
|
||||||
import { PersistentSubject, Subject } from "../classes/subject";
|
import { PersistentSubject, Subject } from "../classes/subject";
|
||||||
import signingService from "./signing";
|
import signingService from "./signing";
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ class ClientRelayService {
|
|||||||
readRelays = new PersistentSubject<RelayConfig[]>([]);
|
readRelays = new PersistentSubject<RelayConfig[]>([]);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
let lastSubject: Subject<UserRelays> | undefined;
|
let lastSubject: Subject<ParsedUserRelays> | undefined;
|
||||||
accountService.current.subscribe((account) => {
|
accountService.current.subscribe((account) => {
|
||||||
this.relays.next([]);
|
this.relays.next([]);
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ class ClientRelayService {
|
|||||||
this.relays.subscribe((relays) => this.readRelays.next(relays.filter((r) => r.mode & RelayMode.READ)));
|
this.relays.subscribe((relays) => this.readRelays.next(relays.filter((r) => r.mode & RelayMode.READ)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleRelayChanged(relays: UserRelays) {
|
private handleRelayChanged(relays: ParsedUserRelays) {
|
||||||
this.relays.next(relays.relays);
|
this.relays.next(relays.relays);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,15 +6,14 @@ import accountService from "./account";
|
|||||||
import clientRelaysService from "./client-relays";
|
import clientRelaysService from "./client-relays";
|
||||||
import relayScoreboardService from "./relay-scoreboard";
|
import relayScoreboardService from "./relay-scoreboard";
|
||||||
import userContactsService, { UserContacts } from "./user-contacts";
|
import userContactsService, { UserContacts } from "./user-contacts";
|
||||||
import { UserRelays } from "./user-relays";
|
import userRelaysService, { ParsedUserRelays } from "./user-relays";
|
||||||
import userRelaysFallbackService from "./user-relays-fallback";
|
|
||||||
|
|
||||||
type pubkey = string;
|
type pubkey = string;
|
||||||
type relay = string;
|
type relay = string;
|
||||||
|
|
||||||
class PubkeyRelayAssignmentService {
|
class PubkeyRelayAssignmentService {
|
||||||
pubkeys = new Map<pubkey, relay[]>();
|
pubkeys = new Map<pubkey, relay[]>();
|
||||||
pubkeyRelays = new SuperMap<string, Subject<UserRelays>>(() => new Subject());
|
pubkeyRelays = new SuperMap<string, Subject<ParsedUserRelays>>(() => new Subject());
|
||||||
assignments = new PersistentSubject<Record<pubkey, relay[]>>({});
|
assignments = new PersistentSubject<Record<pubkey, relay[]>>({});
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -46,7 +45,7 @@ class PubkeyRelayAssignmentService {
|
|||||||
this.pubkeys.set(pubkey, relays);
|
this.pubkeys.set(pubkey, relays);
|
||||||
|
|
||||||
const readRelays = clientRelaysService.getReadUrls();
|
const readRelays = clientRelaysService.getReadUrls();
|
||||||
const subject = userRelaysFallbackService.requestRelays(pubkey, unique([...readRelays, ...relays]));
|
const subject = userRelaysService.requestRelays(pubkey, unique([...readRelays, ...relays]));
|
||||||
this.pubkeyRelays.set(pubkey, subject);
|
this.pubkeyRelays.set(pubkey, subject);
|
||||||
// subject.subscribe(this.updateAssignments, this);
|
// subject.subscribe(this.updateAssignments, this);
|
||||||
}
|
}
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
import Subject from "../classes/subject";
|
|
||||||
import userContactsService from "./user-contacts";
|
|
||||||
import userRelaysService, { UserRelays } from "./user-relays";
|
|
||||||
|
|
||||||
class UserRelaysFallbackService {
|
|
||||||
subjects = new Map<string, Subject<UserRelays>>();
|
|
||||||
|
|
||||||
requestRelays(pubkey: string, relays: string[], alwaysFetch = false) {
|
|
||||||
let subject = this.subjects.get(pubkey);
|
|
||||||
if (!subject) {
|
|
||||||
subject = new Subject();
|
|
||||||
this.subjects.set(pubkey, subject);
|
|
||||||
|
|
||||||
subject.connectWithHandler(userRelaysService.getSubject(pubkey), (userRelays, next, value) => {
|
|
||||||
if (!value || userRelays.created_at > value.created_at) {
|
|
||||||
next(userRelays);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
subject.connectWithHandler(userContactsService.getSubject(pubkey), (contacts, next, value) => {
|
|
||||||
if (contacts.relays.length > 0 && (!value || contacts.created_at > value.created_at)) {
|
|
||||||
next({ pubkey: contacts.pubkey, relays: contacts.relays, created_at: contacts.created_at });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
userRelaysService.requestRelays(pubkey, relays, alwaysFetch);
|
|
||||||
userContactsService.requestContacts(pubkey, relays, alwaysFetch);
|
|
||||||
|
|
||||||
return subject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const userRelaysFallbackService = new UserRelaysFallbackService();
|
|
||||||
|
|
||||||
if (import.meta.env.DEV) {
|
|
||||||
// @ts-ignore
|
|
||||||
window.userRelaysFallbackService = userRelaysFallbackService;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default userRelaysFallbackService;
|
|
@ -6,14 +6,15 @@ import { CachedPubkeyEventRequester } from "../classes/cached-pubkey-event-reque
|
|||||||
import { SuperMap } from "../classes/super-map";
|
import { SuperMap } from "../classes/super-map";
|
||||||
import Subject from "../classes/subject";
|
import Subject from "../classes/subject";
|
||||||
import { normalizeRelayConfigs } from "../helpers/relay";
|
import { normalizeRelayConfigs } from "../helpers/relay";
|
||||||
|
import userContactsService from "./user-contacts";
|
||||||
|
|
||||||
export type UserRelays = {
|
export type ParsedUserRelays = {
|
||||||
pubkey: string;
|
pubkey: string;
|
||||||
relays: RelayConfig[];
|
relays: RelayConfig[];
|
||||||
created_at: number;
|
created_at: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
function parseRelaysEvent(event: NostrEvent): UserRelays {
|
function parseRelaysEvent(event: NostrEvent): ParsedUserRelays {
|
||||||
return {
|
return {
|
||||||
pubkey: event.pubkey,
|
pubkey: event.pubkey,
|
||||||
relays: normalizeRelayConfigs(event.tags.filter(isRTag).map(parseRTag)),
|
relays: normalizeRelayConfigs(event.tags.filter(isRTag).map(parseRTag)),
|
||||||
@ -25,25 +26,27 @@ class UserRelaysService {
|
|||||||
requester: CachedPubkeyEventRequester;
|
requester: CachedPubkeyEventRequester;
|
||||||
constructor() {
|
constructor() {
|
||||||
this.requester = new CachedPubkeyEventRequester(10002, "user-relays");
|
this.requester = new CachedPubkeyEventRequester(10002, "user-relays");
|
||||||
this.requester.readCache = this.readCache;
|
this.requester.readCache = (pubkey) => db.get("userRelays", pubkey);
|
||||||
this.requester.writeCache = this.writeCache;
|
this.requester.writeCache = (pubkey, event) => db.put("userRelays", event);
|
||||||
}
|
}
|
||||||
|
|
||||||
readCache(pubkey: string) {
|
private subjects = new SuperMap<string, Subject<ParsedUserRelays>>(() => new Subject<ParsedUserRelays>());
|
||||||
return db.get("userRelays", pubkey);
|
getRelays(pubkey: string) {
|
||||||
}
|
|
||||||
writeCache(pubkey: string, event: NostrEvent) {
|
|
||||||
return db.put("userRelays", event);
|
|
||||||
}
|
|
||||||
|
|
||||||
private subjects = new SuperMap<string, Subject<UserRelays>>(() => new Subject<UserRelays>());
|
|
||||||
getSubject(pubkey: string) {
|
|
||||||
return this.subjects.get(pubkey);
|
return this.subjects.get(pubkey);
|
||||||
}
|
}
|
||||||
requestRelays(pubkey: string, relays: string[], alwaysRequest = false) {
|
requestRelays(pubkey: string, relays: string[], alwaysRequest = false) {
|
||||||
const sub = this.subjects.get(pubkey);
|
const sub = this.subjects.get(pubkey);
|
||||||
const requestSub = this.requester.requestEvent(pubkey, relays, alwaysRequest);
|
const requestSub = this.requester.requestEvent(pubkey, relays, alwaysRequest);
|
||||||
sub.connectWithHandler(requestSub, (event, next) => next(parseRelaysEvent(event)));
|
sub.connectWithHandler(requestSub, (event, next) => next(parseRelaysEvent(event)));
|
||||||
|
|
||||||
|
// also fetch the relays from the users contacts
|
||||||
|
const contactsSub = userContactsService.requestContacts(pubkey, relays, alwaysRequest);
|
||||||
|
sub.connectWithHandler(contactsSub, (contacts, next, value) => {
|
||||||
|
if (contacts.relays.length > 0 && (!value || contacts.created_at > value.created_at)) {
|
||||||
|
next({ pubkey: contacts.pubkey, relays: contacts.relays, created_at: contacts.created_at });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return sub;
|
return sub;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import { Flex, Heading, SkeletonText, Text, Link, IconButton, Image } from "@chakra-ui/react";
|
import { Flex, Heading, SkeletonText, Text, Link, IconButton } from "@chakra-ui/react";
|
||||||
import { nip19 } from "nostr-tools";
|
|
||||||
import { useMemo } from "react";
|
|
||||||
import { useNavigate, Link as RouterLink } from "react-router-dom";
|
import { useNavigate, Link as RouterLink } from "react-router-dom";
|
||||||
import { RelayMode } from "../../../classes/relay";
|
|
||||||
import { CopyIconButton } from "../../../components/copy-icon-button";
|
import { CopyIconButton } from "../../../components/copy-icon-button";
|
||||||
import { ChatIcon, ExternalLinkIcon, KeyIcon, SettingsIcon } from "../../../components/icons";
|
import { ChatIcon, ExternalLinkIcon, KeyIcon, SettingsIcon } from "../../../components/icons";
|
||||||
import { QrIconButton } from "./share-qr-button";
|
import { QrIconButton } from "./share-qr-button";
|
||||||
@ -15,23 +12,9 @@ import { truncatedId } from "../../../helpers/nostr-event";
|
|||||||
import { fixWebsiteUrl, getUserDisplayName } from "../../../helpers/user-metadata";
|
import { fixWebsiteUrl, getUserDisplayName } from "../../../helpers/user-metadata";
|
||||||
import { useCurrentAccount } from "../../../hooks/use-current-account";
|
import { useCurrentAccount } from "../../../hooks/use-current-account";
|
||||||
import { useIsMobile } from "../../../hooks/use-is-mobile";
|
import { useIsMobile } from "../../../hooks/use-is-mobile";
|
||||||
import useFallbackUserRelays from "../../../hooks/use-fallback-user-relays";
|
|
||||||
import { useUserMetadata } from "../../../hooks/use-user-metadata";
|
import { useUserMetadata } from "../../../hooks/use-user-metadata";
|
||||||
import relayScoreboardService from "../../../services/relay-scoreboard";
|
|
||||||
import { UserProfileMenu } from "./user-profile-menu";
|
import { UserProfileMenu } from "./user-profile-menu";
|
||||||
|
|
||||||
function useUserShareLink(pubkey: string) {
|
|
||||||
const userRelays = useFallbackUserRelays(pubkey);
|
|
||||||
|
|
||||||
return useMemo(() => {
|
|
||||||
const writeUrls = userRelays.filter((r) => r.mode & RelayMode.WRITE).map((r) => r.url);
|
|
||||||
const ranked = relayScoreboardService.getRankedRelays(writeUrls);
|
|
||||||
const onlyTwo = ranked.slice(0, 2);
|
|
||||||
|
|
||||||
return onlyTwo.length > 0 ? nip19.nprofileEncode({ pubkey, relays: onlyTwo }) : nip19.npubEncode(pubkey);
|
|
||||||
}, [userRelays]);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Header({
|
export default function Header({
|
||||||
pubkey,
|
pubkey,
|
||||||
showRelaySelectionModal,
|
showRelaySelectionModal,
|
||||||
|
@ -15,33 +15,19 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
Flex,
|
Flex,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { useMemo } from "react";
|
|
||||||
import { RelayMode } from "../../../classes/relay";
|
|
||||||
import { QrCodeIcon } from "../../../components/icons";
|
import { QrCodeIcon } from "../../../components/icons";
|
||||||
import QrCodeSvg from "../../../components/qr-code-svg";
|
import QrCodeSvg from "../../../components/qr-code-svg";
|
||||||
import { Bech32Prefix, normalizeToBech32 } from "../../../helpers/nip19";
|
import { Bech32Prefix, normalizeToBech32 } from "../../../helpers/nip19";
|
||||||
import useFallbackUserRelays from "../../../hooks/use-fallback-user-relays";
|
|
||||||
import relayScoreboardService from "../../../services/relay-scoreboard";
|
|
||||||
import { nip19 } from "nostr-tools";
|
|
||||||
import { CopyIconButton } from "../../../components/copy-icon-button";
|
import { CopyIconButton } from "../../../components/copy-icon-button";
|
||||||
|
import { useSharableProfileId } from "../../../hooks/use-shareable-profile-id";
|
||||||
function useUserShareLink(pubkey: string) {
|
|
||||||
const userRelays = useFallbackUserRelays(pubkey);
|
|
||||||
|
|
||||||
return useMemo(() => {
|
|
||||||
const writeUrls = userRelays.filter((r) => r.mode & RelayMode.WRITE).map((r) => r.url);
|
|
||||||
const ranked = relayScoreboardService.getRankedRelays(writeUrls);
|
|
||||||
const onlyTwo = ranked.slice(0, 2);
|
|
||||||
|
|
||||||
return onlyTwo.length > 0 ? nip19.nprofileEncode({ pubkey, relays: onlyTwo }) : nip19.npubEncode(pubkey);
|
|
||||||
}, [userRelays]);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const QrIconButton = ({ pubkey, ...props }: { pubkey: string } & Omit<IconButtonProps, "icon">) => {
|
export const QrIconButton = ({ pubkey, ...props }: { pubkey: string } & Omit<IconButtonProps, "icon">) => {
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
|
|
||||||
const npub = normalizeToBech32(pubkey, Bech32Prefix.Pubkey) || pubkey;
|
const npub = normalizeToBech32(pubkey, Bech32Prefix.Pubkey) || pubkey;
|
||||||
const nprofile = useUserShareLink(pubkey);
|
const npubLink = "nostr:" + npub;
|
||||||
|
const nprofile = useSharableProfileId(pubkey);
|
||||||
|
const nprofileLink = "nostr:" + nprofile;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -59,17 +45,17 @@ export const QrIconButton = ({ pubkey, ...props }: { pubkey: string } & Omit<Ico
|
|||||||
|
|
||||||
<TabPanels>
|
<TabPanels>
|
||||||
<TabPanel p="0" pt="2">
|
<TabPanel p="0" pt="2">
|
||||||
<QrCodeSvg content={"nostr:" + nprofile} border={2} />
|
<QrCodeSvg content={nprofileLink} border={2} />
|
||||||
<Flex gap="2" mt="2">
|
<Flex gap="2" mt="2">
|
||||||
<Input readOnly value={"nostr:" + nprofile} />
|
<Input readOnly value={nprofileLink} />
|
||||||
<CopyIconButton text={"nostr:" + nprofile} aria-label="copy nprofile" />
|
<CopyIconButton text={nprofileLink} aria-label="copy nprofile" />
|
||||||
</Flex>
|
</Flex>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel p="0" pt="2">
|
<TabPanel p="0" pt="2">
|
||||||
<QrCodeSvg content={"nostr:" + npub} border={2} />
|
<QrCodeSvg content={npubLink} border={2} />
|
||||||
<Flex gap="2" mt="2">
|
<Flex gap="2" mt="2">
|
||||||
<Input readOnly value={"nostr:" + npub} />
|
<Input readOnly value={npubLink} />
|
||||||
<CopyIconButton text={"nostr:" + npub} aria-label="copy npub" />
|
<CopyIconButton text={npubLink} aria-label="copy npub" />
|
||||||
</Flex>
|
</Flex>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabPanels>
|
</TabPanels>
|
||||||
|
@ -24,7 +24,7 @@ export const UserProfileMenu = ({
|
|||||||
const [_clipboardState, copyToClipboard] = useCopyToClipboard();
|
const [_clipboardState, copyToClipboard] = useCopyToClipboard();
|
||||||
|
|
||||||
const loginAsUser = () => {
|
const loginAsUser = () => {
|
||||||
const readRelays = userRelays?.relays.filter((r) => r.mode === RelayMode.READ).map((r) => r.url) ?? [];
|
const readRelays = userRelays.filter((r) => r.mode === RelayMode.READ).map((r) => r.url) ?? [];
|
||||||
if (!accountService.hasAccount(pubkey)) {
|
if (!accountService.hasAccount(pubkey)) {
|
||||||
accountService.addAccount({
|
accountService.addAccount({
|
||||||
pubkey,
|
pubkey,
|
||||||
|
@ -32,7 +32,6 @@ import { Bech32Prefix, isHex, normalizeToBech32 } from "../../helpers/nip19";
|
|||||||
import { useAppTitle } from "../../hooks/use-app-title";
|
import { useAppTitle } from "../../hooks/use-app-title";
|
||||||
import Header from "./components/header";
|
import Header from "./components/header";
|
||||||
import { Suspense, useState } from "react";
|
import { Suspense, useState } from "react";
|
||||||
import useFallbackUserRelays from "../../hooks/use-fallback-user-relays";
|
|
||||||
import { useReadRelayUrls } from "../../hooks/use-client-relays";
|
import { useReadRelayUrls } from "../../hooks/use-client-relays";
|
||||||
import relayScoreboardService from "../../services/relay-scoreboard";
|
import relayScoreboardService from "../../services/relay-scoreboard";
|
||||||
import { RelayMode } from "../../classes/relay";
|
import { RelayMode } from "../../classes/relay";
|
||||||
@ -40,6 +39,7 @@ import { AdditionalRelayProvider } from "../../providers/additional-relay-contex
|
|||||||
import { nip19 } from "nostr-tools";
|
import { nip19 } from "nostr-tools";
|
||||||
import { unique } from "../../helpers/array";
|
import { unique } from "../../helpers/array";
|
||||||
import { RelayFavicon } from "../../components/relay-favicon";
|
import { RelayFavicon } from "../../components/relay-favicon";
|
||||||
|
import { useUserRelays } from "../../hooks/use-user-relays";
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{ label: "Notes", path: "notes" },
|
{ label: "Notes", path: "notes" },
|
||||||
@ -68,12 +68,12 @@ function useUserPointer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function useUserTopRelays(pubkey: string, count: number = 4) {
|
function useUserTopRelays(pubkey: string, count: number = 4) {
|
||||||
|
const readRelays = useReadRelayUrls();
|
||||||
// get user relays
|
// get user relays
|
||||||
const userRelays = useFallbackUserRelays(pubkey)
|
const userRelays = useUserRelays(pubkey, readRelays)
|
||||||
.filter((r) => r.mode & RelayMode.WRITE)
|
.filter((r) => r.mode & RelayMode.WRITE)
|
||||||
.map((r) => r.url);
|
.map((r) => r.url);
|
||||||
// merge the users relays with client relays
|
// merge the users relays with client relays
|
||||||
const readRelays = useReadRelayUrls();
|
|
||||||
if (userRelays.length === 0) return readRelays;
|
if (userRelays.length === 0) return readRelays;
|
||||||
const sorted = relayScoreboardService.getRankedRelays(userRelays);
|
const sorted = relayScoreboardService.getRankedRelays(userRelays);
|
||||||
|
|
||||||
|
@ -2,13 +2,14 @@ import { Text, Box, IconButton, Flex, Badge } from "@chakra-ui/react";
|
|||||||
import { useNavigate, useOutletContext } from "react-router-dom";
|
import { useNavigate, useOutletContext } from "react-router-dom";
|
||||||
import { GlobalIcon } from "../../components/icons";
|
import { GlobalIcon } from "../../components/icons";
|
||||||
import { RelayMode } from "../../classes/relay";
|
import { RelayMode } from "../../classes/relay";
|
||||||
import useFallbackUserRelays from "../../hooks/use-fallback-user-relays";
|
|
||||||
import { RelayScoreBreakdown } from "../../components/relay-score-breakdown";
|
import { RelayScoreBreakdown } from "../../components/relay-score-breakdown";
|
||||||
import useRankedRelayConfigs from "../../hooks/use-ranked-relay-configs";
|
import useRankedRelayConfigs from "../../hooks/use-ranked-relay-configs";
|
||||||
|
import { useUserRelays } from "../../hooks/use-user-relays";
|
||||||
|
import { useReadRelayUrls } from "../../hooks/use-client-relays";
|
||||||
|
|
||||||
const UserRelaysTab = () => {
|
const UserRelaysTab = () => {
|
||||||
const { pubkey } = useOutletContext() as { pubkey: string };
|
const { pubkey } = useOutletContext() as { pubkey: string };
|
||||||
const userRelays = useFallbackUserRelays(pubkey);
|
const userRelays = useUserRelays(pubkey);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const ranked = useRankedRelayConfigs(userRelays);
|
const ranked = useRankedRelayConfigs(userRelays);
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
import { Box, Button, Flex, Spinner, Text } from "@chakra-ui/react";
|
import { Button, Flex, Spinner, Text } from "@chakra-ui/react";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { useOutletContext } from "react-router-dom";
|
import { useOutletContext } from "react-router-dom";
|
||||||
import { RelayMode } from "../../classes/relay";
|
|
||||||
import { NoteLink } from "../../components/note-link";
|
import { NoteLink } from "../../components/note-link";
|
||||||
import { UserLink } from "../../components/user-link";
|
import { UserLink } from "../../components/user-link";
|
||||||
import { filterTagsByContentRefs, truncatedId } from "../../helpers/nostr-event";
|
import { filterTagsByContentRefs, 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 { useTimelineLoader } from "../../hooks/use-timeline-loader";
|
||||||
import relayScoreboardService from "../../services/relay-scoreboard";
|
|
||||||
import { isETag, isPTag, NostrEvent } from "../../types/nostr-event";
|
import { isETag, isPTag, NostrEvent } from "../../types/nostr-event";
|
||||||
|
import { useAdditionalRelayContext } from "../../providers/additional-relay-context";
|
||||||
|
|
||||||
function ReportEvent({ report }: { report: NostrEvent }) {
|
function ReportEvent({ report }: { report: NostrEvent }) {
|
||||||
const reportedEvent = report.tags.filter(isETag)[0]?.[1];
|
const reportedEvent = report.tags.filter(isETag)[0]?.[1];
|
||||||
@ -39,14 +36,7 @@ function ReportEvent({ report }: { report: NostrEvent }) {
|
|||||||
|
|
||||||
export default function UserReportsTab() {
|
export default function UserReportsTab() {
|
||||||
const { pubkey } = useOutletContext() as { pubkey: string };
|
const { pubkey } = useOutletContext() as { pubkey: string };
|
||||||
// get user relays
|
const contextRelays = useAdditionalRelayContext();
|
||||||
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 = relayScoreboardService.getRankedRelays(userRelays.length === 0 ? readRelays : userRelays).slice(0, 4);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
events: reports,
|
events: reports,
|
||||||
@ -54,7 +44,7 @@ export default function UserReportsTab() {
|
|||||||
loadMore,
|
loadMore,
|
||||||
} = useTimelineLoader(
|
} = useTimelineLoader(
|
||||||
`${truncatedId(pubkey)}-reports`,
|
`${truncatedId(pubkey)}-reports`,
|
||||||
relays,
|
contextRelays,
|
||||||
{ authors: [pubkey], kinds: [1984] },
|
{ authors: [pubkey], kinds: [1984] },
|
||||||
{ pageSize: moment.duration(1, "week").asSeconds() }
|
{ pageSize: moment.duration(1, "week").asSeconds() }
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user