refactor event handler

This commit is contained in:
Ren Amamiya 2023-02-23 21:34:06 +07:00
parent 31a3a712ec
commit 4b73715927
9 changed files with 82 additions and 236 deletions

View File

@ -57,12 +57,12 @@ export default function CreatePost() {
return (
<Dialog.Root>
<Dialog.Trigger asChild>
<div className="flex flex-col">
<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="flex flex-col gap-1.5">
<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
readOnly
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>
<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">

View File

@ -2,14 +2,14 @@
import { truncate } from '@utils/truncate';
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 { onEvent } = useNostrEvents({
filter: {
authors: [userPubkey],
authors: [pubkey],
kinds: [0],
},
});
@ -28,9 +28,7 @@ export default function RootUser({ userPubkey, action }: { userPubkey: string; a
return (
<div className="text-zinc-400">
<p>
{profile.name ? profile.name : truncate(userPubkey, 16, ' .... ')} {action}
</p>
<p>{profile.name ? profile.name : truncate(pubkey, 16, ' .... ')} repost</p>
</div>
);
}
});

View File

@ -1,4 +1,7 @@
/* 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 { 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 }) {
const [preview, setPreview] = useState({});
const content = useRef(data);
const content = useRef(data.content);
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
),
[data]
[]
);
useEffect(() => {
@ -61,25 +64,36 @@ export default function Content({ data }: { data: any }) {
return (
<div className="flex flex-col">
<div>
<MarkdownPreview
source={content.current}
className={
'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'
}
linkTarget="_blank"
disallowedElements={[
'Table',
'Heading ID',
'Highlight',
'Fenced Code Block',
'Footnote',
'Definition List',
'Task List',
]}
/>
<User pubkey={data.pubkey} time={data.created_at} />
<div className="-mt-4 pl-[60px]">
<div className="flex flex-col gap-6">
<div className="flex flex-col">
<div>
<MarkdownPreview
source={content.current}
className={
'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'
}
linkTarget="_blank"
disallowedElements={[
'Table',
'Heading ID',
'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>{previewAttachment()}</div>
</div>
);
}

View File

@ -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 />;
}
});

View File

@ -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 />;
}
}

View File

@ -1,55 +1,56 @@
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';
/* eslint-disable @typescript-eslint/no-explicit-any */
import { UserRepost } from '@components/note/atoms/userRepost';
import Content from '@components/note/content';
import { Placeholder } from '@components/note/placeholder';
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 { memo } from 'react';
export const Repost = memo(function Repost({
eventUser,
sourceID,
}: {
eventUser: string;
sourceID: string;
}) {
const Modal = dynamic(() => import('@components/note/modal'), {
ssr: false,
loading: () => <></>,
});
export const Repost = memo(function Repost({ root, user }: { root: any; user: string }) {
const { events } = useNostrEvents({
filter: {
ids: [sourceID],
ids: [root[0][1]],
since: 0,
kinds: [1],
limit: 1,
},
});
if (events !== undefined && events.length > 0) {
if (events !== null) {
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">
<RepostIcon className="h-4 w-4 text-zinc-400" />
<div className="ml-2">
<RootUser userPubkey={eventUser} action={'repost'} />
</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} />
<Dialog.Root>
<Dialog.Trigger>
<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">
<RepostIcon className="h-4 w-4 text-zinc-400" />
<div className="ml-2">
<UserRepost pubkey={user} />
</div>
</div>
<Content data={events[0]} />
</div>
</div>
</div>
<Dialog.Portal>
<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 {
return <Placeholder />;
return (
<div className="border-b border-zinc-800">
<Placeholder />
</div>
);
}
});

View File

@ -1,7 +1,4 @@
/* 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 * 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
export const Single = memo(function Single({ event }: { event: any }) {
console.log(event);
return (
<Dialog.Root>
<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 flex-col">
<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>
<Content data={event} />
</div>
</Dialog.Trigger>
<Dialog.Portal>

View File

@ -1,5 +1,3 @@
import { Liked } from '@components/note/liked';
// import { Multi } from '@components/note/multi';
import { Placeholder } from '@components/note/placeholder';
import { Repost } from '@components/note/repost';
import { Single } from '@components/note/single';
@ -13,15 +11,12 @@ export function Thread({ data }: { data: any }) {
(index: string | number) => {
const event = data[index];
if (event.kind === 7) {
// type: like
return <Liked key={index} eventUser={event.pubkey} sourceID={event.tags[0][1]} />;
} else if (event.content === '#[0]') {
if (event.content.includes('#[0]') && event.tags[0][0] == 'e') {
// type: repost
return <Repost key={index} eventUser={event.pubkey} sourceID={event.tags[0][1]} />;
return <Repost root={event.tags} user={event.pubkey} />;
} else {
// type: default
return <Single key={index} event={event} />;
return <Single event={event} />;
}
},
[data]

View File

@ -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>
);
};