This commit is contained in:
2023-11-08 08:21:52 +07:00
parent ce864c8990
commit 6b030f2902
18 changed files with 55 additions and 327 deletions

View File

@@ -35,11 +35,11 @@ export function ArticleNote({ event }: { event: NDKEvent }) {
<div className="mb-3 h-min w-full px-3">
<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>
<div className="px-3">
<Link
to={`/notes/article/${event.id}`}
preventScrollReset={true}
className="flex w-full flex-col rounded-lg border border-neutral-200 bg-neutral-100 dark:border-neutral-800 dark:bg-neutral-900"
className="flex w-full flex-col rounded-lg bg-neutral-100 dark:bg-neutral-900"
>
{metadata.image && (
<img
@@ -48,7 +48,7 @@ export function ArticleNote({ event }: { event: NDKEvent }) {
className="h-56 w-full rounded-t-lg object-cover"
/>
)}
<div className="flex flex-col gap-1 rounded-b-lg bg-neutral-200 px-3 py-3 dark:bg-neutral-800">
<div className="flex flex-col gap-1 rounded-b-lg rounded-t-lg bg-neutral-100 px-3 py-3 dark:bg-neutral-900">
<h5 className="break-all font-semibold text-neutral-900 dark:text-neutral-100">
{metadata.title}
</h5>

View File

@@ -6,7 +6,6 @@ export * from './child';
export * from './notify';
export * from './unknown';
export * from './skeleton';
export * from './stats';
export * from './actions';
export * from './actions/reaction';
export * from './actions/reply';

View File

@@ -3,7 +3,7 @@ import { download } from '@tauri-apps/plugin-upload';
import { SyntheticEvent } from 'react';
import Zoom from 'react-medium-image-zoom';
import { DownloadIcon } from '@shared/icons';
import { CancelIcon, DownloadIcon } from '@shared/icons';
export function ImagePreview({ url }: { url: string }) {
const downloadImage = async (url: string) => {
@@ -17,7 +17,7 @@ export function ImagePreview({ url }: { url: string }) {
};
return (
<Zoom key={url} zoomMargin={50}>
<Zoom key={url} zoomMargin={50} IconUnzoom={() => <CancelIcon className="h-4 w-4" />}>
<div className="group relative mt-2">
<img
src={url}

View File

@@ -49,7 +49,7 @@ export function LinkPreview({ url }: { url: string }) {
<img
src={data.image}
alt={url}
className="h-44 w-full rounded-t-lg bg-white object-cover"
className="h-48 w-full rounded-t-lg bg-white object-cover"
/>
) : null}
<div className="flex flex-col items-start px-3 py-3">

View File

@@ -12,25 +12,15 @@ export function Reply({ event, root }: { event: NDKEventWithReplies; root?: stri
const [open, setOpen] = useState(false);
return (
<div>
<div className="flex flex-col gap-2">
<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}
root={root}
extraButtons={false}
/>
</div>
</div>
<div className="pl-4">
<Collapsible.Root open={open} onOpenChange={setOpen}>
{event.replies?.length > 0 ? (
<div>
<Collapsible.Root open={open} onOpenChange={setOpen}>
<div className="rounded-xl bg-neutral-50 dark:bg-neutral-950">
<div className="flex flex-col gap-2 pt-3">
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
<MemoizedTextKind content={event.content} />
<div className="-ml-1 flex items-center justify-between">
{event.replies?.length > 0 ? (
<Collapsible.Trigger asChild>
<div className="inline-flex h-10 items-center gap-1 font-semibold text-blue-500">
<div className="ml-4 inline-flex h-14 items-center gap-1 font-semibold text-blue-500">
<NavArrowDownIcon
className={twMerge('h-3 w-3', open ? 'rotate-180 transform' : '')}
/>
@@ -39,13 +29,23 @@ export function Reply({ event, root }: { event: NDKEventWithReplies; root?: stri
(event.replies?.length === 1 ? 'reply' : 'replies')}
</div>
</Collapsible.Trigger>
<Collapsible.Content>
{event.replies?.map((sub) => <SubReply key={sub.id} event={sub} />)}
</Collapsible.Content>
</div>
) : null}
<NoteActions
id={event.id}
pubkey={event.pubkey}
root={root}
extraButtons={false}
/>
</div>
</div>
<div className={twMerge('px-3', open ? 'pb-3' : '')}>
{event.replies?.length > 0 ? (
<Collapsible.Content>
{event.replies?.map((sub) => <SubReply key={sub.id} event={sub} />)}
</Collapsible.Content>
) : null}
</Collapsible.Root>
</div>
</div>
</div>
</Collapsible.Root>
);
}

View File

@@ -48,6 +48,7 @@ export function ReplyList({ eventId }: { eventId: string }) {
return (
<div className="mt-3 flex flex-col gap-5">
<h3 className="font-semibold">Replies</h3>
{data?.length === 0 ? (
<div className="mt-2 flex w-full items-center justify-center">
<div className="flex flex-col items-center justify-center gap-2 py-6">

View File

@@ -5,7 +5,7 @@ import { User } from '@shared/user';
export function SubReply({ event }: { event: NDKEvent }) {
return (
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-2 rounded-lg bg-neutral-100 pt-3 dark:bg-neutral-900">
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
<MemoizedTextKind content={event.content} />
<div className="-ml-1">

View File

@@ -1,85 +0,0 @@
import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk';
import { useQuery } from '@tanstack/react-query';
import { decode } from 'light-bolt11-decoder';
import { useNDK } from '@libs/ndk/provider';
import { LoaderIcon } from '@shared/icons';
import { compactNumber } from '@utils/number';
export function NoteStats({ id }: { id: string }) {
const { ndk } = useNDK();
const { status, data } = useQuery({
queryKey: ['note-stats', id],
queryFn: async () => {
let reactions = 0;
let reposts = 0;
let zaps = 0;
const filter: NDKFilter = {
'#e': [id],
kinds: [6, 7, 9735],
};
const events = await ndk.fetchEvents(filter);
events.forEach((event: NDKEvent) => {
switch (event.kind) {
case 6:
reposts += 1;
break;
case 7:
reactions += 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');
const sats = amount.value / 1000;
zaps += sats;
}
break;
}
default:
break;
}
});
return { reposts, reactions, zaps };
},
refetchOnWindowFocus: false,
refetchOnReconnect: false,
});
if (status === 'pending') {
return (
<div className="flex h-11 items-center">
<LoaderIcon className="h-4 w-4 animate-spin text-white" />
</div>
);
}
return (
<div className="mt-3 flex w-full flex-wrap gap-2">
<div className="flex flex-1 flex-col rounded-lg bg-neutral-100 px-3 py-2 dark:bg-neutral-900">
<div className="text-lg font-semibold text-neutral-900 dark:text-neutral-100">
{compactNumber.format(data.reactions)}
</div>
<div className="text-sm text-neutral-500 dark:text-neutral-300">Reactions</div>
</div>
<div className="flex flex-1 flex-col rounded-lg bg-neutral-100 px-3 py-2 dark:bg-neutral-900">
<div className="text-lg font-semibold text-neutral-900 dark:text-neutral-100">
{compactNumber.format(data.reposts)}
</div>
<div className="text-sm text-neutral-500 dark:text-neutral-300">Reposts</div>
</div>
<div className="flex flex-1 flex-col rounded-lg bg-neutral-100 px-3 py-2 dark:bg-neutral-900">
<div className="text-lg font-semibold text-neutral-900 dark:text-neutral-100">
{compactNumber.format(data.zaps)}
</div>
<div className="text-sm text-neutral-500 dark:text-neutral-300">Zaps</div>
</div>
</div>
);
}