Skip to content

CircuitX Navigation#1669

Merged
stagg merged 33 commits intomainfrom
j-circuitx-navigation
May 4, 2025
Merged

CircuitX Navigation#1669
stagg merged 33 commits intomainfrom
j-circuitx-navigation

Conversation

@stagg
Copy link
Copy Markdown
Collaborator

@stagg stagg commented Sep 20, 2024

Upstreaming a chunk of our internal circuit navigator which provides apis for intercepting navigation and navigation event listening. This extends the core Circuit navigation system by adding support for navigation interceptors, navigation event listeners, and intercept failure reporting.

CircuitInterceptingNavigator

  • CircuitInterceptingNavigator acts as a wrapper around a standard Circuit Navigator.
  • It allows for the interception of navigation events (goTo, pop, resetRoot) before they are handled by the underlying Navigator.
  • rememberCircuitInterceptingNavigator was added to create and manage the CircuitInterceptingNavigator instance to correctly capture the various navigation events.

CircuitNavigationInterceptor

  • The interface mirrors the possible navigator methods (goTo, pop, resetRoot)
  • Each interceptor can return an InterceptorResult which is handled by the CircuitInterceptingNavigator, if the result is consumed no other interceptor can intercept the result
  • Possible InterceptorResult's:
    • Skipped: The interceptor isn't interested in this navigation event.
    • Success: The interceptor has handled the event successfully (optionally consuming it).
    • Failure: The interceptor has failed to handle the event (optionally consuming it).
    • Rewrite: Used only by goTo and resetRoot, allowing the interceptor to replace the navigated screen with another.

FailureNotifier

  • FailureNotifier provides a way to handle InterceptorResult.Failure events for any failed intercepted navigation

CircuitNavigationEventListener

  • When the navigators goTo, pop or resetRoot methods are called, and not intercepted, the listeners are notified.
    • Also the listener can be notified if any change happens to the backstack.
  • Unlike interceptors, listeners cannot modify navigation; they simply receive notifications. This is useful for logging, analytics, or other side effects.

PR todos

  • Update docs (Branch)
  • Add a sample for logging/reporting
  • Add tests for intercepting (Branch)

@stagg stagg mentioned this pull request Apr 17, 2025
github-merge-queue bot pushed a commit that referenced this pull request Apr 17, 2025
- Little clearer what the sample is for 
- Avoids a name collision with the api validation and #1669
Copy link
Copy Markdown

@nesfeder nesfeder left a comment

Choose a reason for hiding this comment

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

This looks great @stagg, thank you!


// CircuitX Navigation
val interceptors =
persistentListOf(AndroidScreenAwareNavigationInterceptor(this), InfoScreenRewriteInterceptor)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Okay nice! So the idea here is that AndroidScreenAwareNavigationInterceptor will remove the need to

val backStack = rememberSaveableBackStack(root = root)
val delegate = rememberCircuitNavigator(backStack)
val navigator = rememberAndroidScreenAwareNavigator(delegate, this@MainActivity)
val interceptingNavigator = rememberCircuitInterceptingNavigator(
    navigator = navigator,
    ...
)

if we're already using a AndroidScreenAwareNavigator?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Correct, using the interceptor provides an alternative to the nested delegation approach.

@stagg stagg marked this pull request as ready for review April 30, 2025 21:23
@stagg stagg requested a review from ZacSweers April 30, 2025 21:23
Copy link
Copy Markdown
Collaborator

@ZacSweers ZacSweers left a comment

Choose a reason for hiding this comment

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

Couple high-level thoughts mostly focused around simplifying the naming. I think some of the "Circuit" in the names probably comes from our internal disambiguation but is a little redundant in first-party circuit APIs?

  • Rename CircuitNavigationInterceptor -> NavigationInterceptor
    • rememberCircuitInterceptingNavigator -> rememberInterceptingNavigator
  • Rename CircuitNavigationEventListener -> NavigationEventListener

import kotlinx.collections.immutable.ImmutableList

/** A [CircuitNavigationEventListener] that adds Logcat logging for Circuit navigation. */
public object LoggingNavigationEventListener : CircuitNavigationEventListener {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we hoist the impl into commonMain and then supply a logcat impl option? Similar to how Timber has a LogcatTree

* A [CircuitInterceptingNavigator.FailureNotifier] that adds Logcat logging for CircuitX navigation
* interception failures.
*/
public object LoggingNavigatorFailureNotifier : CircuitInterceptingNavigator.FailureNotifier {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

same here re: common impl + logcat helper

* Notifies of a [InterceptorResult.Failure] from a [CircuitNavigationInterceptor] during a
* [CircuitNavigationInterceptor.goTo].
*/
public fun goToInterceptorFailure(interceptorResult: InterceptorResult.Failure)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: goToFailure? Since this is already being used in the context of an interceptor

}

/** The result of [CircuitNavigationInterceptor.goTo] being intercepted. */
public sealed interface InterceptorGoToResult {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: Intercepted?

@stagg stagg added this pull request to the merge queue May 4, 2025
Merged via the queue into main with commit 75ae14e May 4, 2025
5 checks passed
@stagg stagg deleted the j-circuitx-navigation branch May 4, 2025 04:00
github-merge-queue bot pushed a commit that referenced this pull request May 4, 2025
Follow up docs for #1669 

<img width="1373" alt="Screenshot 2025-05-04 at 09 26 22"
src="https://github.com/user-attachments/assets/d9f9fde9-3a60-499f-aa8a-92a5fde08292"
/>
github-merge-queue bot pushed a commit that referenced this pull request May 4, 2025
Follow up testing for #1669

- Adds tests for the `NavigationEventListener`, `FailureNotifier`, and
`NavigationInterceptor`
- The `NavigationInterceptorTest` is the bulk of the PR, with tests for
single and multiple interceptor uses
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.

3 participants