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 queryClient = useQueryClient()
const agent = useAgent() const agent = useAgent()
@@ -212,6 +218,8 @@ export function useSetThreadViewPreferencesMutation() {
queryKey: preferencesQueryKey, queryKey: preferencesQueryKey,
}) })
}, },
onSuccess,
onError,
}) })
} }
@@ -1,5 +1,6 @@
import {useCallback, useMemo, useRef, useState} from 'react' import {useCallback, useMemo, useRef, useState} from 'react'
import {type AppBskyUnspeccedGetPostThreadV2} from '@atproto/api' import {type AppBskyUnspeccedGetPostThreadV2} from '@atproto/api'
import {useFocusEffect} from '@react-navigation/native'
import debounce from 'lodash.debounce' import debounce from 'lodash.debounce'
import {useCallOnce} from '#/lib/once' import {useCallOnce} from '#/lib/once'
@@ -70,26 +71,37 @@ export function useThreadPreferences({
} }
const userUpdatedPrefs = useRef(false) const userUpdatedPrefs = useRef(false)
const [isSaving, setIsSaving] = useState(false) const {mutate, isPending: isSaving} = useSetThreadViewPreferencesMutation({
const {mutateAsync} = 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(() => { const savePrefs = useMemo(() => {
return debounce(async (prefs: ThreadViewPreferences) => { return debounce(
try { (prefs: ThreadViewPreferences) => {
setIsSaving(true) mutate(prefs)
await mutateAsync(prefs) },
ax.metric('thread:preferences:update', { 2e3,
sort: prefs.sort, {leading: true, trailing: true},
view: prefs.lab_treeViewEnabled ? 'tree' : 'linear', )
}) }, [mutate])
} catch (e) {
ax.logger.error('useThreadPreferences failed to save', { // flush on leave screen
safeMessage: e, useFocusEffect(
}) useCallback(() => {
} finally { return () => {
setIsSaving(false) void savePrefs.flush()
} }
}, 4e3) }, [savePrefs]),
}, [mutateAsync]) )
if (save && userUpdatedPrefs.current) { if (save && userUpdatedPrefs.current) {
savePrefs({ savePrefs({