From a25bc8f249954771d7a438d0baff6ead7f687972 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Wed, 11 Feb 2026 07:41:04 +0000 Subject: [PATCH] Debounce thread preferences on leading edge, reduce to 2s (#9851) * debounce on leading edge, reduce to 2s * flush debounce on leave screen * simplify by using useMutation properly --- src/state/queries/preferences/index.ts | 10 +++- .../preferences/useThreadPreferences.ts | 48 ++++++++++++------- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts index 58df94fcc3..e78a5d73f1 100644 --- a/src/state/queries/preferences/index.ts +++ b/src/state/queries/preferences/index.ts @@ -200,7 +200,13 @@ export function useSetFeedViewPreferencesMutation() { }) } -export function useSetThreadViewPreferencesMutation() { +export function useSetThreadViewPreferencesMutation({ + onSuccess, + onError, +}: { + onSuccess?: (data: void, variables: Partial) => void + onError?: (error: unknown) => void +}) { const queryClient = useQueryClient() const agent = useAgent() @@ -212,6 +218,8 @@ export function useSetThreadViewPreferencesMutation() { queryKey: preferencesQueryKey, }) }, + onSuccess, + onError, }) } diff --git a/src/state/queries/preferences/useThreadPreferences.ts b/src/state/queries/preferences/useThreadPreferences.ts index 7825f4c688..629d1c61b8 100644 --- a/src/state/queries/preferences/useThreadPreferences.ts +++ b/src/state/queries/preferences/useThreadPreferences.ts @@ -1,5 +1,6 @@ import {useCallback, useMemo, useRef, useState} from 'react' import {type AppBskyUnspeccedGetPostThreadV2} from '@atproto/api' +import {useFocusEffect} from '@react-navigation/native' import debounce from 'lodash.debounce' import {useCallOnce} from '#/lib/once' @@ -70,26 +71,37 @@ export function useThreadPreferences({ } const userUpdatedPrefs = useRef(false) - const [isSaving, setIsSaving] = useState(false) - const {mutateAsync} = useSetThreadViewPreferencesMutation() + const {mutate, isPending: isSaving} = useSetThreadViewPreferencesMutation({ + onSuccess: (_data, prefs) => { + ax.metric('thread:preferences:update', { + sort: prefs.sort, + view: prefs.lab_treeViewEnabled ? 'tree' : 'linear', + }) + }, + onError: err => { + ax.logger.error('useThreadPreferences failed to save', { + safeMessage: err, + }) + }, + }) const savePrefs = useMemo(() => { - return debounce(async (prefs: ThreadViewPreferences) => { - try { - setIsSaving(true) - await mutateAsync(prefs) - ax.metric('thread:preferences:update', { - sort: prefs.sort, - view: prefs.lab_treeViewEnabled ? 'tree' : 'linear', - }) - } catch (e) { - ax.logger.error('useThreadPreferences failed to save', { - safeMessage: e, - }) - } finally { - setIsSaving(false) + return debounce( + (prefs: ThreadViewPreferences) => { + mutate(prefs) + }, + 2e3, + {leading: true, trailing: true}, + ) + }, [mutate]) + + // flush on leave screen + useFocusEffect( + useCallback(() => { + return () => { + void savePrefs.flush() } - }, 4e3) - }, [mutateAsync]) + }, [savePrefs]), + ) if (save && userUpdatedPrefs.current) { savePrefs({