Skip to content

Fixed the same predictive back gesture animation repeating when quickly started again#988

Merged
arkivanov merged 1 commit into
masterfrom
fixed-back-gesture-repeat
Apr 24, 2026
Merged

Fixed the same predictive back gesture animation repeating when quickly started again#988
arkivanov merged 1 commit into
masterfrom
fixed-back-gesture-repeat

Conversation

@arkivanov

@arkivanov arkivanov commented Mar 18, 2026

Copy link
Copy Markdown
Owner

If you start a new predictive back gesture before the current gesture is fully finished, the same animation is started again. Instead, it should do nothing and allow the current animation to finish.

Summary by CodeRabbit

  • Refactor

    • Improved predictive back gesture handling with a clearer state machine for more reliable interruption, finishing, and re-entry behavior.
    • Switched animation item keys to a consistent string-based identification for better compatibility.
  • Tests

    • Added tests covering finishing + new-gesture/back interactions to prevent duplicate animations and unintended stack pops.

@coderabbitai

coderabbitai Bot commented Mar 18, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Reworks PredictiveBack lifecycle in DefaultStackAnimation by replacing nullable handlers with a sealed State machine (Idle/Started/Progress/Finishing), switching keyed item collections to String keys, adding four exported stable properties for state variants, and extending tests with coroutine-capable animatable finishing and new restart/collision scenarios.

Changes

Cohort / File(s) Summary
API Declarations
extensions-compose-experimental/api/extensions-compose-experimental.klib.api
Added four exported stable properties and corresponding getter functions for DefaultStackAnimation state variants (State_Finishing, State_Idle, State_Progress, State_Started).
DefaultStackAnimation core
extensions-compose-experimental/src/commonMain/kotlin/.../stack/animation/DefaultStackAnimation.kt
Refactored predictive-back lifecycle to an explicit sealed State (Idle/Started/Progress/Finishing); replaced Any keys with String keys for animation item collections; updated event handlers to enforce state-guarded transitions and update items via getAnimationItems(...) at the correct points.
Predictive back tests
extensions-compose-experimental/src/jvmTest/kotlin/.../stack/animation/PredictiveBackGestureTest.kt
Enhanced test animatable to accept an optional suspending finish callback; added tests ensuring no new animatable is created while a finishing animatable is completing and that invoking back() during finishing does not pop the stack; minor formatting tweak in instantiation.

Sequence Diagram

sequenceDiagram
    actor User
    participant Client
    participant PredictiveBackCallback
    participant StateMachine
    participant AnimationHandler
    participant Stack

    User->>Client: start back gesture
    Client->>PredictiveBackCallback: onBackStarted()
    PredictiveBackCallback->>StateMachine: transition -> Started
    StateMachine->>AnimationHandler: create/start handler
    AnimationHandler->>Stack: compute animation items

    loop gesture moves
        User->>Client: gesture progress
        Client->>PredictiveBackCallback: onBackProgressed(progress)
        PredictiveBackCallback->>StateMachine: (must be Progress) update progress
        StateMachine->>AnimationHandler: update progress
    end

    alt user cancels
        User->>Client: cancel gesture
        Client->>PredictiveBackCallback: onBackCancelled()
        PredictiveBackCallback->>StateMachine: transition -> Idle
        StateMachine->>AnimationHandler: cancel handler
        AnimationHandler->>Stack: restore current items
    else user completes
        User->>Client: complete gesture
        Client->>PredictiveBackCallback: onBack()
        PredictiveBackCallback->>StateMachine: if Progress -> Finishing -> Idle
        StateMachine->>AnimationHandler: finish handler
        AnimationHandler->>Stack: finalize items and invoke onBack()
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main issue being fixed: preventing predictive back gesture animations from repeating when started again during an ongoing animation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fixed-back-gesture-repeat

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@arkivanov arkivanov force-pushed the fixed-back-gesture-repeat branch from f2f44f7 to be43189 Compare March 18, 2026 05:45
@arkivanov arkivanov force-pushed the fixed-back-gesture-repeat branch 2 times, most recently from b10d1ac to 4f20870 Compare April 20, 2026 20:45
@arkivanov arkivanov marked this pull request as ready for review April 20, 2026 22:55

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@extensions-compose-experimental/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/DefaultStackAnimation.kt`:
- Around line 298-327: The bug is that onBackCancelled() and onBack() allow a
second predictive gesture to interrupt or duplicate the finishing coroutine;
update both handlers to treat State.Finishing as sticky by ignoring terminal
events while finishing: in onBackCancelled() only act when currentState is
State.Progress (leave state as Finishing and do not reset to Idle if
currentState is Finishing), and in onBack() only start the finish flow when
currentState is State.Progress; if currentState is State.Finishing simply return
(do not call predictiveBackParams.onBack or mutate state); preserve the existing
scope.launch logic that cancels/finishes currentState.animationHandler and then
sets state = State.Idle and calls setItems(getAnimationItems(...)) so the
finishing coroutine still completes uninterrupted.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3cb91604-bd9e-4ff2-a88f-f6b00e5de780

📥 Commits

Reviewing files that changed from the base of the PR and between a0ea252 and 985ec4a.

📒 Files selected for processing (3)
  • extensions-compose-experimental/api/extensions-compose-experimental.klib.api
  • extensions-compose-experimental/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/DefaultStackAnimation.kt
  • extensions-compose-experimental/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/PredictiveBackGestureTest.kt

…ly started again

If you start a new predictive back gesture before the current gesture is fully finished, the same animation is started again. Instead, it should do nothing and allow the current animation to finish.
@arkivanov arkivanov force-pushed the fixed-back-gesture-repeat branch from 985ec4a to efaa7bb Compare April 24, 2026 19:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
extensions-compose-experimental/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/PredictiveBackGestureTest.kt (1)

538-576: Remove the unused animationCount from this test.

animationCount is declared at line 541 and incremented at line 546, but never asserted in this test — it looks like a leftover from copy‑pasting GIVEN_gesture_finishing_WHEN_new_gesture_started_THEN_new_animation_not_started. The scenario here only needs to verify the stack isn't popped, so the counter (and the related animatable side effect) is dead code.

♻️ Proposed cleanup
     `@Test`
     fun GIVEN_gesture_finishing_WHEN_back_THEN_stack_not_popped() {
         var stack by mutableStateOf(stack("1", "2"))
-        var animationCount = 0
 
         val animation =
             DefaultStackAnimation(
                 predictiveBackAnimatable = {
-                    animationCount++
-
                     TestAnimatable(
                         initialBackEvent = it,
                         finish = {
                             suspendCancellableCoroutine {
                                 // Simulate a long-running animation
                             }
                         },
                     )
                 },
                 onBack = { stack = stack.dropLast() },
             )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@extensions-compose-experimental/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/PredictiveBackGestureTest.kt`
around lines 538 - 576, Remove the unused animationCount and its side-effect in
the test GIVEN_gesture_finishing_WHEN_back_THEN_stack_not_popped: delete the
declaration var animationCount and the animationCount++ inside the
predictiveBackAnimatable lambda passed to DefaultStackAnimation (which
constructs the TestAnimatable), leaving the TestAnimatable/finish behavior and
onBack logic intact so the test only verifies the stack isn't popped.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@extensions-compose-experimental/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/PredictiveBackGestureTest.kt`:
- Around line 538-576: Remove the unused animationCount and its side-effect in
the test GIVEN_gesture_finishing_WHEN_back_THEN_stack_not_popped: delete the
declaration var animationCount and the animationCount++ inside the
predictiveBackAnimatable lambda passed to DefaultStackAnimation (which
constructs the TestAnimatable), leaving the TestAnimatable/finish behavior and
onBack logic intact so the test only verifies the stack isn't popped.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c8968cc9-352d-480a-9e87-5b0def0fd8b7

📥 Commits

Reviewing files that changed from the base of the PR and between 985ec4a and efaa7bb.

📒 Files selected for processing (3)
  • extensions-compose-experimental/api/extensions-compose-experimental.klib.api
  • extensions-compose-experimental/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/DefaultStackAnimation.kt
  • extensions-compose-experimental/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/PredictiveBackGestureTest.kt
✅ Files skipped from review due to trivial changes (1)
  • extensions-compose-experimental/api/extensions-compose-experimental.klib.api

@arkivanov arkivanov merged commit e2b5725 into master Apr 24, 2026
3 checks passed
@arkivanov arkivanov deleted the fixed-back-gesture-repeat branch April 24, 2026 20:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant