fix: stop losing drag activations that no gesture callback will recover - #610
Draft
MatiPl01 wants to merge 5 commits into
Draft
fix: stop losing drag activations that no gesture callback will recover#610MatiPl01 wants to merge 5 commits into
MatiPl01 wants to merge 5 commits into
Conversation
Drag cleanup ran only from the onTouchesUp and onTouchesCancelled touch callbacks. Those are not guaranteed to arrive when a drag ends for a reason other than the user lifting a finger, such as another gesture winning or the handler being cancelled. When they are skipped, handleDragEnd never runs, so the item keeps a non-zero activation progress and stays the active item, and the activation gate then refuses to start a drag on it ever again. Wire the cleanup to onFinalize, which gesture-handler dispatches from handler state transitions for END, FAILED and CANCELLED alike. The touch callbacks keep running the same cleanup so a touch released before the activation delay still cancels its pending activation timeout.
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
onFinalize covers a drag that ends without the user lifting a finger, but a handler detached mid-gesture reaches no terminal state and emits no callback at all, so cleanup cannot depend on it alone. Three activation paths could silently strand the container with nothing on screen to show for it: - a handler re-registering mid-touch replayed onTouchesDown, and re-arming the activation timeout on every replay pushed activation out of reach, so the item never lifted; - the pending activation lived in a single container-wide slot that any item could clear, so a sibling ending its own gesture revoked the activation the finger was waiting on - onFinalize made that far more frequent; - the activation timeout returned without failing the gesture when the item had no measured position or dimensions. Give the pending activation an owner so only that item can cancel it, keep a replayed touch stream from restarting or killing a drag, fail the gesture instead of returning silently, and discard activation state that outlived the drag that created it. Also memoize the v3 gesture config so it is pushed to the native handler once rather than on every render.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR started from the assumption that
onFinalizealways runs when a drag terminates, so wiring cleanup to it would cover the cases the touch callbacks miss. That assumption is wrong. Measured on iOS with gesture-handler 3.0.2:onTouchesDown,onTouchesUponTouchesDown,onTouchesUponTouchesDown,onTouchesUp,onFinalizeonTouchesDown,onTouchesCancelled,onFinalizeA touch that ends before the item activates never reaches
onFinalize, and a handler detached mid-gesture reaches no terminal state and emits nothing at all.onFinalizestays as a safety net for terminations the touch callbacks miss, but it cannot be the only cleanup path: moving cleanup out of the touch callbacks leaves a tapped item lifted with no finger down and the container unable to sort.Looking for what actually strands the container turned up three activation paths that die leaving nothing on screen to show for it, which matches reports of tiles that stop responding after a re-render while still rendering normally:
onTouchesDownfor the finger already down, and re-arming the activation timeout on every replay pushed activation out of reach, so the item never lifted;onFinalizerunshandleDragEndon every failed touch, which made this far more frequent;The pending activation now has an owner, so only that item can cancel it. A replayed touch stream neither restarts nor kills a drag in progress. The timeout fails the gesture instead of returning silently. Activation state that outlived the drag that created it is discarded on the next touch. The v3 gesture config is also memoized so it reaches the native handler once instead of on every render.
dragActivationRecovery.test.tsxcovers these, six of its cases failing without the change. Verified on the iOS simulator on the new architecture with gesture-handler v3: consecutive drags, drags after re-render-forcing data changes, and custom drag handles all sort, and a tap leaves the grid untouched.Known limitation: every failure here comes from gesture-handler not delivering a callback, and nothing in JS can recover a handler that is detached and never re-attached.