diff --git a/package.json b/package.json index e1cf2ab9..c43575c1 100644 --- a/package.json +++ b/package.json @@ -15,13 +15,11 @@ "dependencies": { "@floating-ui/react": "^0.23.1", "@headlessui/react": "^1.7.15", - "@tanstack/react-query": "^4.29.12", "@tanstack/react-virtual": "3.0.0-beta.54", "@tauri-apps/api": "^1.3.0", "@vidstack/react": "^0.4.5", "dayjs": "^1.11.8", "destr": "^1.2.2", - "embla-carousel-react": "8.0.0-rc06", "immer": "^10.0.2", "light-bolt11-decoder": "^3.0.0", "nostr-relaypool": "^0.6.28", @@ -49,7 +47,7 @@ "@types/react": "^18.2.8", "@types/react-dom": "^18.2.4", "@types/youtube-player": "^5.5.7", - "@vitejs/plugin-react-swc": "^3.3.1", + "@vitejs/plugin-react-swc": "^3.3.2", "autoprefixer": "^10.4.14", "cross-env": "^7.0.3", "csstype": "^3.1.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f7fc226c..74a928f3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,9 +7,6 @@ dependencies: '@headlessui/react': specifier: ^1.7.15 version: 1.7.15(react-dom@18.2.0)(react@18.2.0) - '@tanstack/react-query': - specifier: ^4.29.12 - version: 4.29.12(react-dom@18.2.0)(react@18.2.0) '@tanstack/react-virtual': specifier: 3.0.0-beta.54 version: 3.0.0-beta.54(react@18.2.0) @@ -25,9 +22,6 @@ dependencies: destr: specifier: ^1.2.2 version: 1.2.2 - embla-carousel-react: - specifier: 8.0.0-rc06 - version: 8.0.0-rc06(react@18.2.0) immer: specifier: ^10.0.2 version: 10.0.2 @@ -106,8 +100,8 @@ devDependencies: specifier: ^5.5.7 version: 5.5.7 '@vitejs/plugin-react-swc': - specifier: ^3.3.1 - version: 3.3.1(vite@4.3.9) + specifier: ^3.3.2 + version: 3.3.2(vite@4.3.9) autoprefixer: specifier: ^10.4.14 version: 10.4.14(postcss@8.4.24) @@ -710,28 +704,6 @@ packages: tailwindcss: 3.3.2 dev: true - /@tanstack/query-core@4.29.11: - resolution: {integrity: sha512-8C+hF6SFAb/TlFZyS9FItgNwrw4PMa7YeX+KQYe2ZAiEz6uzg6yIr+QBzPkUwZ/L0bXvGd1sufTm3wotoz+GwQ==} - dev: false - - /@tanstack/react-query@4.29.12(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-zhcN6+zF6cxprxhTHQajHGlvxgK8npnp9uLe9yaWhGc6sYcPWXzyO4raL4HomUzQOPzu3jLvkriJQ7BOrDM8vA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-native: '*' - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - dependencies: - '@tanstack/query-core': 4.29.11 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - use-sync-external-store: 1.2.0(react@18.2.0) - dev: false - /@tanstack/react-virtual@3.0.0-beta.54(react@18.2.0): resolution: {integrity: sha512-D1mDMf4UPbrtHRZZriCly5bXTBMhylslm4dhcHqTtDJ6brQcgGmk8YD9JdWBGWfGSWPKoh2x1H3e7eh+hgPXtQ==} peerDependencies: @@ -925,8 +897,8 @@ packages: vidstack: 0.4.5 dev: false - /@vitejs/plugin-react-swc@3.3.1(vite@4.3.9): - resolution: {integrity: sha512-ZoYjGxMniXP7X+5ry/W1tpY7w0OeLUEsBF5RHFPmAhpgwwNWie8OF4056MRXRi9QgvYYoZPDzdOXGK3wlCoTfQ==} + /@vitejs/plugin-react-swc@3.3.2(vite@4.3.9): + resolution: {integrity: sha512-VJFWY5sfoZerQRvJrh518h3AcQt6f/yTuWn4/TRB+dqmYU0NX1qz7qM5Wfd+gOQqUzQW4gxKqKN3KpE/P3+zrA==} peerDependencies: vite: ^4 dependencies: @@ -1015,7 +987,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.21.7 - caniuse-lite: 1.0.30001492 + caniuse-lite: 1.0.30001495 fraction.js: 4.2.0 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -1055,8 +1027,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001492 - electron-to-chromium: 1.4.419 + caniuse-lite: 1.0.30001495 + electron-to-chromium: 1.4.421 node-releases: 2.0.12 update-browserslist-db: 1.0.11(browserslist@4.21.7) dev: true @@ -1076,8 +1048,8 @@ packages: engines: {node: '>= 6'} dev: true - /caniuse-lite@1.0.30001492: - resolution: {integrity: sha512-2efF8SAZwgAX1FJr87KWhvuJxnGJKOnctQa8xLOskAXNXq8oiuqgl6u1kk3fFpsp3GgvzlRjiK1sl63hNtFADw==} + /caniuse-lite@1.0.30001495: + resolution: {integrity: sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg==} dev: true /ccount@2.0.1: @@ -1255,32 +1227,10 @@ packages: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} dev: true - /electron-to-chromium@1.4.419: - resolution: {integrity: sha512-jdie3RiEgygvDTyS2sgjq71B36q2cDSBfPlwzUyuOrfYTNoYWyBxxjGJV/HAu3A2hB0Y+HesvCVkVAFoCKwCSw==} + /electron-to-chromium@1.4.421: + resolution: {integrity: sha512-wZOyn3s/aQOtLGAwXMZfteQPN68kgls2wDAnYOA8kCjBvKVrW5RwmWVspxJYTqrcN7Y263XJVsC66VCIGzDO3g==} dev: true - /embla-carousel-react@8.0.0-rc06(react@18.2.0): - resolution: {integrity: sha512-aZfVqTptCA6uq3VLPFXLKGSkxVdoiNjQa1sjVOJc+9FL6pbwImaVR0oL7jA0kfW9l67XoQOEOq1gRiImbYr/Ew==} - peerDependencies: - react: ^16.8.0 || ^17.0.1 || ^18.0.0 - dependencies: - embla-carousel: 8.0.0-rc06 - embla-carousel-reactive-utils: 8.0.0-rc06(embla-carousel@8.0.0-rc06) - react: 18.2.0 - dev: false - - /embla-carousel-reactive-utils@8.0.0-rc06(embla-carousel@8.0.0-rc06): - resolution: {integrity: sha512-AlTrD9eh8aNlDsSjnEOM15Z9f4vQVgG+ULLMSl3BfWdqYvxsbinRcVQd2Buj3KlNq3pH7pm20G63CEz1h8p/Ig==} - peerDependencies: - embla-carousel: 8.0.0-rc06 - dependencies: - embla-carousel: 8.0.0-rc06 - dev: false - - /embla-carousel@8.0.0-rc06: - resolution: {integrity: sha512-Dq27CG9OgZjKxK+JJS3uyEW4dwQ3oVkvlTHEP17idVnP8vmuhi4WtGe4UVGj8m0O2WASR4kNu4efFXkF7u6A+g==} - dev: false - /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true @@ -2508,8 +2458,8 @@ packages: resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} dev: true - /rollup@3.23.0: - resolution: {integrity: sha512-h31UlwEi7FHihLe1zbk+3Q7z1k/84rb9BSwmBSr/XjOCEaBJ2YyedQDuM0t/kfOS0IxM+vk1/zI9XxYj9V+NJQ==} + /rollup@3.23.1: + resolution: {integrity: sha512-ybRdFVHOoljGEFILHLd2g/qateqUdjE6YS41WXq4p3C/WwD3xtWxV4FYWETA1u9TeXQc5K8L8zHE5d/scOvrOQ==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: @@ -3062,7 +3012,7 @@ packages: '@types/node': 18.16.16 esbuild: 0.17.19 postcss: 8.4.24 - rollup: 3.23.0 + rollup: 3.23.1 optionalDependencies: fsevents: 2.3.2 dev: true diff --git a/src/app/note/components/skeleton.tsx b/src/app/note/components/skeleton.tsx index 0835f29e..18e836ce 100644 --- a/src/app/note/components/skeleton.tsx +++ b/src/app/note/components/skeleton.tsx @@ -1,18 +1,17 @@ export function NoteSkeleton() { return (
-
+
-
-
+
-
+
-
-
+
+
diff --git a/src/app/space/components/blocks/feed.tsx b/src/app/space/components/blocks/feed.tsx index eaaf46c7..7af0d6b6 100644 --- a/src/app/space/components/blocks/feed.tsx +++ b/src/app/space/components/blocks/feed.tsx @@ -3,47 +3,44 @@ import { NoteQuoteRepost } from "@app/note/components/quoteRepost"; import { NoteSkeleton } from "@app/note/components/skeleton"; import { CancelIcon } from "@shared/icons"; import { useActiveAccount } from "@stores/accounts"; -import { useInfiniteQuery } from "@tanstack/react-query"; import { useVirtualizer } from "@tanstack/react-virtual"; import { getNotesByAuthor } from "@utils/storage"; -import { useEffect, useRef } from "react"; +import { useEffect, useMemo, useRef } from "react"; +import useSWRInfinite from "swr/infinite"; const ITEM_PER_PAGE = 10; const TIME = Math.floor(Date.now() / 1000); +const fetcher = async ([pubkey, offset]) => + getNotesByAuthor(pubkey, TIME, ITEM_PER_PAGE, offset); + export function FeedBlock({ params }: { params: any }) { const removeBlock = useActiveAccount((state: any) => state.removeBlock); - const { - status, - data, - fetchNextPage, - hasNextPage, - isFetching, - isFetchingNextPage, - }: any = useInfiniteQuery({ - queryKey: [params.title], - queryFn: async ({ pageParam = 0 }) => { - return await getNotesByAuthor( - params.content, - TIME, - ITEM_PER_PAGE, - pageParam, - ); - }, - getNextPageParam: (lastPage) => lastPage.nextCursor, - }); + const close = () => { + removeBlock(params.id); + }; + + const getKey = (pageIndex, previousPageData) => { + if (previousPageData && !previousPageData.data) return null; + if (pageIndex === 0) return [params.content, 0]; + return [params.content, previousPageData.nextCursor]; + }; + + const { data, isLoading, size, setSize } = useSWRInfinite(getKey, fetcher); + + const notes = useMemo( + () => (data ? data.flatMap((d) => d.data) : []), + [data], + ); - const allRows = data ? data.pages.flatMap((d: { data: any }) => d.data) : []; const parentRef = useRef(); - const rowVirtualizer = useVirtualizer({ - count: hasNextPage ? allRows.length + 1 : allRows.length, + count: notes.length, getScrollElement: () => parentRef.current, estimateSize: () => 400, overscan: 2, }); - const itemsVirtualizer = rowVirtualizer.getVirtualItems(); useEffect(() => { @@ -53,18 +50,10 @@ export function FeedBlock({ params }: { params: any }) { return; } - if ( - lastItem.index >= allRows.length - 1 && - hasNextPage && - !isFetchingNextPage - ) { - fetchNextPage(); + if (lastItem.index >= notes.length - 1) { + setSize(size + 1); } - }, [fetchNextPage, allRows.length, rowVirtualizer.getVirtualItems()]); - - const close = () => { - removeBlock(params.id); - }; + }, [notes.length, rowVirtualizer.getVirtualItems()]); return (
@@ -87,7 +76,7 @@ export function FeedBlock({ params }: { params: any }) { className="scrollbar-hide flex w-full h-full flex-col justify-between gap-1.5 pt-1.5 overflow-y-auto" style={{ contain: "strict" }} > - {status === "loading" ? ( + {!data || isLoading ? (
@@ -110,7 +99,7 @@ export function FeedBlock({ params }: { params: any }) { }} > {rowVirtualizer.getVirtualItems().map((virtualRow) => { - const note = allRows[virtualRow.index]; + const note = notes[virtualRow.index]; if (note) { if (note.kind === 1) { return ( @@ -138,15 +127,6 @@ export function FeedBlock({ params }: { params: any }) {
)} -
- {isFetching && !isFetchingNextPage && ( -
-
- -
-
- )} -
); diff --git a/src/app/space/components/blocks/following.tsx b/src/app/space/components/blocks/following.tsx index 1b870b07..89a17555 100644 --- a/src/app/space/components/blocks/following.tsx +++ b/src/app/space/components/blocks/following.tsx @@ -1,42 +1,37 @@ import { NoteBase } from "@app/note/components/base"; import { NoteQuoteRepost } from "@app/note/components/quoteRepost"; import { NoteSkeleton } from "@app/note/components/skeleton"; - -import { getNotes } from "@utils/storage"; - -import { useInfiniteQuery } from "@tanstack/react-query"; import { useVirtualizer } from "@tanstack/react-virtual"; -import { useEffect, useRef } from "react"; +import { getNotes } from "@utils/storage"; +import { useEffect, useMemo, useRef } from "react"; +import useSWRInfinite from "swr/infinite"; const ITEM_PER_PAGE = 10; const TIME = Math.floor(Date.now() / 1000); +const fetcher = async ([, offset]) => getNotes(TIME, ITEM_PER_PAGE, offset); + export function FollowingBlock() { - const { - status, - data, - fetchNextPage, - hasNextPage, - isFetching, - isFetchingNextPage, - }: any = useInfiniteQuery({ - queryKey: ["following"], - queryFn: async ({ pageParam = 0 }) => { - return await getNotes(TIME, ITEM_PER_PAGE, pageParam); - }, - getNextPageParam: (lastPage) => lastPage.nextCursor, - }); + const getKey = (pageIndex, previousPageData) => { + if (previousPageData && !previousPageData.data) return null; + if (pageIndex === 0) return ["following", 0]; + return ["following", previousPageData.nextCursor]; + }; + + const { data, isLoading, size, setSize } = useSWRInfinite(getKey, fetcher); + + const notes = useMemo( + () => (data ? data.flatMap((d) => d.data) : []), + [data], + ); - const allRows = data ? data.pages.flatMap((d: { data: any }) => d.data) : []; const parentRef = useRef(); - const rowVirtualizer = useVirtualizer({ - count: hasNextPage ? allRows.length + 1 : allRows.length, + count: notes.length, getScrollElement: () => parentRef.current, estimateSize: () => 400, overscan: 2, }); - const itemsVirtualizer = rowVirtualizer.getVirtualItems(); useEffect(() => { @@ -46,14 +41,10 @@ export function FollowingBlock() { return; } - if ( - lastItem.index >= allRows.length - 1 && - hasNextPage && - !isFetchingNextPage - ) { - fetchNextPage(); + if (lastItem.index >= notes.length - 1) { + setSize(size + 1); } - }, [fetchNextPage, allRows.length, rowVirtualizer.getVirtualItems()]); + }, [notes.length, rowVirtualizer.getVirtualItems()]); return (
@@ -68,7 +59,7 @@ export function FollowingBlock() { className="scrollbar-hide flex w-full h-full flex-col justify-between gap-1.5 pt-1.5 overflow-y-auto" style={{ contain: "strict" }} > - {status === "loading" ? ( + {!data || isLoading ? (
@@ -91,7 +82,7 @@ export function FollowingBlock() { }} > {rowVirtualizer.getVirtualItems().map((virtualRow) => { - const note = allRows[virtualRow.index]; + const note = notes[virtualRow.index]; if (note) { if (note.kind === 1) { return ( @@ -119,15 +110,6 @@ export function FollowingBlock() {
)} -
- {isFetching && !isFetchingNextPage && ( -
-
- -
-
- )} -
); diff --git a/src/app/space/layout.tsx b/src/app/space/layout.tsx index 00abfbb7..af305a5f 100644 --- a/src/app/space/layout.tsx +++ b/src/app/space/layout.tsx @@ -1,5 +1,6 @@ import { MultiAccounts } from "@shared/multiAccounts"; import { Navigation } from "@shared/navigation"; +import { SWRConfig } from "swr"; export function LayoutSpace({ children }: { children: React.ReactNode }) { return ( @@ -8,7 +9,9 @@ export function LayoutSpace({ children }: { children: React.ReactNode }) {
-
{children}
+
+ new Map() }}>{children} +
); } diff --git a/src/renderer/shell.tsx b/src/renderer/shell.tsx index 0a22b20b..32937747 100644 --- a/src/renderer/shell.tsx +++ b/src/renderer/shell.tsx @@ -1,14 +1,11 @@ import { LayoutDefault } from "./layoutDefault"; import { PageContext } from "./types"; import { RelayProvider } from "@shared/relayProvider"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { dateToUnix } from "@utils/date"; import { PageContextProvider } from "@utils/hooks/usePageContext"; import { updateLastLogin } from "@utils/storage"; import { useEffect } from "react"; -const queryClient = new QueryClient(); - export function Shell({ children, pageContext, @@ -37,11 +34,7 @@ export function Shell({ return ( - - - {children} - - + {children} );