mirror of
https://github.com/lumehq/lume.git
synced 2025-09-28 22:03:29 +02:00
add note wrapper
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { Header } from '@lume/app/daily/components/header';
|
import { Header } from '@lume/app/daily/components/header';
|
||||||
import NoteBase from '@lume/app/note/components/base';
|
import { NoteBase } from '@lume/app/note/components/base';
|
||||||
import { NoteQuoteRepost } from '@lume/app/note/components/quoteRepost';
|
import { NoteQuoteRepost } from '@lume/app/note/components/quoteRepost';
|
||||||
import { getNotes } from '@lume/utils/storage';
|
import { getNotes } from '@lume/utils/storage';
|
||||||
|
|
||||||
|
@@ -2,28 +2,17 @@ import { NoteContent } from '@lume/app/note/components/content';
|
|||||||
import NoteMetadata from '@lume/app/note/components/metadata';
|
import NoteMetadata from '@lume/app/note/components/metadata';
|
||||||
import { NoteParent } from '@lume/app/note/components/parent';
|
import { NoteParent } from '@lume/app/note/components/parent';
|
||||||
import { NoteDefaultUser } from '@lume/app/note/components/user/default';
|
import { NoteDefaultUser } from '@lume/app/note/components/user/default';
|
||||||
|
import { NoteWrapper } from '@lume/app/note/components/wrapper';
|
||||||
import { noteParser } from '@lume/utils/parser';
|
import { noteParser } from '@lume/utils/parser';
|
||||||
|
|
||||||
import { navigate } from 'vite-plugin-ssr/client/router';
|
export const NoteBase = ({ event }: { event: any }) => {
|
||||||
|
|
||||||
export default function NoteBase({ event }: { event: any }) {
|
|
||||||
const content = noteParser(event);
|
const content = noteParser(event);
|
||||||
|
const href = event.parent_id ? `/app/note?id=${event.parent_id}` : `/app/note?id=${event.event_id}`;
|
||||||
const openNote = (e) => {
|
|
||||||
const selection = window.getSelection();
|
|
||||||
if (selection.toString().length === 0) {
|
|
||||||
navigate(`/app/note?id=${event.parent_id}`);
|
|
||||||
} else {
|
|
||||||
e.stopPropagation();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div onClick={(e) => openNote(e)} className="h-min w-full select-text px-3 py-1.5">
|
<NoteWrapper href={href} className="h-min w-full px-3 py-1.5">
|
||||||
<div className="rounded-md border border-zinc-800 bg-zinc-900 px-3 pt-3 shadow-input shadow-black/20">
|
<div className="rounded-md border border-zinc-800 bg-zinc-900 px-3 pt-3 shadow-input shadow-black/20">
|
||||||
{event.parent_id && event.parent_id !== event.event_id && (
|
{event.parent_id && event.parent_id !== event.event_id && <NoteParent id={event.parent_id} />}
|
||||||
<NoteParent key={event.parent_id} id={event.parent_id} />
|
|
||||||
)}
|
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<NoteDefaultUser pubkey={event.pubkey} time={event.created_at} />
|
<NoteDefaultUser pubkey={event.pubkey} time={event.created_at} />
|
||||||
<div className="mt-1 pl-[52px]">
|
<div className="mt-1 pl-[52px]">
|
||||||
@@ -32,6 +21,6 @@ export default function NoteBase({ event }: { event: any }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</NoteWrapper>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
@@ -12,7 +12,7 @@ export const NoteContent = ({ content }: { content: any }) => {
|
|||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
remarkPlugins={[[remarkGfm]]}
|
remarkPlugins={[[remarkGfm]]}
|
||||||
linkTarget="_blank"
|
linkTarget="_blank"
|
||||||
className="prose prose-zinc max-w-none break-words dark:prose-invert prose-p:text-[15px] prose-p:leading-tight prose-a:text-[15px] prose-a:leading-tight prose-a:text-fuchsia-500 prose-a:no-underline hover:prose-a:text-fuchsia-600 hover:prose-a:underline prose-ol:mb-1 prose-ul:mb-1 prose-li:text-[15px] prose-li:leading-tight"
|
className="prose prose-zinc max-w-none select-text break-words dark:prose-invert prose-p:text-[15px] prose-p:leading-tight prose-a:text-[15px] prose-a:leading-tight prose-a:text-fuchsia-500 prose-a:no-underline hover:prose-a:text-fuchsia-600 hover:prose-a:underline prose-ol:mb-1 prose-ul:mb-1 prose-li:text-[15px] prose-li:leading-tight"
|
||||||
components={{
|
components={{
|
||||||
h5: ({ ...props }) => <NoteMentionUser pubkey={props.content} />,
|
h5: ({ ...props }) => <NoteMentionUser pubkey={props.content} />,
|
||||||
h6: ({ ...props }) => <NoteQuote id={props.content} />,
|
h6: ({ ...props }) => <NoteQuote id={props.content} />,
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { NoteContent } from '@lume/app/note/components/content';
|
import { NoteContent } from '@lume/app/note/components/content';
|
||||||
import { NoteDefaultUser } from '@lume/app/note/components/user/default';
|
import { NoteDefaultUser } from '@lume/app/note/components/user/default';
|
||||||
|
import { NoteWrapper } from '@lume/app/note/components/wrapper';
|
||||||
import { RelayContext } from '@lume/shared/relayProvider';
|
import { RelayContext } from '@lume/shared/relayProvider';
|
||||||
import { READONLY_RELAYS } from '@lume/stores/constants';
|
import { READONLY_RELAYS } from '@lume/stores/constants';
|
||||||
import { noteParser } from '@lume/utils/parser';
|
import { noteParser } from '@lume/utils/parser';
|
||||||
@@ -7,7 +8,6 @@ import { noteParser } from '@lume/utils/parser';
|
|||||||
import { memo, useContext } from 'react';
|
import { memo, useContext } from 'react';
|
||||||
import Skeleton from 'react-loading-skeleton';
|
import Skeleton from 'react-loading-skeleton';
|
||||||
import useSWRSubscription from 'swr/subscription';
|
import useSWRSubscription from 'swr/subscription';
|
||||||
import { navigate } from 'vite-plugin-ssr/client/router';
|
|
||||||
|
|
||||||
export const NoteQuote = memo(function NoteQuote({ id }: { id: string }) {
|
export const NoteQuote = memo(function NoteQuote({ id }: { id: string }) {
|
||||||
const pool: any = useContext(RelayContext);
|
const pool: any = useContext(RelayContext);
|
||||||
@@ -37,17 +37,11 @@ export const NoteQuote = memo(function NoteQuote({ id }: { id: string }) {
|
|||||||
|
|
||||||
const content = !error && data ? noteParser(data) : null;
|
const content = !error && data ? noteParser(data) : null;
|
||||||
|
|
||||||
const openNote = (e) => {
|
|
||||||
const selection = window.getSelection();
|
|
||||||
if (selection.toString().length === 0) {
|
|
||||||
navigate(`/app/note?id=${id}`);
|
|
||||||
} else {
|
|
||||||
e.stopPropagation();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div onClick={(e) => openNote(e)} className="mb-2 mt-3 flex flex-col rounded-lg border border-zinc-800 p-2 py-3">
|
<NoteWrapper
|
||||||
|
href={`/app/note?id=${id}`}
|
||||||
|
className="mb-2 mt-3 flex flex-col rounded-lg border border-zinc-800 p-2 py-3"
|
||||||
|
>
|
||||||
{data ? (
|
{data ? (
|
||||||
<>
|
<>
|
||||||
<NoteDefaultUser pubkey={data.pubkey} time={data.created_at} />
|
<NoteDefaultUser pubkey={data.pubkey} time={data.created_at} />
|
||||||
@@ -58,6 +52,6 @@ export const NoteQuote = memo(function NoteQuote({ id }: { id: string }) {
|
|||||||
) : (
|
) : (
|
||||||
<Skeleton baseColor="#27272a" containerClassName="flex-1" />
|
<Skeleton baseColor="#27272a" containerClassName="flex-1" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</NoteWrapper>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@@ -1,14 +1,13 @@
|
|||||||
import { RootNote } from '@lume/app/note/components/rootNote';
|
import { RootNote } from '@lume/app/note/components/rootNote';
|
||||||
import { NoteRepostUser } from '@lume/app/note/components/user/repost';
|
import { NoteRepostUser } from '@lume/app/note/components/user/repost';
|
||||||
|
import { NoteWrapper } from '@lume/app/note/components/wrapper';
|
||||||
import { getQuoteID } from '@lume/utils/transform';
|
import { getQuoteID } from '@lume/utils/transform';
|
||||||
|
|
||||||
import { memo } from 'react';
|
export const NoteQuoteRepost = ({ event }: { event: any }) => {
|
||||||
|
|
||||||
export const NoteQuoteRepost = memo(function NoteQuoteRepost({ event }: { event: any }) {
|
|
||||||
const rootID = getQuoteID(event.tags);
|
const rootID = getQuoteID(event.tags);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-min w-full select-text px-3 py-1.5">
|
<NoteWrapper href={`/app/note?id=${rootID}`} className="h-min w-full px-3 py-1.5">
|
||||||
<div className="rounded-md border border-zinc-800 bg-zinc-900 shadow-input shadow-black/20">
|
<div className="rounded-md border border-zinc-800 bg-zinc-900 shadow-input shadow-black/20">
|
||||||
<div className="relative px-3 pb-5 pt-3">
|
<div className="relative px-3 pb-5 pt-3">
|
||||||
<div className="absolute left-[32px] top-[20px] h-[70px] w-0.5 bg-gradient-to-t from-zinc-800 to-zinc-600"></div>
|
<div className="absolute left-[32px] top-[20px] h-[70px] w-0.5 bg-gradient-to-t from-zinc-800 to-zinc-600"></div>
|
||||||
@@ -16,6 +15,6 @@ export const NoteQuoteRepost = memo(function NoteQuoteRepost({ event }: { event:
|
|||||||
</div>
|
</div>
|
||||||
<RootNote id={rootID} fallback={event.content} />
|
<RootNote id={rootID} fallback={event.content} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</NoteWrapper>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
@@ -36,7 +36,7 @@ export default function NoteReplyForm({ id }: { id: string }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-3 px-5 py-4">
|
<div className="flex gap-3 px-3 py-4">
|
||||||
<div>
|
<div>
|
||||||
<div className="relative h-9 w-9 shrink-0 overflow-hidden rounded-md">
|
<div className="relative h-9 w-9 shrink-0 overflow-hidden rounded-md">
|
||||||
<Image src={profile?.picture} alt={account?.pubkey} className="h-9 w-9 rounded-md object-cover" />
|
<Image src={profile?.picture} alt={account?.pubkey} className="h-9 w-9 rounded-md object-cover" />
|
||||||
|
@@ -6,7 +6,7 @@ export default function Reply({ data }: { data: any }) {
|
|||||||
const content = noteParser(data);
|
const content = noteParser(data);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-min min-h-min w-full select-text flex-col px-5 py-3.5 hover:bg-black/20">
|
<div className="flex h-min min-h-min w-full select-text flex-col px-3 py-3">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<NoteReplyUser pubkey={data.pubkey} time={data.created_at} />
|
<NoteReplyUser pubkey={data.pubkey} time={data.created_at} />
|
||||||
<div className="-mt-[17px] pl-[48px]">
|
<div className="-mt-[17px] pl-[48px]">
|
||||||
|
@@ -42,13 +42,13 @@ export default function RepliesList({ id }: { id: string }) {
|
|||||||
<NoteReplyForm id={id} />
|
<NoteReplyForm id={id} />
|
||||||
{error && <div>failed to load</div>}
|
{error && <div>failed to load</div>}
|
||||||
{!data ? (
|
{!data ? (
|
||||||
<div className="flex gap-2 px-5 py-4">
|
<div className="flex gap-2 px-3 py-4">
|
||||||
<div className="relative h-9 w-9 shrink animate-pulse rounded-md bg-zinc-800"></div>
|
<div className="relative h-9 w-9 shrink animate-pulse rounded-md bg-zinc-800"></div>
|
||||||
<div className="flex w-full flex-1 flex-col justify-center gap-1">
|
<div className="flex w-full flex-1 flex-col justify-center gap-1">
|
||||||
<div className="flex items-baseline gap-2 text-sm">
|
<div className="flex items-baseline gap-2 text-sm">
|
||||||
<div className="h-2.5 w-20 animate-pulse rounded bg-zinc-800"></div>
|
<div className="h-2.5 w-20 animate-pulse rounded-sm bg-zinc-800"></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-4 w-44 animate-pulse rounded bg-zinc-800"></div>
|
<div className="h-4 w-44 animate-pulse rounded-sm bg-zinc-800"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
26
src/app/note/components/wrapper.tsx
Normal file
26
src/app/note/components/wrapper.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { navigate } from 'vite-plugin-ssr/client/router';
|
||||||
|
|
||||||
|
export const NoteWrapper = ({
|
||||||
|
children,
|
||||||
|
href,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
href: string;
|
||||||
|
className: string;
|
||||||
|
}) => {
|
||||||
|
const openThread = (event: any, href: string) => {
|
||||||
|
const selection = window.getSelection();
|
||||||
|
if (selection.toString().length === 0) {
|
||||||
|
navigate(href, { keepScrollPosition: true });
|
||||||
|
} else {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div onClick={(event) => openThread(event, href)} className={className}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@@ -1,6 +1,5 @@
|
|||||||
|
import { NoteContent } from '@lume/app/note/components/content';
|
||||||
import NoteMetadata from '@lume/app/note/components/metadata';
|
import NoteMetadata from '@lume/app/note/components/metadata';
|
||||||
import ImagePreview from '@lume/app/note/components/preview/image';
|
|
||||||
import VideoPreview from '@lume/app/note/components/preview/video';
|
|
||||||
import RepliesList from '@lume/app/note/components/replies/list';
|
import RepliesList from '@lume/app/note/components/replies/list';
|
||||||
import { NoteDefaultUser } from '@lume/app/note/components/user/default';
|
import { NoteDefaultUser } from '@lume/app/note/components/user/default';
|
||||||
import { RelayContext } from '@lume/shared/relayProvider';
|
import { RelayContext } from '@lume/shared/relayProvider';
|
||||||
@@ -67,31 +66,15 @@ export function Page() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<div className="relative z-10 flex flex-col">
|
||||||
<div className="relative z-10 flex flex-col">
|
<div className="px-3 pt-3">
|
||||||
<div className="px-5 pt-5">
|
<NoteDefaultUser pubkey={data.pubkey} time={data.created_at} />
|
||||||
<NoteDefaultUser pubkey={data.pubkey} time={data.created_at} />
|
<div className="mt-3">
|
||||||
<div className="mt-3">
|
<NoteContent content={content} />
|
||||||
<div className="whitespace-pre-line break-words text-[15px] leading-tight text-zinc-100">
|
|
||||||
{content ? content.parsed : ''}
|
|
||||||
</div>
|
|
||||||
{Array.isArray(content.images) && content.images.length ? (
|
|
||||||
<ImagePreview urls={content.images} />
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
|
||||||
{Array.isArray(content.videos) && content.videos.length ? (
|
|
||||||
<VideoPreview urls={content.videos} />
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div onClick={(e) => e.stopPropagation()} className="mt-5 border-t border-zinc-800 px-5 py-5">
|
|
||||||
<NoteMetadata id={noteID} eventPubkey={data.pubkey} />
|
<NoteMetadata id={noteID} eventPubkey={data.pubkey} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<RepliesList id={noteID} />
|
<RepliesList id={noteID} />
|
||||||
|
Reference in New Issue
Block a user