mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-19 11:43:30 +00:00
wip
This commit is contained in:
parent
ee3e8eb105
commit
ce864c8990
@ -2,13 +2,13 @@ import { memo } from 'react';
|
|||||||
|
|
||||||
import { useRichContent } from '@utils/hooks/useRichContent';
|
import { useRichContent } from '@utils/hooks/useRichContent';
|
||||||
|
|
||||||
export function TextKind({ content, truncate }: { content: string; truncate?: boolean }) {
|
export function TextKind({ content, textmode }: { content: string; textmode?: boolean }) {
|
||||||
const { parsedContent } = useRichContent(content);
|
const { parsedContent } = useRichContent(content, textmode);
|
||||||
|
|
||||||
if (truncate) {
|
if (textmode) {
|
||||||
return (
|
return (
|
||||||
<div className="break-p select-text whitespace-pre-line leading-normal text-neutral-900 dark:text-neutral-100">
|
<div className="break-p line-clamp-3 select-text leading-normal text-neutral-900 dark:text-neutral-100">
|
||||||
{content}
|
{parsedContent}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
|
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
import { PlusIcon } from '@shared/icons';
|
|
||||||
import {
|
import {
|
||||||
MemoizedArticleKind,
|
MemoizedArticleKind,
|
||||||
MemoizedFileKind,
|
MemoizedFileKind,
|
||||||
@ -22,7 +21,7 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) {
|
|||||||
const renderKind = (event: NDKEvent) => {
|
const renderKind = (event: NDKEvent) => {
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
case NDKKind.Text:
|
case NDKKind.Text:
|
||||||
return <MemoizedTextKind content={event.content} />;
|
return <MemoizedTextKind content={event.content} textmode />;
|
||||||
case NDKKind.Article:
|
case NDKKind.Article:
|
||||||
return <MemoizedArticleKind id={event.id} tags={event.tags} />;
|
return <MemoizedArticleKind id={event.id} tags={event.tags} />;
|
||||||
case 1063:
|
case 1063:
|
||||||
@ -42,10 +41,25 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="my-2 flex w-full cursor-default flex-col gap-1 rounded-lg bg-neutral-100 dark:bg-neutral-900">
|
<div className="my-2 flex w-full cursor-default flex-col gap-1 rounded-lg bg-neutral-100 dark:bg-neutral-900">
|
||||||
<div className="px-3 pt-3">
|
<div className="mt-3 px-3">
|
||||||
<User pubkey={data.pubkey} time={data.created_at} variant="mention" />
|
<User pubkey={data.pubkey} time={data.created_at} variant="mention" />
|
||||||
</div>
|
</div>
|
||||||
<div>{renderKind(data)}</div>
|
<div className="mt-1 px-3 pb-3">
|
||||||
|
{renderKind(data)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
addWidget.mutate({
|
||||||
|
kind: WidgetKinds.local.thread,
|
||||||
|
title: 'Thread',
|
||||||
|
content: data.id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="mt-2 text-blue-500 hover:text-blue-600"
|
||||||
|
>
|
||||||
|
Show more
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,12 @@ import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
|
|||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
import { ShareIcon } from '@shared/icons';
|
import { ShareIcon } from '@shared/icons';
|
||||||
import { MemoizedArticleKind, MemoizedFileKind, NoteSkeleton } from '@shared/notes';
|
import {
|
||||||
|
MemoizedArticleKind,
|
||||||
|
MemoizedFileKind,
|
||||||
|
MemoizedTextKind,
|
||||||
|
NoteSkeleton,
|
||||||
|
} from '@shared/notes';
|
||||||
import { User } from '@shared/user';
|
import { User } from '@shared/user';
|
||||||
|
|
||||||
import { WidgetKinds } from '@stores/constants';
|
import { WidgetKinds } from '@stores/constants';
|
||||||
@ -21,11 +26,7 @@ export function NotifyNote({ event }: { event: NDKEvent }) {
|
|||||||
const renderKind = (event: NDKEvent) => {
|
const renderKind = (event: NDKEvent) => {
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
case NDKKind.Text:
|
case NDKKind.Text:
|
||||||
return (
|
return <MemoizedTextKind key={event.id} content={event.content} textmode />;
|
||||||
<div className="break-p line-clamp-3 select-text leading-normal text-neutral-900 dark:text-neutral-100">
|
|
||||||
{event.content}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case NDKKind.Article:
|
case NDKKind.Article:
|
||||||
return <MemoizedArticleKind key={event.id} id={event.id} tags={event.tags} />;
|
return <MemoizedArticleKind key={event.id} id={event.id} tags={event.tags} />;
|
||||||
case 1063:
|
case 1063:
|
||||||
|
@ -3,19 +3,15 @@ import { useState } from 'react';
|
|||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
import { useNDK } from '@libs/ndk/provider';
|
import { useNDK } from '@libs/ndk/provider';
|
||||||
import { useStorage } from '@libs/storage/provider';
|
|
||||||
|
|
||||||
import { ReplyMediaUploader } from '@shared/notes';
|
import { ReplyMediaUploader } from '@shared/notes';
|
||||||
import { User } from '@shared/user';
|
|
||||||
|
|
||||||
export function NoteReplyForm({ id }: { id: string }) {
|
export function NoteReplyForm({ eventId }: { eventId: string }) {
|
||||||
const { db } = useStorage();
|
|
||||||
const { ndk, relayUrls } = useNDK();
|
const { ndk, relayUrls } = useNDK();
|
||||||
|
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
const tags = [['e', id, relayUrls[0], 'root']];
|
const tags = [['e', eventId, relayUrls[0], 'root']];
|
||||||
|
|
||||||
// publish event
|
// publish event
|
||||||
const event = new NDKEvent(ndk);
|
const event = new NDKEvent(ndk);
|
||||||
@ -31,17 +27,15 @@ export function NoteReplyForm({ id }: { id: string }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-3 flex gap-3">
|
<div className="mt-3 flex flex-col rounded-xl bg-neutral-50 dark:bg-neutral-950">
|
||||||
<User pubkey={db.account.pubkey} variant="miniavatar" />
|
|
||||||
<div className="relative flex flex-1 flex-col rounded-xl bg-neutral-100 dark:bg-neutral-900">
|
|
||||||
<textarea
|
<textarea
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => setValue(e.target.value)}
|
onChange={(e) => setValue(e.target.value)}
|
||||||
placeholder="Reply to this thread..."
|
placeholder="Reply to this post..."
|
||||||
className="relative h-36 w-full resize-none bg-transparent px-5 py-4 text-neutral-900 !outline-none placeholder:text-neutral-600 dark:text-neutral-100 dark:placeholder:text-neutral-400"
|
className="h-28 w-full resize-none rounded-t-xl bg-neutral-100 px-5 py-4 text-neutral-900 !outline-none placeholder:text-neutral-600 dark:bg-neutral-900 dark:text-neutral-100 dark:placeholder:text-neutral-400"
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
/>
|
/>
|
||||||
<div className="inline-flex items-center justify-end gap-2 rounded-b-xl border-t border-neutral-200 p-2 dark:border-neutral-800">
|
<div className="inline-flex items-center justify-end gap-2 rounded-b-xl p-2">
|
||||||
<ReplyMediaUploader setValue={setValue} />
|
<ReplyMediaUploader setValue={setValue} />
|
||||||
<button
|
<button
|
||||||
onClick={() => submit()}
|
onClick={() => submit()}
|
||||||
@ -52,6 +46,5 @@ export function NoteReplyForm({ id }: { id: string }) {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import { useState } from 'react';
|
|||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
import { NavArrowDownIcon } from '@shared/icons';
|
import { NavArrowDownIcon } from '@shared/icons';
|
||||||
import { MemoizedTextNote, NoteActions, SubReply } from '@shared/notes';
|
import { MemoizedTextKind, NoteActions, SubReply } from '@shared/notes';
|
||||||
import { User } from '@shared/user';
|
import { User } from '@shared/user';
|
||||||
|
|
||||||
import { NDKEventWithReplies } from '@utils/types';
|
import { NDKEventWithReplies } from '@utils/types';
|
||||||
@ -12,13 +12,11 @@ export function Reply({ event, root }: { event: NDKEventWithReplies; root?: stri
|
|||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div>
|
||||||
<div className="relative flex flex-col">
|
<div className="flex flex-col gap-2">
|
||||||
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
|
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
|
||||||
<div className="-mt-4 flex items-start gap-3">
|
<MemoizedTextKind content={event.content} />
|
||||||
<div className="w-10 shrink-0" />
|
<div className="-ml-1">
|
||||||
<div className="relative z-10 flex-1">
|
|
||||||
<MemoizedTextNote content={event.content} />
|
|
||||||
<NoteActions
|
<NoteActions
|
||||||
id={event.id}
|
id={event.id}
|
||||||
pubkey={event.pubkey}
|
pubkey={event.pubkey}
|
||||||
@ -27,8 +25,7 @@ export function Reply({ event, root }: { event: NDKEventWithReplies; root?: stri
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="pl-4">
|
||||||
<div className="pl-[48px]">
|
|
||||||
<Collapsible.Root open={open} onOpenChange={setOpen}>
|
<Collapsible.Root open={open} onOpenChange={setOpen}>
|
||||||
{event.replies?.length > 0 ? (
|
{event.replies?.length > 0 ? (
|
||||||
<div>
|
<div>
|
||||||
|
@ -6,7 +6,7 @@ import { Reply } from '@shared/notes';
|
|||||||
import { useNostr } from '@utils/hooks/useNostr';
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
import { NDKEventWithReplies } from '@utils/types';
|
import { NDKEventWithReplies } from '@utils/types';
|
||||||
|
|
||||||
export function ReplyList({ id }: { id: string }) {
|
export function ReplyList({ eventId }: { eventId: string }) {
|
||||||
const { fetchAllReplies, sub } = useNostr();
|
const { fetchAllReplies, sub } = useNostr();
|
||||||
const [data, setData] = useState<null | NDKEventWithReplies[]>(null);
|
const [data, setData] = useState<null | NDKEventWithReplies[]>(null);
|
||||||
|
|
||||||
@ -14,31 +14,32 @@ export function ReplyList({ id }: { id: string }) {
|
|||||||
let isCancelled = false;
|
let isCancelled = false;
|
||||||
|
|
||||||
async function fetchRepliesAndSub() {
|
async function fetchRepliesAndSub() {
|
||||||
const events = await fetchAllReplies(id);
|
const events = await fetchAllReplies(eventId);
|
||||||
if (!isCancelled) {
|
if (!isCancelled) {
|
||||||
setData(events);
|
setData(events);
|
||||||
}
|
}
|
||||||
// subscribe for new replies
|
// subscribe for new replies
|
||||||
sub(
|
sub(
|
||||||
{
|
{
|
||||||
'#e': [id],
|
'#e': [eventId],
|
||||||
since: Math.floor(Date.now() / 1000),
|
since: Math.floor(Date.now() / 1000),
|
||||||
},
|
},
|
||||||
(event: NDKEventWithReplies) => setData((prev) => [event, ...prev]),
|
(event: NDKEventWithReplies) => setData((prev) => [event, ...prev]),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchRepliesAndSub();
|
fetchRepliesAndSub();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
isCancelled = true;
|
isCancelled = true;
|
||||||
};
|
};
|
||||||
}, [id]);
|
}, [eventId]);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return (
|
return (
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<div className="flex h-16 items-center justify-center rounded-xl bg-neutral-100 px-3 py-3 dark:bg-neutral-900">
|
<div className="flex h-16 items-center justify-center rounded-xl bg-neutral-50 p-3 dark:bg-neutral-950">
|
||||||
<LoaderIcon className="h-5 w-5 animate-spin" />
|
<LoaderIcon className="h-5 w-5 animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -52,12 +53,12 @@ export function ReplyList({ id }: { id: string }) {
|
|||||||
<div className="flex flex-col items-center justify-center gap-2 py-6">
|
<div className="flex flex-col items-center justify-center gap-2 py-6">
|
||||||
<h3 className="text-3xl">👋</h3>
|
<h3 className="text-3xl">👋</h3>
|
||||||
<p className="leading-none text-neutral-600 dark:text-neutral-400">
|
<p className="leading-none text-neutral-600 dark:text-neutral-400">
|
||||||
Share your thought on it...
|
Be the first to Reply!
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
data.map((event) => <Reply key={event.id} event={event} root={id} />)
|
data.map((event) => <Reply key={event.id} event={event} root={eventId} />)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,19 +1,16 @@
|
|||||||
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
import { NDKEvent } from '@nostr-dev-kit/ndk';
|
||||||
|
|
||||||
import { MemoizedTextNote, NoteActions } from '@shared/notes';
|
import { MemoizedTextKind, NoteActions } from '@shared/notes';
|
||||||
import { User } from '@shared/user';
|
import { User } from '@shared/user';
|
||||||
|
|
||||||
export function SubReply({ event }: { event: NDKEvent }) {
|
export function SubReply({ event }: { event: NDKEvent }) {
|
||||||
return (
|
return (
|
||||||
<div className="mb-3 flex flex-col">
|
<div className="flex flex-col gap-2">
|
||||||
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
|
<User pubkey={event.pubkey} time={event.created_at} eventId={event.id} />
|
||||||
<div className="-mt-4 flex items-start gap-3">
|
<MemoizedTextKind content={event.content} />
|
||||||
<div className="w-10 shrink-0" />
|
<div className="-ml-1">
|
||||||
<div className="flex-1">
|
|
||||||
<MemoizedTextNote content={event.content} />
|
|
||||||
<NoteActions id={event.id} pubkey={event.pubkey} extraButtons={false} />
|
<NoteActions id={event.id} pubkey={event.pubkey} extraButtons={false} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,15 @@ import { memo } from 'react';
|
|||||||
import { ChildNote, NoteActions } from '@shared/notes';
|
import { ChildNote, NoteActions } from '@shared/notes';
|
||||||
import { User } from '@shared/user';
|
import { User } from '@shared/user';
|
||||||
|
|
||||||
|
import { WidgetKinds } from '@stores/constants';
|
||||||
|
|
||||||
import { useNostr } from '@utils/hooks/useNostr';
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
import { useRichContent } from '@utils/hooks/useRichContent';
|
import { useRichContent } from '@utils/hooks/useRichContent';
|
||||||
|
import { useWidget } from '@utils/hooks/useWidget';
|
||||||
|
|
||||||
export function TextNote({ event }: { event: NDKEvent }) {
|
export function TextNote({ event }: { event: NDKEvent }) {
|
||||||
const { parsedContent } = useRichContent(event.content);
|
const { parsedContent } = useRichContent(event.content);
|
||||||
|
const { addWidget } = useWidget();
|
||||||
const { getEventThread } = useNostr();
|
const { getEventThread } = useNostr();
|
||||||
|
|
||||||
const thread = getEventThread(event);
|
const thread = getEventThread(event);
|
||||||
@ -22,6 +26,19 @@ export function TextNote({ event }: { event: NDKEvent }) {
|
|||||||
<div className="flex h-min w-full flex-col gap-3 rounded-lg bg-neutral-100 p-3 dark:bg-neutral-900">
|
<div className="flex h-min w-full flex-col gap-3 rounded-lg bg-neutral-100 p-3 dark:bg-neutral-900">
|
||||||
{thread.rootEventId ? <ChildNote id={thread.rootEventId} isRoot /> : null}
|
{thread.rootEventId ? <ChildNote id={thread.rootEventId} isRoot /> : null}
|
||||||
{thread.replyEventId ? <ChildNote id={thread.replyEventId} /> : null}
|
{thread.replyEventId ? <ChildNote id={thread.replyEventId} /> : null}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
addWidget.mutate({
|
||||||
|
kind: WidgetKinds.local.thread,
|
||||||
|
title: 'Thread',
|
||||||
|
content: thread.rootEventId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="self-start text-blue-500 hover:text-blue-600"
|
||||||
|
>
|
||||||
|
Show full thread
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -102,7 +102,7 @@ export const User = memo(function User({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Avatar.Root className="shrink-0">
|
<Avatar.Root className="h-8 w-8 shrink-0">
|
||||||
<Avatar.Image
|
<Avatar.Image
|
||||||
src={user?.picture || user?.image}
|
src={user?.picture || user?.image}
|
||||||
alt={pubkey}
|
alt={pubkey}
|
||||||
@ -308,14 +308,16 @@ export const User = memo(function User({
|
|||||||
/>
|
/>
|
||||||
</Avatar.Fallback>
|
</Avatar.Fallback>
|
||||||
</Avatar.Root>
|
</Avatar.Root>
|
||||||
<div className="absolute left-2 top-2 font-semibold leading-tight">
|
<div className="absolute left-2 top-2 inline-flex items-center gap-1.5 font-semibold leading-tight">
|
||||||
|
<div className="w-full max-w-[10rem] truncate">
|
||||||
{user?.display_name ||
|
{user?.display_name ||
|
||||||
user?.name ||
|
user?.name ||
|
||||||
user?.displayName ||
|
user?.displayName ||
|
||||||
displayNpub(pubkey, 16)}{' '}
|
displayNpub(pubkey, 16)}{' '}
|
||||||
<span className="font-normal text-neutral-700 dark:text-neutral-300">
|
</div>
|
||||||
|
<div className="font-normal text-neutral-700 dark:text-neutral-300">
|
||||||
{subtext}:
|
{subtext}:
|
||||||
</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -440,20 +442,20 @@ export const User = memo(function User({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex h-16 items-center gap-3 px-3">
|
||||||
<Avatar.Root className="h-10 w-10 shrink-0">
|
<Avatar.Root className="h-10 w-10 shrink-0">
|
||||||
<Avatar.Image
|
<Avatar.Image
|
||||||
src={user?.picture || user?.image}
|
src={user?.picture || user?.image}
|
||||||
alt={pubkey}
|
alt={pubkey}
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
className="h-10 w-10 rounded-lg"
|
className="h-10 w-10 rounded-lg ring-1 ring-neutral-200/50 dark:ring-neutral-800/50"
|
||||||
/>
|
/>
|
||||||
<Avatar.Fallback delayMs={300}>
|
<Avatar.Fallback delayMs={300}>
|
||||||
<img
|
<img
|
||||||
src={svgURI}
|
src={svgURI}
|
||||||
alt={pubkey}
|
alt={pubkey}
|
||||||
className="h-10 w-10 rounded-lg bg-black dark:bg-white"
|
className="h-10 w-10 rounded-lg bg-black ring-1 ring-neutral-200/50 dark:bg-white dark:ring-neutral-800/50"
|
||||||
/>
|
/>
|
||||||
</Avatar.Fallback>
|
</Avatar.Fallback>
|
||||||
</Avatar.Root>
|
</Avatar.Root>
|
||||||
|
@ -4,13 +4,11 @@ import { WVList } from 'virtua';
|
|||||||
|
|
||||||
import { LoaderIcon } from '@shared/icons';
|
import { LoaderIcon } from '@shared/icons';
|
||||||
import {
|
import {
|
||||||
MemoizedArticleNote,
|
MemoizedArticleKind,
|
||||||
MemoizedFileNote,
|
MemoizedFileKind,
|
||||||
MemoizedTextNote,
|
MemoizedTextKind,
|
||||||
NoteActions,
|
NoteActions,
|
||||||
NoteReplyForm,
|
NoteReplyForm,
|
||||||
NoteStats,
|
|
||||||
UnknownNote,
|
|
||||||
} from '@shared/notes';
|
} from '@shared/notes';
|
||||||
import { ReplyList } from '@shared/notes/replies/list';
|
import { ReplyList } from '@shared/notes/replies/list';
|
||||||
import { TitleBar } from '@shared/titleBar';
|
import { TitleBar } from '@shared/titleBar';
|
||||||
@ -27,13 +25,13 @@ export function LocalThreadWidget({ params }: { params: Widget }) {
|
|||||||
(event: NDKEvent) => {
|
(event: NDKEvent) => {
|
||||||
switch (event.kind) {
|
switch (event.kind) {
|
||||||
case NDKKind.Text:
|
case NDKKind.Text:
|
||||||
return <MemoizedTextNote content={event.content} />;
|
return <MemoizedTextKind content={event.content} />;
|
||||||
case NDKKind.Article:
|
case NDKKind.Article:
|
||||||
return <MemoizedArticleNote event={event} />;
|
return <MemoizedArticleKind id={event.id} tags={event.tags} />;
|
||||||
case 1063:
|
case 1063:
|
||||||
return <MemoizedFileNote event={event} />;
|
return <MemoizedFileKind tags={event.tags} />;
|
||||||
default:
|
default:
|
||||||
return <UnknownNote event={event} />;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[data]
|
[data]
|
||||||
@ -42,23 +40,22 @@ export function LocalThreadWidget({ params }: { params: Widget }) {
|
|||||||
return (
|
return (
|
||||||
<WidgetWrapper>
|
<WidgetWrapper>
|
||||||
<TitleBar id={params.id} title={params.title} />
|
<TitleBar id={params.id} title={params.title} />
|
||||||
<WVList className="flex-1 overflow-y-auto px-3">
|
<WVList className="flex-1 overflow-y-auto px-3 pb-5">
|
||||||
{status === 'pending' ? (
|
{status === 'pending' ? (
|
||||||
<div className="flex h-16 items-center justify-center rounded-xl bg-neutral-100 px-3 py-3 dark:bg-neutral-900">
|
<div className="flex h-16 items-center justify-center rounded-xl bg-neutral-50 px-3 py-3 dark:bg-neutral-950">
|
||||||
<LoaderIcon className="h-5 w-5 animate-spin" />
|
<LoaderIcon className="h-5 w-5 animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="rounded-xl bg-neutral-100 px-3 py-3 dark:bg-neutral-900">
|
<>
|
||||||
|
<div className="flex flex-col rounded-xl bg-neutral-50 dark:bg-neutral-950">
|
||||||
<User pubkey={data.pubkey} time={data.created_at} variant="thread" />
|
<User pubkey={data.pubkey} time={data.created_at} variant="thread" />
|
||||||
<div className="mt-2">{renderKind(data)}</div>
|
{renderKind(data)}
|
||||||
<NoteActions id={params.content} pubkey={data.pubkey} extraButtons={false} />
|
<NoteActions id={data.id} pubkey={data.pubkey} />
|
||||||
</div>
|
</div>
|
||||||
|
<NoteReplyForm eventId={params.content} />
|
||||||
|
<ReplyList eventId={data.id} />
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<NoteStats id={params.content} />
|
|
||||||
<hr className="my-4 h-px w-full border-none bg-neutral-100" />
|
|
||||||
<NoteReplyForm id={params.content} />
|
|
||||||
<ReplyList id={params.content} />
|
|
||||||
<div className="h-10" />
|
|
||||||
</WVList>
|
</WVList>
|
||||||
</WidgetWrapper>
|
</WidgetWrapper>
|
||||||
);
|
);
|
||||||
|
@ -42,17 +42,23 @@ const VIDEOS = [
|
|||||||
'.m3u8',
|
'.m3u8',
|
||||||
];
|
];
|
||||||
|
|
||||||
export function useRichContent(content: string) {
|
export function useRichContent(content: string, textmode: boolean = false) {
|
||||||
let parsedContent: string | ReactNode[] = content;
|
let parsedContent: string | ReactNode[] = content;
|
||||||
let linkPreview: string;
|
let linkPreview: string;
|
||||||
|
let images: string[] = [];
|
||||||
|
let videos: string[] = [];
|
||||||
|
let events: string[] = [];
|
||||||
|
|
||||||
const text = content;
|
const text = content;
|
||||||
const words = text.split(/(\s+)/);
|
const words = text.split(/(\s+)/);
|
||||||
|
|
||||||
const images = words.filter((word) => IMAGES.some((el) => word.endsWith(el)));
|
if (!textmode) {
|
||||||
const videos = words.filter((word) => VIDEOS.some((el) => word.endsWith(el)));
|
images = words.filter((word) => IMAGES.some((el) => word.endsWith(el)));
|
||||||
|
videos = words.filter((word) => VIDEOS.some((el) => word.endsWith(el)));
|
||||||
|
events = words.filter((word) => NOSTR_EVENTS.some((el) => word.startsWith(el)));
|
||||||
|
}
|
||||||
|
|
||||||
const hashtags = words.filter((word) => word.startsWith('#'));
|
const hashtags = words.filter((word) => word.startsWith('#'));
|
||||||
const events = words.filter((word) => NOSTR_EVENTS.some((el) => word.startsWith(el)));
|
|
||||||
const mentions = words.filter((word) =>
|
const mentions = words.filter((word) =>
|
||||||
NOSTR_MENTIONS.some((el) => word.startsWith(el))
|
NOSTR_MENTIONS.some((el) => word.startsWith(el))
|
||||||
);
|
);
|
||||||
@ -127,7 +133,7 @@ export function useRichContent(content: string) {
|
|||||||
const url = new URL(match);
|
const url = new URL(match);
|
||||||
url.search = '';
|
url.search = '';
|
||||||
|
|
||||||
if (!linkPreview) {
|
if (!linkPreview && !textmode) {
|
||||||
linkPreview = match;
|
linkPreview = match;
|
||||||
return <LinkPreview key={match + i} url={url.toString()} />;
|
return <LinkPreview key={match + i} url={url.toString()} />;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user