mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-07-10 00:00:03 +02:00
fix metadata and contracts service pruning
This commit is contained in:
@ -1,9 +1,9 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Button, Text, useDisclosure } from "@chakra-ui/react";
|
import { Button, useDisclosure } from "@chakra-ui/react";
|
||||||
import { useInterval } from "react-use";
|
|
||||||
import { Relay } from "../services/relays";
|
import { Relay } from "../services/relays";
|
||||||
import relayPool from "../services/relays/relay-pool";
|
import relayPool from "../services/relays/relay-pool";
|
||||||
import { DevModel } from "./dev-modal";
|
import { DevModel } from "./dev-modal";
|
||||||
|
import { useInterval } from "react-use";
|
||||||
|
|
||||||
export const ConnectedRelays = () => {
|
export const ConnectedRelays = () => {
|
||||||
const [relays, setRelays] = useState<Relay[]>(relayPool.getRelays());
|
const [relays, setRelays] = useState<Relay[]>(relayPool.getRelays());
|
||||||
|
@ -54,3 +54,8 @@ export const TrashIcon = createIcon({
|
|||||||
displayName: "delete-bin-line",
|
displayName: "delete-bin-line",
|
||||||
d: "M17 6h5v2h-2v13a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V8H2V6h5V3a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v3zm1 2H6v12h12V8zm-9 3h2v6H9v-6zm4 0h2v6h-2v-6zM9 4v2h6V4H9z",
|
d: "M17 6h5v2h-2v13a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V8H2V6h5V3a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v3zm1 2H6v12h12V8zm-9 3h2v6H9v-6zm4 0h2v6h-2v-6zM9 4v2h6V4H9z",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const AddIcon = createIcon({
|
||||||
|
displayName: "delete-bin-line",
|
||||||
|
d: "M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z",
|
||||||
|
});
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import settings from "../services/settings";
|
|
||||||
import userContactsService from "../services/user-contacts";
|
import userContactsService from "../services/user-contacts";
|
||||||
import useSubject from "./use-subject";
|
import useSubject from "./use-subject";
|
||||||
|
|
||||||
export function useUserContacts(pubkey: string) {
|
export function useUserContacts(pubkey: string, relays: string[] = [], alwaysRequest = false) {
|
||||||
const relays = useSubject(settings.relays);
|
const observable = useMemo(() => userContactsService.requestContacts(pubkey, relays, alwaysRequest), [pubkey]);
|
||||||
const observable = useMemo(() => userContactsService.requestContacts(pubkey, relays), [pubkey, relays]);
|
|
||||||
const contacts = useSubject(observable) ?? undefined;
|
const contacts = useSubject(observable) ?? undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useObservable } from "react-use";
|
|
||||||
import userMetadataService from "../services/user-metadata";
|
import userMetadataService from "../services/user-metadata";
|
||||||
|
import useSubject from "./use-subject";
|
||||||
|
|
||||||
export function useUserMetadata(pubkey: string, relays?: string[], alwaysRequest = false) {
|
export function useUserMetadata(pubkey: string, relays: string[] = [], alwaysRequest = false) {
|
||||||
const observable = useMemo(() => userMetadataService.requestMetadata(pubkey, relays, alwaysRequest), [pubkey]);
|
const observable = useMemo(() => userMetadataService.requestMetadata(pubkey, relays, alwaysRequest), [pubkey]);
|
||||||
const metadata = useObservable(observable) ?? undefined;
|
const metadata = useSubject(observable) ?? undefined;
|
||||||
|
|
||||||
return metadata;
|
return metadata;
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ function flushRequests() {
|
|||||||
|
|
||||||
function pruneMemoryCache() {
|
function pruneMemoryCache() {
|
||||||
const keys = userSubjects.prune();
|
const keys = userSubjects.prune();
|
||||||
for (const [key] of keys) {
|
for (const key of keys) {
|
||||||
pendingRequests.removePubkey(key);
|
pendingRequests.removePubkey(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,7 +96,7 @@ setInterval(() => {
|
|||||||
pruneMemoryCache();
|
pruneMemoryCache();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
const userContactsService = { requestContacts, flushRequests };
|
const userContactsService = { requestContacts, flushRequests, pendingRequests };
|
||||||
|
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -8,19 +8,20 @@ import { Kind0ParsedContent, NostrEvent } from "../types/nostr-event";
|
|||||||
import { NostrQuery } from "../types/nostr-query";
|
import { NostrQuery } from "../types/nostr-query";
|
||||||
import { unique } from "../helpers/array";
|
import { unique } from "../helpers/array";
|
||||||
|
|
||||||
|
type Metadata = Kind0ParsedContent & { created_at: number };
|
||||||
|
|
||||||
const subscription = new NostrSubscription([], undefined, "user-metadata");
|
const subscription = new NostrSubscription([], undefined, "user-metadata");
|
||||||
const userMetadataSubjects = new PubkeySubjectCache<NostrEvent>();
|
const userMetadataSubjects = new PubkeySubjectCache<Metadata>();
|
||||||
const pendingRequests = new PubkeyRequestList();
|
const pendingRequests = new PubkeyRequestList();
|
||||||
|
|
||||||
function requestMetadataEvent(
|
function requestMetadata(pubkey: string, relays: string[], alwaysRequest = false) {
|
||||||
pubkey: string,
|
|
||||||
relays: string[],
|
|
||||||
alwaysRequest = false
|
|
||||||
): BehaviorSubject<NostrEvent | null> {
|
|
||||||
let subject = userMetadataSubjects.getSubject(pubkey);
|
let subject = userMetadataSubjects.getSubject(pubkey);
|
||||||
|
|
||||||
db.get("user-metadata", pubkey).then((cached) => {
|
db.get("user-metadata", pubkey).then((cached) => {
|
||||||
if (cached) subject.next(cached);
|
if (cached) {
|
||||||
|
const parsed = parseMetadata(cached);
|
||||||
|
if (parsed) subject.next(parsed);
|
||||||
|
}
|
||||||
|
|
||||||
if (alwaysRequest || !cached) {
|
if (alwaysRequest || !cached) {
|
||||||
pendingRequests.addPubkey(pubkey, relays);
|
pendingRequests.addPubkey(pubkey, relays);
|
||||||
@ -30,17 +31,11 @@ function requestMetadataEvent(
|
|||||||
return subject;
|
return subject;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEvent(e: NostrEvent | null): e is NostrEvent {
|
function parseMetadata(event: NostrEvent): Metadata | undefined {
|
||||||
return !!e;
|
|
||||||
}
|
|
||||||
function parseMetadata(event: NostrEvent): Kind0ParsedContent | undefined {
|
|
||||||
try {
|
try {
|
||||||
return JSON.parse(event.content);
|
return { ...JSON.parse(event.content), created_at: event.created_at };
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
function requestMetadata(pubkey: string, relays: string[] = [], alwaysRequest = false) {
|
|
||||||
return requestMetadataEvent(pubkey, relays, alwaysRequest).pipe(filter(isEvent), map(parseMetadata));
|
|
||||||
}
|
|
||||||
|
|
||||||
function flushRequests() {
|
function flushRequests() {
|
||||||
if (!pendingRequests.needsFlush) return;
|
if (!pendingRequests.needsFlush) return;
|
||||||
@ -59,7 +54,7 @@ function flushRequests() {
|
|||||||
|
|
||||||
function pruneMemoryCache() {
|
function pruneMemoryCache() {
|
||||||
const keys = userMetadataSubjects.prune();
|
const keys = userMetadataSubjects.prune();
|
||||||
for (const [key] of keys) {
|
for (const key of keys) {
|
||||||
pendingRequests.removePubkey(key);
|
pendingRequests.removePubkey(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,7 +81,7 @@ setInterval(() => {
|
|||||||
pruneMemoryCache();
|
pruneMemoryCache();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
const userMetadataService = { requestMetadata, requestMetadataEvent, flushRequests };
|
const userMetadataService = { requestMetadata, flushRequests, pendingRequests };
|
||||||
|
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -1,10 +1,31 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { Flex, SkeletonText, Text } from "@chakra-ui/react";
|
import moment from "moment";
|
||||||
import settings from "../../services/settings";
|
import { Box, Flex, Grid, Heading, IconButton, SkeletonText, Text, Link } from "@chakra-ui/react";
|
||||||
|
import { Link as ReactRouterLink } from "react-router-dom";
|
||||||
|
|
||||||
import useSubject from "../../hooks/use-subject";
|
import useSubject from "../../hooks/use-subject";
|
||||||
import userContactsService from "../../services/user-contacts";
|
import userContactsService from "../../services/user-contacts";
|
||||||
import { UserAvatarLink } from "../../components/user-avatar-link";
|
import { useUserMetadata } from "../../hooks/use-user-metadata";
|
||||||
import moment from "moment";
|
import { getUserDisplayName } from "../../helpers/user-metadata";
|
||||||
|
import { AddIcon } from "../../components/icons";
|
||||||
|
import { UserAvatar } from "../../components/user-avatar";
|
||||||
|
import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip-19";
|
||||||
|
|
||||||
|
const UserCard = ({ pubkey }: { pubkey: string }) => {
|
||||||
|
const metadata = useUserMetadata(pubkey);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box borderWidth="1px" borderRadius="lg" pl="3" pr="3" pt="2" pb="2" overflow="hidden">
|
||||||
|
<Flex gap="4" alignItems="center">
|
||||||
|
<UserAvatar pubkey={pubkey} />
|
||||||
|
<Link as={ReactRouterLink} to={`/u/${normalizeToBech32(pubkey, Bech32Prefix.Pubkey)}`}>
|
||||||
|
<Heading size="sm">{getUserDisplayName(metadata, pubkey)}</Heading>
|
||||||
|
</Link>
|
||||||
|
<IconButton size="sm" icon={<AddIcon />} aria-label="Follow user" title="Follow" ml="auto" />
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const UserFollowingTab = ({ pubkey }: { pubkey: string }) => {
|
export const UserFollowingTab = ({ pubkey }: { pubkey: string }) => {
|
||||||
const observable = useMemo(() => userContactsService.requestContacts(pubkey, [], true), [pubkey]);
|
const observable = useMemo(() => userContactsService.requestContacts(pubkey, [], true), [pubkey]);
|
||||||
@ -14,12 +35,12 @@ export const UserFollowingTab = ({ pubkey }: { pubkey: string }) => {
|
|||||||
<Flex gap="2" direction="column">
|
<Flex gap="2" direction="column">
|
||||||
{contacts ? (
|
{contacts ? (
|
||||||
<>
|
<>
|
||||||
<Flex flexWrap="wrap" gap="2">
|
<Grid templateColumns={{ base: "1fr", xl: "repeat(2, 1fr)", "2xl": "repeat(3, 1fr)" }} gap="2">
|
||||||
{contacts.contacts.map((contact, i) => (
|
{contacts.contacts.map((contact, i) => (
|
||||||
<UserAvatarLink key={contact.pubkey + i} pubkey={contact.pubkey} />
|
<UserCard key={contact.pubkey} pubkey={contact.pubkey} />
|
||||||
))}
|
))}
|
||||||
</Flex>
|
</Grid>
|
||||||
<Text>{`Updated ${moment(contacts?.created_at).fromNow()}`}</Text>
|
<Text>{`Updated ${moment(contacts?.created_at * 1000).fromNow()}`}</Text>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<SkeletonText />
|
<SkeletonText />
|
||||||
|
Reference in New Issue
Block a user