From 72870bb1316adbb9e323f99876e54b1d827a0d03 Mon Sep 17 00:00:00 2001 From: reya Date: Sat, 13 Jan 2024 17:12:44 +0700 Subject: [PATCH] feat: add activity screen --- apps/desktop/src/router.tsx | 18 ++++ .../activty/components/activityRepost.tsx | 28 ++++++ .../activty/components/activityText.tsx | 28 ++++++ .../routes/activty/components/activityZap.tsx | 33 +++++++ .../src/routes/activty/components/list.tsx | 95 +++++++++++++++++++ .../routes/activty/components/rootNote.tsx | 40 ++++++++ .../activty/components/singleRepost.tsx | 33 +++++++ .../routes/activty/components/singleText.tsx | 53 +++++++++++ .../routes/activty/components/singleZap.tsx | 39 ++++++++ apps/desktop/src/routes/activty/id.tsx | 27 ++++++ apps/desktop/src/routes/activty/index.tsx | 18 ++++ .../ark/src/components/note/mentions/note.tsx | 2 +- packages/lume-column-activity/package.json | 26 +++++ packages/lume-column-activity/src/index.tsx | 0 .../lume-column-activity/tailwind.config.js | 8 ++ packages/lume-column-activity/tsconfig.json | 8 ++ packages/lume-column-timeline/src/home.tsx | 1 + packages/ui/src/layouts/app.tsx | 2 - packages/ui/src/navigation.tsx | 55 +++++------ pnpm-lock.yaml | 52 ++++++++++ 20 files changed, 530 insertions(+), 36 deletions(-) create mode 100644 apps/desktop/src/routes/activty/components/activityRepost.tsx create mode 100644 apps/desktop/src/routes/activty/components/activityText.tsx create mode 100644 apps/desktop/src/routes/activty/components/activityZap.tsx create mode 100644 apps/desktop/src/routes/activty/components/list.tsx create mode 100644 apps/desktop/src/routes/activty/components/rootNote.tsx create mode 100644 apps/desktop/src/routes/activty/components/singleRepost.tsx create mode 100644 apps/desktop/src/routes/activty/components/singleText.tsx create mode 100644 apps/desktop/src/routes/activty/components/singleZap.tsx create mode 100644 apps/desktop/src/routes/activty/id.tsx create mode 100644 apps/desktop/src/routes/activty/index.tsx create mode 100644 packages/lume-column-activity/package.json create mode 100644 packages/lume-column-activity/src/index.tsx create mode 100644 packages/lume-column-activity/tailwind.config.js create mode 100644 packages/lume-column-activity/tsconfig.json diff --git a/apps/desktop/src/router.tsx b/apps/desktop/src/router.tsx index 86dbde34..e2f4c144 100644 --- a/apps/desktop/src/router.tsx +++ b/apps/desktop/src/router.tsx @@ -137,6 +137,24 @@ export default function Router() { }, ], }, + { + path: "activity", + async lazy() { + const { ActivityScreen } = await import("./routes/activty"); + return { Component: ActivityScreen }; + }, + children: [ + { + path: ":id", + async lazy() { + const { ActivityIdScreen } = await import( + "./routes/activty/id" + ); + return { Component: ActivityIdScreen }; + }, + }, + ], + }, { path: "depot", children: [ diff --git a/apps/desktop/src/routes/activty/components/activityRepost.tsx b/apps/desktop/src/routes/activty/components/activityRepost.tsx new file mode 100644 index 00000000..3386ba6d --- /dev/null +++ b/apps/desktop/src/routes/activty/components/activityRepost.tsx @@ -0,0 +1,28 @@ +import { User } from "@lume/ark"; +import { NDKEvent } from "@nostr-dev-kit/ndk"; +import { Link } from "react-router-dom"; + +export function ActivityRepost({ event }: { event: NDKEvent }) { + return ( + + + +
+ +
+ +

reposted

+
+
+ +
+
+ + ); +} diff --git a/apps/desktop/src/routes/activty/components/activityText.tsx b/apps/desktop/src/routes/activty/components/activityText.tsx new file mode 100644 index 00000000..8967353c --- /dev/null +++ b/apps/desktop/src/routes/activty/components/activityText.tsx @@ -0,0 +1,28 @@ +import { User } from "@lume/ark"; +import { NDKEvent } from "@nostr-dev-kit/ndk"; +import { Link } from "react-router-dom"; + +export function ActivityText({ event }: { event: NDKEvent }) { + return ( + + + +
+ +
+ +

mention you

+
+
+ +
+
+ + ); +} diff --git a/apps/desktop/src/routes/activty/components/activityZap.tsx b/apps/desktop/src/routes/activty/components/activityZap.tsx new file mode 100644 index 00000000..85c9571c --- /dev/null +++ b/apps/desktop/src/routes/activty/components/activityZap.tsx @@ -0,0 +1,33 @@ +import { User } from "@lume/ark"; +import { compactNumber } from "@lume/utils"; +import { NDKEvent, zapInvoiceFromEvent } from "@nostr-dev-kit/ndk"; +import { Link } from "react-router-dom"; + +export function ActivityZap({ event }: { event: NDKEvent }) { + const invoice = zapInvoiceFromEvent(event); + + return ( + + + +
+ +
+ +

+ zapped {compactNumber.format(invoice.amount)} sats +

+
+
+ +
+
+ + ); +} diff --git a/apps/desktop/src/routes/activty/components/list.tsx b/apps/desktop/src/routes/activty/components/list.tsx new file mode 100644 index 00000000..8640264d --- /dev/null +++ b/apps/desktop/src/routes/activty/components/list.tsx @@ -0,0 +1,95 @@ +import { useArk } from "@lume/ark"; +import { LoaderIcon } from "@lume/icons"; +import { FETCH_LIMIT } from "@lume/utils"; +import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; +import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query"; +import { useCallback, useMemo } from "react"; +import { ActivityRepost } from "./activityRepost"; +import { ActivityText } from "./activityText"; +import { ActivityZap } from "./activityZap"; + +export function ActivityList() { + const ark = useArk(); + const queryClient = useQueryClient(); + + const { data, hasNextPage, isLoading, 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": [ + "126103bfddc8df256b6e0abfd7f3797c80dcc4ea88f7c2f87dd4104220b4d65f", + ark.account.pubkey, + ], + }, + limit: 200, + pageParam, + signal, + }); + + return events; + }, + getNextPageParam: (lastPage) => { + const lastEvent = lastPage.at(-1); + if (!lastEvent) return; + return lastEvent.created_at - 1; + }, + initialData: () => { + const queryCacheData = queryClient.getQueryState(["activity"]) + ?.data as NDKEvent[]; + if (queryCacheData) { + return { + pageParams: [undefined, 1], + pages: [queryCacheData], + }; + } + }, + + staleTime: 360 * 1000, + refetchOnWindowFocus: false, + refetchOnMount: false, + }); + + const allEvents = useMemo( + () => (data ? data.pages.flatMap((page) => page) : []), + [data], + ); + + const renderEvenKind = useCallback( + (event: NDKEvent) => { + if (event.pubkey === ark.account.pubkey) return null; + switch (event.kind) { + case NDKKind.Text: + return ; + case NDKKind.Repost: + return ; + case NDKKind.Zap: + return ; + default: + return ; + } + }, + [data], + ); + + return ( +
+ {isLoading ? ( +
+ +
+ ) : ( + allEvents.map((event) => renderEvenKind(event)) + )} +
+ ); +} diff --git a/apps/desktop/src/routes/activty/components/rootNote.tsx b/apps/desktop/src/routes/activty/components/rootNote.tsx new file mode 100644 index 00000000..5ba7e589 --- /dev/null +++ b/apps/desktop/src/routes/activty/components/rootNote.tsx @@ -0,0 +1,40 @@ +import { Note, useEvent } from "@lume/ark"; + +export function ActivityRootNote({ eventId }: { eventId: string }) { + const { isLoading, isError, data } = useEvent(eventId); + + if (isLoading) { + return ( +
+
+
+
+
+ ); + } + + if (isError) { + return ( +
+
+ Failed to fetch event +
+
+ ); + } + + return ( + + +
+ +
+ +
+ +
+
+ + + ); +} diff --git a/apps/desktop/src/routes/activty/components/singleRepost.tsx b/apps/desktop/src/routes/activty/components/singleRepost.tsx new file mode 100644 index 00000000..f975d3aa --- /dev/null +++ b/apps/desktop/src/routes/activty/components/singleRepost.tsx @@ -0,0 +1,33 @@ +import { User } from "@lume/ark"; +import { NDKEvent } from "@nostr-dev-kit/ndk"; +import { ActivityRootNote } from "./rootNote"; + +export function ActivitySingleRepost({ event }: { event: NDKEvent }) { + const repostId = event.tags.find((el) => el[0] === "e")[1]; + + return ( +
+
+

Boost

+

+ @ Someone has reposted to your note +

+
+
+
+ + + + + +
+
+

Reposted

+
+
+ +
+
+
+ ); +} diff --git a/apps/desktop/src/routes/activty/components/singleText.tsx b/apps/desktop/src/routes/activty/components/singleText.tsx new file mode 100644 index 00000000..328f7670 --- /dev/null +++ b/apps/desktop/src/routes/activty/components/singleText.tsx @@ -0,0 +1,53 @@ +import { Note, useArk } from "@lume/ark"; +import { NDKEvent } from "@nostr-dev-kit/ndk"; +import { ActivityRootNote } from "./rootNote"; + +export function ActivitySingleText({ event }: { event: NDKEvent }) { + const ark = useArk(); + const thread = ark.getEventThread({ + content: event.content, + tags: event.tags, + }); + + return ( +
+
+

+ Conversation +

+

+ @ Someone has replied to your note +

+
+
+
+ {thread ? ( +
+ + +
+ ) : null} +
+
+

New reply

+
+
+
+ + +
+ +
+ +
+ +
+
+ + +
+
+
+
+ ); +} diff --git a/apps/desktop/src/routes/activty/components/singleZap.tsx b/apps/desktop/src/routes/activty/components/singleZap.tsx new file mode 100644 index 00000000..59e25c42 --- /dev/null +++ b/apps/desktop/src/routes/activty/components/singleZap.tsx @@ -0,0 +1,39 @@ +import { User } from "@lume/ark"; +import { compactNumber } from "@lume/utils"; +import { NDKEvent, zapInvoiceFromEvent } from "@nostr-dev-kit/ndk"; +import { ActivityRootNote } from "./rootNote"; + +export function ActivitySingleZap({ event }: { event: NDKEvent }) { + const zapEventId = event.tags.find((el) => el[0] === "e")[1]; + const invoice = zapInvoiceFromEvent(event); + + return ( +
+
+

+ Conversation +

+

+ @ Someone has replied to your note +

+
+
+
+ + + + + +
+
+

+ Zap you {compactNumber.format(invoice.amount)} sats for +

+
+
+ +
+
+
+ ); +} diff --git a/apps/desktop/src/routes/activty/id.tsx b/apps/desktop/src/routes/activty/id.tsx new file mode 100644 index 00000000..53350b55 --- /dev/null +++ b/apps/desktop/src/routes/activty/id.tsx @@ -0,0 +1,27 @@ +import { useEvent } from "@lume/ark"; +import { LoaderIcon } from "@lume/icons"; +import { NDKKind } from "@nostr-dev-kit/ndk"; +import { useParams } from "react-router-dom"; +import { ActivitySingleRepost } from "./components/singleRepost"; +import { ActivitySingleText } from "./components/singleText"; +import { ActivitySingleZap } from "./components/singleZap"; + +export function ActivityIdScreen() { + const { id } = useParams(); + const { isLoading, data } = useEvent(id); + + if (isLoading || !data) { + return ( +
+ +
+ ); + } + + if (data.kind === NDKKind.Text) return ; + if (data.kind === NDKKind.Zap) return ; + if (data.kind === NDKKind.Repost) + return ; + + return ; +} diff --git a/apps/desktop/src/routes/activty/index.tsx b/apps/desktop/src/routes/activty/index.tsx new file mode 100644 index 00000000..a204cd13 --- /dev/null +++ b/apps/desktop/src/routes/activty/index.tsx @@ -0,0 +1,18 @@ +import { Outlet } from "react-router-dom"; +import { ActivityList } from "./components/list"; + +export function ActivityScreen() { + return ( +
+
+
+ Activity +
+ +
+
+ +
+
+ ); +} diff --git a/packages/ark/src/components/note/mentions/note.tsx b/packages/ark/src/components/note/mentions/note.tsx index 49de8d79..72b9bf94 100644 --- a/packages/ark/src/components/note/mentions/note.tsx +++ b/packages/ark/src/components/note/mentions/note.tsx @@ -34,7 +34,7 @@ export const MentionNote = memo(function MentionNote({ return ( - + diff --git a/packages/lume-column-activity/package.json b/packages/lume-column-activity/package.json new file mode 100644 index 00000000..f2970ea7 --- /dev/null +++ b/packages/lume-column-activity/package.json @@ -0,0 +1,26 @@ +{ + "name": "@columns/activity", + "version": "0.0.0", + "private": true, + "main": "./src/index.tsx", + "dependencies": { + "@lume/ark": "workspace:^", + "@lume/icons": "workspace:^", + "@lume/ui": "workspace:^", + "@lume/utils": "workspace:^", + "@nostr-dev-kit/ndk": "^2.3.2", + "@tanstack/react-query": "^5.17.9", + "react": "^18.2.0", + "react-router-dom": "^6.21.2", + "sonner": "^1.3.1", + "virtua": "^0.20.4" + }, + "devDependencies": { + "@lume/tailwindcss": "workspace:^", + "@lume/tsconfig": "workspace:^", + "@lume/types": "workspace:^", + "@types/react": "^18.2.47", + "tailwind": "^4.0.0", + "typescript": "^5.3.3" + } +} diff --git a/packages/lume-column-activity/src/index.tsx b/packages/lume-column-activity/src/index.tsx new file mode 100644 index 00000000..e69de29b diff --git a/packages/lume-column-activity/tailwind.config.js b/packages/lume-column-activity/tailwind.config.js new file mode 100644 index 00000000..49c48c7a --- /dev/null +++ b/packages/lume-column-activity/tailwind.config.js @@ -0,0 +1,8 @@ +import sharedConfig from "@lume/tailwindcss"; + +const config = { + content: ["./src/**/*.{js,ts,jsx,tsx}"], + presets: [sharedConfig], +}; + +export default config; diff --git a/packages/lume-column-activity/tsconfig.json b/packages/lume-column-activity/tsconfig.json new file mode 100644 index 00000000..34a32891 --- /dev/null +++ b/packages/lume-column-activity/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@lume/tsconfig/base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/lume-column-timeline/src/home.tsx b/packages/lume-column-timeline/src/home.tsx index 20d2d1ab..9e10fcf1 100644 --- a/packages/lume-column-timeline/src/home.tsx +++ b/packages/lume-column-timeline/src/home.tsx @@ -59,6 +59,7 @@ export function HomeRoute({ colKey }: { colKey: string }) { }, staleTime: 120 * 1000, refetchOnWindowFocus: false, + refetchOnMount: false, }); const allEvents = useMemo( diff --git a/packages/ui/src/layouts/app.tsx b/packages/ui/src/layouts/app.tsx index cde1ea4f..584101d1 100644 --- a/packages/ui/src/layouts/app.tsx +++ b/packages/ui/src/layouts/app.tsx @@ -1,7 +1,6 @@ import { type Platform } from "@tauri-apps/plugin-os"; import { Outlet } from "react-router-dom"; import { twMerge } from "tailwind-merge"; -import { Activity } from "../activity/column"; import { Editor } from "../editor/column"; import { Navigation } from "../navigation"; import { WindowTitleBar } from "../titlebar"; @@ -22,7 +21,6 @@ export function AppLayout({ platform }: { platform: Platform }) {
-
diff --git a/packages/ui/src/navigation.tsx b/packages/ui/src/navigation.tsx index 9840c34a..28c146ad 100644 --- a/packages/ui/src/navigation.tsx +++ b/packages/ui/src/navigation.tsx @@ -3,17 +3,13 @@ import { BellIcon, ComposeFilledIcon, ComposeIcon, - DepotFilledIcon, - DepotIcon, HomeFilledIcon, HomeIcon, NwcIcon, - RelayFilledIcon, - RelayIcon, SettingsFilledIcon, SettingsIcon, } from "@lume/icons"; -import { activityAtom, cn, editorAtom } from "@lume/utils"; +import { cn, editorAtom } from "@lume/utils"; import { useAtom } from "jotai"; import { useHotkeys } from "react-hotkeys-hook"; import { NavLink } from "react-router-dom"; @@ -21,8 +17,6 @@ import { ActiveAccount } from "./account/active"; export function Navigation() { const [isEditorOpen, setIsEditorOpen] = useAtom(editorAtom); - const [isActvityOpen, setIsActvityOpen] = useAtom(activityAtom); - useHotkeys("meta+n", () => setIsEditorOpen((state) => !state), []); return ( @@ -32,10 +26,7 @@ export function Navigation() {
)} - - + {({ isActive }) => ( +
+ {isActive ? ( + + ) : ( + + )} +
+ )} +