mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-19 19:46:34 +00:00
add trending notes
This commit is contained in:
parent
efea63b0a0
commit
f8de44fe9f
@ -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";
|
||||
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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} />}
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
@ -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>
|
||||
);
|
||||
}
|
@ -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>
|
||||
);
|
||||
}
|
@ -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>
|
||||
);
|
||||
}
|
@ -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">
|
||||
|
40
src/app/trending/components/trendingNotes.tsx
Normal file
40
src/app/trending/components/trendingNotes.tsx
Normal 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>
|
||||
);
|
||||
}
|
@ -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());
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
@ -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} />}
|
||||
<NoteMetadata
|
||||
id={event.event_id}
|
||||
eventPubkey={event.pubkey}
|
||||
currentBlock={block}
|
||||
/>
|
||||
{metadata ? (
|
||||
<NoteMetadata
|
||||
id={event.event_id}
|
||||
eventPubkey={event.pubkey}
|
||||
currentBlock={block || 1}
|
||||
/>
|
||||
) : (
|
||||
<div className="h-5" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -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 ? (
|
@ -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 (
|
@ -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>
|
||||
);
|
||||
});
|
@ -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">
|
@ -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} />}
|
@ -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">
|
@ -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>
|
@ -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} />
|
@ -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";
|
@ -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} />}
|
@ -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>
|
||||
<span className="font-semibold leading-none text-fuchsia-500">
|
||||
{" "}
|
||||
reposted
|
||||
</span>
|
||||
{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)}
|
@ -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",
|
||||
];
|
||||
|
@ -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";
|
||||
|
@ -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
6
src/utils/types.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||
|
||||
export interface LumeEvent extends NDKEvent {
|
||||
event_id: string;
|
||||
parent_id: string;
|
||||
}
|
Loading…
Reference in New Issue
Block a user