diff --git a/package.json b/package.json index 18223f1..8ae10fe 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "drizzle-zod": "^0.8.3", "embla-carousel-react": "^8.6.0", "fast-xml-parser": "^5.3.1", + "idb-keyval": "^6.2.2", "jimp": "^1.6.0", "jotai": "^2.15.1", "jotai-optics": "^0.4.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f1c74e..6099717 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -137,7 +137,7 @@ importers: version: 11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3) '@trpc/next': specifier: 11.7.1 - version: 11.7.1(@tanstack/react-query@5.90.7(react@19.2.0))(@trpc/client@11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.7.1(typescript@5.9.3))(next@16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + version: 11.7.1(@tanstack/react-query@5.90.7(react@19.2.0))(@trpc/client@11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.7.1(typescript@5.9.3))(next@16.0.1(@babel/core@7.26.10)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3) '@trpc/server': specifier: ^11.7.1 version: 11.7.1(typescript@5.9.3) @@ -152,7 +152,7 @@ importers: version: 19.1.0-rc.2 better-auth: specifier: ^1.3.34 - version: 1.3.34(next@16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 1.3.34(next@16.0.1(@babel/core@7.26.10)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -177,27 +177,30 @@ importers: fast-xml-parser: specifier: ^5.3.1 version: 5.3.1 + idb-keyval: + specifier: ^6.2.2 + version: 6.2.2 jimp: specifier: ^1.6.0 version: 1.6.0 jotai: specifier: ^2.15.1 - version: 2.15.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0) + version: 2.15.1(@babel/core@7.26.10)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0) jotai-optics: specifier: ^0.4.0 - version: 0.4.0(jotai@2.15.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0))(optics-ts@2.4.1) + version: 0.4.0(jotai@2.15.1(@babel/core@7.26.10)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0))(optics-ts@2.4.1) lucide-react: specifier: ^0.553.0 version: 0.553.0(react@19.2.0) next: specifier: 16.0.1 - version: 16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 16.0.1(@babel/core@7.26.10)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) next-mdx-remote: specifier: ^5.0.0 version: 5.0.0(@types/react@19.2.2)(react@19.2.0) next-pwa: specifier: ^5.6.0 - version: 5.6.0(@babel/core@7.28.5)(esbuild@0.25.12)(next@16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(webpack@5.102.1(esbuild@0.25.10)) + version: 5.6.0(@babel/core@7.26.10)(esbuild@0.25.12)(next@16.0.1(@babel/core@7.26.10)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(webpack@5.102.1(esbuild@0.25.10)) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -276,7 +279,7 @@ importers: version: 9.6.1 '@types/next-pwa': specifier: ^5.6.9 - version: 5.6.9(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + version: 5.6.9(@babel/core@7.26.10)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@types/node': specifier: ^24.10.0 version: 24.10.0 @@ -5024,6 +5027,9 @@ packages: resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} engines: {node: '>=0.10.0'} + idb-keyval@6.2.2: + resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==} + idb@7.1.1: resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} @@ -10485,7 +10491,7 @@ snapshots: log-symbols: 4.1.0 module-punycode: punycode@2.3.1 next: 16.0.1(@babel/core@7.26.10)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - next-safe-action: 8.0.11(next@16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + next-safe-action: 8.0.11(next@16.0.1(@babel/core@7.26.10)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) node-html-parser: 7.0.1 ora: 5.4.1 pretty-bytes: 6.1.1 @@ -10825,11 +10831,11 @@ snapshots: '@trpc/server': 11.7.1(typescript@5.9.3) typescript: 5.9.3 - '@trpc/next@11.7.1(@tanstack/react-query@5.90.7(react@19.2.0))(@trpc/client@11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.7.1(typescript@5.9.3))(next@16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3)': + '@trpc/next@11.7.1(@tanstack/react-query@5.90.7(react@19.2.0))(@trpc/client@11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.7.1(typescript@5.9.3))(next@16.0.1(@babel/core@7.26.10)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3)': dependencies: '@trpc/client': 11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3) '@trpc/server': 11.7.1(typescript@5.9.3) - next: 16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + next: 16.0.1(@babel/core@7.26.10)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) typescript: 5.9.3 @@ -10937,12 +10943,12 @@ snapshots: '@types/ms@2.1.0': {} - '@types/next-pwa@5.6.9(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + '@types/next-pwa@5.6.9(@babel/core@7.26.10)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@types/node': 24.10.0 '@types/react': 19.2.2 '@types/react-dom': 19.2.2(@types/react@19.2.2) - next: 13.5.11(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + next: 13.5.11(@babel/core@7.26.10)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) workbox-build: 6.6.0 transitivePeerDependencies: - '@babel/core' @@ -11450,9 +11456,9 @@ snapshots: axobject-query@4.1.0: {} - babel-loader@8.4.1(@babel/core@7.28.5)(webpack@5.102.1(esbuild@0.25.10)): + babel-loader@8.4.1(@babel/core@7.26.10)(webpack@5.102.1(esbuild@0.25.10)): dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.26.10 find-cache-dir: 3.3.2 loader-utils: 2.0.4 make-dir: 3.1.0 @@ -11497,7 +11503,7 @@ snapshots: baseline-browser-mapping@2.8.25: {} - better-auth@1.3.34(next@16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + better-auth@1.3.34(next@16.0.1(@babel/core@7.26.10)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: '@better-auth/core': 1.3.34(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.0.19)(jose@6.1.0)(kysely@0.28.8)(nanostores@1.0.1) '@better-auth/telemetry': 1.3.34(better-call@1.0.19)(jose@6.1.0)(kysely@0.28.8)(nanostores@1.0.1) @@ -11514,7 +11520,7 @@ snapshots: nanostores: 1.0.1 zod: 4.1.12 optionalDependencies: - next: 16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + next: 16.0.1(@babel/core@7.26.10)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) @@ -12967,6 +12973,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + idb-keyval@6.2.2: {} + idb@7.1.1: {} ieee754@1.2.1: {} @@ -13252,14 +13260,14 @@ snapshots: jose@6.1.0: {} - jotai-optics@0.4.0(jotai@2.15.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0))(optics-ts@2.4.1): + jotai-optics@0.4.0(jotai@2.15.1(@babel/core@7.26.10)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0))(optics-ts@2.4.1): dependencies: - jotai: 2.15.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0) + jotai: 2.15.1(@babel/core@7.26.10)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0) optics-ts: 2.4.1 - jotai@2.15.1(@babel/core@7.28.5)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0): + jotai@2.15.1(@babel/core@7.26.10)(@babel/template@7.27.2)(@types/react@19.2.2)(react@19.2.0): optionalDependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.26.10 '@babel/template': 7.27.2 '@types/react': 19.2.2 react: 19.2.0 @@ -13904,12 +13912,12 @@ snapshots: - '@types/react' - supports-color - next-pwa@5.6.0(@babel/core@7.28.5)(esbuild@0.25.12)(next@16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(webpack@5.102.1(esbuild@0.25.10)): + next-pwa@5.6.0(@babel/core@7.26.10)(esbuild@0.25.12)(next@16.0.1(@babel/core@7.26.10)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(webpack@5.102.1(esbuild@0.25.10)): dependencies: - babel-loader: 8.4.1(@babel/core@7.28.5)(webpack@5.102.1(esbuild@0.25.10)) + babel-loader: 8.4.1(@babel/core@7.26.10)(webpack@5.102.1(esbuild@0.25.10)) clean-webpack-plugin: 4.0.0(webpack@5.102.1(esbuild@0.25.10)) globby: 11.1.0 - next: 16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + next: 16.0.1(@babel/core@7.26.10)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) terser-webpack-plugin: 5.3.14(esbuild@0.25.12)(webpack@5.102.1(esbuild@0.25.10)) workbox-webpack-plugin: 6.6.0(webpack@5.102.1(esbuild@0.25.10)) workbox-window: 6.6.0 @@ -13922,9 +13930,9 @@ snapshots: - uglify-js - webpack - next-safe-action@8.0.11(next@16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + next-safe-action@8.0.11(next@16.0.1(@babel/core@7.26.10)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: - next: 16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + next: 16.0.1(@babel/core@7.26.10)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) @@ -13933,7 +13941,7 @@ snapshots: react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - next@13.5.11(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + next@13.5.11(@babel/core@7.26.10)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: '@next/env': 13.5.11 '@swc/helpers': 0.5.2 @@ -13942,7 +13950,7 @@ snapshots: postcss: 8.4.31 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - styled-jsx: 5.1.1(@babel/core@7.28.5)(react@19.2.0) + styled-jsx: 5.1.1(@babel/core@7.26.10)(react@19.2.0) watchpack: 2.4.0 optionalDependencies: '@next/swc-darwin-arm64': 13.5.9 @@ -13982,7 +13990,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@16.0.1(@babel/core@7.28.5)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + next@16.0.1(@babel/core@7.26.10)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: '@next/env': 16.0.1 '@swc/helpers': 0.5.15 @@ -13990,7 +13998,7 @@ snapshots: postcss: 8.4.31 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.2.0) + styled-jsx: 5.1.6(@babel/core@7.26.10)(react@19.2.0) optionalDependencies: '@next/swc-darwin-arm64': 16.0.1 '@next/swc-darwin-x64': 16.0.1 @@ -15178,12 +15186,12 @@ snapshots: dependencies: inline-style-parser: 0.2.6 - styled-jsx@5.1.1(@babel/core@7.28.5)(react@19.2.0): + styled-jsx@5.1.1(@babel/core@7.26.10)(react@19.2.0): dependencies: client-only: 0.0.1 react: 19.2.0 optionalDependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.26.10 styled-jsx@5.1.6(@babel/core@7.26.10)(react@19.0.0): dependencies: @@ -15192,12 +15200,12 @@ snapshots: optionalDependencies: '@babel/core': 7.26.10 - styled-jsx@5.1.6(@babel/core@7.28.5)(react@19.2.0): + styled-jsx@5.1.6(@babel/core@7.26.10)(react@19.2.0): dependencies: client-only: 0.0.1 react: 19.2.0 optionalDependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.26.10 sucrase@3.35.0: dependencies: diff --git a/src/app/(feed)/feed/RefetchItemsButton.tsx b/src/app/(feed)/feed/RefetchItemsButton.tsx index 2430928..a89c18e 100644 --- a/src/app/(feed)/feed/RefetchItemsButton.tsx +++ b/src/app/(feed)/feed/RefetchItemsButton.tsx @@ -5,16 +5,19 @@ import clsx from "clsx"; import { RefreshCwIcon } from "lucide-react"; import { usePathname } from "next/navigation"; import { ButtonWithShortcut } from "~/components/ButtonWithShortcut"; -import { useFeedItemsQuery } from "~/lib/data/feed-items"; +import { useFetchFeedItems, useFetchFeedItemsStatus } from "~/lib/data/store"; import { useShortcut } from "~/lib/hooks/useShortcut"; export function RefetchItemsButton() { const pathname = usePathname(); const queryClient = useQueryClient(); - const { fetchStatus } = useFeedItemsQuery(); + + const fetchStatus = useFetchFeedItemsStatus(); + const fetchFeedItems = useFetchFeedItems(); useShortcut("r", () => { + fetchFeedItems(); queryClient.invalidateQueries(); }); diff --git a/src/app/(feed)/feed/SidebarCategories.tsx b/src/app/(feed)/feed/SidebarCategories.tsx index e80d462..b501bac 100644 --- a/src/app/(feed)/feed/SidebarCategories.tsx +++ b/src/app/(feed)/feed/SidebarCategories.tsx @@ -16,8 +16,6 @@ import { categoryFilterAtom, dateFilterAtom, feedFilterAtom, - useFeedItemsMap, - useFeedItemsOrder, visibilityFilterAtom, } from "~/lib/data/atoms"; import { useContentCategories } from "~/lib/data/content-categories"; @@ -25,10 +23,11 @@ import { useFeedCategories } from "~/lib/data/feed-categories"; import { doesFeedItemPassFilters } from "~/lib/data/feed-items"; import { useDeselectViewFilter } from "~/lib/data/views"; import { useDialogStore } from "./dialogStore"; +import { useFeedItemsDict, useFeedItemsOrder } from "~/lib/data/store"; function useCheckFilteredFeedItemsForCategory() { const feedItemsOrder = useFeedItemsOrder(); - const feedItemsMap = useFeedItemsMap(); + const feedItemsMap = useFeedItemsDict(); const { feedCategories } = useFeedCategories(); const visibilityFilter = useAtomValue(visibilityFilterAtom); diff --git a/src/app/(feed)/feed/SidebarFeeds.tsx b/src/app/(feed)/feed/SidebarFeeds.tsx index 61f71a7..c0b0caf 100644 --- a/src/app/(feed)/feed/SidebarFeeds.tsx +++ b/src/app/(feed)/feed/SidebarFeeds.tsx @@ -15,8 +15,6 @@ import { categoryFilterAtom, dateFilterAtom, feedFilterAtom, - useFeedItemsMap, - useFeedItemsOrder, viewFilterAtom, visibilityFilterAtom, } from "~/lib/data/atoms"; @@ -25,10 +23,11 @@ import { doesFeedItemPassFilters } from "~/lib/data/feed-items"; import { useFeeds } from "~/lib/data/feeds"; import { useDeselectViewFilter } from "~/lib/data/views"; import { useDialogStore } from "./dialogStore"; +import { useFeedItemsDict, useFeedItemsOrder } from "~/lib/data/store"; function useCheckFilteredFeedItemsForFeed() { const feedItemsOrder = useFeedItemsOrder(); - const feedItemsMap = useFeedItemsMap(); + const feedItemsDict = useFeedItemsDict(); const { feedCategories } = useFeedCategories(); const { feeds } = useFeeds(); @@ -42,9 +41,9 @@ function useCheckFilteredFeedItemsForFeed() { if (!feedItemsOrder || !feedCategories) return []; return feedItemsOrder.filter( (item) => - feedItemsMap[item] && + feedItemsDict[item] && doesFeedItemPassFilters( - feedItemsMap[item], + feedItemsDict[item], dateFilter, visibilityFilter, categoryFilter, @@ -57,7 +56,7 @@ function useCheckFilteredFeedItemsForFeed() { }, [ feedItemsOrder, - feedItemsMap, + feedItemsDict, dateFilter, visibilityFilter, categoryFilter, diff --git a/src/app/(feed)/feed/TodayItems.tsx b/src/app/(feed)/feed/TodayItems.tsx index c233c43..f0ea0d4 100644 --- a/src/app/(feed)/feed/TodayItems.tsx +++ b/src/app/(feed)/feed/TodayItems.tsx @@ -22,15 +22,9 @@ import { CardHeader, CardTitle, } from "~/components/ui/card"; -import { - useFeedItemGlobalState, - useHasFetchedFeedItems, -} from "~/lib/data/atoms"; + import { useFeedCategories } from "~/lib/data/feed-categories"; -import { - useFeedItemsQuery, - useFilteredFeedItemsOrder, -} from "~/lib/data/feed-items"; +import { useFilteredFeedItemsOrder } from "~/lib/data/feed-items"; import { useFeedItemsSetWatchedValueMutation, useFeedItemsSetWatchLaterValueMutation, @@ -39,6 +33,12 @@ import { useFeeds } from "~/lib/data/feeds"; import { useViews } from "~/lib/data/views"; import { useDialogStore } from "./dialogStore"; import { memo } from "react"; +import { + feedItemsStore, + useFeedItemsLastFetchedAt, + useFeedItemValue, + useFetchFeedItemsStatus, +} from "~/lib/data/store"; function timeAgo(date: string | Date) { const diff = dayjs().diff(date); @@ -133,10 +133,13 @@ function TodayItemsFeedEmptyState() { } function LoaderDisplay() { - const hasFetchedFeedItems = useHasFetchedFeedItems(); - const { fetchStatus: feedItemsFetchStatus } = useFeedItemsQuery(); + const feedItemsLastFetchedAt = useFeedItemsLastFetchedAt(); + const feedItemsFetchStatus = useFetchFeedItemsStatus(); - if (feedItemsFetchStatus === "idle" || hasFetchedFeedItems) { + if ( + feedItemsFetchStatus !== "fetching" || + (feedItemsFetchStatus === "fetching" && feedItemsLastFetchedAt !== null) + ) { return null; } @@ -158,13 +161,15 @@ function LoaderDisplay() { function ItemDisplay({ contentId }: { contentId: string }) { const { feeds } = useFeeds(); - const [item] = useFeedItemGlobalState(contentId); + const item = useFeedItemValue(contentId); const { mutateAsync: setWatchedValue } = useFeedItemsSetWatchedValueMutation(contentId); const { mutateAsync: setWatchLaterValue } = useFeedItemsSetWatchLaterValueMutation(contentId); + if (!item) return null; + const feed = feeds.find((f) => f.id === item.feedId); const itemDestination = item.platform === "website" ? "read" : "watch"; @@ -263,7 +268,7 @@ export function TodayItems() { const { feeds, hasFetchedFeeds } = useFeeds(); const { hasFetchedFeedCategories } = useFeedCategories(); const { views } = useViews(); - const hasFetchedFeedItems = useHasFetchedFeedItems(); + const feedItemsLastFetchedAt = useFeedItemsLastFetchedAt(); const filteredFeedItemsOrder = useFilteredFeedItemsOrder(); @@ -279,7 +284,7 @@ export function TodayItems() { if ( hasFetchedFeeds && - hasFetchedFeedItems && + feedItemsLastFetchedAt !== null && hasFetchedFeedCategories && !filteredFeedItemsOrder.length ) { diff --git a/src/app/(feed)/feed/TopRightHeaderContent.tsx b/src/app/(feed)/feed/TopRightHeaderContent.tsx index 9f16abc..4c95db5 100644 --- a/src/app/(feed)/feed/TopRightHeaderContent.tsx +++ b/src/app/(feed)/feed/TopRightHeaderContent.tsx @@ -8,25 +8,25 @@ import { } from "lucide-react"; import Link from "next/link"; import { usePathname } from "next/navigation"; +import { useState } from "react"; +import { EditFeedDialog } from "~/components/AddFeedDialog"; import { ButtonWithShortcut } from "~/components/ButtonWithShortcut"; import { CustomVideoButton } from "~/components/CustomVideoButton"; import { Button } from "~/components/ui/button"; import { useSidebar } from "~/components/ui/sidebar"; -import { useFeedItemGlobalState } from "~/lib/data/atoms"; +import { PLATFORM_TO_FORMATTED_NAME_MAP } from "~/lib/data/feeds/utils"; +import { useFeedItemValue } from "~/lib/data/store"; import { useShortcut } from "~/lib/hooks/useShortcut"; import { OpenRightSidebarButton } from "./OpenRightSidebarButton"; import { RefetchItemsButton } from "./RefetchItemsButton"; import { MAX_ZOOM, MIN_ZOOM, useZoom } from "./watch/[id]/useZoom"; -import { useState } from "react"; -import { EditFeedDialog } from "~/components/AddFeedDialog"; -import { PLATFORM_TO_FORMATTED_NAME_MAP } from "~/lib/data/feeds/utils"; function OpenInYouTubeButton() { const pathname = usePathname(); const videoId = pathname.split("/feed/watch/")[1]!; const contentId = pathname.split("/feed/read/")[1]!; - const [feedItem] = useFeedItemGlobalState(videoId || contentId || ""); + const feedItem = useFeedItemValue(videoId || contentId || ""); // If not a Serial item, assume YouTube if (!feedItem) { @@ -61,7 +61,7 @@ function EditFeedButton() { const videoId = pathname.split("/feed/watch/")[1]!; const contentId = pathname.split("/feed/read/")[1]!; - const [feedItem] = useFeedItemGlobalState(videoId || contentId || ""); + const feedItem = useFeedItemValue(videoId || contentId || ""); const [selectedFeedForEditing, setSelectedFeedForEditing] = useState< null | number diff --git a/src/app/(feed)/feed/read/[id]/page.tsx b/src/app/(feed)/feed/read/[id]/page.tsx index e28d469..60a9c43 100644 --- a/src/app/(feed)/feed/read/[id]/page.tsx +++ b/src/app/(feed)/feed/read/[id]/page.tsx @@ -3,17 +3,17 @@ import clsx from "clsx"; import { use } from "react"; -import { useFeedItemGlobalState } from "~/lib/data/atoms"; import { useZoom } from "../../watch/[id]/useZoom"; -import classes from "./article.module.css"; -import { useFeeds } from "~/lib/data/feeds"; -import { unified } from "unified"; import rehypeParse from "rehype-parse"; import rehypeSanitize from "rehype-sanitize"; import rehypeStringify from "rehype-stringify"; +import { unified } from "unified"; +import { useFeeds } from "~/lib/data/feeds"; +import { useFeedItemValue } from "~/lib/data/store"; import { useFlagState } from "~/lib/hooks/useFlagState"; import { ContentActions } from "../../watch/[id]/ContentActions"; +import classes from "./article.module.css"; const parser = unified() .use(rehypeParse, { fragment: true }) @@ -34,7 +34,7 @@ export default function ReadPage(props: { params: Promise<{ id: string }> }) { const [articleStyle] = useFlagState("ARTICLE_STYLE"); const params = use(props.params); - const [feedItem] = useFeedItemGlobalState(params?.id ?? ""); + const feedItem = useFeedItemValue(params?.id ?? ""); const { feeds } = useFeeds(); const feed = feeds.find((f) => f.id === feedItem?.feedId); diff --git a/src/app/(feed)/feed/watch/[id]/ContentActions.tsx b/src/app/(feed)/feed/watch/[id]/ContentActions.tsx index 9a787d6..422b94c 100644 --- a/src/app/(feed)/feed/watch/[id]/ContentActions.tsx +++ b/src/app/(feed)/feed/watch/[id]/ContentActions.tsx @@ -1,7 +1,6 @@ import { CheckIcon, ClockIcon, EyeIcon, EyeOffIcon } from "lucide-react"; import { ButtonWithShortcut } from "~/components/ButtonWithShortcut"; import { Button } from "~/components/ui/button"; -import { useFeedItemGlobalState } from "~/lib/data/atoms"; import { useFeedItemsSetWatchedValueMutation, useFeedItemsSetWatchLaterValueMutation, @@ -9,11 +8,12 @@ import { import { useMediaQuery } from "~/lib/hooks/use-media-query"; import { useView } from "./useView"; import { useShortcut } from "~/lib/hooks/useShortcut"; +import { useFeedItemValue } from "~/lib/data/store"; export function ContentActions({ contentID }: { contentID: string }) { const { view } = useView(); - const [video] = useFeedItemGlobalState(contentID); + const video = useFeedItemValue(contentID); const { mutateAsync: setWatchedValue } = useFeedItemsSetWatchedValueMutation(contentID); diff --git a/src/app/(feed)/feed/watch/[id]/VideoDisplay.tsx b/src/app/(feed)/feed/watch/[id]/VideoDisplay.tsx index 1598608..a412693 100644 --- a/src/app/(feed)/feed/watch/[id]/VideoDisplay.tsx +++ b/src/app/(feed)/feed/watch/[id]/VideoDisplay.tsx @@ -1,11 +1,11 @@ import clsx from "clsx"; import { useEffect, useState } from "react"; import ResponsiveVideo from "~/components/ResponsiveVideo"; +import { useFeedItemValue } from "~/lib/data/store"; import { useShortcut } from "~/lib/hooks/useShortcut"; -import { useView } from "./useView"; import { ContentActions } from "./ContentActions"; import { useVideoNavigationShortcuts } from "./useVideoNavigationShortcuts"; -import { useFeedItemGlobalState } from "~/lib/data/atoms"; +import { useView } from "./useView"; export function VideoDisplay({ id, @@ -14,7 +14,7 @@ export function VideoDisplay({ id: string; isInactive: boolean; }) { - const [item] = useFeedItemGlobalState(id); + const item = useFeedItemValue(id); const [showVideo, setShowVideo] = useState(false); const { view, toggleView } = useView(); @@ -34,6 +34,8 @@ export function VideoDisplay({ }; }, []); + if (!item) return null; + return ( <>