added note metadta component

This commit is contained in:
Ren Amamiya 2023-03-17 13:05:10 +07:00
parent e43a999ca8
commit b3f95366d3
9 changed files with 189 additions and 99 deletions

View File

@ -1,38 +1,8 @@
import { RelayContext } from '@components/contexts/relay';
import { useLocalStorage } from '@rehooks/local-storage';
import { memo, useContext, useEffect, useState } from 'react';
export const Reply = memo(function Reply({ eventID }: { eventID: string }) {
const relayPool: any = useContext(RelayContext);
const [relays]: any = useLocalStorage('relays');
const [reply, setReply] = useState(0);
useEffect(() => {
relayPool.subscribe(
[
{
'#e': [eventID],
since: 0,
kinds: [1],
limit: 10,
},
],
relays,
() => {
setReply(reply + 1);
},
undefined,
(events: any, relayURL: any) => {
console.log(events, relayURL);
}
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [eventID, relayPool, relays]);
import { memo } from 'react';
export const CommentsCounter = memo(function CommentsCounter({ comments }: { comments: number }) {
return (
<div className="group flex w-16 items-center gap-1.5 text-sm text-zinc-500">
<div className="group flex w-16 items-center gap-1 text-sm text-zinc-500">
<div className="rounded-md p-1 group-hover:bg-zinc-800">
<svg
xmlns="http://www.w3.org/2000/svg"
@ -49,7 +19,7 @@ export const Reply = memo(function Reply({ eventID }: { eventID: string }) {
/>
</svg>
</div>
<span>{reply}</span>
<span>{comments}</span>
</div>
);
});

View File

@ -4,66 +4,50 @@ import { dateToUnix } from '@utils/getDate';
import { useLocalStorage } from '@rehooks/local-storage';
import { getEventHash, signEvent } from 'nostr-tools';
import { memo, useContext, useEffect, useState } from 'react';
import { memo, useCallback, useContext, useState } from 'react';
export const Reaction = memo(function Reaction({ eventID, eventPubkey }: { eventID: string; eventPubkey: string }) {
export const LikesCounter = memo(function LikesCounter({
likes,
eventID,
eventPubkey,
}: {
likes: number;
eventID: string;
eventPubkey: string;
}) {
const relayPool: any = useContext(RelayContext);
const [relays]: any = useLocalStorage('relays');
const [reaction, setReaction] = useState(0);
const [isReact, setIsReact] = useState(false);
const [currentUser]: any = useLocalStorage('current-user');
const handleReaction = (e: any) => {
e.stopPropagation();
const [isReact, setIsReact] = useState(false);
const event: any = {
content: '+',
kind: 7,
tags: [
['e', eventID],
['p', eventPubkey],
],
created_at: dateToUnix(),
pubkey: currentUser.id,
};
event.id = getEventHash(event);
event.sig = signEvent(event, currentUser.privkey);
// publish event to all relays
relayPool.publish(event, relays);
// update state to change icon to filled heart
setIsReact(true);
// update reaction count
setReaction(reaction + 1);
};
const handleLike = useCallback(
(e: any) => {
e.stopPropagation();
useEffect(() => {
relayPool.subscribe(
[
{
'#e': [eventID],
since: 0,
kinds: [7],
limit: 10,
},
],
relays,
(event: any) => {
if (event.content === '🤙' || event.content === '+') {
setReaction(reaction + 1);
}
},
undefined,
(events: any, relayURL: any) => {
console.log(events, relayURL);
}
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [eventID, relayPool, relays]);
const event: any = {
content: '+',
kind: 7,
tags: [
['e', eventID],
['p', eventPubkey],
],
created_at: dateToUnix(),
pubkey: currentUser.id,
};
event.id = getEventHash(event);
event.sig = signEvent(event, currentUser.privkey);
// publish event to all relays
relayPool.publish(event, relays);
// update state to change icon to filled heart
setIsReact(true);
},
[currentUser.id, currentUser.privkey, eventID, eventPubkey, relayPool, relays]
);
return (
<button onClick={(e) => handleReaction(e)} className="group flex w-16 items-center gap-1.5 text-sm text-zinc-500">
<button onClick={(e) => handleLike(e)} className="group flex w-16 items-center gap-1 text-sm text-zinc-500">
<div className="rounded-md p-1 group-hover:bg-zinc-800">
{isReact ? (
<svg
@ -91,7 +75,7 @@ export const Reaction = memo(function Reaction({ eventID, eventPubkey }: { event
</svg>
)}
</div>
<span>{reaction}</span>
<span>{likes}</span>
</button>
);
});

View File

@ -1,7 +1,6 @@
import NoteMetadata from '@components/note/content/metadata';
import { ImageCard } from '@components/note/content/preview/imageCard';
import { Video } from '@components/note/content/preview/video';
import { Reaction } from '@components/note/reaction';
import { Reply } from '@components/note/reply';
import { UserExtend } from '@components/user/extend';
import dynamic from 'next/dynamic';
@ -87,10 +86,7 @@ export const Content = memo(function Content({ data }: { data: any }) {
</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>
<NoteMetadata eventID={data.id} eventPubkey={data.pubkey} />
</div>
</div>
</div>

View File

@ -0,0 +1,53 @@
import { RelayContext } from '@components/contexts/relay';
import { CommentsCounter } from '@components/note/content/counter/comments';
import { LikesCounter } from '@components/note/content/counter/likes';
import { useLocalStorage } from '@rehooks/local-storage';
import { useContext, useMemo, useState } from 'react';
export default function NoteMetadata({ eventID, eventPubkey }: { eventID: string; eventPubkey: string }) {
const relayPool: any = useContext(RelayContext);
const [relays]: any = useLocalStorage('relays');
const [likes, setLikes] = useState(0);
const [comments, setComments] = useState(0);
useMemo(() => {
relayPool.subscribe(
[
{
'#e': [eventID],
since: 0,
kinds: [1, 7],
},
],
relays,
(event: any) => {
switch (event.kind) {
case 1:
setComments((comments) => (comments += 1));
break;
case 7:
if (event.content === '🤙' || event.content === '+') {
setLikes((likes) => (likes += 1));
}
break;
default:
break;
}
},
undefined,
(events: any, relayURL: any) => {
console.log(events, relayURL);
}
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [eventID, relayPool, relays]);
return (
<div className="relative z-10 -ml-1 flex items-center gap-8">
<CommentsCounter comments={comments} />
<LikesCounter likes={likes} eventID={eventID} eventPubkey={eventPubkey} />
</div>
);
}

View File

@ -14,7 +14,7 @@ const MDEditor = dynamic(() => import('@uiw/react-md-editor').then((mod) => mod.
ssr: false,
});
export default function NoteForm() {
export default function FormBasic() {
const relayPool: any = useContext(RelayContext);
const [relays]: any = useLocalStorage('relays');
@ -95,7 +95,20 @@ export default function NoteForm() {
</svg>
</span>
<span className="inline-flex h-6 w-6 cursor-pointer items-center justify-center rounded-md hover:bg-zinc-700">
<ImageIcon className="h-4 w-4 text-zinc-400" />
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="h-4 w-4 text-zinc-400"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z"
/>
</svg>
</span>
</div>
</div>

View File

@ -1,12 +1,22 @@
import { Content } from '@components/note/content';
import { RootNote } from '@components/note/root';
import { useRouter } from 'next/router';
import { memo, useEffect, useState } from 'react';
export const Note = memo(function Note({ event }: { event: any }) {
const router = useRouter();
const [root, setRoot] = useState(null);
const tags = JSON.parse(event.tags);
const openThread = () => {
router.push({
pathname: '/newsfeed/thread',
query: { id: root ? root : event.id },
});
};
useEffect(() => {
if (tags.length > 0) {
if (tags[0][0] === 'e') {
@ -16,7 +26,10 @@ export const Note = memo(function Note({ event }: { event: any }) {
}, [tags]);
return (
<div className="relative z-10 flex h-min min-h-min w-full cursor-pointer select-text flex-col border-b border-zinc-800 py-5 px-3">
<div
onClick={() => openThread()}
className="relative z-10 flex h-min min-h-min w-full cursor-pointer select-text flex-col border-b border-zinc-800 py-5 px-3 hover:bg-black/20"
>
{root && (
<>
<RootNote id={root} />

View File

@ -32,7 +32,7 @@ export const RootNote = memo(function RootNote({ id }: { id: string }) {
if (event) {
return (
<div className="relative pb-5">
<div className="absolute top-0 left-[21px] h-full w-px bg-zinc-800"></div>
<div className="absolute top-0 left-[21px] h-full w-0.5 bg-gradient-to-t from-zinc-800 to-zinc-600"></div>
<Content data={event} />
</div>
);

View File

@ -3,7 +3,7 @@ import WithSidebarLayout from '@layouts/withSidebar';
import { DatabaseContext } from '@components/contexts/database';
import { Note } from '@components/note';
import NoteForm from '@components/note/form';
import FormBasic from '@components/note/form/basic';
import { Placeholder } from '@components/note/placeholder';
import { atomHasNewerNote } from '@stores/note';
@ -102,7 +102,7 @@ export default function Page() {
<div className="absolute top-8 left-1/2 z-50 -translate-x-1/2 transform">
<button
onClick={() => loadNewest()}
className="inline-flex h-8 transform items-center justify-center gap-1 rounded-full bg-fuchsia-500 px-3 text-sm shadow-lg shadow-lg active:translate-y-1"
className="inline-flex h-8 transform items-center justify-center gap-1 rounded-full bg-fuchsia-500 px-3 text-sm shadow-lg shadow-fuchsia-900/50 active:translate-y-1"
>
<span className="text-white drop-shadow">Load newest</span>
</button>
@ -112,7 +112,7 @@ export default function Page() {
data={data}
itemContent={ItemContent}
components={{
Header: () => <NoteForm />,
Header: () => <FormBasic />,
EmptyPlaceholder: () => <Placeholder />,
ScrollSeekPlaceholder: () => <Placeholder />,
}}

View File

@ -0,0 +1,61 @@
import BaseLayout from '@layouts/base';
import WithSidebarLayout from '@layouts/withSidebar';
import { DatabaseContext } from '@components/contexts/database';
import { Content } from '@components/note/content';
import FormBasic from '@components/note/form/basic';
import { useRouter } from 'next/router';
import {
JSXElementConstructor,
ReactElement,
ReactFragment,
ReactPortal,
useCallback,
useContext,
useEffect,
useState,
} from 'react';
export default function Page() {
const { db }: any = useContext(DatabaseContext);
const [root, setRoot] = useState(null);
const router = useRouter();
const { id }: any = router.query;
const fetchRoot = useCallback(async () => {
const result = await db.select(`SELECT * FROM cache_notes WHERE id = "${id}"`);
setRoot(result[0]);
}, [db, id]);
useEffect(() => {
fetchRoot().catch(console.error);
}, [fetchRoot]);
return (
<div className="scrollbar-hide flex h-full flex-col overflow-y-auto py-5">
<div className="flex h-min min-h-min w-full select-text flex-col px-3">{root && <Content data={root} />}</div>
<div>
<FormBasic />
</div>
</div>
);
}
Page.getLayout = function getLayout(
page:
| string
| number
| boolean
| ReactElement<unknown, string | JSXElementConstructor<unknown>>
| ReactFragment
| ReactPortal
) {
return (
<BaseLayout>
<WithSidebarLayout>{page}</WithSidebarLayout>
</BaseLayout>
);
};