mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-29 16:30:55 +00:00
refactor(note): only support kind 1
This commit is contained in:
parent
fcde669685
commit
542b6033c2
36
packages/ark/src/components/note/content.tsx
Normal file
36
packages/ark/src/components/note/content.tsx
Normal 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>
|
||||
);
|
||||
}
|
@ -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";
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
@ -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>
|
||||
);
|
||||
}
|
@ -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>
|
||||
);
|
||||
}
|
@ -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}`}
|
||||
|
@ -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 />
|
||||
|
@ -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 />
|
||||
|
@ -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>
|
||||
|
@ -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 />
|
||||
|
@ -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 />
|
||||
|
@ -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}`,
|
||||
|
Loading…
Reference in New Issue
Block a user