refactor(note): only support kind 1

This commit is contained in:
reya 2024-01-04 12:35:21 +07:00
parent fcde669685
commit 542b6033c2
12 changed files with 82 additions and 232 deletions

View File

@ -0,0 +1,36 @@
import { cn } from "@lume/utils";
import { NDKKind } from "@nostr-dev-kit/ndk";
import { useNoteContext, useRichContent } from "../..";
export function NoteContent({
className,
}: {
className?: string;
}) {
const event = useNoteContext();
const { parsedContent } = useRichContent(event.content);
if (event.kind === NDKKind.Text) {
return (
<div
className={cn(
"break-p select-text whitespace-pre-line text-balance leading-normal",
className,
)}
>
{parsedContent}
</div>
);
}
return (
<div
className={cn(
"break-p select-text whitespace-pre-line text-balance leading-normal",
className,
)}
>
Unsupported kind
</div>
);
}

View File

@ -4,9 +4,7 @@ import { NoteReply } from "./buttons/reply";
import { NoteRepost } from "./buttons/repost";
import { NoteZap } from "./buttons/zap";
import { NoteChild } from "./child";
import { NoteArticleContent } from "./kinds/article";
import { NoteMediaContent } from "./kinds/media";
import { NoteTextContent } from "./kinds/text";
import { NoteContent } from "./content";
import { NoteMenu } from "./menu";
import { NoteProvider } from "./provider";
import { NoteRoot } from "./root";
@ -21,13 +19,11 @@ export const Note = {
Reply: NoteReply,
Repost: NoteRepost,
Reaction: NoteReaction,
Content: NoteContent,
Zap: NoteZap,
Pin: NotePin,
Child: NoteChild,
Thread: NoteThread,
TextContent: NoteTextContent,
MediaContent: NoteMediaContent,
ArticleContent: NoteArticleContent,
};
export * from "./provider";

View File

@ -1,63 +0,0 @@
import { NDKTag } from '@nostr-dev-kit/ndk';
import { Link } from 'react-router-dom';
export function NoteArticleContent({
eventId,
tags,
}: {
eventId: string;
tags: NDKTag[];
}) {
const getMetadata = () => {
const title = tags.find((tag) => tag[0] === 'title')?.[1];
const image = tags.find((tag) => tag[0] === 'image')?.[1];
const summary = tags.find((tag) => tag[0] === 'summary')?.[1];
let publishedAt: Date | string | number = tags.find(
(tag) => tag[0] === 'published_at'
)?.[1];
publishedAt = new Date(parseInt(publishedAt) * 1000).toLocaleDateString('en-US');
return {
title,
image,
publishedAt,
summary,
};
};
const metadata = getMetadata();
return (
<Link
to={`/events/${eventId}`}
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"
>
{metadata.image && (
<img
src={metadata.image}
alt={metadata.title}
loading="lazy"
decoding="async"
style={{ contentVisibility: 'auto' }}
className="h-auto 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">
<h5 className="break-all font-semibold text-neutral-900 dark:text-neutral-100">
{metadata.title}
</h5>
{metadata.summary ? (
<p className="line-clamp-3 break-all text-sm text-neutral-600 dark:text-neutral-400">
{metadata.summary}
</p>
) : null}
<span className="mt-2.5 text-sm text-neutral-600 dark:text-neutral-400">
{metadata.publishedAt.toString()}
</span>
</div>
</Link>
);
}

View File

@ -1,80 +0,0 @@
import { DownloadIcon } from "@lume/icons";
import { fileType } from "@lume/utils";
import { NDKTag } from "@nostr-dev-kit/ndk";
import { downloadDir } from "@tauri-apps/api/path";
import { download } from "@tauri-apps/plugin-upload";
import { MediaPlayer, MediaProvider } from "@vidstack/react";
import {
DefaultVideoLayout,
defaultLayoutIcons,
} from "@vidstack/react/player/layouts/default";
import { Link } from "react-router-dom";
import { twMerge } from "tailwind-merge";
export function NoteMediaContent({
tags,
className,
}: {
tags: NDKTag[];
className?: string;
}) {
const url = tags.find((el) => el[0] === "url")[1];
const type = fileType(url);
const downloadImage = async (url: string) => {
const downloadDirPath = await downloadDir();
const filename = url.substring(url.lastIndexOf("/") + 1);
return await download(url, downloadDirPath + `/${filename}`);
};
if (type === "image") {
return (
<div key={url} className={twMerge("group relative", className)}>
<img
src={url}
alt={url}
loading="lazy"
decoding="async"
style={{ contentVisibility: "auto" }}
className="h-auto w-full object-cover"
/>
<button
type="button"
onClick={() => downloadImage(url)}
className="absolute right-2 top-2 hidden h-10 w-10 items-center justify-center rounded-lg bg-black/50 backdrop-blur-xl group-hover:inline-flex hover:bg-blue-500"
>
<DownloadIcon className="h-5 w-5 text-white" />
</button>
</div>
);
}
if (type === "video") {
return (
<div className={className}>
<MediaPlayer
src={url}
className="w-full overflow-hidden rounded-lg"
aspectRatio="16/9"
load="visible"
>
<MediaProvider />
<DefaultVideoLayout icons={defaultLayoutIcons} />
</MediaPlayer>
</div>
);
}
return (
<div className={className}>
<Link
to={url}
target="_blank"
rel="noreferrer"
className="text-blue-500 hover:text-blue-600"
>
{url}
</Link>
</div>
);
}

View File

@ -1,23 +0,0 @@
import { cn } from "@lume/utils";
import { useRichContent } from "../../../hooks/useRichContent";
export function NoteTextContent({
content,
className,
}: {
content: string;
className?: string;
}) {
const { parsedContent } = useRichContent(content);
return (
<div
className={cn(
"break-p select-text whitespace-pre-line text-balance leading-normal",
className,
)}
>
{parsedContent}
</div>
);
}

View File

@ -1,4 +1,3 @@
import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
import { memo } from "react";
import { Link } from "react-router-dom";
import { Note } from "..";
@ -10,19 +9,6 @@ export const MentionNote = memo(function MentionNote({
}: { eventId: string; openable?: boolean }) {
const { isLoading, isError, data } = useEvent(eventId);
const renderKind = (event: NDKEvent) => {
switch (event.kind) {
case NDKKind.Text:
return <Note.TextContent content={event.content} />;
case NDKKind.Article:
return <Note.ArticleContent eventId={event.id} tags={event.tags} />;
case 1063:
return <Note.MediaContent tags={event.tags} />;
default:
return <Note.TextContent content={event.content} />;
}
};
if (isLoading) {
return (
<div
@ -52,7 +38,7 @@ export const MentionNote = memo(function MentionNote({
<Note.User variant="mention" />
</div>
<div className="px-3 pb-3 mt-1">
{renderKind(data)}
<Note.Content />
{openable ? (
<Link
to={`/events/${data.id}`}

View File

@ -3,15 +3,17 @@ import { Note } from "..";
export function ChildReply({
event,
rootEventId,
}: { event: NDKEvent; rootEventId?: string }) {
return (
<Note.Provider event={event}>
<Note.Root className="pl-4 gap-2 mb-5">
<Note.User />
<Note.TextContent content={event.content} className="min-w-0" />
<div className="-ml-1 flex items-center gap-10">
<Note.Reply rootEventId={rootEventId} />
<Note.Root className="gap-2 pl-4 mb-5">
<div className="flex items-center justify-between px-3 h-14">
<Note.User className="flex-1 pr-1" />
<Note.Menu />
</div>
<Note.Content className="min-w-0" />
<div className="flex items-center gap-10 -ml-1">
<Note.Reply />
<Note.Reaction />
<Note.Repost />
<Note.Zap />

View File

@ -8,10 +8,8 @@ import { ChildReply } from "./childReply";
export function Reply({
event,
rootEvent,
}: {
event: NDKEventWithReplies;
rootEvent: string;
}) {
const [open, setOpen] = useState(false);
@ -19,12 +17,15 @@ export function Reply({
<Collapsible.Root open={open} onOpenChange={setOpen}>
<Note.Provider event={event}>
<Note.Root>
<Note.User className="h-14 px-3" />
<Note.TextContent content={event.content} className="min-w-0 px-3" />
<div className="-ml-1 flex items-center justify-between h-14 px-3">
<div className="flex items-center justify-between px-3 h-14">
<Note.User className="flex-1 pr-1" />
<Note.Menu />
</div>
<Note.Content className="min-w-0 px-3" />
<div className="flex items-center justify-between px-3 -ml-1 h-14">
{event.replies?.length > 0 ? (
<Collapsible.Trigger asChild>
<div className="ml-1 inline-flex h-14 items-center gap-1 font-semibold text-blue-500">
<div className="inline-flex items-center gap-1 ml-1 font-semibold text-blue-500 h-14">
<NavArrowDownIcon
className={twMerge(
"h-3 w-3",
@ -38,7 +39,7 @@ export function Reply({
</Collapsible.Trigger>
) : null}
<div className="inline-flex items-center gap-10">
<Note.Reply rootEventId={rootEvent} />
<Note.Reply />
<Note.Reaction />
<Note.Repost />
<Note.Zap />

View File

@ -1,4 +1,4 @@
import { NDKEvent, NDKKind, NostrEvent } from "@nostr-dev-kit/ndk";
import { NDKEvent, NostrEvent } from "@nostr-dev-kit/ndk";
import { useQuery } from "@tanstack/react-query";
import { Note } from "..";
import { useArk } from "../../../provider";
@ -30,18 +30,6 @@ export function RepostNote({
refetchOnWindowFocus: false,
});
const renderContentByKind = () => {
if (!repostEvent) return null;
switch (repostEvent.kind) {
case NDKKind.Text:
return <Note.TextContent content={repostEvent.content} />;
case 1063:
return <Note.MediaContent tags={repostEvent.tags} />;
default:
return null;
}
};
if (isLoading) {
return <div className="w-full px-3 pb-3">Loading...</div>;
}
@ -52,8 +40,8 @@ export function RepostNote({
<Note.Provider event={event}>
<Note.User variant="repost" className="h-14" />
</Note.Provider>
<div className="select-text px-3 mb-3">
<div className="bg-red-100 dark:bg-red-900 flex-col py-3 rounded-lg flex items-start justify-start px-3">
<div className="px-3 mb-3 select-text">
<div className="flex flex-col items-start justify-start px-3 py-3 bg-red-100 rounded-lg dark:bg-red-900">
<p className="text-red-500">Failed to get event</p>
<p className="text-sm">
You can consider enable Outbox in Settings for better event
@ -72,9 +60,12 @@ export function RepostNote({
</Note.Provider>
<Note.Provider event={repostEvent}>
<div className="relative flex flex-col gap-2 px-3">
<Note.User />
{renderContentByKind()}
<div className="flex h-14 items-center justify-between">
<div className="flex items-center justify-between">
<Note.User className="flex-1 pr-1" />
<Note.Menu />
</div>
<Note.Content />
<div className="flex items-center justify-between h-14">
<Note.Pin />
<div className="inline-flex items-center gap-10">
<Note.Reply />
@ -82,6 +73,7 @@ export function RepostNote({
<Note.Repost />
<Note.Zap />
</div>
N
</div>
</div>
</Note.Provider>

View File

@ -12,16 +12,16 @@ export function TextNote({
return (
<Note.Provider event={event}>
<Note.Root className={className}>
<div className="h-14 px-3 flex items-center justify-between">
<div className="flex items-center justify-between px-3 h-14">
<Note.User className="flex-1 pr-1" />
<Note.Menu />
</div>
<Note.Thread thread={thread} className="mb-2" />
<Note.TextContent content={event.content} className="min-w-0 px-3" />
<div className="flex h-14 items-center justify-between px-3">
<Note.Content className="min-w-0 px-3" />
<div className="flex items-center justify-between px-3 h-14">
<Note.Pin />
<div className="inline-flex items-center gap-10">
<Note.Reply rootEventId={thread?.rootEventId} />
<Note.Reply />
<Note.Reaction />
<Note.Repost />
<Note.Zap />

View File

@ -14,16 +14,15 @@ export function ThreadNote({ eventId }: { eventId: string }) {
return (
<>
<Note.Thread thread={thread} className="mb-2" />
<Note.TextContent content={data.content} className="min-w-0 px-3" />
<Note.Content className="min-w-0 px-3" />
</>
);
case NDKKind.Article:
return <Note.ArticleContent eventId={event.id} tags={event.tags} />;
case 1063:
return <Note.MediaContent tags={event.tags} />;
default:
return (
<Note.TextContent content={data.content} className="min-w-0 px-3" />
<>
<Note.Thread thread={thread} className="mb-2" />
<Note.Content className="min-w-0 px-3" />
</>
);
}
};
@ -35,9 +34,12 @@ export function ThreadNote({ eventId }: { eventId: string }) {
return (
<Note.Provider event={data}>
<Note.Root>
<Note.User variant="thread" className="h-16 px-3" />
<div className="flex items-center justify-between px-3 h-14">
<Note.User className="flex-1 pr-1" />
<Note.Menu />
</div>
{renderEventKind(data)}
<div className="flex h-14 items-center justify-between px-3">
<div className="flex items-center justify-between px-3 h-14">
<Note.Pin />
<div className="inline-flex items-center gap-10">
<Note.Reply />

View File

@ -3,6 +3,7 @@ import { NDKCacheAdapterTauri } from "@lume/ndk-cache-tauri";
import { LumeStorage } from "@lume/storage";
import { QUOTES, delay, sendNativeNotification } from "@lume/utils";
import NDK, {
NDKKind,
NDKNip46Signer,
NDKPrivateKeySigner,
NDKRelay,
@ -154,8 +155,8 @@ const LumeProvider = ({ children }: PropsWithChildren<object>) => {
// auth
ndk.relayAuthDefaultPolicy = async (relay: NDKRelay, challenge: string) => {
const signIn = NDKRelayAuthPolicies.signIn({ ndk, signer });
const event = await signIn(relay, challenge);
const signIn = NDKRelayAuthPolicies.signIn({ ndk });
const event = await signIn(relay, challenge).catch((e) => console.log(e));
if (event) {
sendNativeNotification(
`You've sign in sucessfully to relay: ${relay.url}`,