From 0223b49e3bb3e3da0d1b918ad4fbc0aa12d0d97c Mon Sep 17 00:00:00 2001 From: Isaac Udy Date: Wed, 22 May 2024 00:52:25 +1200 Subject: [PATCH] Mark NavigationFlow.update as public again, and add a documentation comment explaining what it should be used for. --- CHANGELOG.md | 3 ++ .../core/result/flows/FlowResultChannel.kt | 53 ++++++++++++++++++- .../enro/core/result/flows/FlowStepActions.kt | 2 +- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc2f1ef1..bfc6e74d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 2.4.2 +* Added `update` to the public API for `NavigationFlow`, as this is required for some use cases where the flow needs to be updated after changes in external state which may affect the logic of the flow. This function was previously named `next`, and removed from the public API in 2.4.0. + ## 2.4.1 * Added `EnroBackConfiguration`, which can be set when creating a `NavigationController`. This controls how Enro handles back presses. * EnroBackConfiguration.Default will use the behavior that has been standard in Enro until this point diff --git a/enro-core/src/main/java/dev/enro/core/result/flows/FlowResultChannel.kt b/enro-core/src/main/java/dev/enro/core/result/flows/FlowResultChannel.kt index 496034dc..73198590 100644 --- a/enro-core/src/main/java/dev/enro/core/result/flows/FlowResultChannel.kt +++ b/enro-core/src/main/java/dev/enro/core/result/flows/FlowResultChannel.kt @@ -63,7 +63,49 @@ public class NavigationFlow internal constructor( } } - internal fun update() { + /** + * This method is used to cause the flow to re-evaluate it's current state. This happens once automatically when the flow + * is created, and then once every time a step returns a result, or a step is edited using [FlowStepActions]. However, you + * may want to call this method yourself if there is external state that may have changed that would affect the flow. + * + * An example of where this is useful is if you wanted to call a suspending function, and then update external state + * based on the result of the suspending function before resuming the flow. An example of how this would work: + * ``` + * class ExampleViewModel( + * savedStateHandle: SavedStateHandle, + * ) : ViewModel() { + * private var suspendingFunctionInvoked = false + * + * private val resultFlow by registerForFlowResult( + * savedStateHandle = savedStateHandle, + * flow = { + * // ... + * if (!suspendingFunctionInvoked) { + * performSuspendingAction() + * escape() + * } + * // ... + * }, + * onCompleted = { /*...*/ } + * ) + * + * private fun performSuspendingAction() { + * viewModelScope.launch { + * delay(1000) + * suspendingFunctionInvoked = true + * resultFlow.update() + * } + * } + * } + * ``` + * + * In the example above, when the if statement is reached, and "suspendingFunctionInvoked" is not true, the flow will call + * "performSuspendingAction" and then immediately call "escape". The call to "escape" will cause the flow to stop evaluating + * which steps should occur. The call to [update] inside "performSuspendingAction" will cause the flow to re-evaluate + * the state, and continue the flow from where it left off. Because "suspendingFunctionInvoked" will have been set to true, + * the flow won't execute that if statement, and will instead continue on to whatever logic is next. + */ + public fun update() { val flowScope = NavigationFlowScope(this, resultManager, reference) runCatching { return@update onCompleted(flowScope.flow()) } .recover { @@ -127,6 +169,15 @@ public class NavigationFlow internal constructor( } } +/** + * This method creates a NavigationFlow in the scope of a ViewModel. There can only be one NavigationFlow created within each + * NavigationDestination. The [flow] lambda will be invoked multiple times over the lifecycle of the NavigationFlow, and should + * generally not cause external side effects. The [onCompleted] lambda will be invoked when the flow completes and returns a + * result. + * + * [NavigationFlow.update] is triggered automatically as part of this function, you do not need to manually call update to + * begin the flow. + */ public fun ViewModel.registerForFlowResult( savedStateHandle: SavedStateHandle, flow: NavigationFlowScope.() -> T, diff --git a/enro-core/src/main/java/dev/enro/core/result/flows/FlowStepActions.kt b/enro-core/src/main/java/dev/enro/core/result/flows/FlowStepActions.kt index 10664c59..a7b4a500 100644 --- a/enro-core/src/main/java/dev/enro/core/result/flows/FlowStepActions.kt +++ b/enro-core/src/main/java/dev/enro/core/result/flows/FlowStepActions.kt @@ -31,7 +31,7 @@ public class FlowStepActions>( } /** - * Triggers editing of the step in the NavigationFlow. This clears the result, and immediately triggers an update on + * Triggers editing of the step in the NavigationFlow. This clears the result, and immediately triggers an [update] on * the flow. * * If you want to cause multiple steps to be cleared before editing, you should call [clearResult] on each step before