perf(pulse): pause interval refetches when hidden

Finding: L7 pulse fixed-interval refetches.\n\nGate Pulse timeline/reaction refetch intervals on document visibility so the visible tab stays fresh while hidden windows stop issuing periodic social API requests. Initial query enablement and invalidation behavior are unchanged.
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
This commit is contained in:
npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757
2026-07-08 10:18:41 -04:00
committed by npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d
parent d49811f3df
commit 597c5d1eaa

View File

@@ -1,4 +1,5 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import * as React from "react";
import {
getGlobalNotes,
@@ -13,6 +14,37 @@ import { allPulseTimelinesQueryKey } from "@/features/profile/hooks";
import { withoutProjectComments } from "@/features/pulse/lib/projectComments";
import type { UserNote, UserNotesResponse } from "@/shared/api/socialTypes";
function isDocumentVisible() {
return typeof document === "undefined"
? true
: document.visibilityState === "visible";
}
function useDocumentVisible() {
const [visible, setVisible] = React.useState(isDocumentVisible);
React.useEffect(() => {
if (typeof document === "undefined") {
return;
}
function handleVisibilityChange() {
setVisible(isDocumentVisible());
}
document.addEventListener("visibilitychange", handleVisibilityChange);
return () => {
document.removeEventListener("visibilitychange", handleVisibilityChange);
};
}, []);
return visible;
}
function useVisibleRefetchInterval(intervalMs: number) {
return useDocumentVisible() ? intervalMs : false;
}
// ── Query keys ──────────────────────────────────────────────────────────────
export const pulseQueryKeys = {
@@ -31,6 +63,8 @@ export const pulseQueryKeys = {
// ── Own notes ───────────────────────────────────────────────────────────────
export function useLikedNotesQuery(pubkey?: string, enabled = true) {
const refetchInterval = useVisibleRefetchInterval(30_000);
return useQuery<UserNotesResponse>({
queryKey: pulseQueryKeys.likedNotes(pubkey ?? ""),
queryFn: async () =>
@@ -39,11 +73,13 @@ export function useLikedNotesQuery(pubkey?: string, enabled = true) {
enabled: enabled && !!pubkey,
staleTime: 15_000,
gcTime: 5 * 60_000,
refetchInterval: 30_000,
refetchInterval,
});
}
export function useMyNotesQuery(pubkey?: string) {
const refetchInterval = useVisibleRefetchInterval(30_000);
return useQuery<UserNotesResponse>({
queryKey: pulseQueryKeys.myNotes(pubkey ?? ""),
queryFn: async () =>
@@ -52,13 +88,15 @@ export function useMyNotesQuery(pubkey?: string) {
enabled: !!pubkey,
staleTime: 15_000,
gcTime: 5 * 60_000,
refetchInterval: 30_000,
refetchInterval,
});
}
// ── Timeline (notes from contacts) ─────────────────────────────────────────
export function useTimelineQuery(contactPubkeys: string[], enabled: boolean) {
const refetchInterval = useVisibleRefetchInterval(30_000);
return useQuery<UserNotesResponse>({
queryKey: pulseQueryKeys.timeline(contactPubkeys),
queryFn: async () =>
@@ -66,7 +104,7 @@ export function useTimelineQuery(contactPubkeys: string[], enabled: boolean) {
enabled: enabled && contactPubkeys.length > 0,
staleTime: 15_000,
gcTime: 5 * 60_000,
refetchInterval: 30_000,
refetchInterval,
});
}
@@ -79,6 +117,8 @@ export function usePulseReactionsQuery(
noteIds: string[],
currentPubkey?: string,
) {
const refetchInterval = useVisibleRefetchInterval(60_000);
return useQuery<Map<string, PulseReactionState>>({
queryKey: pulseQueryKeys.reactions(noteIds),
queryFn: async () => {
@@ -100,7 +140,7 @@ export function usePulseReactionsQuery(
enabled: noteIds.length > 0,
staleTime: 15_000,
gcTime: 5 * 60_000,
refetchInterval: 60_000,
refetchInterval,
});
}
@@ -115,6 +155,8 @@ export function useNoteByIdQuery(noteId: string | null) {
}
export function useGlobalNotesQuery(enabled: boolean) {
const refetchInterval = useVisibleRefetchInterval(30_000);
return useQuery<UserNotesResponse>({
queryKey: pulseQueryKeys.globalNotes,
queryFn: async () =>
@@ -122,7 +164,7 @@ export function useGlobalNotesQuery(enabled: boolean) {
enabled,
staleTime: 15_000,
gcTime: 5 * 60_000,
refetchInterval: 30_000,
refetchInterval,
});
}