migrate to ndk
This commit is contained in:
@@ -14,7 +14,7 @@ export function MentionUser(props: { children: any[] }) {
|
||||
|
||||
return (
|
||||
<span className="text-fuchsia-500">
|
||||
@{user?.name || user?.display_name || shortenKey(pubkey)}
|
||||
@{user?.name || user?.displayName || shortenKey(pubkey)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { NoteReply } from "@app/note/components/metadata/reply";
|
||||
import { NoteRepost } from "@app/note/components/metadata/repost";
|
||||
import { NoteZap } from "@app/note/components/metadata/zap";
|
||||
import { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||
import { NDKSubscription } from "@nostr-dev-kit/ndk";
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
import { useActiveAccount } from "@stores/accounts";
|
||||
import { READONLY_RELAYS } from "@stores/constants";
|
||||
import { createReplyNote } from "@utils/storage";
|
||||
import { decode } from "light-bolt11-decoder";
|
||||
import { useContext, useState } from "react";
|
||||
@@ -16,65 +16,57 @@ export function NoteMetadata({
|
||||
id: string;
|
||||
eventPubkey: string;
|
||||
}) {
|
||||
const pool: any = useContext(RelayContext);
|
||||
const account = useActiveAccount((state: any) => state.account);
|
||||
const ndk = useContext(RelayContext);
|
||||
|
||||
const [replies, setReplies] = useState(0);
|
||||
const [reposts, setReposts] = useState(0);
|
||||
const [zaps, setZaps] = useState(0);
|
||||
|
||||
useSWRSubscription(id ? ["note-metadata", id] : null, ([, key]) => {
|
||||
const unsubscribe = pool.subscribe(
|
||||
[
|
||||
{
|
||||
"#e": [key],
|
||||
kinds: [1, 6, 9735],
|
||||
limit: 20,
|
||||
},
|
||||
],
|
||||
READONLY_RELAYS,
|
||||
(event: any) => {
|
||||
switch (event.kind) {
|
||||
case 1:
|
||||
setReplies((replies) => replies + 1);
|
||||
createReplyNote(
|
||||
event.id,
|
||||
account.id,
|
||||
event.pubkey,
|
||||
event.kind,
|
||||
event.tags,
|
||||
event.content,
|
||||
event.created_at,
|
||||
key,
|
||||
);
|
||||
break;
|
||||
case 6:
|
||||
setReposts((reposts) => reposts + 1);
|
||||
break;
|
||||
case 9735: {
|
||||
const bolt11 = event.tags.find((tag) => tag[0] === "bolt11")[1];
|
||||
if (bolt11) {
|
||||
const decoded = decode(bolt11);
|
||||
const amount = decoded.sections.find(
|
||||
(item) => item.name === "amount",
|
||||
);
|
||||
setZaps(amount.value / 1000);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
useSWRSubscription(id ? ["note-metadata", id] : null, () => {
|
||||
const sub: NDKSubscription = ndk.subscribe(
|
||||
{
|
||||
unsubscribeOnEose: true,
|
||||
"#e": [id],
|
||||
kinds: [1, 6, 9735],
|
||||
limit: 20,
|
||||
},
|
||||
{ closeOnEose: false },
|
||||
);
|
||||
|
||||
sub.addListener("event", (event: NDKEvent) => {
|
||||
switch (event.kind) {
|
||||
case 1:
|
||||
setReplies((replies) => replies + 1);
|
||||
createReplyNote(
|
||||
event.id,
|
||||
event.pubkey,
|
||||
event.kind,
|
||||
event.tags,
|
||||
event.content,
|
||||
event.created_at,
|
||||
id,
|
||||
);
|
||||
break;
|
||||
case 6:
|
||||
setReposts((reposts) => reposts + 1);
|
||||
break;
|
||||
case 9735: {
|
||||
const bolt11 = event.tags.find((tag) => tag[0] === "bolt11")[1];
|
||||
if (bolt11) {
|
||||
const decoded = decode(bolt11);
|
||||
const amount = decoded.sections.find(
|
||||
(item) => item.name === "amount",
|
||||
);
|
||||
setZaps(amount.value / 1000);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
sub.stop();
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
import { LikeIcon } from "@shared/icons";
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
import { useActiveAccount } from "@stores/accounts";
|
||||
import { WRITEONLY_RELAYS } from "@stores/constants";
|
||||
import { dateToUnix } from "@utils/date";
|
||||
import { getEventHash, getSignature } from "nostr-tools";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
|
||||
export function NoteLike({
|
||||
id,
|
||||
pubkey,
|
||||
likes,
|
||||
}: { id: string; pubkey: string; likes: number }) {
|
||||
const pool: any = useContext(RelayContext);
|
||||
const account = useActiveAccount((state: any) => state.account);
|
||||
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
const submitEvent = (e: any) => {
|
||||
e.stopPropagation();
|
||||
|
||||
const event: any = {
|
||||
content: "+",
|
||||
kind: 7,
|
||||
tags: [
|
||||
["e", id],
|
||||
["p", pubkey],
|
||||
],
|
||||
created_at: dateToUnix(),
|
||||
pubkey: account.pubkey,
|
||||
};
|
||||
|
||||
event.id = getEventHash(event);
|
||||
event.sig = getSignature(event, account.privkey);
|
||||
|
||||
// publish event to all relays
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
// update state
|
||||
setCount(count + 1);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setCount(likes);
|
||||
}, [likes]);
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => submitEvent(e)}
|
||||
className="group inline-flex items-center gap-1.5"
|
||||
>
|
||||
<LikeIcon
|
||||
width={16}
|
||||
height={16}
|
||||
className="text-zinc-400 group-hover:text-rose-400"
|
||||
/>
|
||||
<span className="text-base leading-none text-zinc-400 group-hover:text-white">
|
||||
{count}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { NDKEvent, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
|
||||
import { ReplyIcon } from "@shared/icons";
|
||||
import { Image } from "@shared/image";
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
import { useActiveAccount } from "@stores/accounts";
|
||||
import { WRITEONLY_RELAYS } from "@stores/constants";
|
||||
import { dateToUnix } from "@utils/date";
|
||||
import { compactNumber } from "@utils/number";
|
||||
import { getEventHash, getSignature } from "nostr-tools";
|
||||
import { Fragment, useContext, useEffect, useState } from "react";
|
||||
|
||||
export function NoteReply({ id, replies }: { id: string; replies: number }) {
|
||||
const pool: any = useContext(RelayContext);
|
||||
const ndk = useContext(RelayContext);
|
||||
const account = useActiveAccount((state: any) => state.account);
|
||||
|
||||
const [count, setCount] = useState(0);
|
||||
@@ -26,19 +25,19 @@ export function NoteReply({ id, replies }: { id: string; replies: number }) {
|
||||
};
|
||||
|
||||
const submitEvent = () => {
|
||||
const event: any = {
|
||||
content: value,
|
||||
created_at: dateToUnix(),
|
||||
kind: 1,
|
||||
pubkey: account.pubkey,
|
||||
tags: [["e", id]],
|
||||
};
|
||||
const signer = new NDKPrivateKeySigner(account.privkey);
|
||||
ndk.signer = signer;
|
||||
|
||||
event.id = getEventHash(event);
|
||||
event.sig = getSignature(event, account.privkey);
|
||||
const event = new NDKEvent(ndk);
|
||||
// build event
|
||||
event.content = value;
|
||||
event.kind = 1;
|
||||
event.created_at = dateToUnix();
|
||||
event.pubkey = account.pubkey;
|
||||
event.tags = [["e", id]];
|
||||
|
||||
// publish event
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
event.publish();
|
||||
|
||||
// close modal
|
||||
setIsOpen(false);
|
||||
@@ -96,7 +95,7 @@ export function NoteReply({ id, replies }: { id: string; replies: number }) {
|
||||
<div>
|
||||
<div className="relative h-11 w-11 shrink-0 overflow-hidden rounded-md border border-white/10">
|
||||
<Image
|
||||
src={account?.picture}
|
||||
src={account?.image}
|
||||
alt="user's avatar"
|
||||
className="h-11 w-11 rounded-md object-cover"
|
||||
/>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { NDKEvent, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
|
||||
import { RepostIcon } from "@shared/icons";
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
import { useActiveAccount } from "@stores/accounts";
|
||||
import { WRITEONLY_RELAYS } from "@stores/constants";
|
||||
import { dateToUnix } from "@utils/date";
|
||||
import { compactNumber } from "@utils/number";
|
||||
import { getEventHash, getSignature } from "nostr-tools";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
|
||||
export function NoteRepost({
|
||||
@@ -12,7 +11,7 @@ export function NoteRepost({
|
||||
pubkey,
|
||||
reposts,
|
||||
}: { id: string; pubkey: string; reposts: number }) {
|
||||
const pool: any = useContext(RelayContext);
|
||||
const ndk = useContext(RelayContext);
|
||||
const account = useActiveAccount((state: any) => state.account);
|
||||
|
||||
const [count, setCount] = useState(0);
|
||||
@@ -20,22 +19,22 @@ export function NoteRepost({
|
||||
const submitEvent = (e: any) => {
|
||||
e.stopPropagation();
|
||||
|
||||
const event: any = {
|
||||
content: "",
|
||||
kind: 6,
|
||||
tags: [
|
||||
["e", id],
|
||||
["p", pubkey],
|
||||
],
|
||||
created_at: dateToUnix(),
|
||||
pubkey: account.pubkey,
|
||||
};
|
||||
const signer = new NDKPrivateKeySigner(account.privkey);
|
||||
ndk.signer = signer;
|
||||
|
||||
event.id = getEventHash(event);
|
||||
event.sig = getSignature(event, account.privkey);
|
||||
const event = new NDKEvent(ndk);
|
||||
// build event
|
||||
event.content = "";
|
||||
event.kind = 6;
|
||||
event.created_at = dateToUnix();
|
||||
event.pubkey = account.pubkey;
|
||||
event.tags = [
|
||||
["e", id],
|
||||
["p", pubkey],
|
||||
];
|
||||
|
||||
// publish event to all relays
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
// publish event
|
||||
event.publish();
|
||||
|
||||
// update state
|
||||
setCount(count + 1);
|
||||
|
||||
@@ -17,11 +17,13 @@ export function LinkPreview({ urls }: { urls: string[] }) {
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<Image
|
||||
src={data["og:image"]}
|
||||
alt={urls[0]}
|
||||
className="w-full h-auto border-t-lg object-cover"
|
||||
/>
|
||||
{data["og:image"] && (
|
||||
<Image
|
||||
src={data["og:image"]}
|
||||
alt={urls[0]}
|
||||
className="w-full h-auto border-t-lg object-cover"
|
||||
/>
|
||||
)}
|
||||
<div className="flex flex-col gap-2 px-3 py-3">
|
||||
<h5 className="leading-none font-medium text-zinc-200">
|
||||
{data["og:title"]}
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
import { Image } from "@shared/image";
|
||||
import { NDKEvent, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk";
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
import { useActiveAccount } from "@stores/accounts";
|
||||
import { WRITEONLY_RELAYS } from "@stores/constants";
|
||||
import { dateToUnix } from "@utils/date";
|
||||
import { useProfile } from "@utils/hooks/useProfile";
|
||||
import { getEventHash, getSignature } from "nostr-tools";
|
||||
import { useContext, useState } from "react";
|
||||
|
||||
export function NoteReplyForm({ id }: { id: string }) {
|
||||
const pool: any = useContext(RelayContext);
|
||||
const ndk = useContext(RelayContext);
|
||||
const account = useActiveAccount((state: any) => state.account);
|
||||
|
||||
const [value, setValue] = useState("");
|
||||
|
||||
const submitEvent = () => {
|
||||
const event: any = {
|
||||
content: value,
|
||||
created_at: dateToUnix(),
|
||||
kind: 1,
|
||||
pubkey: account.pubkey,
|
||||
tags: [["e", id]],
|
||||
};
|
||||
const signer = new NDKPrivateKeySigner(account.privkey);
|
||||
ndk.signer = signer;
|
||||
|
||||
event.id = getEventHash(event);
|
||||
event.sig = getSignature(event, account.privkey);
|
||||
const event = new NDKEvent(ndk);
|
||||
// build event
|
||||
event.content = value;
|
||||
event.kind = 1;
|
||||
event.created_at = dateToUnix();
|
||||
event.pubkey = account.pubkey;
|
||||
event.tags = [["e", id]];
|
||||
|
||||
// publish note
|
||||
pool.publish(event, WRITEONLY_RELAYS);
|
||||
// publish event
|
||||
event.publish();
|
||||
|
||||
// reset form
|
||||
setValue("");
|
||||
|
||||
@@ -1,34 +1,35 @@
|
||||
import { NoteReplyForm } from "@app/note/components/replies/form";
|
||||
import { Reply } from "@app/note/components/replies/item";
|
||||
import { NostrEvent } from "@nostr-dev-kit/ndk";
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
import { READONLY_RELAYS } from "@stores/constants";
|
||||
import { sortEvents } from "@utils/transform";
|
||||
import { useContext } from "react";
|
||||
import useSWRSubscription from "swr/subscription";
|
||||
|
||||
export function RepliesList({ id }: { id: string }) {
|
||||
const pool: any = useContext(RelayContext);
|
||||
const ndk = useContext(RelayContext);
|
||||
|
||||
const { data, error } = useSWRSubscription(
|
||||
id ? ["note-replies", id] : null,
|
||||
([, key], { next }) => {
|
||||
// subscribe to note
|
||||
const unsubscribe = pool.subscribe(
|
||||
[
|
||||
{
|
||||
"#e": [key],
|
||||
kinds: [1],
|
||||
limit: 20,
|
||||
},
|
||||
],
|
||||
READONLY_RELAYS,
|
||||
(event: any) => {
|
||||
next(null, (prev: any) => (prev ? [...prev, event] : [event]));
|
||||
const sub = ndk.subscribe(
|
||||
{
|
||||
"#e": [key],
|
||||
kinds: [1],
|
||||
limit: 20,
|
||||
},
|
||||
{
|
||||
closeOnEose: true,
|
||||
},
|
||||
);
|
||||
|
||||
sub.addListener("event", (event: NostrEvent) => {
|
||||
next(null, (prev: any) => (prev ? [...prev, event] : [event]));
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
sub.stop();
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
@@ -3,8 +3,8 @@ import { Kind1063 } from "@app/note/components/kind1063";
|
||||
import { NoteMetadata } from "@app/note/components/metadata";
|
||||
import { NoteSkeleton } from "@app/note/components/skeleton";
|
||||
import { NoteDefaultUser } from "@app/note/components/user/default";
|
||||
import { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
import { READONLY_RELAYS } from "@stores/constants";
|
||||
import { noteParser } from "@utils/parser";
|
||||
import { memo, useContext } from "react";
|
||||
import useSWRSubscription from "swr/subscription";
|
||||
@@ -23,31 +23,22 @@ export const RootNote = memo(function RootNote({
|
||||
id,
|
||||
fallback,
|
||||
}: { id: string; fallback?: any }) {
|
||||
const pool: any = useContext(RelayContext);
|
||||
const ndk = useContext(RelayContext);
|
||||
const parseFallback = isJSON(fallback) ? JSON.parse(fallback) : null;
|
||||
|
||||
const { data, error } = useSWRSubscription(
|
||||
parseFallback ? null : id,
|
||||
(key, { next }) => {
|
||||
const unsubscribe = pool.subscribe(
|
||||
[
|
||||
{
|
||||
ids: [key],
|
||||
},
|
||||
],
|
||||
READONLY_RELAYS,
|
||||
(event: any) => {
|
||||
next(null, event);
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
unsubscribeOnEose: true,
|
||||
},
|
||||
);
|
||||
const sub = ndk.subscribe({
|
||||
ids: [key],
|
||||
});
|
||||
|
||||
sub.addListener("event", (event: NDKEvent) => {
|
||||
next(null, event);
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
sub.stop();
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
@@ -19,7 +19,7 @@ export function NoteDefaultUser({
|
||||
<Popover className="relative flex items-start gap-3">
|
||||
<Popover.Button className="h-11 w-11 shrink-0 overflow-hidden rounded-md bg-zinc-900">
|
||||
<Image
|
||||
src={user?.picture || DEFAULT_AVATAR}
|
||||
src={user?.image || DEFAULT_AVATAR}
|
||||
alt={pubkey}
|
||||
className="h-11 w-11 object-cover"
|
||||
/>
|
||||
@@ -50,14 +50,14 @@ export function NoteDefaultUser({
|
||||
>
|
||||
<div className="flex items-start gap-2.5 border-b border-zinc-800 px-3 py-3">
|
||||
<Image
|
||||
src={user?.picture || DEFAULT_AVATAR}
|
||||
src={user?.image || DEFAULT_AVATAR}
|
||||
alt={pubkey}
|
||||
className="h-14 w-14 shrink-0 rounded-lg object-cover"
|
||||
/>
|
||||
<div className="flex w-full flex-1 flex-col gap-2">
|
||||
<div className="inline-flex w-2/3 flex-col gap-0.5">
|
||||
<h5 className="text-base font-semibold leading-none">
|
||||
{user?.display_name || user?.name || (
|
||||
{user?.displayName || user?.name || (
|
||||
<div className="h-3 w-20 animate-pulse rounded-sm bg-zinc-700" />
|
||||
)}
|
||||
</h5>
|
||||
|
||||
@@ -20,7 +20,7 @@ export function NoteQuoteUser({
|
||||
<div className="group flex items-center gap-2">
|
||||
<div className="relative h-6 w-6 shrink-0 rounded">
|
||||
<Image
|
||||
src={user?.picture || DEFAULT_AVATAR}
|
||||
src={user?.image || DEFAULT_AVATAR}
|
||||
alt={pubkey}
|
||||
className="h-6 w-6 rounded object-cover"
|
||||
/>
|
||||
|
||||
@@ -20,7 +20,7 @@ export function NoteReplyUser({
|
||||
<div className="group flex items-start gap-2.5">
|
||||
<div className="relative h-11 w-11 shrink-0 rounded-md">
|
||||
<Image
|
||||
src={user?.picture || DEFAULT_AVATAR}
|
||||
src={user?.image || DEFAULT_AVATAR}
|
||||
alt={pubkey}
|
||||
className="h-11 w-11 rounded-md object-cover"
|
||||
/>
|
||||
|
||||
@@ -19,7 +19,7 @@ export function NoteRepostUser({
|
||||
<Popover className="relative flex items-start gap-3">
|
||||
<Popover.Button className="h-11 w-11 shrink-0 overflow-hidden rounded-md bg-zinc-900">
|
||||
<Image
|
||||
src={user?.picture || DEFAULT_AVATAR}
|
||||
src={user?.image || DEFAULT_AVATAR}
|
||||
alt={pubkey}
|
||||
className="h-11 w-11 rounded-md object-cover"
|
||||
/>
|
||||
@@ -54,14 +54,14 @@ export function NoteRepostUser({
|
||||
>
|
||||
<div className="flex items-start gap-2.5 border-b border-zinc-800 px-3 py-3">
|
||||
<Image
|
||||
src={user?.picture || DEFAULT_AVATAR}
|
||||
src={user?.image || DEFAULT_AVATAR}
|
||||
alt={pubkey}
|
||||
className="h-14 w-14 shrink-0 rounded-lg object-cover"
|
||||
/>
|
||||
<div className="flex w-full flex-1 flex-col gap-2">
|
||||
<div className="inline-flex w-2/3 flex-col gap-0.5">
|
||||
<h5 className="text-base font-semibold leading-none">
|
||||
{user?.display_name || user?.name || (
|
||||
{user?.displayName || user?.name || (
|
||||
<div className="h-3 w-20 animate-pulse rounded-sm bg-zinc-700" />
|
||||
)}
|
||||
</h5>
|
||||
|
||||
@@ -2,41 +2,19 @@ import { Kind1 } from "@app/note/components/kind1";
|
||||
import { NoteMetadata } from "@app/note/components/metadata";
|
||||
import { RepliesList } from "@app/note/components/replies/list";
|
||||
import { NoteDefaultUser } from "@app/note/components/user/default";
|
||||
import { RelayContext } from "@shared/relayProvider";
|
||||
import { READONLY_RELAYS } from "@stores/constants";
|
||||
import { usePageContext } from "@utils/hooks/usePageContext";
|
||||
import { noteParser } from "@utils/parser";
|
||||
import { useContext } from "react";
|
||||
import useSWRSubscription from "swr/subscription";
|
||||
import { getNoteByID } from "@utils/storage";
|
||||
import useSWR from "swr";
|
||||
|
||||
const fetcher = ([, id]) => getNoteByID(id);
|
||||
|
||||
export function Page() {
|
||||
const pool: any = useContext(RelayContext);
|
||||
const pageContext = usePageContext();
|
||||
const searchParams: any = pageContext.urlParsed.search;
|
||||
|
||||
const noteID = searchParams.id;
|
||||
|
||||
const { data, error } = useSWRSubscription(
|
||||
noteID ? ["note", noteID] : null,
|
||||
([, key], { next }) => {
|
||||
// subscribe to note
|
||||
const unsubscribe = pool.subscribe(
|
||||
[
|
||||
{
|
||||
ids: [key],
|
||||
},
|
||||
],
|
||||
READONLY_RELAYS,
|
||||
(event: any) => {
|
||||
next(null, event);
|
||||
},
|
||||
);
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
},
|
||||
);
|
||||
const { data, error } = useSWR(["note", noteID], fetcher);
|
||||
|
||||
const content = !error && data ? noteParser(data) : null;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user