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
This commit is contained in:
Samuel Newman
2026-02-11 07:41:04 +00:00
committed by GitHub
parent 177a6e50bd
commit a25bc8f249
2 changed files with 39 additions and 19 deletions
+9 -1
View File
@@ -200,7 +200,13 @@ export function useSetFeedViewPreferencesMutation() {
})
}
export function useSetThreadViewPreferencesMutation() {
export function useSetThreadViewPreferencesMutation({
onSuccess,
onError,
}: {
onSuccess?: (data: void, variables: Partial<ThreadViewPreferences>) => void
onError?: (error: unknown) => void
}) {
const queryClient = useQueryClient()
const agent = useAgent()
@@ -212,6 +218,8 @@ export function useSetThreadViewPreferencesMutation() {
queryKey: preferencesQueryKey,
})
},
onSuccess,
onError,
})
}
@@ -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({