refactor(note): only support kind 1

This commit is contained in:
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 { NoteRepost } from "./buttons/repost";
import { NoteZap } from "./buttons/zap"; import { NoteZap } from "./buttons/zap";
import { NoteChild } from "./child"; import { NoteChild } from "./child";
import { NoteArticleContent } from "./kinds/article"; import { NoteContent } from "./content";
import { NoteMediaContent } from "./kinds/media";
import { NoteTextContent } from "./kinds/text";
import { NoteMenu } from "./menu"; import { NoteMenu } from "./menu";
import { NoteProvider } from "./provider"; import { NoteProvider } from "./provider";
import { NoteRoot } from "./root"; import { NoteRoot } from "./root";
@@ -21,13 +19,11 @@ export const Note = {
Reply: NoteReply, Reply: NoteReply,
Repost: NoteRepost, Repost: NoteRepost,
Reaction: NoteReaction, Reaction: NoteReaction,
Content: NoteContent,
Zap: NoteZap, Zap: NoteZap,
Pin: NotePin, Pin: NotePin,
Child: NoteChild, Child: NoteChild,
Thread: NoteThread, Thread: NoteThread,
TextContent: NoteTextContent,
MediaContent: NoteMediaContent,
ArticleContent: NoteArticleContent,
}; };
export * from "./provider"; 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 { memo } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { Note } from ".."; import { Note } from "..";
@@ -10,19 +9,6 @@ export const MentionNote = memo(function MentionNote({
}: { eventId: string; openable?: boolean }) { }: { eventId: string; openable?: boolean }) {
const { isLoading, isError, data } = useEvent(eventId); 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) { if (isLoading) {
return ( return (
<div <div
@@ -52,7 +38,7 @@ export const MentionNote = memo(function MentionNote({
<Note.User variant="mention" /> <Note.User variant="mention" />
</div> </div>
<div className="px-3 pb-3 mt-1"> <div className="px-3 pb-3 mt-1">
{renderKind(data)} <Note.Content />
{openable ? ( {openable ? (
<Link <Link
to={`/events/${data.id}`} to={`/events/${data.id}`}

View File

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

View File

@@ -8,10 +8,8 @@ import { ChildReply } from "./childReply";
export function Reply({ export function Reply({
event, event,
rootEvent,
}: { }: {
event: NDKEventWithReplies; event: NDKEventWithReplies;
rootEvent: string;
}) { }) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
@@ -19,12 +17,15 @@ export function Reply({
<Collapsible.Root open={open} onOpenChange={setOpen}> <Collapsible.Root open={open} onOpenChange={setOpen}>
<Note.Provider event={event}> <Note.Provider event={event}>
<Note.Root> <Note.Root>
<Note.User className="h-14 px-3" /> <div className="flex items-center justify-between px-3 h-14">
<Note.TextContent content={event.content} className="min-w-0 px-3" /> <Note.User className="flex-1 pr-1" />
<div className="-ml-1 flex items-center justify-between h-14 px-3"> <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 ? ( {event.replies?.length > 0 ? (
<Collapsible.Trigger asChild> <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 <NavArrowDownIcon
className={twMerge( className={twMerge(
"h-3 w-3", "h-3 w-3",
@@ -38,7 +39,7 @@ export function Reply({
</Collapsible.Trigger> </Collapsible.Trigger>
) : null} ) : null}
<div className="inline-flex items-center gap-10"> <div className="inline-flex items-center gap-10">
<Note.Reply rootEventId={rootEvent} /> <Note.Reply />
<Note.Reaction /> <Note.Reaction />
<Note.Repost /> <Note.Repost />
<Note.Zap /> <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 { useQuery } from "@tanstack/react-query";
import { Note } from ".."; import { Note } from "..";
import { useArk } from "../../../provider"; import { useArk } from "../../../provider";
@@ -30,18 +30,6 @@ export function RepostNote({
refetchOnWindowFocus: false, 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) { if (isLoading) {
return <div className="w-full px-3 pb-3">Loading...</div>; return <div className="w-full px-3 pb-3">Loading...</div>;
} }
@@ -52,8 +40,8 @@ export function RepostNote({
<Note.Provider event={event}> <Note.Provider event={event}>
<Note.User variant="repost" className="h-14" /> <Note.User variant="repost" className="h-14" />
</Note.Provider> </Note.Provider>
<div className="select-text px-3 mb-3"> <div className="px-3 mb-3 select-text">
<div className="bg-red-100 dark:bg-red-900 flex-col py-3 rounded-lg flex items-start justify-start px-3"> <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-red-500">Failed to get event</p>
<p className="text-sm"> <p className="text-sm">
You can consider enable Outbox in Settings for better event You can consider enable Outbox in Settings for better event
@@ -72,9 +60,12 @@ export function RepostNote({
</Note.Provider> </Note.Provider>
<Note.Provider event={repostEvent}> <Note.Provider event={repostEvent}>
<div className="relative flex flex-col gap-2 px-3"> <div className="relative flex flex-col gap-2 px-3">
<Note.User /> <div className="flex items-center justify-between">
{renderContentByKind()} <Note.User className="flex-1 pr-1" />
<div className="flex h-14 items-center justify-between"> <Note.Menu />
</div>
<Note.Content />
<div className="flex items-center justify-between h-14">
<Note.Pin /> <Note.Pin />
<div className="inline-flex items-center gap-10"> <div className="inline-flex items-center gap-10">
<Note.Reply /> <Note.Reply />
@@ -82,6 +73,7 @@ export function RepostNote({
<Note.Repost /> <Note.Repost />
<Note.Zap /> <Note.Zap />
</div> </div>
N
</div> </div>
</div> </div>
</Note.Provider> </Note.Provider>

View File

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

View File

@@ -14,16 +14,15 @@ export function ThreadNote({ eventId }: { eventId: string }) {
return ( return (
<> <>
<Note.Thread thread={thread} className="mb-2" /> <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: default:
return ( 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 ( return (
<Note.Provider event={data}> <Note.Provider event={data}>
<Note.Root> <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)} {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 /> <Note.Pin />
<div className="inline-flex items-center gap-10"> <div className="inline-flex items-center gap-10">
<Note.Reply /> <Note.Reply />

View File

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