mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-18 11:13:30 +00:00
refactor event handler
This commit is contained in:
parent
31a3a712ec
commit
4b73715927
@ -57,12 +57,12 @@ export default function CreatePost() {
|
|||||||
return (
|
return (
|
||||||
<Dialog.Root>
|
<Dialog.Root>
|
||||||
<Dialog.Trigger asChild>
|
<Dialog.Trigger asChild>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col gap-1.5">
|
||||||
<div className="relative shrink-0 before:pointer-events-none before:absolute before:-inset-1 before:rounded-[11px] before:border before:border-blue-500 before:opacity-0 before:ring-2 before:ring-blue-500/20 before:transition after:pointer-events-none after:absolute after:inset-px after:rounded-[7px] after:shadow-highlight after:shadow-white/5 after:transition focus-within:before:opacity-100 focus-within:after:shadow-blue-500/100 dark:focus-within:after:shadow-blue-500/20">
|
<div className="relative h-16 shrink-0 before:pointer-events-none before:absolute before:-inset-1 before:rounded-[11px] before:border before:border-blue-500 before:opacity-0 before:ring-2 before:ring-blue-500/20 before:transition after:pointer-events-none after:absolute after:inset-px after:rounded-[7px] after:shadow-highlight after:shadow-white/5 after:transition focus-within:before:opacity-100 focus-within:after:shadow-blue-500/100 dark:focus-within:after:shadow-blue-500/20">
|
||||||
<textarea
|
<textarea
|
||||||
readOnly
|
readOnly
|
||||||
placeholder="What's your thought?"
|
placeholder="What's your thought?"
|
||||||
className="relative w-full resize-none rounded-lg border border-black/5 px-3.5 py-3 text-sm shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-zinc-200 dark:shadow-black/10 dark:placeholder:text-zinc-500"
|
className="relative h-16 w-full resize-none rounded-lg border border-black/5 px-3.5 py-3 text-sm shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-zinc-200 dark:shadow-black/10 dark:placeholder:text-zinc-500"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button className="inline-flex h-9 w-full items-center justify-center rounded-lg border border-white/10 bg-[radial-gradient(ellipse_at_bottom_right,_var(--tw-gradient-stops))] from-gray-300 via-fuchsia-600 to-orange-600 text-sm font-semibold shadow-input">
|
<button className="inline-flex h-9 w-full items-center justify-center rounded-lg border border-white/10 bg-[radial-gradient(ellipse_at_bottom_right,_var(--tw-gradient-stops))] from-gray-300 via-fuchsia-600 to-orange-600 text-sm font-semibold shadow-input">
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
import { truncate } from '@utils/truncate';
|
import { truncate } from '@utils/truncate';
|
||||||
|
|
||||||
import { useNostrEvents } from 'nostr-react';
|
import { useNostrEvents } from 'nostr-react';
|
||||||
import { useState } from 'react';
|
import { memo, useState } from 'react';
|
||||||
|
|
||||||
export default function RootUser({ userPubkey, action }: { userPubkey: string; action: string }) {
|
export const UserRepost = memo(function UserRepost({ pubkey }: { pubkey: string }) {
|
||||||
const [profile, setProfile] = useState({ picture: null, name: null });
|
const [profile, setProfile] = useState({ picture: null, name: null });
|
||||||
|
|
||||||
const { onEvent } = useNostrEvents({
|
const { onEvent } = useNostrEvents({
|
||||||
filter: {
|
filter: {
|
||||||
authors: [userPubkey],
|
authors: [pubkey],
|
||||||
kinds: [0],
|
kinds: [0],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -28,9 +28,7 @@ export default function RootUser({ userPubkey, action }: { userPubkey: string; a
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="text-zinc-400">
|
<div className="text-zinc-400">
|
||||||
<p>
|
<p>{profile.name ? profile.name : truncate(pubkey, 16, ' .... ')} repost</p>
|
||||||
{profile.name ? profile.name : truncate(userPubkey, 16, ' .... ')} {action}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
});
|
@ -1,4 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
import Reaction from '@components/note/atoms/reaction';
|
||||||
|
import Reply from '@components/note/atoms/reply';
|
||||||
|
import { User } from '@components/note/atoms/user';
|
||||||
import { ImageCard } from '@components/note/content/preview/imageCard';
|
import { ImageCard } from '@components/note/content/preview/imageCard';
|
||||||
import { Video } from '@components/note/content/preview/video';
|
import { Video } from '@components/note/content/preview/video';
|
||||||
|
|
||||||
@ -14,13 +17,13 @@ const MarkdownPreview = dynamic(() => import('@uiw/react-markdown-preview'), {
|
|||||||
export default function Content({ data }: { data: any }) {
|
export default function Content({ data }: { data: any }) {
|
||||||
const [preview, setPreview] = useState({});
|
const [preview, setPreview] = useState({});
|
||||||
|
|
||||||
const content = useRef(data);
|
const content = useRef(data.content);
|
||||||
const urls = useMemo(
|
const urls = useMemo(
|
||||||
() =>
|
() =>
|
||||||
data.match(
|
content.current.match(
|
||||||
/((http|ftp|https):\/\/)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi
|
/((http|ftp|https):\/\/)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi
|
||||||
),
|
),
|
||||||
[data]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -61,25 +64,36 @@ export default function Content({ data }: { data: any }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div>
|
<User pubkey={data.pubkey} time={data.created_at} />
|
||||||
<MarkdownPreview
|
<div className="-mt-4 pl-[60px]">
|
||||||
source={content.current}
|
<div className="flex flex-col gap-6">
|
||||||
className={
|
<div className="flex flex-col">
|
||||||
'prose prose-zinc max-w-none break-words dark:prose-invert prose-headings:mt-3 prose-headings:mb-2 prose-p:m-0 prose-p:leading-normal prose-ul:mt-2 prose-li:my-1'
|
<div>
|
||||||
}
|
<MarkdownPreview
|
||||||
linkTarget="_blank"
|
source={content.current}
|
||||||
disallowedElements={[
|
className={
|
||||||
'Table',
|
'prose prose-zinc max-w-none break-words dark:prose-invert prose-headings:mt-3 prose-headings:mb-2 prose-p:m-0 prose-p:leading-normal prose-ul:mt-2 prose-li:my-1'
|
||||||
'Heading ID',
|
}
|
||||||
'Highlight',
|
linkTarget="_blank"
|
||||||
'Fenced Code Block',
|
disallowedElements={[
|
||||||
'Footnote',
|
'Table',
|
||||||
'Definition List',
|
'Heading ID',
|
||||||
'Task List',
|
'Highlight',
|
||||||
]}
|
'Fenced Code Block',
|
||||||
/>
|
'Footnote',
|
||||||
|
'Definition List',
|
||||||
|
'Task List',
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<>{previewAttachment()}</>
|
||||||
|
</div>
|
||||||
|
<div className="relative z-10 -ml-1 flex items-center gap-8">
|
||||||
|
<Reply eventID={data.id} />
|
||||||
|
<Reaction eventID={data.id} eventPubkey={data.pubkey} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>{previewAttachment()}</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
import Reaction from '@components/note/atoms/reaction';
|
|
||||||
import Reply from '@components/note/atoms/reply';
|
|
||||||
import RootUser from '@components/note/atoms/rootUser';
|
|
||||||
import { User } from '@components/note/atoms/user';
|
|
||||||
import Content from '@components/note/content';
|
|
||||||
import { Placeholder } from '@components/note/placeholder';
|
|
||||||
|
|
||||||
import LikeSolidIcon from '@assets/icons/LikeSolid';
|
|
||||||
|
|
||||||
import { useNostrEvents } from 'nostr-react';
|
|
||||||
import { memo } from 'react';
|
|
||||||
|
|
||||||
export const Liked = memo(function Liked({
|
|
||||||
eventUser,
|
|
||||||
sourceID,
|
|
||||||
}: {
|
|
||||||
eventUser: string;
|
|
||||||
sourceID: string;
|
|
||||||
}) {
|
|
||||||
const { events } = useNostrEvents({
|
|
||||||
filter: {
|
|
||||||
ids: [sourceID],
|
|
||||||
since: 0,
|
|
||||||
kinds: [1],
|
|
||||||
limit: 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (events !== undefined && events.length > 0) {
|
|
||||||
return (
|
|
||||||
<div className="flex h-min min-h-min w-full select-text flex-col border-b border-zinc-800 py-6 px-6">
|
|
||||||
<div className="flex items-center gap-1 pl-8 text-sm">
|
|
||||||
<LikeSolidIcon className="h-4 w-4 text-zinc-400" />
|
|
||||||
<div className="ml-2">
|
|
||||||
<RootUser userPubkey={eventUser} action={'like'} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<User pubkey={events[0].pubkey} time={events[0].created_at} />
|
|
||||||
<div className="-mt-4 pl-[60px]">
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<Content data={events[0].content} />
|
|
||||||
<div className="-ml-1 flex items-center gap-8">
|
|
||||||
<Reply eventID={events[0].id} />
|
|
||||||
<Reaction eventID={events[0].id} eventPubkey={events[0].pubkey} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return <Placeholder />;
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,55 +0,0 @@
|
|||||||
import Reaction from '@components/note/atoms/reaction';
|
|
||||||
import Reply from '@components/note/atoms/reply';
|
|
||||||
import { User } from '@components/note/atoms/user';
|
|
||||||
import Content from '@components/note/content';
|
|
||||||
import { Placeholder } from '@components/note/placeholder';
|
|
||||||
|
|
||||||
import { useNostrEvents } from 'nostr-react';
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
export function Multi({ event }: { event: any }) {
|
|
||||||
const tags = event.tags;
|
|
||||||
|
|
||||||
const { events } = useNostrEvents({
|
|
||||||
filter: {
|
|
||||||
ids: [tags[0][1]],
|
|
||||||
since: 0,
|
|
||||||
kinds: [1],
|
|
||||||
limit: 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (events !== undefined && events.length > 0) {
|
|
||||||
return (
|
|
||||||
<div className="relative flex h-min min-h-min w-full select-text flex-col overflow-hidden border-b border-zinc-800">
|
|
||||||
<div className="absolute left-[45px] top-6 h-full w-[2px] bg-zinc-800"></div>
|
|
||||||
<div className="flex flex-col bg-zinc-900 px-6 pt-6 pb-2">
|
|
||||||
<User pubkey={events[0].pubkey} time={events[0].created_at} />
|
|
||||||
<div className="-mt-4 pl-[60px]">
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<Content data={events[0].content} />
|
|
||||||
<div className="-ml-1 flex items-center gap-8">
|
|
||||||
<Reply eventID={events[0].id} />
|
|
||||||
<Reaction eventID={events[0].id} eventPubkey={events[0].pubkey} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="relative flex flex-col bg-zinc-900 px-6 pb-6">
|
|
||||||
<User pubkey={event.pubkey} time={event.created_at} />
|
|
||||||
<div className="relative z-10 -mt-4 pl-[60px]">
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<Content data={event.content} />
|
|
||||||
<div className="-ml-1 flex items-center gap-8">
|
|
||||||
<Reply eventID={event.id} />
|
|
||||||
<Reaction eventID={event.id} eventPubkey={event.pubkey} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return <Placeholder />;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +1,56 @@
|
|||||||
import Reaction from '@components/note/atoms/reaction';
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import Reply from '@components/note/atoms/reply';
|
import { UserRepost } from '@components/note/atoms/userRepost';
|
||||||
import RootUser from '@components/note/atoms/rootUser';
|
|
||||||
import { User } from '@components/note/atoms/user';
|
|
||||||
import Content from '@components/note/content';
|
import Content from '@components/note/content';
|
||||||
import { Placeholder } from '@components/note/placeholder';
|
import { Placeholder } from '@components/note/placeholder';
|
||||||
|
|
||||||
import RepostIcon from '@assets/icons/Repost';
|
import RepostIcon from '@assets/icons/Repost';
|
||||||
|
|
||||||
|
import * as Dialog from '@radix-ui/react-dialog';
|
||||||
|
import dynamic from 'next/dynamic';
|
||||||
import { useNostrEvents } from 'nostr-react';
|
import { useNostrEvents } from 'nostr-react';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
export const Repost = memo(function Repost({
|
const Modal = dynamic(() => import('@components/note/modal'), {
|
||||||
eventUser,
|
ssr: false,
|
||||||
sourceID,
|
loading: () => <></>,
|
||||||
}: {
|
});
|
||||||
eventUser: string;
|
|
||||||
sourceID: string;
|
export const Repost = memo(function Repost({ root, user }: { root: any; user: string }) {
|
||||||
}) {
|
|
||||||
const { events } = useNostrEvents({
|
const { events } = useNostrEvents({
|
||||||
filter: {
|
filter: {
|
||||||
ids: [sourceID],
|
ids: [root[0][1]],
|
||||||
since: 0,
|
since: 0,
|
||||||
kinds: [1],
|
kinds: [1],
|
||||||
limit: 1,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (events !== undefined && events.length > 0) {
|
if (events !== null) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-min min-h-min w-full select-text flex-col border-b border-zinc-800 py-6 px-6">
|
<Dialog.Root>
|
||||||
<div className="flex items-center gap-1 pl-8 text-sm">
|
<Dialog.Trigger>
|
||||||
<RepostIcon className="h-4 w-4 text-zinc-400" />
|
<div className="flex h-min min-h-min w-full select-text flex-col border-b border-zinc-800 py-6 px-6">
|
||||||
<div className="ml-2">
|
<div className="flex items-center gap-1 pl-8 text-sm">
|
||||||
<RootUser userPubkey={eventUser} action={'repost'} />
|
<RepostIcon className="h-4 w-4 text-zinc-400" />
|
||||||
</div>
|
<div className="ml-2">
|
||||||
</div>
|
<UserRepost pubkey={user} />
|
||||||
<div className="flex flex-col">
|
|
||||||
<User pubkey={events[0].pubkey} time={events[0].created_at} />
|
|
||||||
<div className="-mt-4 pl-[60px]">
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<Content data={events[0].content} />
|
|
||||||
<div className="-ml-1 flex items-center gap-8">
|
|
||||||
<Reply eventID={events[0].id} />
|
|
||||||
<Reaction eventID={events[0].id} eventPubkey={events[0].pubkey} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Content data={events[0]} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<Dialog.Portal>
|
||||||
</div>
|
<Dialog.Overlay className="fixed inset-0 bg-black bg-opacity-30 backdrop-blur-sm data-[state=open]:animate-overlayShow" />
|
||||||
|
<Dialog.Content className="fixed inset-0 overflow-y-auto">
|
||||||
|
<Modal event={events[0]} />
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Portal>
|
||||||
|
</Dialog.Trigger>
|
||||||
|
</Dialog.Root>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return <Placeholder />;
|
return (
|
||||||
|
<div className="border-b border-zinc-800">
|
||||||
|
<Placeholder />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import Reaction from '@components/note/atoms/reaction';
|
|
||||||
import Reply from '@components/note/atoms/reply';
|
|
||||||
import { User } from '@components/note/atoms/user';
|
|
||||||
import Content from '@components/note/content';
|
import Content from '@components/note/content';
|
||||||
|
|
||||||
import * as Dialog from '@radix-ui/react-dialog';
|
import * as Dialog from '@radix-ui/react-dialog';
|
||||||
@ -15,22 +12,13 @@ const Modal = dynamic(() => import('@components/note/modal'), {
|
|||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export const Single = memo(function Single({ event }: { event: any }) {
|
export const Single = memo(function Single({ event }: { event: any }) {
|
||||||
|
console.log(event);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Root>
|
<Dialog.Root>
|
||||||
<Dialog.Trigger asChild>
|
<Dialog.Trigger asChild>
|
||||||
<div className="flex h-min min-h-min w-full cursor-pointer select-text flex-col border-b border-zinc-800 py-4 px-6 hover:bg-zinc-800">
|
<div className="flex h-min min-h-min w-full cursor-pointer select-text flex-col border-b border-zinc-800 py-4 px-6 hover:bg-zinc-800">
|
||||||
<div className="flex flex-col">
|
<Content data={event} />
|
||||||
<User pubkey={event.pubkey} time={event.created_at} />
|
|
||||||
<div className="-mt-4 pl-[60px]">
|
|
||||||
<div className="flex flex-col gap-6">
|
|
||||||
<Content data={event.content} />
|
|
||||||
<div className="relative z-10 -ml-1 flex items-center gap-8">
|
|
||||||
<Reply eventID={event.id} />
|
|
||||||
<Reaction eventID={event.id} eventPubkey={event.pubkey} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Dialog.Trigger>
|
</Dialog.Trigger>
|
||||||
<Dialog.Portal>
|
<Dialog.Portal>
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import { Liked } from '@components/note/liked';
|
|
||||||
// import { Multi } from '@components/note/multi';
|
|
||||||
import { Placeholder } from '@components/note/placeholder';
|
import { Placeholder } from '@components/note/placeholder';
|
||||||
import { Repost } from '@components/note/repost';
|
import { Repost } from '@components/note/repost';
|
||||||
import { Single } from '@components/note/single';
|
import { Single } from '@components/note/single';
|
||||||
@ -13,15 +11,12 @@ export function Thread({ data }: { data: any }) {
|
|||||||
(index: string | number) => {
|
(index: string | number) => {
|
||||||
const event = data[index];
|
const event = data[index];
|
||||||
|
|
||||||
if (event.kind === 7) {
|
if (event.content.includes('#[0]') && event.tags[0][0] == 'e') {
|
||||||
// type: like
|
|
||||||
return <Liked key={index} eventUser={event.pubkey} sourceID={event.tags[0][1]} />;
|
|
||||||
} else if (event.content === '#[0]') {
|
|
||||||
// type: repost
|
// type: repost
|
||||||
return <Repost key={index} eventUser={event.pubkey} sourceID={event.tags[0][1]} />;
|
return <Repost root={event.tags} user={event.pubkey} />;
|
||||||
} else {
|
} else {
|
||||||
// type: default
|
// type: default
|
||||||
return <Single key={index} event={event} />;
|
return <Single event={event} />;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[data]
|
[data]
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
import BaseLayout from '@layouts/baseLayout';
|
|
||||||
import UserLayout from '@layouts/userLayout';
|
|
||||||
|
|
||||||
import { GetStaticPaths } from 'next';
|
|
||||||
import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal } from 'react';
|
|
||||||
|
|
||||||
export default function Page({ id }: { id: string }) {
|
|
||||||
return <div>{id}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getStaticPaths: GetStaticPaths = async () => {
|
|
||||||
return {
|
|
||||||
paths: [],
|
|
||||||
fallback: 'blocking',
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function getStaticProps(context) {
|
|
||||||
const id = context.params.id;
|
|
||||||
return {
|
|
||||||
props: { id },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Page.getLayout = function getLayout(
|
|
||||||
page:
|
|
||||||
| string
|
|
||||||
| number
|
|
||||||
| boolean
|
|
||||||
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
|
|
||||||
| ReactFragment
|
|
||||||
| ReactPortal
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<BaseLayout>
|
|
||||||
<UserLayout>{page}</UserLayout>
|
|
||||||
</BaseLayout>
|
|
||||||
);
|
|
||||||
};
|
|
Loading…
Reference in New Issue
Block a user