mirror of
https://github.com/lumehq/lume.git
synced 2025-10-04 23:03:35 +02:00
readd note metadata
This commit is contained in:
51
src/app/newsfeed/components/metadata/like.tsx
Normal file
51
src/app/newsfeed/components/metadata/like.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import LikeIcon from '@lume/shared/icons/like';
|
||||||
|
import { RelayContext } from '@lume/shared/relayProvider';
|
||||||
|
import { WRITEONLY_RELAYS } from '@lume/stores/constants';
|
||||||
|
import { dateToUnix } from '@lume/utils/getDate';
|
||||||
|
import { useActiveAccount } from '@lume/utils/hooks/useActiveAccount';
|
||||||
|
|
||||||
|
import { getEventHash, signEvent } from 'nostr-tools';
|
||||||
|
import { useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export default function NoteLike({ id, pubkey, likes }: { id: string; pubkey: string; likes: number }) {
|
||||||
|
const pool: any = useContext(RelayContext);
|
||||||
|
const { account, isLoading, isError } = useActiveAccount();
|
||||||
|
|
||||||
|
const [count, setCount] = useState(0);
|
||||||
|
|
||||||
|
const submitEvent = (e: any) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (!isLoading && !isError && account) {
|
||||||
|
const event: any = {
|
||||||
|
content: '+',
|
||||||
|
kind: 7,
|
||||||
|
tags: [
|
||||||
|
['e', id],
|
||||||
|
['p', pubkey],
|
||||||
|
],
|
||||||
|
created_at: dateToUnix(),
|
||||||
|
pubkey: account.pubkey,
|
||||||
|
};
|
||||||
|
event.id = getEventHash(event);
|
||||||
|
event.sig = signEvent(event, account.privkey);
|
||||||
|
// publish event to all relays
|
||||||
|
pool.publish(event, WRITEONLY_RELAYS);
|
||||||
|
// update state
|
||||||
|
setCount(count + 1);
|
||||||
|
} else {
|
||||||
|
console.log('error');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCount(likes);
|
||||||
|
}, [likes]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button type="button" onClick={(e) => submitEvent(e)} className="inline-flex w-min items-center gap-1.5">
|
||||||
|
<LikeIcon width={20} height={20} className="text-zinc-400" />
|
||||||
|
<span className="text-sm leading-none text-zinc-400">{count}</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
@@ -1,35 +1,22 @@
|
|||||||
import { AccountContext } from '@lume/shared/accountProvider';
|
import ReplyIcon from '@lume/shared/icons/reply';
|
||||||
import { RelayContext } from '@lume/shared/relaysProvider';
|
import { RelayContext } from '@lume/shared/relayProvider';
|
||||||
import { UserExtend } from '@lume/shared/user/extend';
|
|
||||||
import { WRITEONLY_RELAYS } from '@lume/stores/constants';
|
import { WRITEONLY_RELAYS } from '@lume/stores/constants';
|
||||||
import { dateToUnix } from '@lume/utils/getDate';
|
import { dateToUnix } from '@lume/utils/getDate';
|
||||||
|
import { useActiveAccount } from '@lume/utils/hooks/useActiveAccount';
|
||||||
|
|
||||||
import { Dialog, Transition } from '@headlessui/react';
|
import { Dialog, Transition } from '@headlessui/react';
|
||||||
import { ChatLines, OpenNewWindow } from 'iconoir-react';
|
|
||||||
import { getEventHash, signEvent } from 'nostr-tools';
|
import { getEventHash, signEvent } from 'nostr-tools';
|
||||||
import { Fragment, useContext, useState } from 'react';
|
import { Fragment, useContext, useEffect, useState } from 'react';
|
||||||
import { navigate } from 'vite-plugin-ssr/client/router';
|
|
||||||
|
|
||||||
export const NoteComment = ({
|
export default function NoteReply({ id, replies }: { id: string; replies: number }) {
|
||||||
count,
|
|
||||||
eventID,
|
|
||||||
eventPubkey,
|
|
||||||
eventContent,
|
|
||||||
eventTime,
|
|
||||||
}: {
|
|
||||||
count: number;
|
|
||||||
eventID: string;
|
|
||||||
eventPubkey: string;
|
|
||||||
eventTime: number;
|
|
||||||
eventContent: any;
|
|
||||||
}) => {
|
|
||||||
const pool: any = useContext(RelayContext);
|
const pool: any = useContext(RelayContext);
|
||||||
const activeAccount: any = useContext(AccountContext);
|
|
||||||
|
|
||||||
|
const [count, setCount] = useState(0);
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
|
|
||||||
const profile = activeAccount ? JSON.parse(activeAccount.metadata) : null;
|
const { account, isLoading, isError } = useActiveAccount();
|
||||||
|
const profile = account ? JSON.parse(account.metadata) : null;
|
||||||
|
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
@@ -39,36 +26,37 @@ export const NoteComment = ({
|
|||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const openThread = () => {
|
|
||||||
navigate(`/newsfeed/note?id=${eventID}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const submitEvent = () => {
|
const submitEvent = () => {
|
||||||
const event: any = {
|
if (!isLoading && !isError && account) {
|
||||||
content: value,
|
const event: any = {
|
||||||
created_at: dateToUnix(),
|
content: value,
|
||||||
kind: 1,
|
created_at: dateToUnix(),
|
||||||
pubkey: activeAccount.pubkey,
|
kind: 1,
|
||||||
tags: [['e', eventID]],
|
pubkey: account.pubkey,
|
||||||
};
|
tags: [['e', id]],
|
||||||
event.id = getEventHash(event);
|
};
|
||||||
event.sig = signEvent(event, activeAccount.privkey);
|
event.id = getEventHash(event);
|
||||||
|
event.sig = signEvent(event, account.privkey);
|
||||||
|
|
||||||
pool.publish(event, WRITEONLY_RELAYS);
|
// publish event
|
||||||
setIsOpen(false);
|
pool.publish(event, WRITEONLY_RELAYS);
|
||||||
|
// close modal
|
||||||
|
setIsOpen(false);
|
||||||
|
setCount(count + 1);
|
||||||
|
} else {
|
||||||
|
console.log('error');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCount(replies);
|
||||||
|
}, [replies]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<button type="button" onClick={() => openModal()} className="inline-flex w-min items-center gap-1.5">
|
||||||
type="button"
|
<ReplyIcon width={20} height={20} className="text-zinc-400" />
|
||||||
onClick={() => openModal()}
|
<span className="text-sm leading-none text-zinc-400">{count}</span>
|
||||||
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">
|
|
||||||
<ChatLines width={20} height={20} className="text-zinc-500" />
|
|
||||||
</div>
|
|
||||||
<span>{count}</span>
|
|
||||||
</button>
|
</button>
|
||||||
<Transition appear show={isOpen} as={Fragment}>
|
<Transition appear show={isOpen} as={Fragment}>
|
||||||
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
||||||
@@ -93,20 +81,8 @@ export const NoteComment = ({
|
|||||||
leaveFrom="opacity-100 scale-100"
|
leaveFrom="opacity-100 scale-100"
|
||||||
leaveTo="opacity-0 scale-95"
|
leaveTo="opacity-0 scale-95"
|
||||||
>
|
>
|
||||||
<Dialog.Panel className="relative flex h-min w-full max-w-lg flex-col gap-2 rounded-lg border border-zinc-800 bg-zinc-900">
|
<Dialog.Panel className="relative flex h-min w-full max-w-lg flex-col gap-2 rounded-lg border border-zinc-800 bg-zinc-900 p-3">
|
||||||
{/* root note */}
|
{/* root note */}
|
||||||
<div className="relative z-10 flex flex-col pb-6">
|
|
||||||
<div className="relative z-10">
|
|
||||||
<UserExtend pubkey={eventPubkey} time={eventTime} />
|
|
||||||
</div>
|
|
||||||
<div className="-mt-5 pl-[52px]">
|
|
||||||
<div className="prose prose-zinc max-w-none break-words leading-tight dark:prose-invert prose-headings:mb-2 prose-headings:mt-3 prose-p:m-0 prose-p:leading-tight prose-a:font-normal prose-a:text-fuchsia-500 prose-a:no-underline prose-ul:mt-2 prose-li:my-1">
|
|
||||||
{eventContent}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* divider */}
|
|
||||||
<div className="absolute left-[21px] top-0 h-full w-0.5 bg-gradient-to-t from-zinc-800 to-zinc-600"></div>
|
|
||||||
</div>
|
|
||||||
{/* comment form */}
|
{/* comment form */}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<div>
|
<div>
|
||||||
@@ -114,25 +90,19 @@ export const NoteComment = ({
|
|||||||
<img src={profile?.picture} alt="user's avatar" className="h-11 w-11 rounded-md object-cover" />
|
<img src={profile?.picture} alt="user's avatar" className="h-11 w-11 rounded-md object-cover" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative h-36 w-full flex-1 overflow-hidden 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-24 w-full flex-1 overflow-hidden 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>
|
<div>
|
||||||
<textarea
|
<textarea
|
||||||
name="content"
|
name="content"
|
||||||
onChange={(e) => setValue(e.target.value)}
|
onChange={(e) => setValue(e.target.value)}
|
||||||
placeholder="Send your comment"
|
placeholder="Send your comment"
|
||||||
className="relative h-36 w-full resize-none rounded-md 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-24 w-full resize-none rounded-md 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"
|
||||||
spellCheck={false}
|
spellCheck={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute bottom-2 w-full px-2">
|
<div className="absolute bottom-2 w-full px-2">
|
||||||
<div className="flex w-full items-center justify-between bg-zinc-800">
|
<div className="flex w-full items-center justify-between bg-zinc-800">
|
||||||
<div className="flex items-center gap-2 divide-x divide-zinc-700">
|
<div className="flex items-center gap-2 divide-x divide-zinc-700">
|
||||||
<button
|
|
||||||
onClick={() => openThread()}
|
|
||||||
className="inline-flex h-6 w-6 cursor-pointer items-center justify-center rounded-md hover:bg-zinc-700"
|
|
||||||
>
|
|
||||||
<OpenNewWindow width={16} height={16} className="text-zinc-400" />
|
|
||||||
</button>
|
|
||||||
<div className="flex items-center gap-2 pl-2"></div>
|
<div className="flex items-center gap-2 pl-2"></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
@@ -155,4 +125,4 @@ export const NoteComment = ({
|
|||||||
</Transition>
|
</Transition>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
}
|
51
src/app/newsfeed/components/metadata/repost.tsx
Normal file
51
src/app/newsfeed/components/metadata/repost.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import RepostIcon from '@lume/shared/icons/repost';
|
||||||
|
import { RelayContext } from '@lume/shared/relayProvider';
|
||||||
|
import { WRITEONLY_RELAYS } from '@lume/stores/constants';
|
||||||
|
import { dateToUnix } from '@lume/utils/getDate';
|
||||||
|
import { useActiveAccount } from '@lume/utils/hooks/useActiveAccount';
|
||||||
|
|
||||||
|
import { getEventHash, signEvent } from 'nostr-tools';
|
||||||
|
import { useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export default function NoteRepost({ id, pubkey, reposts }: { id: string; pubkey: string; reposts: number }) {
|
||||||
|
const pool: any = useContext(RelayContext);
|
||||||
|
const { account, isLoading, isError } = useActiveAccount();
|
||||||
|
|
||||||
|
const [count, setCount] = useState(0);
|
||||||
|
|
||||||
|
const submitEvent = (e: any) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (!isLoading && !isError && account) {
|
||||||
|
const event: any = {
|
||||||
|
content: '',
|
||||||
|
kind: 6,
|
||||||
|
tags: [
|
||||||
|
['e', id],
|
||||||
|
['p', pubkey],
|
||||||
|
],
|
||||||
|
created_at: dateToUnix(),
|
||||||
|
pubkey: account.pubkey,
|
||||||
|
};
|
||||||
|
event.id = getEventHash(event);
|
||||||
|
event.sig = signEvent(event, account.privkey);
|
||||||
|
// publish event to all relays
|
||||||
|
pool.publish(event, WRITEONLY_RELAYS);
|
||||||
|
// update state
|
||||||
|
setCount(count + 1);
|
||||||
|
} else {
|
||||||
|
console.log('error');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCount(reposts);
|
||||||
|
}, [reposts]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button type="button" onClick={(e) => submitEvent(e)} className="inline-flex w-min items-center gap-1.5">
|
||||||
|
<RepostIcon width={20} height={20} className="text-zinc-400" />
|
||||||
|
<span className="text-sm leading-none text-zinc-400">{count}</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
import { contentParser } from '@lume/app/newsfeed/components/contentParser';
|
import { contentParser } from '@lume/app/newsfeed/components/contentParser';
|
||||||
|
import NoteMetadata from '@lume/app/newsfeed/components/note/metadata';
|
||||||
import { NoteParent } from '@lume/app/newsfeed/components/note/parent';
|
import { NoteParent } from '@lume/app/newsfeed/components/note/parent';
|
||||||
import { NoteDefaultUser } from '@lume/app/newsfeed/components/user/default';
|
import { NoteDefaultUser } from '@lume/app/newsfeed/components/user/default';
|
||||||
|
|
||||||
@@ -44,7 +45,9 @@ export const NoteBase = memo(function NoteBase({ event }: { event: any }) {
|
|||||||
<div className="mt-1 pl-[52px]">
|
<div className="mt-1 pl-[52px]">
|
||||||
<div className="whitespace-pre-line break-words text-[15px] leading-tight text-zinc-100">{content}</div>
|
<div className="whitespace-pre-line break-words text-[15px] leading-tight text-zinc-100">{content}</div>
|
||||||
</div>
|
</div>
|
||||||
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]"></div>
|
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
|
||||||
|
<NoteMetadata id={event.event_id} eventPubkey={event.pubkey} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@@ -1,69 +0,0 @@
|
|||||||
import { AccountContext } from '@lume/shared/accountProvider';
|
|
||||||
import { RelayContext } from '@lume/shared/relaysProvider';
|
|
||||||
import { WRITEONLY_RELAYS } from '@lume/stores/constants';
|
|
||||||
import { dateToUnix } from '@lume/utils/getDate';
|
|
||||||
|
|
||||||
import { Heart } from 'iconoir-react';
|
|
||||||
import { getEventHash, signEvent } from 'nostr-tools';
|
|
||||||
import { useContext, useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
export const NoteReaction = ({
|
|
||||||
count,
|
|
||||||
liked,
|
|
||||||
eventID,
|
|
||||||
eventPubkey,
|
|
||||||
}: {
|
|
||||||
count: number;
|
|
||||||
liked: boolean;
|
|
||||||
eventID: string;
|
|
||||||
eventPubkey: string;
|
|
||||||
}) => {
|
|
||||||
const pool: any = useContext(RelayContext);
|
|
||||||
const activeAccount: any = useContext(AccountContext);
|
|
||||||
|
|
||||||
const [isReact, setIsReact] = useState(false);
|
|
||||||
const [like, setLike] = useState(0);
|
|
||||||
|
|
||||||
const handleLike = (e: any) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
if (liked === false || isReact === false) {
|
|
||||||
const event: any = {
|
|
||||||
content: '+',
|
|
||||||
kind: 7,
|
|
||||||
tags: [
|
|
||||||
['e', eventID],
|
|
||||||
['p', eventPubkey],
|
|
||||||
],
|
|
||||||
created_at: dateToUnix(),
|
|
||||||
pubkey: activeAccount.pubkey,
|
|
||||||
};
|
|
||||||
event.id = getEventHash(event);
|
|
||||||
event.sig = signEvent(event, activeAccount.privkey);
|
|
||||||
// publish event to all relays
|
|
||||||
pool.publish(event, WRITEONLY_RELAYS);
|
|
||||||
// update state to change icon to filled heart
|
|
||||||
setIsReact(true);
|
|
||||||
// update counter
|
|
||||||
setLike((like) => (like += 1));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsReact(liked);
|
|
||||||
setLike(count);
|
|
||||||
}, [count, liked]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<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 ? (
|
|
||||||
<Heart width={20} height={20} className="fill-red-500 text-transparent" />
|
|
||||||
) : (
|
|
||||||
<Heart width={20} height={20} className="text-zinc-500" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<span>{like}</span>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
};
|
|
@@ -1,5 +1,65 @@
|
|||||||
import { memo } from 'react';
|
import NoteReply from '@lume/app/newsfeed/components/metadata/reply';
|
||||||
|
import ZapIcon from '@lume/shared/icons/zap';
|
||||||
|
import { RelayContext } from '@lume/shared/relayProvider';
|
||||||
|
import { READONLY_RELAYS } from '@lume/stores/constants';
|
||||||
|
|
||||||
export const NoteMetadata = memo(function NoteMetadata() {
|
import { useContext, useState } from 'react';
|
||||||
return <div className="relative z-10 -ml-1 flex items-center gap-8"></div>;
|
import useSWRSubscription from 'swr/subscription';
|
||||||
});
|
|
||||||
|
import NoteLike from '../metadata/like';
|
||||||
|
import NoteRepost from '../metadata/repost';
|
||||||
|
|
||||||
|
export default function NoteMetadata({ id, eventPubkey }: { id: string; eventPubkey: string }) {
|
||||||
|
const pool: any = useContext(RelayContext);
|
||||||
|
|
||||||
|
const [replies, setReplies] = useState(0);
|
||||||
|
const [likes, setLikes] = useState(0);
|
||||||
|
const [reposts, setReposts] = useState(0);
|
||||||
|
|
||||||
|
useSWRSubscription(id ? ['metadata', id] : null, ([, key], {}) => {
|
||||||
|
const unsubscribe = pool.subscribe(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'#e': [key],
|
||||||
|
since: 0,
|
||||||
|
kinds: [1, 6, 7],
|
||||||
|
limit: 20,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
READONLY_RELAYS,
|
||||||
|
(event: any) => {
|
||||||
|
switch (event.kind) {
|
||||||
|
case 1:
|
||||||
|
setReplies(replies + 1);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
setReposts(reposts + 1);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
if (event.content === '🤙' || event.content === '+') {
|
||||||
|
setLikes(likes + 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unsubscribe();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-16">
|
||||||
|
<NoteReply id={id} replies={replies} />
|
||||||
|
<NoteLike id={id} pubkey={eventPubkey} likes={likes} />
|
||||||
|
<NoteRepost id={id} pubkey={eventPubkey} reposts={reposts} />
|
||||||
|
<button className="inline-flex w-min items-center gap-1.5">
|
||||||
|
<ZapIcon width={20} height={20} className="text-zinc-400" />
|
||||||
|
<span className="text-sm leading-none text-zinc-400">{0}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { contentParser } from '@lume/app/newsfeed/components/contentParser';
|
import { contentParser } from '@lume/app/newsfeed/components/contentParser';
|
||||||
|
import NoteMetadata from '@lume/app/newsfeed/components/note/metadata';
|
||||||
import { NoteDefaultUser } from '@lume/app/newsfeed/components/user/default';
|
import { NoteDefaultUser } from '@lume/app/newsfeed/components/user/default';
|
||||||
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';
|
||||||
@@ -36,7 +37,7 @@ export const NoteParent = memo(function NoteParent({ id }: { id: string }) {
|
|||||||
<div className="relative pb-5">
|
<div className="relative pb-5">
|
||||||
{error && <div>failed to load</div>}
|
{error && <div>failed to load</div>}
|
||||||
{!data ? (
|
{!data ? (
|
||||||
<div className="animated-pulse relative z-10">
|
<div className="animated-pulse">
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<div className="relative h-11 w-11 shrink overflow-hidden rounded-md bg-zinc-700" />
|
<div className="relative h-11 w-11 shrink overflow-hidden rounded-md bg-zinc-700" />
|
||||||
<div className="flex w-full flex-1 items-start justify-between">
|
<div className="flex w-full flex-1 items-start justify-between">
|
||||||
@@ -67,7 +68,9 @@ export const NoteParent = memo(function NoteParent({ id }: { id: string }) {
|
|||||||
{contentParser(data.content, data.tags)}
|
{contentParser(data.content, data.tags)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]"></div>
|
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
|
||||||
|
<NoteMetadata id={data.event_id} eventPubkey={data.pubkey} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { contentParser } from '@lume/app/newsfeed/components/contentParser';
|
import { contentParser } from '@lume/app/newsfeed/components/contentParser';
|
||||||
|
import NoteMetadata from '@lume/app/newsfeed/components/note/metadata';
|
||||||
import { NoteDefaultUser } from '@lume/app/newsfeed/components/user/default';
|
import { NoteDefaultUser } from '@lume/app/newsfeed/components/user/default';
|
||||||
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';
|
||||||
@@ -9,7 +10,7 @@ import { navigate } from 'vite-plugin-ssr/client/router';
|
|||||||
|
|
||||||
export const RootNote = memo(function RootNote({ id, fallback }: { id: string; fallback?: any }) {
|
export const RootNote = memo(function RootNote({ id, fallback }: { id: string; fallback?: any }) {
|
||||||
const pool: any = useContext(RelayContext);
|
const pool: any = useContext(RelayContext);
|
||||||
const parseFallback = fallback.length > 0 ? JSON.parse(fallback) : null;
|
const parseFallback = fallback.length > 1 ? JSON.parse(fallback) : null;
|
||||||
|
|
||||||
const { data, error } = useSWRSubscription(parseFallback ? null : id, (key, { next }) => {
|
const { data, error } = useSWRSubscription(parseFallback ? null : id, (key, { next }) => {
|
||||||
const unsubscribe = pool.subscribe(
|
const unsubscribe = pool.subscribe(
|
||||||
@@ -90,7 +91,9 @@ export const RootNote = memo(function RootNote({ id, fallback }: { id: string; f
|
|||||||
{contentParser(data.content, data.tags)}
|
{contentParser(data.content, data.tags)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]"></div>
|
<div onClick={(e) => e.stopPropagation()} className="mt-5 pl-[52px]">
|
||||||
|
<NoteMetadata id={data.event_id} eventPubkey={data.pubkey} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
14
src/shared/icons/like.tsx
Normal file
14
src/shared/icons/like.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { SVGProps } from 'react';
|
||||||
|
|
||||||
|
export default function LikeIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||||
|
<path
|
||||||
|
d="M12 5.57193C18.3331 -0.86765 29.1898 11.0916 12 20.75C-5.18982 11.0916 5.66687 -0.867651 12 5.57193Z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
15
src/shared/icons/reply.tsx
Normal file
15
src/shared/icons/reply.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { SVGProps } from 'react';
|
||||||
|
|
||||||
|
export default function ReplyIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||||
|
<path
|
||||||
|
d="M12 21.25C17.1086 21.25 21.25 17.1086 21.25 12C21.25 6.89137 17.1086 2.75 12 2.75C6.89137 2.75 2.75 6.89137 2.75 12C2.75 13.529 3.12096 14.9713 3.77778 16.2418L2.75 21.25L7.88889 20.2885C9.12732 20.9039 10.5232 21.25 12 21.25Z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
15
src/shared/icons/repost.tsx
Normal file
15
src/shared/icons/repost.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { SVGProps } from 'react';
|
||||||
|
|
||||||
|
export default function RepostIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||||
|
<path
|
||||||
|
d="M17.25 21.25L20.25 18.25L17.25 15.25M6.75 2.75L3.75 5.75L6.75 8.75M5.25 5.75H20.25V10.75M3.75 13.75V18.25H18.75"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
14
src/shared/icons/zap.tsx
Normal file
14
src/shared/icons/zap.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { SVGProps } from 'react';
|
||||||
|
|
||||||
|
export default function ZapIcon(props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||||
|
<path
|
||||||
|
d="M20.25 8.75H13.25V1.75L3.75 15.0473H10.75V22.25L20.25 8.75Z"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
Reference in New Issue
Block a user