From 447102eada0de3f525316dc9c800fe06188a6ab4 Mon Sep 17 00:00:00 2001 From: Simo Date: Thu, 9 Oct 2025 09:14:15 +0200 Subject: [PATCH 1/5] wip Signed-off-by: Simo --- app/my-stream/notifications/page.tsx | 2 +- components/react-query-wrapper/ReactQueryWrapper.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/my-stream/notifications/page.tsx b/app/my-stream/notifications/page.tsx index 2736838eec..132abe46b9 100644 --- a/app/my-stream/notifications/page.tsx +++ b/app/my-stream/notifications/page.tsx @@ -21,7 +21,7 @@ export default async function NotificationsPage() { )?.value; if ( - notificationsFetched && + !notificationsFetched || +notificationsFetched < Time.now().toMillis() - 60000 ) { await prefetchAuthenticatedNotifications({ diff --git a/components/react-query-wrapper/ReactQueryWrapper.tsx b/components/react-query-wrapper/ReactQueryWrapper.tsx index b9485ea863..c3e0ca48f7 100644 --- a/components/react-query-wrapper/ReactQueryWrapper.tsx +++ b/components/react-query-wrapper/ReactQueryWrapper.tsx @@ -1194,7 +1194,7 @@ export default function ReactQueryWrapper({ Cookies.set([QueryKey.FEED_ITEMS].toString(), `${Time.now().toMillis()}`); }); - useQueryKeyListener([QueryKey.FEED_ITEMS], () => { + useQueryKeyListener([QueryKey.IDENTITY_NOTIFICATIONS], () => { Cookies.set( [QueryKey.IDENTITY_NOTIFICATIONS].toString(), `${Time.now().toMillis()}` From 5316859dc3296f24a2056c6f108f1f74ccaec049 Mon Sep 17 00:00:00 2001 From: Simo Date: Thu, 9 Oct 2025 09:19:45 +0200 Subject: [PATCH 2/5] wip Signed-off-by: Simo --- hooks/useNotificationsQuery.tsx | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/hooks/useNotificationsQuery.tsx b/hooks/useNotificationsQuery.tsx index 3a9739612e..08bcb048ba 100644 --- a/hooks/useNotificationsQuery.tsx +++ b/hooks/useNotificationsQuery.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect } from "react"; +import { useMemo } from "react"; import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query"; import { commonApiFetch } from "@/services/api/common-api"; import { @@ -81,9 +81,6 @@ export function useNotificationsQuery({ }: UseNotificationsQueryProps) { const queryClient = useQueryClient(); - const [items, setItems] = useState([]); - const [isInitialQueryDone, setIsInitialQueryDone] = useState(false); - /** * OPTIONAL: Prefetch the first few pages of notifications. * This is similar to how `useMyStreamQuery` sets up prefetching. @@ -111,31 +108,20 @@ export function useNotificationsQuery({ staleTime: 60000, }); - useEffect(() => { - setItems([]); - setIsInitialQueryDone(false); - }, [identity, cause]); - - /** - * Flatten all pages and (optionally) reverse them. Store in local state. - */ - useEffect(() => { + const items = useMemo(() => { if (!query.data) { - return; + return [] as TypedNotification[]; } - let data: TypedNotification[] = ( + const data = ( query.data.pages as TypedNotificationsResponse[] ).flatMap((page) => page.notifications); - if (reverse) { - data = data.reverse(); - } - - setItems(data); - setIsInitialQueryDone(true); + return reverse ? [...data].reverse() : data; }, [query.data, reverse]); + const isInitialQueryDone = query.isSuccess || query.isError; + // Return everything the query provides, plus our flattened items & readiness indicator. return { ...query, From c64c66d897aef0267690963f7f88be9bc11fb762 Mon Sep 17 00:00:00 2001 From: Simo Date: Thu, 9 Oct 2025 09:32:12 +0200 Subject: [PATCH 3/5] wip Signed-off-by: Simo --- .../hooks/useNotificationsQuery.test.tsx | 20 +++++++++++--- hooks/useNotificationsQuery.tsx | 26 ++++++++++++------- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/__tests__/hooks/useNotificationsQuery.test.tsx b/__tests__/hooks/useNotificationsQuery.test.tsx index 59f51bbdc8..b61f434be2 100644 --- a/__tests__/hooks/useNotificationsQuery.test.tsx +++ b/__tests__/hooks/useNotificationsQuery.test.tsx @@ -25,7 +25,9 @@ describe('useNotificationsQuery', () => { fetchNextPage: jest.fn(), hasNextPage: true, isLoading: false, - isLoadingNextPage: false + isLoadingNextPage: false, + isSuccess: true, + isError: false }); const { result } = renderHook(() => useNotificationsQuery({ identity: 'id', reverse: true })); expect(result.current.items.map(i => i.id)).toEqual([3,2,1]); @@ -34,7 +36,11 @@ describe('useNotificationsQuery', () => { }); it('returns empty when no data', () => { - useInfiniteQueryMock.mockReturnValue({ data: undefined }); + useInfiniteQueryMock.mockReturnValue({ + data: undefined, + isSuccess: false, + isError: false + }); const { result } = renderHook(() => useNotificationsQuery({ identity: 'id' })); expect(result.current.items).toEqual([]); expect(result.current.isInitialQueryDone).toBe(false); @@ -42,7 +48,9 @@ describe('useNotificationsQuery', () => { it('resets when identity changes', async () => { useInfiniteQueryMock.mockReturnValue({ - data: { pages: [{ notifications: [{ id: 1 }] }] } + data: { pages: [{ notifications: [{ id: 1 }] }] }, + isSuccess: true, + isError: false }); const { result, rerender } = renderHook( ({ identity }) => useNotificationsQuery({ identity }), @@ -50,7 +58,11 @@ describe('useNotificationsQuery', () => { ); expect(result.current.items).toHaveLength(1); - useInfiniteQueryMock.mockReturnValue({ data: undefined }); + useInfiniteQueryMock.mockReturnValue({ + data: undefined, + isSuccess: false, + isError: false + }); rerender({ identity: 'b' }); expect(result.current.items).toEqual([]); diff --git a/hooks/useNotificationsQuery.tsx b/hooks/useNotificationsQuery.tsx index 08bcb048ba..820b9a832f 100644 --- a/hooks/useNotificationsQuery.tsx +++ b/hooks/useNotificationsQuery.tsx @@ -1,6 +1,6 @@ "use client"; -import { useMemo } from "react"; +import { useEffect, useMemo } from "react"; import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query"; import { commonApiFetch } from "@/services/api/common-api"; import { @@ -85,15 +85,21 @@ export function useNotificationsQuery({ * OPTIONAL: Prefetch the first few pages of notifications. * This is similar to how `useMyStreamQuery` sets up prefetching. */ - queryClient.prefetchInfiniteQuery({ - queryKey: getIdentityNotificationsQueryKey(identity, limit, cause), - queryFn: ({ pageParam }: { pageParam?: number | null }) => - fetchNotifications({ limit, cause, pageParam }), - initialPageParam: null, - getNextPageParam: (lastPage) => lastPage.notifications.at(-1)?.id ?? null, - pages: 3, - staleTime: 60000, - }); + useEffect(() => { + if (!identity || activeProfileProxy) { + return; + } + + queryClient.prefetchInfiniteQuery({ + queryKey: getIdentityNotificationsQueryKey(identity, limit, cause), + queryFn: ({ pageParam }: { pageParam?: number | null }) => + fetchNotifications({ limit, cause, pageParam }), + initialPageParam: null, + getNextPageParam: (lastPage) => lastPage.notifications.at(-1)?.id ?? null, + pages: 3, + staleTime: 60000, + }); + }, [queryClient, identity, activeProfileProxy, limit, cause]); /** * Now the actual Infinite Query for notifications From b27a9a694e272148a2b566d4cf1c0f603a6fefb7 Mon Sep 17 00:00:00 2001 From: Simo Date: Thu, 9 Oct 2025 10:14:12 +0200 Subject: [PATCH 4/5] wip Signed-off-by: Simo --- hooks/useNotificationsQuery.tsx | 66 ++++++++++++++++----------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/hooks/useNotificationsQuery.tsx b/hooks/useNotificationsQuery.tsx index 820b9a832f..ede32aa409 100644 --- a/hooks/useNotificationsQuery.tsx +++ b/hooks/useNotificationsQuery.tsx @@ -1,6 +1,6 @@ "use client"; -import { useEffect, useMemo } from "react"; +import { useCallback, useEffect, useMemo } from "react"; import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query"; import { commonApiFetch } from "@/services/api/common-api"; import { @@ -79,7 +79,7 @@ export function useNotificationsQuery({ limit = "30", cause = null, }: UseNotificationsQueryProps) { - const queryClient = useQueryClient(); + const prefetch = usePrefetchNotifications(); /** * OPTIONAL: Prefetch the first few pages of notifications. @@ -90,16 +90,8 @@ export function useNotificationsQuery({ return; } - queryClient.prefetchInfiniteQuery({ - queryKey: getIdentityNotificationsQueryKey(identity, limit, cause), - queryFn: ({ pageParam }: { pageParam?: number | null }) => - fetchNotifications({ limit, cause, pageParam }), - initialPageParam: null, - getNextPageParam: (lastPage) => lastPage.notifications.at(-1)?.id ?? null, - pages: 3, - staleTime: 60000, - }); - }, [queryClient, identity, activeProfileProxy, limit, cause]); + prefetch({ identity, limit, cause }); + }, [prefetch, identity, activeProfileProxy, limit, cause]); /** * Now the actual Infinite Query for notifications @@ -116,7 +108,7 @@ export function useNotificationsQuery({ const items = useMemo(() => { if (!query.data) { - return [] as TypedNotification[]; + return []; } const data = ( @@ -139,26 +131,30 @@ export function useNotificationsQuery({ export function usePrefetchNotifications() { const queryClient = useQueryClient(); - return ({ - identity, - cause = null, - limit = "30", - }: { - identity: string | null; - cause?: ApiNotificationCause[] | null; - limit?: string; - }) => { - if (!identity) { - return; - } - queryClient.prefetchInfiniteQuery({ - queryKey: getIdentityNotificationsQueryKey(identity, limit, cause), - queryFn: ({ pageParam }: { pageParam?: number | null }) => - fetchNotifications({ limit, cause, pageParam }), - initialPageParam: null, - getNextPageParam: (lastPage) => lastPage.notifications.at(-1)?.id ?? null, - pages: 3, - staleTime: 60000, - }); - }; + return useCallback( + ({ + identity, + cause = null, + limit = "30", + }: { + identity: string | null; + cause?: ApiNotificationCause[] | null; + limit?: string; + }) => { + if (!identity) { + return; + } + queryClient.prefetchInfiniteQuery({ + queryKey: getIdentityNotificationsQueryKey(identity, limit, cause), + queryFn: ({ pageParam }: { pageParam?: number | null }) => + fetchNotifications({ limit, cause, pageParam }), + initialPageParam: null, + getNextPageParam: (lastPage) => + lastPage.notifications.at(-1)?.id ?? null, + pages: 3, + staleTime: 60000, + }); + }, + [queryClient] + ); } From c0bacadbb3d6c22b4c426dff09d75217140a4fc6 Mon Sep 17 00:00:00 2001 From: Simo Date: Thu, 9 Oct 2025 10:17:53 +0200 Subject: [PATCH 5/5] wip Signed-off-by: Simo --- hooks/useNotificationsQuery.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/hooks/useNotificationsQuery.tsx b/hooks/useNotificationsQuery.tsx index ede32aa409..4aae11d6a7 100644 --- a/hooks/useNotificationsQuery.tsx +++ b/hooks/useNotificationsQuery.tsx @@ -4,7 +4,6 @@ import { useCallback, useEffect, useMemo } from "react"; import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query"; import { commonApiFetch } from "@/services/api/common-api"; import { - TypedNotification, TypedNotificationsResponse, } from "@/types/feed.types"; import { ApiNotificationCause } from "@/generated/models/ApiNotificationCause";