mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-18 11:13:30 +00:00
refactor publish event
This commit is contained in:
parent
fee4ad7b98
commit
dc5b4f8ac1
@ -1,4 +1,4 @@
|
|||||||
import { NDKEvent, NDKKind, NDKTag } from '@nostr-dev-kit/ndk';
|
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
|
||||||
import CharacterCount from '@tiptap/extension-character-count';
|
import CharacterCount from '@tiptap/extension-character-count';
|
||||||
import Image from '@tiptap/extension-image';
|
import Image from '@tiptap/extension-image';
|
||||||
import Placeholder from '@tiptap/extension-placeholder';
|
import Placeholder from '@tiptap/extension-placeholder';
|
||||||
@ -21,7 +21,7 @@ import { WIDGET_KIND } from '@stores/constants';
|
|||||||
import { useWidget } from '@utils/hooks/useWidget';
|
import { useWidget } from '@utils/hooks/useWidget';
|
||||||
|
|
||||||
export function NewPostScreen() {
|
export function NewPostScreen() {
|
||||||
const { ndk, relayUrls } = useNDK();
|
const { ndk } = useNDK();
|
||||||
const { addWidget } = useWidget();
|
const { addWidget } = useWidget();
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@ -56,12 +56,6 @@ export function NewPostScreen() {
|
|||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
const reply = {
|
|
||||||
id: searchParams.get('id'),
|
|
||||||
root: searchParams.get('root'),
|
|
||||||
pubkey: searchParams.get('pubkey'),
|
|
||||||
};
|
|
||||||
|
|
||||||
// get plaintext content
|
// get plaintext content
|
||||||
const html = editor.getHTML();
|
const html = editor.getHTML();
|
||||||
const serializedContent = convert(html, {
|
const serializedContent = convert(html, {
|
||||||
@ -71,37 +65,23 @@ export function NewPostScreen() {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
// define tags
|
|
||||||
let tags: NDKTag[] = [];
|
|
||||||
|
|
||||||
// add reply to tags if present
|
|
||||||
if (reply.id && reply.pubkey) {
|
|
||||||
if (reply.root) {
|
|
||||||
tags = [
|
|
||||||
['e', reply.root, relayUrls[0], 'root'],
|
|
||||||
['e', reply.id, relayUrls[0], 'reply'],
|
|
||||||
['p', reply.pubkey],
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
tags = [
|
|
||||||
['e', reply.id, relayUrls[0], 'reply'],
|
|
||||||
['p', reply.pubkey],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add hashtag to tags if present
|
|
||||||
const hashtags = serializedContent
|
|
||||||
.split(/\s/gm)
|
|
||||||
.filter((s: string) => s.startsWith('#'));
|
|
||||||
hashtags?.forEach((tag: string) => {
|
|
||||||
tags.push(['t', tag.replace('#', '')]);
|
|
||||||
});
|
|
||||||
|
|
||||||
const event = new NDKEvent(ndk);
|
const event = new NDKEvent(ndk);
|
||||||
event.content = serializedContent;
|
event.content = serializedContent;
|
||||||
event.kind = NDKKind.Text;
|
event.kind = NDKKind.Text;
|
||||||
event.tags = tags;
|
|
||||||
|
// add reply to tags if present
|
||||||
|
const replyTo = searchParams.get('replyTo');
|
||||||
|
const rootReplyTo = searchParams.get('rootReplyTo');
|
||||||
|
|
||||||
|
if (rootReplyTo) {
|
||||||
|
const rootEvent = await ndk.fetchEvent(rootReplyTo);
|
||||||
|
event.tag(rootEvent, 'root');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (replyTo) {
|
||||||
|
const replyEvent = await ndk.fetchEvent(replyTo);
|
||||||
|
event.tag(replyEvent, 'reply');
|
||||||
|
}
|
||||||
|
|
||||||
// publish event
|
// publish event
|
||||||
const publishedRelays = await event.publish();
|
const publishedRelays = await event.publish();
|
||||||
@ -114,7 +94,7 @@ export function NewPostScreen() {
|
|||||||
setSearchParams({});
|
setSearchParams({});
|
||||||
|
|
||||||
// open new widget with this event id
|
// open new widget with this event id
|
||||||
if (!reply.id && !reply.pubkey) {
|
if (!replyTo) {
|
||||||
addWidget.mutate({
|
addWidget.mutate({
|
||||||
title: 'Thread',
|
title: 'Thread',
|
||||||
content: event.id,
|
content: event.id,
|
||||||
@ -146,9 +126,9 @@ export function NewPostScreen() {
|
|||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
autoCapitalize="off"
|
autoCapitalize="off"
|
||||||
/>
|
/>
|
||||||
{searchParams.get('id') && (
|
{searchParams.get('replyTo') && (
|
||||||
<div className="relative max-w-lg">
|
<div className="relative max-w-lg">
|
||||||
<MentionNote id={searchParams.get('id')} editing />
|
<MentionNote id={searchParams.get('replyTo')} editing />
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setSearchParams({})}
|
onClick={() => setSearchParams({})}
|
||||||
|
@ -111,14 +111,14 @@ export function TextNoteScreen() {
|
|||||||
<User pubkey={data.pubkey} time={data.created_at} variant="thread" />
|
<User pubkey={data.pubkey} time={data.created_at} variant="thread" />
|
||||||
<div className="mt-3">{renderKind(data)}</div>
|
<div className="mt-3">{renderKind(data)}</div>
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<NoteActions id={id} pubkey={data.pubkey} extraButtons={false} />
|
<NoteActions event={data} canOpenEvent={false} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div ref={replyRef} className="px-3">
|
<div ref={replyRef} className="px-3">
|
||||||
<div className="mb-3 border-b border-neutral-100 pb-3 dark:border-neutral-900">
|
<div className="mb-3 border-b border-neutral-100 pb-3 dark:border-neutral-900">
|
||||||
<NoteReplyForm eventId={id} />
|
<NoteReplyForm rootEvent={data} />
|
||||||
</div>
|
</div>
|
||||||
<ReplyList eventId={id} />
|
<ReplyList eventId={id} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,7 +15,10 @@ export function RelayCard() {
|
|||||||
queryKey: ['relays'],
|
queryKey: ['relays'],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const user = ndk.getUser({ pubkey: db.account.pubkey });
|
const user = ndk.getUser({ pubkey: db.account.pubkey });
|
||||||
return await user.relayList();
|
const relays = await user.relayList();
|
||||||
|
|
||||||
|
if (!relays) return Promise.reject(new Error("user's relay set not found"));
|
||||||
|
return relays;
|
||||||
},
|
},
|
||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
});
|
});
|
||||||
@ -29,7 +32,7 @@ export function RelayCard() {
|
|||||||
) : (
|
) : (
|
||||||
<div className="flex h-full w-full flex-col justify-between p-4">
|
<div className="flex h-full w-full flex-col justify-between p-4">
|
||||||
<h3 className="pt-1 text-5xl font-semibold tabular-nums text-neutral-900 dark:text-neutral-100">
|
<h3 className="pt-1 text-5xl font-semibold tabular-nums text-neutral-900 dark:text-neutral-100">
|
||||||
{compactNumber.format(data?.relays?.length)}
|
{compactNumber.format(data?.relays?.length || 0)}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="mt-auto flex h-6 w-full items-center justify-between">
|
<div className="mt-auto flex h-6 w-full items-center justify-between">
|
||||||
<p className="text-xl font-medium leading-none text-neutral-600 dark:text-neutral-400">
|
<p className="text-xl font-medium leading-none text-neutral-600 dark:text-neutral-400">
|
||||||
|
@ -40,7 +40,7 @@ export function ZapCard() {
|
|||||||
<div className="flex h-full w-full flex-col justify-between p-4">
|
<div className="flex h-full w-full flex-col justify-between p-4">
|
||||||
<h3 className="pt-1 text-5xl font-semibold tabular-nums text-neutral-900 dark:text-neutral-100">
|
<h3 className="pt-1 text-5xl font-semibold tabular-nums text-neutral-900 dark:text-neutral-100">
|
||||||
{compactNumber.format(
|
{compactNumber.format(
|
||||||
data.stats[db.account.pubkey].zaps_received.msats / 1000
|
data?.stats[db.account.pubkey]?.zaps_received?.msats / 1000 || 0
|
||||||
)}
|
)}
|
||||||
</h3>
|
</h3>
|
||||||
<div className="mt-auto flex h-6 items-center text-xl font-medium leading-none text-neutral-600 dark:text-neutral-400">
|
<div className="mt-auto flex h-6 items-center text-xl font-medium leading-none text-neutral-600 dark:text-neutral-400">
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
import * as Tooltip from '@radix-ui/react-tooltip';
|
import * as Tooltip from '@radix-ui/react-tooltip';
|
||||||
|
import { createSearchParams, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { FocusIcon } from '@shared/icons';
|
import { FocusIcon, ReplyIcon } from '@shared/icons';
|
||||||
import { NoteReaction } from '@shared/notes/actions/reaction';
|
import { NoteReaction } from '@shared/notes/actions/reaction';
|
||||||
import { NoteReply } from '@shared/notes/actions/reply';
|
|
||||||
import { NoteRepost } from '@shared/notes/actions/repost';
|
import { NoteRepost } from '@shared/notes/actions/repost';
|
||||||
import { NoteZap } from '@shared/notes/actions/zap';
|
import { NoteZap } from '@shared/notes/actions/zap';
|
||||||
|
|
||||||
@ -11,22 +12,21 @@ import { WIDGET_KIND } from '@stores/constants';
|
|||||||
import { useWidget } from '@utils/hooks/useWidget';
|
import { useWidget } from '@utils/hooks/useWidget';
|
||||||
|
|
||||||
export function NoteActions({
|
export function NoteActions({
|
||||||
id,
|
event,
|
||||||
pubkey,
|
rootEventId,
|
||||||
extraButtons = true,
|
canOpenEvent = true,
|
||||||
root,
|
|
||||||
}: {
|
}: {
|
||||||
id: string;
|
event: NDKEvent;
|
||||||
pubkey: string;
|
rootEventId?: string;
|
||||||
extraButtons?: boolean;
|
canOpenEvent?: boolean;
|
||||||
root?: string;
|
|
||||||
}) {
|
}) {
|
||||||
const { addWidget } = useWidget();
|
const { addWidget } = useWidget();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip.Provider>
|
<Tooltip.Provider>
|
||||||
<div className="flex h-14 items-center justify-between px-3">
|
<div className="flex h-14 items-center justify-between px-3">
|
||||||
{extraButtons && (
|
{canOpenEvent && (
|
||||||
<div className="inline-flex items-center gap-3">
|
<div className="inline-flex items-center gap-3">
|
||||||
<Tooltip.Root delayDuration={150}>
|
<Tooltip.Root delayDuration={150}>
|
||||||
<Tooltip.Trigger asChild>
|
<Tooltip.Trigger asChild>
|
||||||
@ -36,7 +36,7 @@ export function NoteActions({
|
|||||||
addWidget.mutate({
|
addWidget.mutate({
|
||||||
kind: WIDGET_KIND.thread,
|
kind: WIDGET_KIND.thread,
|
||||||
title: 'Thread',
|
title: 'Thread',
|
||||||
content: id,
|
content: event.id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
className="inline-flex h-7 w-max items-center justify-center gap-2 rounded-full bg-neutral-100 px-2 text-sm font-medium dark:bg-neutral-900"
|
className="inline-flex h-7 w-max items-center justify-center gap-2 rounded-full bg-neutral-100 px-2 text-sm font-medium dark:bg-neutral-900"
|
||||||
@ -55,10 +55,34 @@ export function NoteActions({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="inline-flex items-center gap-10">
|
<div className="inline-flex items-center gap-10">
|
||||||
<NoteReply id={id} pubkey={pubkey} root={root} />
|
<Tooltip.Root delayDuration={150}>
|
||||||
<NoteReaction id={id} pubkey={pubkey} />
|
<Tooltip.Trigger asChild>
|
||||||
<NoteRepost id={id} pubkey={pubkey} />
|
<button
|
||||||
<NoteZap id={id} pubkey={pubkey} />
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
navigate({
|
||||||
|
pathname: '/new/',
|
||||||
|
search: createSearchParams({
|
||||||
|
replyTo: event.id,
|
||||||
|
rootReplyTo: rootEventId,
|
||||||
|
}).toString(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="group inline-flex h-7 w-7 items-center justify-center text-neutral-600 dark:text-neutral-400"
|
||||||
|
>
|
||||||
|
<ReplyIcon className="h-5 w-5 group-hover:text-blue-500" />
|
||||||
|
</button>
|
||||||
|
</Tooltip.Trigger>
|
||||||
|
<Tooltip.Portal>
|
||||||
|
<Tooltip.Content className="-left-10 inline-flex h-7 select-none items-center justify-center rounded-md bg-neutral-200 px-3.5 text-sm text-neutral-900 will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade dark:bg-neutral-800 dark:text-neutral-100">
|
||||||
|
Quick reply
|
||||||
|
<Tooltip.Arrow className="fill-neutral-200 dark:fill-neutral-800" />
|
||||||
|
</Tooltip.Content>
|
||||||
|
</Tooltip.Portal>
|
||||||
|
</Tooltip.Root>
|
||||||
|
<NoteReaction event={event} />
|
||||||
|
<NoteRepost event={event} />
|
||||||
|
<NoteZap event={event} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip.Provider>
|
</Tooltip.Provider>
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
|
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
import * as Popover from '@radix-ui/react-popover';
|
import * as Popover from '@radix-ui/react-popover';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { toast } from 'sonner';
|
||||||
import { useNDK } from '@libs/ndk/provider';
|
|
||||||
|
|
||||||
import { ReactionIcon } from '@shared/icons';
|
import { ReactionIcon } from '@shared/icons';
|
||||||
|
|
||||||
@ -29,9 +28,7 @@ const REACTIONS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function NoteReaction({ id, pubkey }: { id: string; pubkey: string }) {
|
export function NoteReaction({ event }: { event: NDKEvent }) {
|
||||||
const { ndk } = useNDK();
|
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [reaction, setReaction] = useState<string | null>(null);
|
const [reaction, setReaction] = useState<string | null>(null);
|
||||||
|
|
||||||
@ -41,19 +38,14 @@ export function NoteReaction({ id, pubkey }: { id: string; pubkey: string }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const react = async (content: string) => {
|
const react = async (content: string) => {
|
||||||
setReaction(content);
|
try {
|
||||||
|
setReaction(content);
|
||||||
|
|
||||||
const event = new NDKEvent(ndk);
|
// react
|
||||||
event.content = content;
|
await event.react(content);
|
||||||
event.kind = NDKKind.Reaction;
|
|
||||||
event.tags = [
|
|
||||||
['e', id],
|
|
||||||
['p', pubkey],
|
|
||||||
];
|
|
||||||
|
|
||||||
const publishedRelays = await event.publish();
|
|
||||||
if (publishedRelays) {
|
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
|
} catch (e) {
|
||||||
|
toast.error(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
import * as Tooltip from '@radix-ui/react-tooltip';
|
|
||||||
import { createSearchParams, useNavigate } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { ReplyIcon } from '@shared/icons';
|
|
||||||
|
|
||||||
export function NoteReply({
|
|
||||||
id,
|
|
||||||
pubkey,
|
|
||||||
root,
|
|
||||||
}: {
|
|
||||||
id: string;
|
|
||||||
pubkey: string;
|
|
||||||
root?: string;
|
|
||||||
}) {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tooltip.Root delayDuration={150}>
|
|
||||||
<Tooltip.Trigger asChild>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() =>
|
|
||||||
navigate({
|
|
||||||
pathname: '/new/',
|
|
||||||
search: createSearchParams({
|
|
||||||
id,
|
|
||||||
pubkey,
|
|
||||||
root,
|
|
||||||
}).toString(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
className="group inline-flex h-7 w-7 items-center justify-center text-neutral-600 dark:text-neutral-400"
|
|
||||||
>
|
|
||||||
<ReplyIcon className="h-5 w-5 group-hover:text-blue-500" />
|
|
||||||
</button>
|
|
||||||
</Tooltip.Trigger>
|
|
||||||
<Tooltip.Portal>
|
|
||||||
<Tooltip.Content className="-left-10 inline-flex h-7 select-none items-center justify-center rounded-md bg-neutral-200 px-3.5 text-sm text-neutral-900 will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade dark:bg-neutral-800 dark:text-neutral-100">
|
|
||||||
Quick reply
|
|
||||||
<Tooltip.Arrow className="fill-neutral-200 dark:fill-neutral-800" />
|
|
||||||
</Tooltip.Content>
|
|
||||||
</Tooltip.Portal>
|
|
||||||
</Tooltip.Root>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,41 +1,30 @@
|
|||||||
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
|
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
import * as AlertDialog from '@radix-ui/react-alert-dialog';
|
import * as AlertDialog from '@radix-ui/react-alert-dialog';
|
||||||
import * as Tooltip from '@radix-ui/react-tooltip';
|
import * as Tooltip from '@radix-ui/react-tooltip';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
import { useNDK } from '@libs/ndk/provider';
|
|
||||||
|
|
||||||
import { LoaderIcon, RepostIcon } from '@shared/icons';
|
import { LoaderIcon, RepostIcon } from '@shared/icons';
|
||||||
|
|
||||||
export function NoteRepost({ id, pubkey }: { id: string; pubkey: string }) {
|
export function NoteRepost({ event }: { event: NDKEvent }) {
|
||||||
const { ndk, relayUrls } = useNDK();
|
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [isRepost, setIsRepost] = useState(false);
|
const [isRepost, setIsRepost] = useState(false);
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
setIsLoading(true);
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
const tags = [
|
// repsot
|
||||||
['e', id, relayUrls[0], 'root'],
|
await event.repost(true);
|
||||||
['p', pubkey],
|
|
||||||
];
|
|
||||||
|
|
||||||
const event = new NDKEvent(ndk);
|
// reset state
|
||||||
event.content = '';
|
|
||||||
event.kind = NDKKind.Repost;
|
|
||||||
event.tags = tags;
|
|
||||||
|
|
||||||
const publishedRelays = await event.publish();
|
|
||||||
if (publishedRelays) {
|
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
setIsRepost(true);
|
setIsRepost(true);
|
||||||
|
|
||||||
toast.success(`Broadcasted to ${publishedRelays.size} relays successfully.`);
|
toast.success("You've reposted this post successfully");
|
||||||
} else {
|
} catch (e) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
toast.error('Repost failed, try again later');
|
toast.error('Repost failed, try again later');
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { webln } from '@getalby/sdk';
|
import { webln } from '@getalby/sdk';
|
||||||
import { SendPaymentResponse } from '@getalby/sdk/dist/types';
|
import { SendPaymentResponse } from '@getalby/sdk/dist/types';
|
||||||
|
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
import * as Dialog from '@radix-ui/react-dialog';
|
import * as Dialog from '@radix-ui/react-dialog';
|
||||||
import { invoke } from '@tauri-apps/api/primitives';
|
import { invoke } from '@tauri-apps/api/primitives';
|
||||||
import { message } from '@tauri-apps/plugin-dialog';
|
import { message } from '@tauri-apps/plugin-dialog';
|
||||||
@ -9,16 +10,13 @@ import CurrencyInput from 'react-currency-input-field';
|
|||||||
|
|
||||||
import { CancelIcon, ZapIcon } from '@shared/icons';
|
import { CancelIcon, ZapIcon } from '@shared/icons';
|
||||||
|
|
||||||
import { useEvent } from '@utils/hooks/useEvent';
|
|
||||||
import { useNostr } from '@utils/hooks/useNostr';
|
|
||||||
import { useProfile } from '@utils/hooks/useProfile';
|
import { useProfile } from '@utils/hooks/useProfile';
|
||||||
import { sendNativeNotification } from '@utils/notification';
|
import { sendNativeNotification } from '@utils/notification';
|
||||||
import { compactNumber } from '@utils/number';
|
import { compactNumber } from '@utils/number';
|
||||||
|
|
||||||
export function NoteZap({ id, pubkey }: { id: string; pubkey: string }) {
|
export function NoteZap({ event }: { event: NDKEvent }) {
|
||||||
const { createZap } = useNostr();
|
const nwc = useRef(null);
|
||||||
const { user } = useProfile(pubkey);
|
const { user } = useProfile(event.pubkey);
|
||||||
const { data: event } = useEvent(id);
|
|
||||||
|
|
||||||
const [walletConnectURL, setWalletConnectURL] = useState<string>(null);
|
const [walletConnectURL, setWalletConnectURL] = useState<string>(null);
|
||||||
const [amount, setAmount] = useState<string>('21');
|
const [amount, setAmount] = useState<string>('21');
|
||||||
@ -28,12 +26,10 @@ export function NoteZap({ id, pubkey }: { id: string; pubkey: string }) {
|
|||||||
const [isCompleted, setIsCompleted] = useState(false);
|
const [isCompleted, setIsCompleted] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const nwc = useRef(null);
|
|
||||||
|
|
||||||
const createZapRequest = async () => {
|
const createZapRequest = async () => {
|
||||||
try {
|
try {
|
||||||
const zapAmount = parseInt(amount) * 1000;
|
const zapAmount = parseInt(amount) * 1000;
|
||||||
const res = await createZap(event, zapAmount, zapMessage);
|
const res = await event.zap(zapAmount, zapMessage);
|
||||||
|
|
||||||
if (!res)
|
if (!res)
|
||||||
return await message('Cannot create zap request', {
|
return await message('Cannot create zap request', {
|
||||||
|
@ -63,7 +63,7 @@ export function ArticleNote({ event }: { event: NDKEvent }) {
|
|||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<NoteActions id={event.id} pubkey={event.pubkey} />
|
<NoteActions event={event} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -6,7 +6,7 @@ import { useEvent } from '@utils/hooks/useEvent';
|
|||||||
export function ChildNote({ id, isRoot }: { id: string; isRoot?: boolean }) {
|
export function ChildNote({ id, isRoot }: { id: string; isRoot?: boolean }) {
|
||||||
const { status, data } = useEvent(id);
|
const { status, data } = useEvent(id);
|
||||||
|
|
||||||
if (status === 'pending') {
|
if (status === 'pending' || !data) {
|
||||||
return <NoteSkeleton />;
|
return <NoteSkeleton />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ export function FileNote({ event }: { event: NDKEvent }) {
|
|||||||
<div className="relative flex flex-col gap-2 overflow-hidden rounded-xl bg-neutral-50 pt-3 dark:bg-neutral-950">
|
<div className="relative flex flex-col gap-2 overflow-hidden rounded-xl bg-neutral-50 pt-3 dark:bg-neutral-950">
|
||||||
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
|
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
|
||||||
<div className="relative mt-2">{renderFileType()}</div>
|
<div className="relative mt-2">{renderFileType()}</div>
|
||||||
<NoteActions id={event.id} pubkey={event.pubkey} />
|
<NoteActions event={event} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -8,7 +8,6 @@ export * from './unknown';
|
|||||||
export * from './skeleton';
|
export * from './skeleton';
|
||||||
export * from './actions';
|
export * from './actions';
|
||||||
export * from './actions/reaction';
|
export * from './actions/reaction';
|
||||||
export * from './actions/reply';
|
|
||||||
export * from './actions/repost';
|
export * from './actions/repost';
|
||||||
export * from './actions/zap';
|
export * from './actions/zap';
|
||||||
export * from './actions/more';
|
export * from './actions/more';
|
||||||
|
@ -4,25 +4,39 @@ import { toast } from 'sonner';
|
|||||||
|
|
||||||
import { useNDK } from '@libs/ndk/provider';
|
import { useNDK } from '@libs/ndk/provider';
|
||||||
|
|
||||||
|
import { LoaderIcon } from '@shared/icons';
|
||||||
import { ReplyMediaUploader } from '@shared/notes';
|
import { ReplyMediaUploader } from '@shared/notes';
|
||||||
|
|
||||||
export function NoteReplyForm({ eventId }: { eventId: string }) {
|
export function NoteReplyForm({ rootEvent }: { rootEvent: NDKEvent }) {
|
||||||
const { ndk, relayUrls } = useNDK();
|
const { ndk } = useNDK();
|
||||||
|
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
const tags = [['e', eventId, relayUrls[0], 'root']];
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
// publish event
|
const event = new NDKEvent(ndk);
|
||||||
const event = new NDKEvent(ndk);
|
event.content = value;
|
||||||
event.content = value;
|
event.kind = NDKKind.Text;
|
||||||
event.kind = NDKKind.Text;
|
|
||||||
event.tags = tags;
|
|
||||||
|
|
||||||
const publishedRelays = await event.publish();
|
// tag root event
|
||||||
if (publishedRelays) {
|
event.tag(rootEvent, 'reply');
|
||||||
toast.success(`Broadcasted to ${publishedRelays.size} relays successfully.`);
|
|
||||||
setValue('');
|
// publish event
|
||||||
|
const publishedRelays = await event.publish();
|
||||||
|
|
||||||
|
if (publishedRelays) {
|
||||||
|
toast.success(`Broadcasted to ${publishedRelays.size} relays successfully.`);
|
||||||
|
|
||||||
|
// reset state
|
||||||
|
setValue('');
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
setLoading(false);
|
||||||
|
toast.error(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -40,9 +54,9 @@ export function NoteReplyForm({ eventId }: { eventId: string }) {
|
|||||||
<button
|
<button
|
||||||
onClick={() => submit()}
|
onClick={() => submit()}
|
||||||
disabled={value.length === 0 ? true : false}
|
disabled={value.length === 0 ? true : false}
|
||||||
className="h-9 w-20 rounded-lg bg-blue-500 text-white hover:bg-blue-600 disabled:opacity-50"
|
className="inline-flex h-9 w-20 items-center justify-center rounded-lg bg-blue-500 text-white hover:bg-blue-600 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Reply
|
{loading ? <LoaderIcon className="h-4 w-4 animate-spin" /> : 'Reply'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,7 +8,7 @@ import { User } from '@shared/user';
|
|||||||
|
|
||||||
import { NDKEventWithReplies } from '@utils/types';
|
import { NDKEventWithReplies } from '@utils/types';
|
||||||
|
|
||||||
export function Reply({ event, root }: { event: NDKEventWithReplies; root?: string }) {
|
export function Reply({ event }: { event: NDKEventWithReplies }) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -30,12 +30,7 @@ export function Reply({ event, root }: { event: NDKEventWithReplies; root?: stri
|
|||||||
</div>
|
</div>
|
||||||
</Collapsible.Trigger>
|
</Collapsible.Trigger>
|
||||||
) : null}
|
) : null}
|
||||||
<NoteActions
|
<NoteActions event={event} canOpenEvent={false} />
|
||||||
id={event.id}
|
|
||||||
pubkey={event.pubkey}
|
|
||||||
root={root}
|
|
||||||
extraButtons={false}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={twMerge('px-3', open ? 'pb-3' : '')}>
|
<div className={twMerge('px-3', open ? 'pb-3' : '')}>
|
||||||
|
@ -9,7 +9,7 @@ export function SubReply({ event }: { event: NDKEvent }) {
|
|||||||
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
|
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
|
||||||
<MemoizedTextKind content={event.content} />
|
<MemoizedTextKind content={event.content} />
|
||||||
<div className="-ml-1">
|
<div className="-ml-1">
|
||||||
<NoteActions id={event.id} pubkey={event.pubkey} extraButtons={false} />
|
<NoteActions event={event} canOpenEvent={false} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -15,7 +15,7 @@ import { User } from '@shared/user';
|
|||||||
|
|
||||||
export function Repost({ event }: { event: NDKEvent }) {
|
export function Repost({ event }: { event: NDKEvent }) {
|
||||||
const { ndk } = useNDK();
|
const { ndk } = useNDK();
|
||||||
const { status, data } = useQuery({
|
const { status, data: repostEvent } = useQuery({
|
||||||
queryKey: ['repost', event.id],
|
queryKey: ['repost', event.id],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
try {
|
try {
|
||||||
@ -40,14 +40,14 @@ export function Repost({ event }: { event: NDKEvent }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const renderContentByKind = () => {
|
const renderContentByKind = () => {
|
||||||
if (!data) return null;
|
if (!repostEvent) return null;
|
||||||
switch (data.kind) {
|
switch (repostEvent.kind) {
|
||||||
case NDKKind.Text:
|
case NDKKind.Text:
|
||||||
return <MemoizedTextKind content={data.content} />;
|
return <MemoizedTextKind content={repostEvent.content} />;
|
||||||
case 1063:
|
case 1063:
|
||||||
return <MemoizedFileKind tags={data.tags} />;
|
return <MemoizedFileKind tags={repostEvent.tags} />;
|
||||||
case NDKKind.Article:
|
case NDKKind.Article:
|
||||||
return <MemoizedArticleKind id={data.id} tags={data.tags} />;
|
return <MemoizedArticleKind id={repostEvent.id} tags={repostEvent.tags} />;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -66,9 +66,13 @@ export function Repost({ event }: { event: NDKEvent }) {
|
|||||||
<div className="relative flex flex-col gap-2 overflow-hidden rounded-xl bg-neutral-50 pt-3 dark:bg-neutral-950">
|
<div className="relative flex flex-col gap-2 overflow-hidden rounded-xl bg-neutral-50 pt-3 dark:bg-neutral-950">
|
||||||
<User pubkey={event.pubkey} time={event.created_at} variant="repost" />
|
<User pubkey={event.pubkey} time={event.created_at} variant="repost" />
|
||||||
<div className="relative flex flex-col gap-2">
|
<div className="relative flex flex-col gap-2">
|
||||||
<User pubkey={data.pubkey} time={data.created_at} eventId={data.id} />
|
<User
|
||||||
|
pubkey={repostEvent.pubkey}
|
||||||
|
time={repostEvent.created_at}
|
||||||
|
eventId={repostEvent.id}
|
||||||
|
/>
|
||||||
{renderContentByKind()}
|
{renderContentByKind()}
|
||||||
<NoteActions id={data.id} pubkey={data.pubkey} />
|
<NoteActions event={repostEvent} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -47,7 +47,7 @@ export function TextNote({ event }: { event: NDKEvent }) {
|
|||||||
{parsedContent}
|
{parsedContent}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<NoteActions id={event.id} pubkey={event.pubkey} />
|
<NoteActions event={event} rootEventId={thread?.rootEventId} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -21,7 +21,7 @@ export function UnknownNote({ event }: { event: NDKEvent }) {
|
|||||||
{event.content.toString()}
|
{event.content.toString()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<NoteActions id={event.id} pubkey={event.pubkey} />
|
<NoteActions event={event} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -68,9 +68,9 @@ export function ThreadWidget({ widget }: { widget: Widget }) {
|
|||||||
<div className="flex flex-col rounded-xl bg-neutral-50 dark:bg-neutral-950">
|
<div className="flex flex-col rounded-xl bg-neutral-50 dark:bg-neutral-950">
|
||||||
<User pubkey={data.pubkey} time={data.created_at} variant="thread" />
|
<User pubkey={data.pubkey} time={data.created_at} variant="thread" />
|
||||||
{renderKind(data)}
|
{renderKind(data)}
|
||||||
<NoteActions id={data.id} pubkey={data.pubkey} />
|
<NoteActions event={data} />
|
||||||
</div>
|
</div>
|
||||||
<NoteReplyForm eventId={widget.content} />
|
<NoteReplyForm rootEvent={data} />
|
||||||
<ReplyList eventId={data.id} />
|
<ReplyList eventId={data.id} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -23,8 +23,8 @@ export function useEvent(id: undefined | string, embed?: undefined | string) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const rEvent = [...rEvents].slice(-1)[0];
|
const rEvent = [...rEvents].slice(-1)[0];
|
||||||
if (!rEvent) return Promise.reject(new Error('event not found'));
|
|
||||||
|
|
||||||
|
if (!rEvent) return Promise.reject(new Error('event not found'));
|
||||||
return rEvent;
|
return rEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,8 +36,8 @@ export function useEvent(id: undefined | string, embed?: undefined | string) {
|
|||||||
|
|
||||||
// get event from relay
|
// get event from relay
|
||||||
const event = await ndk.fetchEvent(id);
|
const event = await ndk.fetchEvent(id);
|
||||||
if (!event) return Promise.reject(new Error('event not found'));
|
|
||||||
|
|
||||||
|
if (!event) return Promise.reject(new Error('event not found'));
|
||||||
return event;
|
return event;
|
||||||
},
|
},
|
||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
|
@ -20,6 +20,7 @@ export function useProfile(pubkey: string, embed?: string) {
|
|||||||
const cleanPubkey = pubkey.replace(/[^a-zA-Z0-9]/g, '');
|
const cleanPubkey = pubkey.replace(/[^a-zA-Z0-9]/g, '');
|
||||||
const user = ndk.getUser({ pubkey: cleanPubkey });
|
const user = ndk.getUser({ pubkey: cleanPubkey });
|
||||||
|
|
||||||
|
if (!user) return Promise.reject(new Error("user's profile not found"));
|
||||||
return await user.fetchProfile();
|
return await user.fetchProfile();
|
||||||
},
|
},
|
||||||
staleTime: Infinity,
|
staleTime: Infinity,
|
||||||
|
Loading…
Reference in New Issue
Block a user