mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-10 21:00:17 +02:00
show usernames on posts
cache user metadata
This commit is contained in:
parent
40352f77c7
commit
012466383d
@ -18,12 +18,14 @@ import { Link } from "react-router-dom";
|
||||
import moment from "moment";
|
||||
import { PostModal } from "./post-modal";
|
||||
import { NostrEvent } from "../types/nostr-event";
|
||||
import { useUserMetadata } from "../hooks/use-user-metadata";
|
||||
|
||||
export type PostProps = {
|
||||
event: NostrEvent;
|
||||
};
|
||||
export const Post = ({ event }: PostProps) => {
|
||||
export const Post = React.memo(({ event }: PostProps) => {
|
||||
const { isOpen, onClose, onOpen } = useDisclosure();
|
||||
const userMetadata = useUserMetadata(event.pubkey);
|
||||
|
||||
const isLong = event.content.length > 800;
|
||||
|
||||
@ -32,11 +34,16 @@ export const Post = ({ event }: PostProps) => {
|
||||
<CardHeader>
|
||||
<HStack spacing="4">
|
||||
<Flex flex="1" gap="4" alignItems="center" flexWrap="wrap">
|
||||
<Avatar name="Segun Adebayo" src="https://bit.ly/sage-adebayo" />
|
||||
<Avatar
|
||||
name={userMetadata?.name}
|
||||
src="https://bit.ly/sage-adebayo"
|
||||
/>
|
||||
|
||||
<Box>
|
||||
<Heading size="sm">
|
||||
<Link to={`/user/${event.pubkey}`}>{event.pubkey}</Link>
|
||||
<Link to={`/user/${event.pubkey}`}>
|
||||
{userMetadata?.name ?? event.pubkey}
|
||||
</Link>
|
||||
</Heading>
|
||||
<Text>{moment(event.created_at * 1000).fromNow()}</Text>
|
||||
</Box>
|
||||
@ -62,4 +69,4 @@ export const Post = ({ event }: PostProps) => {
|
||||
</CardBody>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
3
src/helpers/date.ts
Normal file
3
src/helpers/date.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function convertTimestampToDate(timestamp: number) {
|
||||
return new Date(timestamp * 1000);
|
||||
}
|
11
src/hooks/use-user-metadata.ts
Normal file
11
src/hooks/use-user-metadata.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { useMemo } from "react";
|
||||
import { useObservable } from "react-use";
|
||||
import userMetadata from "../services/user-metadata";
|
||||
|
||||
export function useUserMetadata(pubkey: string) {
|
||||
const observable = useMemo(
|
||||
() => userMetadata.requestUserMetadata(pubkey),
|
||||
[pubkey]
|
||||
);
|
||||
return useObservable(observable);
|
||||
}
|
@ -23,10 +23,6 @@ const MIGRATIONS: MigrationFunction[] = [
|
||||
const eventsSeen = db.createObjectStore("events-seen", { keyPath: "id" });
|
||||
eventsSeen.createIndex("lastSeen", "lastSeen");
|
||||
|
||||
// db.createObjectStore("contacts", {
|
||||
// keyPath: "pubkey",
|
||||
// });
|
||||
|
||||
// setup data
|
||||
const settings = db.createObjectStore("settings");
|
||||
settings.put(["wss://nostr.rdfriedl.com"], "relays");
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { DBSchema } from "idb";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
|
||||
export interface CustomSchema extends DBSchema {
|
||||
"user-metadata": {
|
||||
key: string;
|
||||
value: any;
|
||||
value: NostrEvent;
|
||||
};
|
||||
"events-seen": {
|
||||
key: string;
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
import { NostrEvent } from "../types/nostr-event";
|
||||
import { Kind0ParsedContent } from "../types/nostr-event";
|
||||
import db from "./db";
|
||||
import settingsService from "./settings";
|
||||
import { Subscription } from "./subscriptions";
|
||||
|
||||
class UserMetadata {
|
||||
requests = new Map<string, BehaviorSubject<NostrEvent | null>>();
|
||||
requests = new Set<string>();
|
||||
subjects = new Map<string, BehaviorSubject<Kind0ParsedContent | null>>();
|
||||
subscription: Subscription;
|
||||
|
||||
constructor(relayUrls: string[] = []) {
|
||||
@ -13,7 +15,8 @@ class UserMetadata {
|
||||
this.subscription.onEvent.subscribe((event) => {
|
||||
try {
|
||||
const metadata = JSON.parse(event.content);
|
||||
this.requests.get(event.pubkey)?.next(metadata);
|
||||
this.getUserSubject(event.pubkey).next(metadata);
|
||||
db.put("user-metadata", event);
|
||||
} catch (e) {}
|
||||
});
|
||||
|
||||
@ -22,12 +25,40 @@ class UserMetadata {
|
||||
}, 1000 * 10);
|
||||
}
|
||||
|
||||
requestUserMetadata(pubkey: string) {
|
||||
if (!this.requests.has(pubkey)) {
|
||||
this.requests.set(pubkey, new BehaviorSubject<NostrEvent | null>(null));
|
||||
this.updateSubscription();
|
||||
private getUserSubject(pubkey: string) {
|
||||
if (!this.subjects.has(pubkey)) {
|
||||
this.subjects.set(
|
||||
pubkey,
|
||||
new BehaviorSubject<Kind0ParsedContent | null>(null)
|
||||
);
|
||||
}
|
||||
return this.requests.get(pubkey);
|
||||
return this.subjects.get(
|
||||
pubkey
|
||||
) as BehaviorSubject<Kind0ParsedContent | null>;
|
||||
}
|
||||
|
||||
requestUserMetadata(pubkey: string, useCache = true) {
|
||||
const subject = this.getUserSubject(pubkey);
|
||||
|
||||
const request = () => {
|
||||
if (!this.requests.has(pubkey)) {
|
||||
this.requests.add(pubkey);
|
||||
this.updateSubscription();
|
||||
}
|
||||
};
|
||||
if (useCache && !subject.getValue()) {
|
||||
db.get("user-metadata", pubkey).then((cachedEvent) => {
|
||||
if (cachedEvent) {
|
||||
try {
|
||||
subject.next(JSON.parse(cachedEvent.content));
|
||||
} catch (e) {
|
||||
request();
|
||||
}
|
||||
} else request();
|
||||
});
|
||||
} else request();
|
||||
|
||||
return subject;
|
||||
}
|
||||
|
||||
updateSubscription() {
|
||||
@ -45,10 +76,10 @@ class UserMetadata {
|
||||
|
||||
pruneRequests() {
|
||||
let removed = false;
|
||||
const requests = Array.from(this.requests.entries());
|
||||
for (const [pubkey, subject] of requests) {
|
||||
if (!subject.observed) {
|
||||
subject.complete();
|
||||
const subjects = Array.from(this.subjects.entries());
|
||||
for (const [pubkey, subject] of subjects) {
|
||||
// if there is a request for the pubkey and no one is observing it. close the request
|
||||
if (this.requests.has(pubkey) && !subject.observed) {
|
||||
this.requests.delete(pubkey);
|
||||
removed = true;
|
||||
}
|
||||
|
@ -11,3 +11,7 @@ export type NostrEvent = {
|
||||
export type IncomingNostrEvent =
|
||||
| ["EVENT", string, NostrEvent]
|
||||
| ["NOTICE", string];
|
||||
|
||||
export type Kind0ParsedContent = {
|
||||
name: string;
|
||||
};
|
||||
|
@ -37,6 +37,8 @@ export const GlobalView = () => {
|
||||
return <SkeletonText />;
|
||||
}
|
||||
|
||||
if (timeline.length > 20) timeline.length = 20;
|
||||
|
||||
return (
|
||||
<>
|
||||
{timeline.map((event) => (
|
||||
|
@ -11,6 +11,9 @@ export const HomeView = () => {
|
||||
consistent look and feel, not just in our design specs, but in
|
||||
production.
|
||||
</span>
|
||||
<Link to="/user/32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245">
|
||||
jb55
|
||||
</Link>
|
||||
<Link to="/user/266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5">
|
||||
self
|
||||
</Link>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useMemo } from "react";
|
||||
import React from "react";
|
||||
import {
|
||||
Heading,
|
||||
Tab,
|
||||
@ -9,8 +9,7 @@ import {
|
||||
} from "@chakra-ui/react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { UserPostsTab } from "./posts";
|
||||
import { useObservable } from "react-use";
|
||||
import userMetadata from "../../services/user-metadata";
|
||||
import { useUserMetadata } from "../../hooks/use-user-metadata";
|
||||
|
||||
export const UserView = () => {
|
||||
const { pubkey } = useParams();
|
||||
@ -19,16 +18,10 @@ export const UserView = () => {
|
||||
throw new Error("No pubkey");
|
||||
}
|
||||
|
||||
const observable = useMemo(
|
||||
() => userMetadata.requestUserMetadata(pubkey),
|
||||
[pubkey]
|
||||
);
|
||||
// @ts-ignore
|
||||
const metadata = useObservable(observable);
|
||||
const metadata = useUserMetadata(pubkey);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* @ts-ignore */}
|
||||
<Heading>{metadata?.name ?? pubkey}</Heading>
|
||||
<Tabs>
|
||||
<TabList>
|
||||
|
@ -37,6 +37,8 @@ export const UserPostsTab = ({ pubkey }: { pubkey: string }) => {
|
||||
return <SkeletonText />;
|
||||
}
|
||||
|
||||
if (timeline.length > 20) timeline.length = 20;
|
||||
|
||||
return (
|
||||
<>
|
||||
{timeline.map((event) => (
|
||||
|
Loading…
x
Reference in New Issue
Block a user