add trending notes

This commit is contained in:
Ren Amamiya 2023-06-16 10:01:20 +07:00
parent efea63b0a0
commit f8de44fe9f
38 changed files with 155 additions and 308 deletions

View File

@ -3,9 +3,9 @@ import { MessageMuteButton } from "@app/channel/components/messages/muteButton";
import { MessageReplyButton } from "@app/channel/components/messages/replyButton";
import { ChannelMessageUser } from "@app/channel/components/messages/user";
import { ChannelMessageUserMute } from "@app/channel/components/messages/userMute";
import { MentionNote } from "@app/space/components/notes/mentions/note";
import { ImagePreview } from "@app/space/components/notes/preview/image";
import { VideoPreview } from "@app/space/components/notes/preview/video";
import { MentionNote } from "@shared/notes/mentions/note";
import { ImagePreview } from "@shared/notes/preview/image";
import { VideoPreview } from "@shared/notes/preview/video";
import { parser } from "@utils/parser";
import { useMemo, useState } from "react";

View File

@ -1,8 +1,8 @@
import { ChatMessageUser } from "@app/chat/components/messages/user";
import { useDecryptMessage } from "@app/chat/hooks/useDecryptMessage";
import { MentionNote } from "@app/space/components/notes/mentions/note";
import { ImagePreview } from "@app/space/components/notes/preview/image";
import { VideoPreview } from "@app/space/components/notes/preview/video";
import { MentionNote } from "@shared/notes/mentions/note";
import { ImagePreview } from "@shared/notes/preview/image";
import { VideoPreview } from "@shared/notes/preview/video";
import { parser } from "@utils/parser";
import { memo } from "react";

View File

@ -6,6 +6,7 @@ import { useActiveAccount } from "@stores/accounts";
import { useChatMessages } from "@stores/chats";
import { dateToUnix } from "@utils/date";
import { usePageContext } from "@utils/hooks/usePageContext";
import { LumeEvent } from "@utils/types";
import { useContext, useEffect } from "react";
import useSWRSubscription from "swr/subscription";
@ -31,7 +32,7 @@ export function Page() {
since: dateToUnix(),
});
sub.addListener("event", (event: any) => {
sub.addListener("event", (event: LumeEvent) => {
add(account.pubkey, event);
});

View File

@ -1,8 +1,8 @@
import { NoteBase } from "@app/space/components/notes/base";
import { NoteQuoteRepost } from "@app/space/components/notes/quoteRepost";
import { NoteSkeleton } from "@app/space/components/notes/skeleton";
import { getNotesByAuthor } from "@libs/storage";
import { CancelIcon } from "@shared/icons";
import { NoteBase } from "@shared/notes/base";
import { NoteQuoteRepost } from "@shared/notes/quoteRepost";
import { NoteSkeleton } from "@shared/notes/skeleton";
import { useActiveAccount } from "@stores/accounts";
import { useVirtualizer } from "@tanstack/react-virtual";
import { useEffect, useMemo, useRef } from "react";

View File

@ -1,8 +1,8 @@
import { NoteBase } from "@app/space/components/notes/base";
import { NoteQuoteRepost } from "@app/space/components/notes/quoteRepost";
import { NoteSkeleton } from "@app/space/components/notes/skeleton";
import { createNote, getNotes } from "@libs/storage";
import { NDKEvent } from "@nostr-dev-kit/ndk";
import { NoteBase } from "@shared/notes/base";
import { NoteQuoteRepost } from "@shared/notes/quoteRepost";
import { NoteSkeleton } from "@shared/notes/skeleton";
import { RelayContext } from "@shared/relayProvider";
import { useActiveAccount } from "@stores/accounts";
import { useVirtualizer } from "@tanstack/react-virtual";

View File

@ -1,12 +1,12 @@
import { Kind1 } from "@app/space/components/notes/kind1";
import { Kind1063 } from "@app/space/components/notes/kind1063";
import { NoteMetadata } from "@app/space/components/notes/metadata";
import { NoteReplyForm } from "@app/space/components/notes/replies/form";
import { RepliesList } from "@app/space/components/notes/replies/list";
import { NoteSkeleton } from "@app/space/components/notes/skeleton";
import { NoteDefaultUser } from "@app/space/components/user/default";
import { getNoteByID } from "@libs/storage";
import { ArrowLeftIcon } from "@shared/icons";
import { Kind1 } from "@shared/notes/kind1";
import { Kind1063 } from "@shared/notes/kind1063";
import { NoteMetadata } from "@shared/notes/metadata";
import { NoteReplyForm } from "@shared/notes/replies/form";
import { RepliesList } from "@shared/notes/replies/list";
import { NoteSkeleton } from "@shared/notes/skeleton";
import { User } from "@shared/user";
import { useActiveAccount } from "@stores/accounts";
import { parser } from "@utils/parser";
import useSWR from "swr";
@ -48,7 +48,7 @@ export function ThreadBlock({ params }: { params: any }) {
) : (
<div className="h-min w-full px-3 py-1.5">
<div className="rounded-md bg-zinc-900 px-5 pt-5">
<NoteDefaultUser pubkey={data.pubkey} time={data.created_at} />
<User pubkey={data.pubkey} time={data.created_at} />
<div className="mt-3">
{data.kind === 1 && <Kind1 content={content} />}
{data.kind === 1063 && <Kind1063 metadata={data.tags} />}

View File

@ -1,34 +0,0 @@
import { useActiveAccount } from "@stores/accounts";
export function NoteWrapper({
children,
thread,
block,
className,
}: {
children: React.ReactNode;
thread: string;
block: number;
className: string;
}) {
const addTempBlock = useActiveAccount((state: any) => state.addTempBlock);
const openThread = (event: any, thread: string) => {
const selection = window.getSelection();
if (selection.toString().length === 0) {
addTempBlock(block, 2, "Thread", thread);
} else {
event.stopPropagation();
}
};
return (
<div
onClick={(e) => openThread(e, thread)}
onKeyDown={(e) => openThread(e, thread)}
className={className}
>
{children}
</div>
);
}

View File

@ -1,97 +0,0 @@
import { Popover, Transition } from "@headlessui/react";
import { Image } from "@shared/image";
import { DEFAULT_AVATAR } from "@stores/constants";
import { useProfile } from "@utils/hooks/useProfile";
import { shortenKey } from "@utils/shortenKey";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import { Fragment } from "react";
dayjs.extend(relativeTime);
export function NoteDefaultUser({
pubkey,
time,
}: {
pubkey: string;
time: number;
}) {
const { user } = useProfile(pubkey);
return (
<Popover className="relative flex items-start gap-3">
<Popover.Button className="h-11 w-11 shrink-0 overflow-hidden rounded-md bg-zinc-900">
<Image
src={user?.image || DEFAULT_AVATAR}
alt={pubkey}
className="h-11 w-11 object-cover"
/>
</Popover.Button>
<div className="flex flex-wrap items-baseline gap-1">
<h5 className="max-w-[10rem] text-base font-semibold leading-none truncate">
{user?.nip05 || user?.name || shortenKey(pubkey)}
</h5>
<span className="leading-none text-zinc-500">·</span>
<span className="leading-none text-zinc-500">
{dayjs().to(dayjs.unix(time), true)}
</span>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute left-0 top-8 z-10 mt-3 w-screen max-w-sm px-4 sm:px-0 lg:max-w-3xl">
<div
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
className="w-full max-w-xs overflow-hidden rounded-lg border border-zinc-700 bg-zinc-900 shadow-input ring-1 ring-black ring-opacity-5"
>
<div className="flex items-start gap-2.5 border-b border-zinc-800 px-3 py-3">
<Image
src={user?.image || DEFAULT_AVATAR}
alt={pubkey}
className="h-14 w-14 shrink-0 rounded-lg object-cover"
/>
<div className="flex w-full flex-1 flex-col gap-2">
<div className="inline-flex w-2/3 flex-col gap-0.5">
<h5 className="text-base font-semibold leading-none">
{user?.displayName || user?.name || (
<div className="h-3 w-20 animate-pulse rounded-sm bg-zinc-700" />
)}
</h5>
<span className="truncate text-base leading-none text-zinc-500">
{user?.nip05 || shortenKey(pubkey)}
</span>
</div>
<div>
<p className="line-clamp-3 text-base leading-tight text-white">
{user?.about}
</p>
</div>
</div>
</div>
<div className="flex items-center gap-2 px-3 py-3">
<a
href={`/app/user?pubkey=${pubkey}`}
className="inline-flex h-10 flex-1 items-center justify-center rounded-md bg-zinc-800 hover:bg-zinc-700 text-base font-medium"
>
View full profile
</a>
<a
href={`/app/chat?pubkey=${pubkey}`}
className="inline-flex h-10 flex-1 items-center justify-center rounded-md bg-zinc-800 hover:bg-zinc-700 text-base font-medium"
>
Message
</a>
</div>
</div>
</Popover.Panel>
</Transition>
</Popover>
);
}

View File

@ -1,41 +0,0 @@
import { Image } from "@shared/image";
import { DEFAULT_AVATAR } from "@stores/constants";
import { useProfile } from "@utils/hooks/useProfile";
import { shortenKey } from "@utils/shortenKey";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
dayjs.extend(relativeTime);
export function NoteQuoteUser({
pubkey,
time,
}: {
pubkey: string;
time: number;
}) {
const { user } = useProfile(pubkey);
return (
<div className="group flex items-center gap-2">
<div className="relative h-6 w-6 shrink-0 rounded">
<Image
src={user?.image || DEFAULT_AVATAR}
alt={pubkey}
className="h-6 w-6 rounded object-cover"
/>
</div>
<div className="flex w-full flex-1 items-start justify-between">
<div className="flex items-baseline gap-2 text-base">
<span className="max-w-[10rem] truncate font-semibold leading-none text-white">
{user?.nip05 || user?.name || shortenKey(pubkey)}
</span>
<span className="leading-none text-zinc-500">·</span>
<span className="leading-none text-zinc-500">
{dayjs().to(dayjs.unix(time), true)}
</span>
</div>
</div>
</div>
);
}

View File

@ -1,41 +0,0 @@
import { Image } from "@shared/image";
import { DEFAULT_AVATAR } from "@stores/constants";
import { useProfile } from "@utils/hooks/useProfile";
import { shortenKey } from "@utils/shortenKey";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
dayjs.extend(relativeTime);
export function NoteReplyUser({
pubkey,
time,
}: {
pubkey: string;
time: number;
}) {
const { user } = useProfile(pubkey);
return (
<div className="group flex items-start gap-2.5">
<div className="relative h-11 w-11 shrink-0 rounded-md">
<Image
src={user?.image || DEFAULT_AVATAR}
alt={pubkey}
className="h-11 w-11 rounded-md object-cover"
/>
</div>
<div className="flex w-full flex-1 items-start justify-between">
<div className="flex items-baseline gap-2 text-base">
<span className="max-w-[10rem] truncate font-semibold leading-none text-white">
{user?.nip05 || user?.name || shortenKey(pubkey)}
</span>
<span className="leading-none text-zinc-500">·</span>
<span className="leading-none text-zinc-500">
{dayjs().to(dayjs.unix(time), true)}
</span>
</div>
</div>
</div>
);
}

View File

@ -22,8 +22,6 @@ export function Profile({ data }: { data: any }) {
</div>
);
console.log(userStats);
return (
<div className="rounded-md bg-zinc-900 px-5 py-5">
<div className="flex items-center gap-2">

View File

@ -0,0 +1,40 @@
import { Profile } from "@app/trending/components/profile";
import { NoteBase } from "@shared/notes/base";
import { NoteSkeleton } from "@shared/notes/skeleton";
import useSWR from "swr";
const fetcher = (url: string) => fetch(url).then((r) => r.json());
export function TrendingNotes() {
const { data, error } = useSWR(
"https://api.nostr.band/v0/trending/notes",
fetcher,
);
return (
<div className="shrink-0 w-[360px] flex-col flex border-r border-zinc-900">
<div
data-tauri-drag-region
className="h-11 w-full flex items-center justify-center px-3 border-b border-zinc-900"
>
<h3 className="font-semibold text-zinc-100">Trending Profiles</h3>
</div>
<div className="scrollbar-hide flex w-full h-full flex-col justify-between gap-1.5 pt-1.5 pb-20 overflow-y-auto">
{error && <p>Failed to load...</p>}
{!data ? (
<div className="px-3 py-1.5">
<div className="rounded-md bg-zinc-900 px-3 py-3 shadow-input shadow-black/20">
<NoteSkeleton />
</div>
</div>
) : (
<div className="relative w-full flex flex-col pt-1.5">
{data.notes.map((item) => (
<NoteBase key={item.id} event={item.event} metadata={false} />
))}
</div>
)}
</div>
</div>
);
}

View File

@ -1,5 +1,5 @@
import { NoteSkeleton } from "@app/space/components/notes/skeleton";
import { Profile } from "@app/trending/components/profile";
import { NoteSkeleton } from "@shared/notes/skeleton";
import useSWR from "swr";
const fetcher = (url: string) => fetch(url).then((r) => r.json());

View File

@ -1,9 +1,11 @@
import { TrendingNotes } from "@app/trending/components/trendingNotes";
import { TrendingProfiles } from "@app/trending/components/trendingProfiles";
export function Page() {
return (
<div className="h-full w-full flex flex-nowrap overflow-x-auto overflow-y-hidden scrollbar-hide">
<TrendingProfiles />
<TrendingNotes />
</div>
);
}

View File

@ -1,13 +1,18 @@
import { Kind1 } from "@app/space/components/notes/kind1";
import { Kind1063 } from "@app/space/components/notes/kind1063";
import { NoteMetadata } from "@app/space/components/notes/metadata";
import { NoteParent } from "@app/space/components/notes/parent";
import { NoteDefaultUser } from "@app/space/components/user/default";
import { Kind1 } from "@shared/notes/kind1";
import { Kind1063 } from "@shared/notes/kind1063";
import { NoteMetadata } from "@shared/notes/metadata";
import { NoteParent } from "@shared/notes/parent";
import { User } from "@shared/user";
import { parser } from "@utils/parser";
import { isTagsIncludeID } from "@utils/transform";
import { LumeEvent } from "@utils/types";
import { useMemo } from "react";
export function NoteBase({ block, event }: { block: number; event: any }) {
export function NoteBase({
event,
block,
metadata,
}: { event: LumeEvent; block?: number; metadata?: boolean }) {
const content = useMemo(() => parser(event), [event]);
const checkParentID = isTagsIncludeID(event.parent_id, event.tags);
@ -21,15 +26,19 @@ export function NoteBase({ block, event }: { block: number; event: any }) {
<></>
)}
<div className="flex flex-col">
<NoteDefaultUser pubkey={event.pubkey} time={event.created_at} />
<User pubkey={event.pubkey} time={event.created_at} />
<div className="-mt-5 pl-[49px]">
{event.kind === 1 && <Kind1 content={content} />}
{event.kind === 1063 && <Kind1063 metadata={event.tags} />}
{metadata ? (
<NoteMetadata
id={event.event_id}
eventPubkey={event.pubkey}
currentBlock={block}
currentBlock={block || 1}
/>
) : (
<div className="h-5" />
)}
</div>
</div>
</div>

View File

@ -1,7 +1,7 @@
import { LinkPreview } from "./preview/link";
import { MentionNote } from "@app/space/components/notes/mentions/note";
import { ImagePreview } from "@app/space/components/notes/preview/image";
import { VideoPreview } from "@app/space/components/notes/preview/video";
import { MentionNote } from "@shared/notes/mentions/note";
import { ImagePreview } from "@shared/notes/preview/image";
import { VideoPreview } from "@shared/notes/preview/video";
import { truncateContent } from "@utils/transform";
export function Kind1({
@ -10,7 +10,7 @@ export function Kind1({
}: { content: any; truncate?: boolean }) {
return (
<>
<div className="select-text whitespace-pre-line break-words text-base leading-tight text-zinc-100">
<div className="select-text whitespace-pre-line break-words text-base text-zinc-100">
{truncate ? truncateContent(content.parsed, 120) : content.parsed}
</div>
{Array.isArray(content.images) && content.images.length ? (

View File

@ -1,10 +1,11 @@
import { NDKTag } from "@nostr-dev-kit/ndk";
import { Image } from "@shared/image";
function isImage(url: string) {
return /\.(jpg|jpeg|gif|png|webp|avif)$/.test(url);
}
export function Kind1063({ metadata }: { metadata: string[] }) {
export function Kind1063({ metadata }: { metadata: NDKTag[] }) {
const url = metadata[0][1];
return (

View File

@ -1,8 +1,7 @@
import { Kind1 } from "@app/space/components/notes/kind1";
import { Kind1063 } from "@app/space/components/notes/kind1063";
import { NoteSkeleton } from "@app/space/components/notes/skeleton";
import { NoteWrapper } from "@app/space/components/notes/wrapper";
import { NoteQuoteUser } from "@app/space/components/user/quote";
import { Kind1 } from "@shared/notes/kind1";
import { Kind1063 } from "@shared/notes/kind1063";
import { NoteSkeleton } from "@shared/notes/skeleton";
import { User } from "@shared/user";
import { useEvent } from "@utils/hooks/useEvent";
import { parser } from "@utils/parser";
import { memo } from "react";
@ -14,14 +13,10 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) {
const kind1063 = data?.kind === 1063 ? data.tags : null;
return (
<NoteWrapper
thread={id}
block={1}
className="mt-3 rounded-lg border border-zinc-800 px-3 py-3"
>
<div className="mt-3 rounded-lg border border-zinc-800 px-3 py-3">
{data ? (
<>
<NoteQuoteUser pubkey={data.pubkey} time={data.created_at} />
<User pubkey={data.pubkey} time={data.created_at} size="small" />
<div className="mt-2">
{kind1 && <Kind1 content={kind1} truncate={true} />}
{kind1063 && <Kind1063 metadata={kind1063} />}
@ -45,6 +40,6 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) {
) : (
<NoteSkeleton />
)}
</NoteWrapper>
</div>
);
});

View File

@ -1,9 +1,9 @@
import { NoteReply } from "@app/space/components/notes/metadata/reply";
import { NoteRepost } from "@app/space/components/notes/metadata/repost";
import { NoteZap } from "@app/space/components/notes/metadata/zap";
import { createReplyNote } from "@libs/storage";
import { NDKEvent, NDKFilter } from "@nostr-dev-kit/ndk";
import { LoaderIcon, ReplyIcon, RepostIcon, ZapIcon } from "@shared/icons";
import { NoteReply } from "@shared/notes/metadata/reply";
import { NoteRepost } from "@shared/notes/metadata/repost";
import { NoteZap } from "@shared/notes/metadata/zap";
import { RelayContext } from "@shared/relayProvider";
import { decode } from "light-bolt11-decoder";
import { useContext } from "react";
@ -70,7 +70,7 @@ export function NoteMetadata({
const { data, isLoading } = useSWR(["note-metadata", ndk, id], fetcher);
return (
<div className="inline-flex items-center w-full h-12 mt-4">
<div className="inline-flex items-center w-full h-12 mt-2">
{!data || isLoading ? (
<>
<div className="w-20 group inline-flex items-center gap-1.5">

View File

@ -1,8 +1,8 @@
import { Kind1 } from "@app/space/components/notes/kind1";
import { Kind1063 } from "@app/space/components/notes/kind1063";
import { NoteMetadata } from "@app/space/components/notes/metadata";
import { NoteSkeleton } from "@app/space/components/notes/skeleton";
import { NoteDefaultUser } from "@app/space/components/user/default";
import { Kind1 } from "@shared/notes/kind1";
import { Kind1063 } from "@shared/notes/kind1063";
import { NoteMetadata } from "@shared/notes/metadata";
import { NoteSkeleton } from "@shared/notes/skeleton";
import { User } from "@shared/user";
import { useEvent } from "@utils/hooks/useEvent";
import { parser } from "@utils/parser";
import { memo } from "react";
@ -18,7 +18,7 @@ export const NoteParent = memo(function NoteParent({ id }: { id: string }) {
<div className="absolute left-[18px] top-0 h-full w-0.5 bg-gradient-to-t from-zinc-800 to-zinc-600" />
{data ? (
<>
<NoteDefaultUser pubkey={data.pubkey} time={data.created_at} />
<User pubkey={data.pubkey} time={data.created_at} />
<div className="-mt-5 pl-[49px]">
{kind1 && <Kind1 content={kind1} />}
{kind1063 && <Kind1063 metadata={kind1063} />}

View File

@ -28,7 +28,7 @@ export function LinkPreview({ urls }: { urls: string[] }) {
<Image
src={data["og:image"]}
alt={urls[0]}
className="w-full h-auto border-t-lg object-cover"
className="w-full h-auto object-cover rounded-t-lg"
/>
)}
<div className="flex flex-col gap-2 px-3 py-3">

View File

@ -1,11 +1,12 @@
import { RootNote } from "@app/space/components/notes/rootNote";
import { NoteRepostUser } from "@app/space/components/user/repost";
import { RootNote } from "@shared/notes/rootNote";
import { User } from "@shared/user";
import { getQuoteID } from "@utils/transform";
import { LumeEvent } from "@utils/types";
export function NoteQuoteRepost({
block,
event,
}: { block: number; event: any }) {
}: { block: number; event: LumeEvent }) {
const rootID = getQuoteID(event.tags);
return (
@ -13,7 +14,7 @@ export function NoteQuoteRepost({
<div className="rounded-md bg-zinc-900">
<div className="relative px-5 pb-5 pt-5">
<div className="absolute left-[35px] top-[20px] h-[70px] w-0.5 bg-gradient-to-t from-zinc-800 to-zinc-600" />
<NoteRepostUser pubkey={event.pubkey} time={event.created_at} />
<User pubkey={event.pubkey} time={event.created_at} repost={true} />
</div>
<RootNote id={rootID} fallback={event.content} currentBlock={block} />
</div>

View File

@ -1,6 +1,6 @@
import { Kind1 } from "@app/space/components/notes/kind1";
import { NoteMetadata } from "@app/space/components/notes/metadata";
import { NoteReplyUser } from "@app/space/components/user/reply";
import { Kind1 } from "@shared/notes/kind1";
import { NoteMetadata } from "@shared/notes/metadata";
import { User } from "@shared/user";
import { parser } from "@utils/parser";
export function Reply({ data }: { data: any }) {
@ -9,7 +9,7 @@ export function Reply({ data }: { data: any }) {
return (
<div className="flex h-min min-h-min w-full select-text flex-col px-3 pt-5 mb-3 rounded-md bg-zinc-900">
<div className="flex flex-col">
<NoteReplyUser pubkey={data.pubkey} time={data.created_at} />
<User pubkey={data.pubkey} time={data.created_at} />
<div className="-mt-[20px] pl-[47px]">
<Kind1 content={content} />
<NoteMetadata id={data.id} eventPubkey={data.pubkey} />

View File

@ -1,5 +1,5 @@
import { Reply } from "@app/space/components/notes/replies/item";
import { NDKEvent, NDKFilter } from "@nostr-dev-kit/ndk";
import { Reply } from "@shared/notes/replies/item";
import { RelayContext } from "@shared/relayProvider";
import { useContext } from "react";
import useSWR from "swr";

View File

@ -1,10 +1,10 @@
import { Kind1 } from "@app/space/components/notes/kind1";
import { Kind1063 } from "@app/space/components/notes/kind1063";
import { NoteMetadata } from "@app/space/components/notes/metadata";
import { NoteSkeleton } from "@app/space/components/notes/skeleton";
import { NoteDefaultUser } from "@app/space/components/user/default";
import { NDKEvent } from "@nostr-dev-kit/ndk";
import { Kind1 } from "@shared/notes/kind1";
import { Kind1063 } from "@shared/notes/kind1063";
import { NoteMetadata } from "@shared/notes/metadata";
import { NoteSkeleton } from "@shared/notes/skeleton";
import { RelayContext } from "@shared/relayProvider";
import { User } from "@shared/user";
import { parser } from "@utils/parser";
import { memo, useContext } from "react";
import useSWRSubscription from "swr/subscription";
@ -51,10 +51,7 @@ export const RootNote = memo(function RootNote({
return (
<div className="flex flex-col px-5">
<NoteDefaultUser
pubkey={parseFallback.pubkey}
time={parseFallback.created_at}
/>
<User pubkey={parseFallback.pubkey} time={parseFallback.created_at} />
<div className="-mt-5 pl-[49px]">
<Kind1 content={contentFallback} />
<NoteMetadata
@ -71,7 +68,7 @@ export const RootNote = memo(function RootNote({
<div className="flex flex-col px-5">
{data ? (
<>
<NoteDefaultUser pubkey={data.pubkey} time={data.created_at} />
<User pubkey={data.pubkey} time={data.created_at} />
<div className="-mt-5 pl-[49px]">
{kind1 && <Kind1 content={kind1} />}
{kind1063 && <Kind1063 metadata={kind1063} />}

View File

@ -9,29 +9,38 @@ import { Fragment } from "react";
dayjs.extend(relativeTime);
export function NoteRepostUser({
export function User({
pubkey,
time,
}: { pubkey: string; time: number }) {
size,
repost,
}: { pubkey: string; time: number; size?: string; repost?: boolean }) {
const { user } = useProfile(pubkey);
const avatarWidth = size === "small" ? "w-6" : "w-11";
const avatarHeight = size === "small" ? "h-6" : "h-11";
return (
<Popover className="relative flex items-start gap-3">
<Popover.Button className="h-11 w-11 shrink-0 overflow-hidden rounded-md bg-zinc-900">
<Popover.Button
className={`${avatarWidth} ${avatarHeight} shrink-0 overflow-hidden rounded-md bg-zinc-900`}
>
<Image
src={user?.image || DEFAULT_AVATAR}
alt={pubkey}
className="h-11 w-11 rounded-md object-cover"
className={`${avatarWidth} ${avatarHeight} rounded-md object-cover`}
/>
</Popover.Button>
<div className="flex flex-wrap items-baseline gap-1">
<h5 className="max-w-[10rem] text-base font-semibold leading-none truncate">
{user?.nip05 || user?.name || shortenKey(pubkey)}
</h5>
{repost && (
<span className="font-semibold leading-none text-fuchsia-500">
{" "}
reposted
</span>
)}
<span className="leading-none text-zinc-500">·</span>
<span className="leading-none text-zinc-500">
{dayjs().to(dayjs.unix(time), true)}

View File

@ -11,6 +11,6 @@ export const FULL_RELAYS = [
"wss://welcome.nostr.wine",
"wss://relay.nostr.band",
"wss://relay.damus.io",
"wss://relayable.org",
"wss://relay.nostrich.land",
"wss://nostr.mutinywallet.com",
];

View File

@ -1,4 +1,4 @@
import { MentionUser } from "@app/space/components/notes/mentions/user";
import { MentionUser } from "@shared/notes/mentions/user";
import destr from "destr";
import getUrls from "get-urls";
import { parseReferences } from "nostr-tools";

View File

@ -1,3 +1,4 @@
import { NDKTag } from "@nostr-dev-kit/ndk";
import destr from "destr";
import { nip19 } from "nostr-tools";
@ -66,7 +67,7 @@ export function getParentID(arr: string[], fallback: string) {
}
// check id present in event tags
export function isTagsIncludeID(id: string, arr: string[]) {
export function isTagsIncludeID(id: string, arr: NDKTag[]) {
const tags = destr(arr);
if (tags.length > 0) {
@ -79,7 +80,7 @@ export function isTagsIncludeID(id: string, arr: string[]) {
}
// get parent id from event tags
export function getQuoteID(arr: string[]) {
export function getQuoteID(arr: NDKTag[]) {
const tags = destr(arr);
let quoteID = null;

6
src/utils/types.ts Normal file
View File

@ -0,0 +1,6 @@
import { NDKEvent } from "@nostr-dev-kit/ndk";
export interface LumeEvent extends NDKEvent {
event_id: string;
parent_id: string;
}