refactor publish event

This commit is contained in:
2023-11-14 15:15:13 +07:00
parent fee4ad7b98
commit dc5b4f8ac1
22 changed files with 143 additions and 191 deletions

View File

@@ -1,8 +1,9 @@
import { NDKEvent } from '@nostr-dev-kit/ndk';
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 { NoteReply } from '@shared/notes/actions/reply';
import { NoteRepost } from '@shared/notes/actions/repost';
import { NoteZap } from '@shared/notes/actions/zap';
@@ -11,22 +12,21 @@ import { WIDGET_KIND } from '@stores/constants';
import { useWidget } from '@utils/hooks/useWidget';
export function NoteActions({
id,
pubkey,
extraButtons = true,
root,
event,
rootEventId,
canOpenEvent = true,
}: {
id: string;
pubkey: string;
extraButtons?: boolean;
root?: string;
event: NDKEvent;
rootEventId?: string;
canOpenEvent?: boolean;
}) {
const { addWidget } = useWidget();
const navigate = useNavigate();
return (
<Tooltip.Provider>
<div className="flex h-14 items-center justify-between px-3">
{extraButtons && (
{canOpenEvent && (
<div className="inline-flex items-center gap-3">
<Tooltip.Root delayDuration={150}>
<Tooltip.Trigger asChild>
@@ -36,7 +36,7 @@ export function NoteActions({
addWidget.mutate({
kind: WIDGET_KIND.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"
@@ -55,10 +55,34 @@ export function NoteActions({
</div>
)}
<div className="inline-flex items-center gap-10">
<NoteReply id={id} pubkey={pubkey} root={root} />
<NoteReaction id={id} pubkey={pubkey} />
<NoteRepost id={id} pubkey={pubkey} />
<NoteZap id={id} pubkey={pubkey} />
<Tooltip.Root delayDuration={150}>
<Tooltip.Trigger asChild>
<button
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>
</Tooltip.Provider>

View File

@@ -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 { useState } from 'react';
import { useNDK } from '@libs/ndk/provider';
import { toast } from 'sonner';
import { ReactionIcon } from '@shared/icons';
@@ -29,9 +28,7 @@ const REACTIONS = [
},
];
export function NoteReaction({ id, pubkey }: { id: string; pubkey: string }) {
const { ndk } = useNDK();
export function NoteReaction({ event }: { event: NDKEvent }) {
const [open, setOpen] = useState(false);
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) => {
setReaction(content);
try {
setReaction(content);
const event = new NDKEvent(ndk);
event.content = content;
event.kind = NDKKind.Reaction;
event.tags = [
['e', id],
['p', pubkey],
];
const publishedRelays = await event.publish();
if (publishedRelays) {
// react
await event.react(content);
setOpen(false);
} catch (e) {
toast.error(e);
}
};

View File

@@ -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>
);
}

View File

@@ -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 Tooltip from '@radix-ui/react-tooltip';
import { useState } from 'react';
import { toast } from 'sonner';
import { twMerge } from 'tailwind-merge';
import { useNDK } from '@libs/ndk/provider';
import { LoaderIcon, RepostIcon } from '@shared/icons';
export function NoteRepost({ id, pubkey }: { id: string; pubkey: string }) {
const { ndk, relayUrls } = useNDK();
export function NoteRepost({ event }: { event: NDKEvent }) {
const [open, setOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [isRepost, setIsRepost] = useState(false);
const submit = async () => {
setIsLoading(true);
try {
setIsLoading(true);
const tags = [
['e', id, relayUrls[0], 'root'],
['p', pubkey],
];
// repsot
await event.repost(true);
const event = new NDKEvent(ndk);
event.content = '';
event.kind = NDKKind.Repost;
event.tags = tags;
const publishedRelays = await event.publish();
if (publishedRelays) {
// reset state
setOpen(false);
setIsRepost(true);
toast.success(`Broadcasted to ${publishedRelays.size} relays successfully.`);
} else {
toast.success("You've reposted this post successfully");
} catch (e) {
setIsLoading(false);
toast.error('Repost failed, try again later');
}

View File

@@ -1,5 +1,6 @@
import { webln } from '@getalby/sdk';
import { SendPaymentResponse } from '@getalby/sdk/dist/types';
import { NDKEvent } from '@nostr-dev-kit/ndk';
import * as Dialog from '@radix-ui/react-dialog';
import { invoke } from '@tauri-apps/api/primitives';
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 { useEvent } from '@utils/hooks/useEvent';
import { useNostr } from '@utils/hooks/useNostr';
import { useProfile } from '@utils/hooks/useProfile';
import { sendNativeNotification } from '@utils/notification';
import { compactNumber } from '@utils/number';
export function NoteZap({ id, pubkey }: { id: string; pubkey: string }) {
const { createZap } = useNostr();
const { user } = useProfile(pubkey);
const { data: event } = useEvent(id);
export function NoteZap({ event }: { event: NDKEvent }) {
const nwc = useRef(null);
const { user } = useProfile(event.pubkey);
const [walletConnectURL, setWalletConnectURL] = useState<string>(null);
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 [isLoading, setIsLoading] = useState(false);
const nwc = useRef(null);
const createZapRequest = async () => {
try {
const zapAmount = parseInt(amount) * 1000;
const res = await createZap(event, zapAmount, zapMessage);
const res = await event.zap(zapAmount, zapMessage);
if (!res)
return await message('Cannot create zap request', {

View File

@@ -63,7 +63,7 @@ export function ArticleNote({ event }: { event: NDKEvent }) {
</div>
</Link>
</div>
<NoteActions id={event.id} pubkey={event.pubkey} />
<NoteActions event={event} />
</div>
</div>
);

View File

@@ -6,7 +6,7 @@ import { useEvent } from '@utils/hooks/useEvent';
export function ChildNote({ id, isRoot }: { id: string; isRoot?: boolean }) {
const { status, data } = useEvent(id);
if (status === 'pending') {
if (status === 'pending' || !data) {
return <NoteSkeleton />;
}

View File

@@ -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">
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
<div className="relative mt-2">{renderFileType()}</div>
<NoteActions id={event.id} pubkey={event.pubkey} />
<NoteActions event={event} />
</div>
</div>
);

View File

@@ -8,7 +8,6 @@ export * from './unknown';
export * from './skeleton';
export * from './actions';
export * from './actions/reaction';
export * from './actions/reply';
export * from './actions/repost';
export * from './actions/zap';
export * from './actions/more';

View File

@@ -4,25 +4,39 @@ import { toast } from 'sonner';
import { useNDK } from '@libs/ndk/provider';
import { LoaderIcon } from '@shared/icons';
import { ReplyMediaUploader } from '@shared/notes';
export function NoteReplyForm({ eventId }: { eventId: string }) {
const { ndk, relayUrls } = useNDK();
export function NoteReplyForm({ rootEvent }: { rootEvent: NDKEvent }) {
const { ndk } = useNDK();
const [value, setValue] = useState('');
const [loading, setLoading] = useState(false);
const submit = async () => {
const tags = [['e', eventId, relayUrls[0], 'root']];
try {
setLoading(true);
// publish event
const event = new NDKEvent(ndk);
event.content = value;
event.kind = NDKKind.Text;
event.tags = tags;
const event = new NDKEvent(ndk);
event.content = value;
event.kind = NDKKind.Text;
const publishedRelays = await event.publish();
if (publishedRelays) {
toast.success(`Broadcasted to ${publishedRelays.size} relays successfully.`);
setValue('');
// tag root event
event.tag(rootEvent, 'reply');
// 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
onClick={() => submit()}
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>
</div>
</div>

View File

@@ -8,7 +8,7 @@ import { User } from '@shared/user';
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);
return (
@@ -30,12 +30,7 @@ export function Reply({ event, root }: { event: NDKEventWithReplies; root?: stri
</div>
</Collapsible.Trigger>
) : null}
<NoteActions
id={event.id}
pubkey={event.pubkey}
root={root}
extraButtons={false}
/>
<NoteActions event={event} canOpenEvent={false} />
</div>
</div>
<div className={twMerge('px-3', open ? 'pb-3' : '')}>

View File

@@ -9,7 +9,7 @@ export function SubReply({ event }: { event: NDKEvent }) {
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
<MemoizedTextKind content={event.content} />
<div className="-ml-1">
<NoteActions id={event.id} pubkey={event.pubkey} extraButtons={false} />
<NoteActions event={event} canOpenEvent={false} />
</div>
</div>
);

View File

@@ -15,7 +15,7 @@ import { User } from '@shared/user';
export function Repost({ event }: { event: NDKEvent }) {
const { ndk } = useNDK();
const { status, data } = useQuery({
const { status, data: repostEvent } = useQuery({
queryKey: ['repost', event.id],
queryFn: async () => {
try {
@@ -40,14 +40,14 @@ export function Repost({ event }: { event: NDKEvent }) {
});
const renderContentByKind = () => {
if (!data) return null;
switch (data.kind) {
if (!repostEvent) return null;
switch (repostEvent.kind) {
case NDKKind.Text:
return <MemoizedTextKind content={data.content} />;
return <MemoizedTextKind content={repostEvent.content} />;
case 1063:
return <MemoizedFileKind tags={data.tags} />;
return <MemoizedFileKind tags={repostEvent.tags} />;
case NDKKind.Article:
return <MemoizedArticleKind id={data.id} tags={data.tags} />;
return <MemoizedArticleKind id={repostEvent.id} tags={repostEvent.tags} />;
default:
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">
<User pubkey={event.pubkey} time={event.created_at} variant="repost" />
<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()}
<NoteActions id={data.id} pubkey={data.pubkey} />
<NoteActions event={repostEvent} />
</div>
</div>
</div>

View File

@@ -47,7 +47,7 @@ export function TextNote({ event }: { event: NDKEvent }) {
{parsedContent}
</div>
</div>
<NoteActions id={event.id} pubkey={event.pubkey} />
<NoteActions event={event} rootEventId={thread?.rootEventId} />
</div>
</div>
);

View File

@@ -21,7 +21,7 @@ export function UnknownNote({ event }: { event: NDKEvent }) {
{event.content.toString()}
</div>
</div>
<NoteActions id={event.id} pubkey={event.pubkey} />
<NoteActions event={event} />
</div>
</div>
);