Add a mutation queue to fix race conditions in toggles (#1933)

* Prototype a queue

* Track both current and pending actions

* Skip unnecessary actions

* Commit last confirmed state to shadow

* Thread state through actions over time

* Fix the logic to skip redundant mutations

* Track status

* Extract an abstraction

* Fix standalone mutations

* Add types

* Move to another file

* Return stable function

* Clean up

* Use queue for muting

* Use queue for blocking

* Convert other follow buttons

* Don't export non-queue mutations

* Properly handle canceled tasks

* Fix copy paste
This commit is contained in:
dan 2023-11-16 22:01:01 +00:00 committed by GitHub
parent 54faa7e176
commit 8475312422
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 453 additions and 188 deletions

View file

@ -26,10 +26,7 @@ import {isWeb} from 'platform/detection'
import {useModerationOpts} from '#/state/queries/preferences'
import {useSuggestedFollowsByActorQuery} from '#/state/queries/suggested-follows'
import {useProfileShadow} from '#/state/cache/profile-shadow'
import {
useProfileFollowMutation,
useProfileUnfollowMutation,
} from '#/state/queries/profile'
import {useProfileFollowMutationQueue} from '#/state/queries/profile'
const OUTER_PADDING = 10
const INNER_PADDING = 14
@ -208,34 +205,28 @@ function SuggestedFollow({
const pal = usePalette('default')
const moderationOpts = useModerationOpts()
const profile = useProfileShadow(profileUnshadowed, dataUpdatedAt)
const followMutation = useProfileFollowMutation()
const unfollowMutation = useProfileUnfollowMutation()
const [queueFollow, queueUnfollow] = useProfileFollowMutationQueue(profile)
const onPressFollow = React.useCallback(async () => {
if (profile.viewer?.following) {
return
}
try {
track('ProfileHeader:SuggestedFollowFollowed')
await followMutation.mutateAsync({did: profile.did})
await queueFollow()
} catch (e: any) {
Toast.show('An issue occurred, please try again.')
if (e?.name !== 'AbortError') {
Toast.show('An issue occurred, please try again.')
}
}
}, [followMutation, profile, track])
}, [queueFollow, track])
const onPressUnfollow = React.useCallback(async () => {
if (!profile.viewer?.following) {
return
}
try {
await unfollowMutation.mutateAsync({
did: profile.did,
followUri: profile.viewer?.following,
})
await queueUnfollow()
} catch (e: any) {
Toast.show('An issue occurred, please try again.')
if (e?.name !== 'AbortError') {
Toast.show('An issue occurred, please try again.')
}
}
}, [unfollowMutation, profile])
}, [queueUnfollow])
if (!moderationOpts) {
return null
@ -284,7 +275,6 @@ function SuggestedFollow({
type="inverted"
labelStyle={{textAlign: 'center'}}
onPress={following ? onPressUnfollow : onPressFollow}
withLoading
/>
</View>
</Link>