fix metadata and contracts service pruning

This commit is contained in:
hzrd149 2023-02-07 17:04:18 -06:00
parent 0c25242c18
commit 976734b81a
7 changed files with 55 additions and 36 deletions

View File

@ -1,9 +1,9 @@
import { useState } from "react";
import { Button, Text, useDisclosure } from "@chakra-ui/react";
import { useInterval } from "react-use";
import { Button, useDisclosure } from "@chakra-ui/react";
import { Relay } from "../services/relays";
import relayPool from "../services/relays/relay-pool";
import { DevModel } from "./dev-modal";
import { useInterval } from "react-use";
export const ConnectedRelays = () => {
const [relays, setRelays] = useState<Relay[]>(relayPool.getRelays());

View File

@ -54,3 +54,8 @@ export const TrashIcon = createIcon({
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",
});
export const AddIcon = createIcon({
displayName: "delete-bin-line",
d: "M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z",
});

View File

@ -1,11 +1,9 @@
import { useMemo } from "react";
import settings from "../services/settings";
import userContactsService from "../services/user-contacts";
import useSubject from "./use-subject";
export function useUserContacts(pubkey: string) {
const relays = useSubject(settings.relays);
const observable = useMemo(() => userContactsService.requestContacts(pubkey, relays), [pubkey, relays]);
export function useUserContacts(pubkey: string, relays: string[] = [], alwaysRequest = false) {
const observable = useMemo(() => userContactsService.requestContacts(pubkey, relays, alwaysRequest), [pubkey]);
const contacts = useSubject(observable) ?? undefined;
return {

View File

@ -1,10 +1,10 @@
import { useMemo } from "react";
import { useObservable } from "react-use";
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 metadata = useObservable(observable) ?? undefined;
const metadata = useSubject(observable) ?? undefined;
return metadata;
}

View File

@ -68,7 +68,7 @@ function flushRequests() {
function pruneMemoryCache() {
const keys = userSubjects.prune();
for (const [key] of keys) {
for (const key of keys) {
pendingRequests.removePubkey(key);
}
}
@ -96,7 +96,7 @@ setInterval(() => {
pruneMemoryCache();
}, 1000);
const userContactsService = { requestContacts, flushRequests };
const userContactsService = { requestContacts, flushRequests, pendingRequests };
if (import.meta.env.DEV) {
// @ts-ignore

View File

@ -8,19 +8,20 @@ import { Kind0ParsedContent, NostrEvent } from "../types/nostr-event";
import { NostrQuery } from "../types/nostr-query";
import { unique } from "../helpers/array";
type Metadata = Kind0ParsedContent & { created_at: number };
const subscription = new NostrSubscription([], undefined, "user-metadata");
const userMetadataSubjects = new PubkeySubjectCache<NostrEvent>();
const userMetadataSubjects = new PubkeySubjectCache<Metadata>();
const pendingRequests = new PubkeyRequestList();
function requestMetadataEvent(
pubkey: string,
relays: string[],
alwaysRequest = false
): BehaviorSubject<NostrEvent | null> {
function requestMetadata(pubkey: string, relays: string[], alwaysRequest = false) {
let subject = userMetadataSubjects.getSubject(pubkey);
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) {
pendingRequests.addPubkey(pubkey, relays);
@ -30,17 +31,11 @@ function requestMetadataEvent(
return subject;
}
function isEvent(e: NostrEvent | null): e is NostrEvent {
return !!e;
}
function parseMetadata(event: NostrEvent): Kind0ParsedContent | undefined {
function parseMetadata(event: NostrEvent): Metadata | undefined {
try {
return JSON.parse(event.content);
return { ...JSON.parse(event.content), created_at: event.created_at };
} catch (e) {}
}
function requestMetadata(pubkey: string, relays: string[] = [], alwaysRequest = false) {
return requestMetadataEvent(pubkey, relays, alwaysRequest).pipe(filter(isEvent), map(parseMetadata));
}
function flushRequests() {
if (!pendingRequests.needsFlush) return;
@ -59,7 +54,7 @@ function flushRequests() {
function pruneMemoryCache() {
const keys = userMetadataSubjects.prune();
for (const [key] of keys) {
for (const key of keys) {
pendingRequests.removePubkey(key);
}
}
@ -86,7 +81,7 @@ setInterval(() => {
pruneMemoryCache();
}, 1000);
const userMetadataService = { requestMetadata, requestMetadataEvent, flushRequests };
const userMetadataService = { requestMetadata, flushRequests, pendingRequests };
if (import.meta.env.DEV) {
// @ts-ignore

View File

@ -1,10 +1,31 @@
import { useMemo } from "react";
import { Flex, SkeletonText, Text } from "@chakra-ui/react";
import settings from "../../services/settings";
import moment from "moment";
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 userContactsService from "../../services/user-contacts";
import { UserAvatarLink } from "../../components/user-avatar-link";
import moment from "moment";
import { useUserMetadata } from "../../hooks/use-user-metadata";
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 }) => {
const observable = useMemo(() => userContactsService.requestContacts(pubkey, [], true), [pubkey]);
@ -14,12 +35,12 @@ export const UserFollowingTab = ({ pubkey }: { pubkey: string }) => {
<Flex gap="2" direction="column">
{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) => (
<UserAvatarLink key={contact.pubkey + i} pubkey={contact.pubkey} />
<UserCard key={contact.pubkey} pubkey={contact.pubkey} />
))}
</Flex>
<Text>{`Updated ${moment(contacts?.created_at).fromNow()}`}</Text>
</Grid>
<Text>{`Updated ${moment(contacts?.created_at * 1000).fromNow()}`}</Text>
</>
) : (
<SkeletonText />