feat: polish
This commit is contained in:
@@ -1,33 +0,0 @@
|
||||
import { activityAtom } from "@lume/utils";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { ActivityContent } from "./content";
|
||||
|
||||
export function Activity() {
|
||||
const isActivityOpen = useAtomValue(activityAtom);
|
||||
|
||||
return (
|
||||
<AnimatePresence initial={false} mode="wait">
|
||||
{isActivityOpen ? (
|
||||
<motion.div
|
||||
key={isActivityOpen ? "activity-open" : "activity-close"}
|
||||
layout
|
||||
initial={{ scale: 0.9, opacity: 0, translateX: -20 }}
|
||||
animate={{
|
||||
scale: [0.95, 1],
|
||||
opacity: [0.5, 1],
|
||||
translateX: [-10, 0],
|
||||
}}
|
||||
exit={{
|
||||
scale: [0.95, 0.9],
|
||||
opacity: [0.5, 0],
|
||||
translateX: [-10, -20],
|
||||
}}
|
||||
className="h-full w-[350px] px-1 pb-1 shrink-0"
|
||||
>
|
||||
<ActivityContent />
|
||||
</motion.div>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
import { useArk } from "@lume/ark";
|
||||
import { LoaderIcon } from "@lume/icons";
|
||||
import { useStorage } from "@lume/storage";
|
||||
import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
|
||||
import { useInfiniteQuery } from "@tanstack/react-query";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { ReplyActivity } from "./reply";
|
||||
import { RepostActivity } from "./repost";
|
||||
import { ZapActivity } from "./zap";
|
||||
|
||||
export function ActivityContent() {
|
||||
const ark = useArk();
|
||||
const storage = useStorage();
|
||||
|
||||
const { isLoading, data, hasNextPage, isFetchingNextPage, fetchNextPage } =
|
||||
useInfiniteQuery({
|
||||
queryKey: ["activity"],
|
||||
initialPageParam: 0,
|
||||
queryFn: async ({
|
||||
signal,
|
||||
pageParam,
|
||||
}: {
|
||||
signal: AbortSignal;
|
||||
pageParam: number;
|
||||
}) => {
|
||||
const events = await ark.getInfiniteEvents({
|
||||
filter: {
|
||||
kinds: [NDKKind.Zap],
|
||||
"#p": [ark.account.pubkey],
|
||||
},
|
||||
limit: 100,
|
||||
pageParam,
|
||||
signal,
|
||||
});
|
||||
|
||||
return events;
|
||||
},
|
||||
getNextPageParam: (lastPage) => {
|
||||
const lastEvent = lastPage.at(-1);
|
||||
if (!lastEvent) return;
|
||||
return lastEvent.created_at - 1;
|
||||
},
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
refetchOnReconnect: false,
|
||||
});
|
||||
|
||||
const allEvents = useMemo(
|
||||
() => (data ? data.pages.flatMap((page) => page) : []),
|
||||
[data],
|
||||
);
|
||||
|
||||
const renderEvent = useCallback((event: NDKEvent) => {
|
||||
if (event.pubkey === ark.account.pubkey) return null;
|
||||
|
||||
switch (event.kind) {
|
||||
case NDKKind.Text:
|
||||
return <ReplyActivity key={event.id} event={event} />;
|
||||
case NDKKind.Repost:
|
||||
return <RepostActivity key={event.id} event={event} />;
|
||||
case NDKKind.Zap:
|
||||
return <ZapActivity key={event.id} event={event} />;
|
||||
default:
|
||||
return <ReplyActivity key={event.id} event={event} />;
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col justify-between rounded-xl overflow-hidden bg-white shadow-[rgba(50,_50,_105,_0.15)_0px_2px_5px_0px,_rgba(0,_0,_0,_0.05)_0px_1px_1px_0px] dark:bg-black dark:shadow-none dark:ring-1 dark:ring-white/10">
|
||||
<div className="h-full w-full min-h-0">
|
||||
{isLoading ? (
|
||||
<div className="w-[350px] h-full flex items-center justify-center">
|
||||
<LoaderIcon className="size-5 animate-spin" />
|
||||
</div>
|
||||
) : allEvents.length < 1 ? (
|
||||
<div className="w-full h-full flex flex-col items-center justify-center">
|
||||
<p className="mb-2 text-2xl">🎉</p>
|
||||
<p className="text-center font-medium">Yo! Nothing new yet.</p>
|
||||
</div>
|
||||
) : (
|
||||
renderEvent(allEvents[0])
|
||||
)}
|
||||
</div>
|
||||
<div className="h-16 shrink-0 px-3 flex items-center gap-3 bg-neutral-100 dark:bg-neutral-900">
|
||||
<button
|
||||
type="button"
|
||||
className="h-11 flex-1 inline-flex items-center justify-center rounded-xl font-medium bg-neutral-200 dark:bg-neutral-800 hover:bg-neutral-300 dark:hover:bg-neutral-700"
|
||||
>
|
||||
Previous
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="h-11 flex-1 inline-flex items-center justify-center rounded-xl font-medium bg-blue-500 text-white hover:bg-blue-600"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import { Note, useArk } from "@lume/ark";
|
||||
import { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||
import { ActivityRootNote } from "./rootNote";
|
||||
|
||||
export function ReplyActivity({ event }: { event: NDKEvent }) {
|
||||
const ark = useArk();
|
||||
const thread = ark.getEventThread({
|
||||
content: event.content,
|
||||
tags: event.tags,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="h-full pb-3 flex flex-col justify-between">
|
||||
<div className="h-14 border-b border-neutral-100 dark:border-neutral-900 flex flex-col items-center justify-center px-3">
|
||||
<h3 className="text-center font-semibold leading-tight">
|
||||
Conversation
|
||||
</h3>
|
||||
<p className="text-sm text-blue-500 font-medium leading-tight">
|
||||
@ Someone has replied to your note
|
||||
</p>
|
||||
</div>
|
||||
<div className="px-3">
|
||||
{thread ? (
|
||||
<div className="flex flex-col gap-3 mb-1">
|
||||
<ActivityRootNote eventId={thread.rootEventId} />
|
||||
<ActivityRootNote eventId={thread.replyEventId} />
|
||||
</div>
|
||||
) : null}
|
||||
<div className="mt-3 flex flex-col gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-teal-500 font-medium">New reply</p>
|
||||
<div className="flex-1 h-px bg-teal-300" />
|
||||
<div className="w-4 shrink-0 h-px bg-teal-300" />
|
||||
</div>
|
||||
<Note.Provider event={event}>
|
||||
<Note.Root>
|
||||
<div className="flex items-center justify-between px-3 h-14">
|
||||
<Note.User className="flex-1 pr-1" />
|
||||
</div>
|
||||
<Note.Content className="min-w-0 px-3" />
|
||||
<div className="flex items-center justify-between px-3 h-14">
|
||||
<Note.Pin />
|
||||
<div className="inline-flex items-center gap-10" />
|
||||
</div>
|
||||
</Note.Root>
|
||||
</Note.Provider>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import { formatCreatedAt } from "@lume/utils";
|
||||
import { NDKEvent } from "@nostr-dev-kit/ndk";
|
||||
import { User } from "../user";
|
||||
import { ActivityRootNote } from "./rootNote";
|
||||
|
||||
export function RepostActivity({ event }: { event: NDKEvent }) {
|
||||
const repostId = event.tags.find((el) => el[0] === "e")[1];
|
||||
const createdAt = formatCreatedAt(event.created_at);
|
||||
|
||||
return (
|
||||
<div className="h-full pb-3 flex flex-col justify-between">
|
||||
<div className="h-14 border-b border-neutral-100 dark:border-neutral-900 flex flex-col items-center justify-center px-3">
|
||||
<h3 className="text-center font-semibold leading-tight">Boost</h3>
|
||||
<p className="text-sm text-blue-500 font-medium leading-tight">
|
||||
@ Someone has reposted to your note
|
||||
</p>
|
||||
</div>
|
||||
<div className="px-3">
|
||||
<div className="flex flex-col gap-3">
|
||||
<User pubkey={event.pubkey} variant="notify2" />
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-teal-500 font-medium">Reposted</p>
|
||||
<div className="flex-1 h-px bg-teal-300" />
|
||||
<div className="w-4 shrink-0 h-px bg-teal-300" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-3 flex flex-col gap-3">
|
||||
<ActivityRootNote eventId={repostId} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
import { Note, useEvent } from "@lume/ark";
|
||||
|
||||
export function ActivityRootNote({ eventId }: { eventId: string }) {
|
||||
const { isLoading, isError, data } = useEvent(eventId);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="relative flex gap-3">
|
||||
<div className="relative flex-1 rounded-md bg-neutral-200 px-2 py-2 dark:bg-neutral-800">
|
||||
<div className="h-4 w-full animate-pulse bg-neutral-300 dark:bg-neutral-700" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="relative flex gap-3">
|
||||
<div className="relative flex-1 rounded-md bg-neutral-200 px-2 py-2 dark:bg-neutral-800">
|
||||
Failed to fetch event
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Note.Provider event={data}>
|
||||
<Note.Root>
|
||||
<div className="flex items-center justify-between px-3 h-14">
|
||||
<Note.User className="flex-1 pr-1" />
|
||||
</div>
|
||||
<Note.Content className="min-w-0 px-3" />
|
||||
<div className="flex items-center justify-between px-3 h-14">
|
||||
<Note.Pin />
|
||||
<div className="inline-flex items-center gap-10" />
|
||||
</div>
|
||||
</Note.Root>
|
||||
</Note.Provider>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import { compactNumber, formatCreatedAt } from "@lume/utils";
|
||||
import { NDKEvent, zapInvoiceFromEvent } from "@nostr-dev-kit/ndk";
|
||||
import { User } from "../user";
|
||||
import { ActivityRootNote } from "./rootNote";
|
||||
|
||||
export function ZapActivity({ event }: { event: NDKEvent }) {
|
||||
const zapEventId = event.tags.find((tag) => tag[0] === "e")[1];
|
||||
const invoice = zapInvoiceFromEvent(event);
|
||||
|
||||
return (
|
||||
<div className="h-full pb-3 flex flex-col justify-between">
|
||||
<div className="h-14 border-b border-neutral-100 dark:border-neutral-900 flex flex-col items-center justify-center px-3">
|
||||
<h3 className="text-center font-semibold leading-tight">Zap</h3>
|
||||
<p className="text-sm text-blue-500 font-medium leading-tight">
|
||||
@ Someone love your note
|
||||
</p>
|
||||
</div>
|
||||
<div className="px-3">
|
||||
<div className="flex flex-col gap-3">
|
||||
<User
|
||||
pubkey={event.pubkey}
|
||||
variant="notify2"
|
||||
subtext={`${compactNumber.format(invoice.amount)} sats`}
|
||||
/>
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-teal-500 font-medium">Zapped</p>
|
||||
<div className="flex-1 h-px bg-teal-300" />
|
||||
<div className="w-4 shrink-0 h-px bg-teal-300" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-3 flex flex-col gap-3">
|
||||
<ActivityRootNote eventId={zapEventId} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -20,7 +20,7 @@ export function SettingsLayout() {
|
||||
twMerge(
|
||||
"flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900",
|
||||
isActive
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900"
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900"
|
||||
: "",
|
||||
)
|
||||
}
|
||||
@@ -34,7 +34,7 @@ export function SettingsLayout() {
|
||||
twMerge(
|
||||
"flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900",
|
||||
isActive
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900"
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900"
|
||||
: "",
|
||||
)
|
||||
}
|
||||
@@ -48,7 +48,7 @@ export function SettingsLayout() {
|
||||
twMerge(
|
||||
"flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900",
|
||||
isActive
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900"
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900"
|
||||
: "",
|
||||
)
|
||||
}
|
||||
@@ -62,7 +62,7 @@ export function SettingsLayout() {
|
||||
twMerge(
|
||||
"flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900",
|
||||
isActive
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900"
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900"
|
||||
: "",
|
||||
)
|
||||
}
|
||||
@@ -76,7 +76,7 @@ export function SettingsLayout() {
|
||||
twMerge(
|
||||
"flex w-20 shrink-0 flex-col items-center justify-center rounded-lg px-2 py-2 text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900",
|
||||
isActive
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-900 dark:hover:bg-neutral-900"
|
||||
? "bg-neutral-100 text-blue-500 hover:bg-neutral-100 dark:bg-neutral-950 dark:hover:bg-neutral-900"
|
||||
: "",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export function OnboardingModal() {
|
||||
return (
|
||||
<Dialog.Root open={onboarding}>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/20 backdrop-blur-xl dark:bg-white/20" />
|
||||
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/10 backdrop-blur-sm dark:bg-white/10" />
|
||||
<Dialog.Content className="fixed inset-0 z-50 flex items-center justify-center min-h-full">
|
||||
<div className="relative w-full max-w-lg bg-white h-[500px] rounded-xl dark:bg-black overflow-hidden">
|
||||
<OnboardingRouter />
|
||||
|
||||
Reference in New Issue
Block a user