diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 35e48fec..c1d06888 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -10,6 +10,7 @@ "@columns/antenas": "workspace:^", "@columns/default": "workspace:^", "@columns/foryou": "workspace:^", + "@columns/global": "workspace:^", "@columns/group": "workspace:^", "@columns/hashtag": "workspace:^", "@columns/thread": "workspace:^", diff --git a/apps/desktop/public/columns/global.jpg b/apps/desktop/public/columns/global.jpg new file mode 100644 index 00000000..2288bfaf Binary files /dev/null and b/apps/desktop/public/columns/global.jpg differ diff --git a/apps/desktop/public/columns/global@2x.jpg b/apps/desktop/public/columns/global@2x.jpg new file mode 100644 index 00000000..e079bfe1 Binary files /dev/null and b/apps/desktop/public/columns/global@2x.jpg differ diff --git a/apps/desktop/src/routes/home/index.tsx b/apps/desktop/src/routes/home/index.tsx index b6e75264..2508bb94 100644 --- a/apps/desktop/src/routes/home/index.tsx +++ b/apps/desktop/src/routes/home/index.tsx @@ -1,6 +1,7 @@ import { Antenas } from "@columns/antenas"; import { Default } from "@columns/default"; import { ForYou } from "@columns/foryou"; +import { Global } from "@columns/global"; import { Group } from "@columns/group"; import { Hashtag } from "@columns/hashtag"; import { Thread } from "@columns/thread"; @@ -46,6 +47,8 @@ export function HomeScreen() { return ; case COL_TYPES.antenas: return ; + case COL_TYPES.global: + return ; case COL_TYPES.trendingNotes: return ; default: diff --git a/packages/lume-column-default/src/index.tsx b/packages/lume-column-default/src/index.tsx index bd3be20e..d02dcae4 100644 --- a/packages/lume-column-default/src/index.tsx +++ b/packages/lume-column-default/src/index.tsx @@ -115,6 +115,39 @@ export function Default({ column }: { column: IColumn }) { +
+
+ global +
+
+
+

Global

+

+ All things around the world +

+
+ +
+
); diff --git a/packages/lume-column-global/package.json b/packages/lume-column-global/package.json new file mode 100644 index 00000000..ecb36ae9 --- /dev/null +++ b/packages/lume-column-global/package.json @@ -0,0 +1,26 @@ +{ + "name": "@columns/global", + "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.3", + "@tanstack/react-query": "^5.17.19", + "react": "^18.2.0", + "react-router-dom": "^6.21.3", + "sonner": "^1.4.0", + "virtua": "^0.23.0" + }, + "devDependencies": { + "@lume/tailwindcss": "workspace:^", + "@lume/tsconfig": "workspace:^", + "@lume/types": "workspace:^", + "@types/react": "^18.2.48", + "tailwind": "^4.0.0", + "typescript": "^5.3.3" + } +} diff --git a/packages/lume-column-global/src/home.tsx b/packages/lume-column-global/src/home.tsx new file mode 100644 index 00000000..a5ff74e1 --- /dev/null +++ b/packages/lume-column-global/src/home.tsx @@ -0,0 +1,126 @@ +import { RepostNote, TextNote, useArk } from "@lume/ark"; +import { ArrowRightCircleIcon, LoaderIcon, SearchIcon } from "@lume/icons"; +import { EmptyFeed } from "@lume/ui"; +import { FETCH_LIMIT } from "@lume/utils"; +import { type NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; +import { useInfiniteQuery } from "@tanstack/react-query"; +import { useEffect, useMemo, useRef } from "react"; +import { Link } from "react-router-dom"; +import { CacheSnapshot, VList, VListHandle } from "virtua"; + +export function HomeRoute({ colKey }: { colKey: string }) { + const ark = useArk(); + const ref = useRef(); + const cacheKey = `${colKey}-vlist`; + + const [offset, cache] = useMemo(() => { + const serialized = sessionStorage.getItem(cacheKey); + if (!serialized) return []; + return JSON.parse(serialized) as [number, CacheSnapshot]; + }, []); + + const { data, hasNextPage, isLoading, isFetchingNextPage, fetchNextPage } = + useInfiniteQuery({ + queryKey: [colKey], + initialPageParam: 0, + queryFn: async ({ + signal, + pageParam, + }: { + signal: AbortSignal; + pageParam: number; + }) => { + if (!ark.account.contacts.length) return []; + + const events = await ark.getInfiniteEvents({ + filter: { + kinds: [NDKKind.Text, NDKKind.Repost], + }, + limit: FETCH_LIMIT, + pageParam, + signal, + }); + + return events; + }, + getNextPageParam: (lastPage) => { + const lastEvent = lastPage.at(-1); + if (!lastEvent) return; + return lastEvent.created_at - 1; + }, + select: (data) => data?.pages.flatMap((page) => page), + refetchOnWindowFocus: false, + refetchOnMount: false, + }); + + const renderItem = (event: NDKEvent) => { + switch (event.kind) { + case NDKKind.Text: + return ; + case NDKKind.Repost: + return ; + default: + return ; + } + }; + + useEffect(() => { + if (!ref.current) return; + const handle = ref.current; + + if (offset) { + handle.scrollTo(offset); + } + + return () => { + sessionStorage.setItem( + cacheKey, + JSON.stringify([handle.scrollOffset, handle.cache]), + ); + }; + }, []); + + return ( +
+ + {isLoading ? ( +
+ +
+ ) : !data.length ? ( +
+ + + + Find accounts to follow + +
+ ) : ( + data.map((item) => renderItem(item)) + )} +
+ {hasNextPage ? ( + + ) : null} +
+
+
+ ); +} diff --git a/packages/lume-column-global/src/index.tsx b/packages/lume-column-global/src/index.tsx new file mode 100644 index 00000000..2efddb8f --- /dev/null +++ b/packages/lume-column-global/src/index.tsx @@ -0,0 +1,19 @@ +import { Column } from "@lume/ark"; +import { IColumn } from "@lume/types"; +import { EventRoute, UserRoute } from "@lume/ui"; +import { HomeRoute } from "./home"; + +export function Global({ column }: { column: IColumn }) { + const colKey = `global-${column.id}`; + + return ( + + + + } /> + } /> + } /> + + + ); +} diff --git a/packages/lume-column-global/tailwind.config.js b/packages/lume-column-global/tailwind.config.js new file mode 100644 index 00000000..49c48c7a --- /dev/null +++ b/packages/lume-column-global/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-global/tsconfig.json b/packages/lume-column-global/tsconfig.json new file mode 100644 index 00000000..34a32891 --- /dev/null +++ b/packages/lume-column-global/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 5a7ae8f0..cacb30e9 100644 --- a/packages/lume-column-timeline/src/home.tsx +++ b/packages/lume-column-timeline/src/home.tsx @@ -3,7 +3,7 @@ import { ArrowRightCircleIcon, LoaderIcon, SearchIcon } from "@lume/icons"; import { EmptyFeed } from "@lume/ui"; import { FETCH_LIMIT } from "@lume/utils"; import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; -import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query"; +import { useInfiniteQuery } from "@tanstack/react-query"; import { useEffect, useMemo, useRef } from "react"; import { Link } from "react-router-dom"; import { CacheSnapshot, VList, VListHandle } from "virtua"; @@ -12,7 +12,6 @@ export function HomeRoute({ colKey }: { colKey: string }) { const ark = useArk(); const ref = useRef(); const cacheKey = `${colKey}-vlist`; - const queryClient = useQueryClient(); const [offset, cache] = useMemo(() => { const serialized = sessionStorage.getItem(cacheKey); diff --git a/packages/utils/src/constants.ts b/packages/utils/src/constants.ts index 70dfd3d0..9aa3c539 100644 --- a/packages/utils/src/constants.ts +++ b/packages/utils/src/constants.ts @@ -57,6 +57,7 @@ export const COL_TYPES = { hashtag: 3, group: 4, antenas: 5, + global: 6, trendingNotes: 9000, foryou: 9998, newsfeed: 9999, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 392e407d..44413422 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -72,6 +72,9 @@ importers: '@columns/foryou': specifier: workspace:^ version: link:../../packages/lume-column-foryou + '@columns/global': + specifier: workspace:^ + version: link:../../packages/lume-column-global '@columns/group': specifier: workspace:^ version: link:../../packages/lume-column-group @@ -593,6 +596,58 @@ importers: specifier: ^5.3.3 version: 5.3.3 + packages/lume-column-global: + dependencies: + '@lume/ark': + specifier: workspace:^ + version: link:../ark + '@lume/icons': + specifier: workspace:^ + version: link:../icons + '@lume/ui': + specifier: workspace:^ + version: link:../ui + '@lume/utils': + specifier: workspace:^ + version: link:../utils + '@nostr-dev-kit/ndk': + specifier: ^2.3.3 + version: 2.3.3(typescript@5.3.3) + '@tanstack/react-query': + specifier: ^5.17.19 + version: 5.17.19(react@18.2.0) + react: + specifier: ^18.2.0 + version: 18.2.0 + react-router-dom: + specifier: ^6.21.3 + version: 6.21.3(react-dom@18.2.0)(react@18.2.0) + sonner: + specifier: ^1.4.0 + version: 1.4.0(react-dom@18.2.0)(react@18.2.0) + virtua: + specifier: ^0.23.0 + version: 0.23.0(react-dom@18.2.0)(react@18.2.0) + devDependencies: + '@lume/tailwindcss': + specifier: workspace:^ + version: link:../tailwindcss + '@lume/tsconfig': + specifier: workspace:^ + version: link:../tsconfig + '@lume/types': + specifier: workspace:^ + version: link:../types + '@types/react': + specifier: ^18.2.48 + version: 18.2.48 + tailwind: + specifier: ^4.0.0 + version: 4.0.0 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + packages/lume-column-group: dependencies: '@lume/ark': @@ -697,6 +752,58 @@ importers: specifier: ^5.3.3 version: 5.3.3 + packages/lume-column-relay: + dependencies: + '@lume/ark': + specifier: workspace:^ + version: link:../ark + '@lume/icons': + specifier: workspace:^ + version: link:../icons + '@lume/ui': + specifier: workspace:^ + version: link:../ui + '@lume/utils': + specifier: workspace:^ + version: link:../utils + '@nostr-dev-kit/ndk': + specifier: ^2.3.3 + version: 2.3.3(typescript@5.3.3) + '@tanstack/react-query': + specifier: ^5.17.19 + version: 5.17.19(react@18.2.0) + react: + specifier: ^18.2.0 + version: 18.2.0 + react-router-dom: + specifier: ^6.21.3 + version: 6.21.3(react-dom@18.2.0)(react@18.2.0) + sonner: + specifier: ^1.4.0 + version: 1.4.0(react-dom@18.2.0)(react@18.2.0) + virtua: + specifier: ^0.23.0 + version: 0.23.0(react-dom@18.2.0)(react@18.2.0) + devDependencies: + '@lume/tailwindcss': + specifier: workspace:^ + version: link:../tailwindcss + '@lume/tsconfig': + specifier: workspace:^ + version: link:../tsconfig + '@lume/types': + specifier: workspace:^ + version: link:../types + '@types/react': + specifier: ^18.2.48 + version: 18.2.48 + tailwind: + specifier: ^4.0.0 + version: 4.0.0 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + packages/lume-column-thread: dependencies: '@lume/ark':