diff --git a/decompose/api/android/decompose.api b/decompose/api/android/decompose.api index 93b7ddb5..d584f3f7 100644 --- a/decompose/api/android/decompose.api +++ b/decompose/api/android/decompose.api @@ -376,8 +376,10 @@ public final class com/arkivanov/decompose/router/panels/PanelsNavigatorExtKt { } public final class com/arkivanov/decompose/router/panels/PanelsWebNavigationKt { - public static final fun childPanelsWebNavigation (Lcom/arkivanov/decompose/router/panels/PanelsNavigator;Lcom/arkivanov/decompose/value/Value;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/router/webhistory/WebNavigation; - public static synthetic fun childPanelsWebNavigation$default (Lcom/arkivanov/decompose/router/panels/PanelsNavigator;Lcom/arkivanov/decompose/value/Value;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/arkivanov/decompose/router/webhistory/WebNavigation; + public static final fun childPanelsWebNavigation (Lcom/arkivanov/decompose/router/panels/PanelsNavigator;Lcom/arkivanov/decompose/value/Value;Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/router/webhistory/WebNavigation; + public static final fun childPanelsWebNavigation (Lcom/arkivanov/decompose/router/panels/PanelsNavigator;Lcom/arkivanov/decompose/value/Value;Lkotlin/Triple;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/router/webhistory/WebNavigation; + public static synthetic fun childPanelsWebNavigation$default (Lcom/arkivanov/decompose/router/panels/PanelsNavigator;Lcom/arkivanov/decompose/value/Value;Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/arkivanov/decompose/router/webhistory/WebNavigation; + public static synthetic fun childPanelsWebNavigation$default (Lcom/arkivanov/decompose/router/panels/PanelsNavigator;Lcom/arkivanov/decompose/value/Value;Lkotlin/Triple;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/arkivanov/decompose/router/webhistory/WebNavigation; } public final class com/arkivanov/decompose/router/slot/ChildSlot { diff --git a/decompose/api/decompose.klib.api b/decompose/api/decompose.klib.api index 4f410b1a..1337ac98 100644 --- a/decompose/api/decompose.klib.api +++ b/decompose/api/decompose.klib.api @@ -495,7 +495,8 @@ final fun <#A: com.arkivanov.decompose/GenericComponentContext<#A>, #B: kotlin/A final fun <#A: com.arkivanov.decompose/GenericComponentContext<#A>, #B: kotlin/Any, #C: kotlin/Any> (#A).com.arkivanov.decompose.router.stack/childStack(com.arkivanov.decompose.router.children/NavigationSource>, kotlinx.serialization/KSerializer<#B>?, #B, kotlin/String = ..., kotlin/Boolean = ..., kotlin/Function2<#B, #A, #C>): com.arkivanov.decompose.value/Value> // com.arkivanov.decompose.router.stack/childStack|childStack@0:0(com.arkivanov.decompose.router.children.NavigationSource>;kotlinx.serialization.KSerializer<0:1>?;0:1;kotlin.String;kotlin.Boolean;kotlin.Function2<0:1,0:0,0:2>){0§>;1§;2§}[0] final fun <#A: com.arkivanov.decompose/GenericComponentContext<#A>, #B: kotlin/Any, #C: kotlin/Any> (#A).com.arkivanov.decompose.router.stack/childStack(com.arkivanov.decompose.router.children/NavigationSource>, kotlinx.serialization/KSerializer<#B>?, kotlin/Function0>, kotlin/String = ..., kotlin/Boolean = ..., kotlin/Function2<#B, #A, #C>): com.arkivanov.decompose.value/Value> // com.arkivanov.decompose.router.stack/childStack|childStack@0:0(com.arkivanov.decompose.router.children.NavigationSource>;kotlinx.serialization.KSerializer<0:1>?;kotlin.Function0>;kotlin.String;kotlin.Boolean;kotlin.Function2<0:1,0:0,0:2>){0§>;1§;2§}[0] final fun <#A: com.arkivanov.decompose/GenericComponentContext<#A>> (#A).com.arkivanov.decompose/childContext(kotlin/String, com.arkivanov.essenty.lifecycle/Lifecycle? = ...): #A // com.arkivanov.decompose/childContext|childContext@0:0(kotlin.String;com.arkivanov.essenty.lifecycle.Lifecycle?){0§>}[0] -final fun <#A: kotlin/Any, #B: kotlin/Any, #C: kotlin/Any, #D: kotlin/Any, #E: kotlin/Any, #F: kotlin/Any> com.arkivanov.decompose.router.panels/childPanelsWebNavigation(com.arkivanov.decompose.router.panels/PanelsNavigator<#A, #C, #E>, com.arkivanov.decompose.value/Value>, kotlinx.serialization/KSerializer<#A>, kotlinx.serialization/KSerializer<#C>, kotlinx.serialization/KSerializer<#E>, kotlin/Function1, kotlin/String?> = ..., kotlin/Function1, kotlin.collections/Map?> = ..., kotlin/Function0 = ..., kotlin/Function1, com.arkivanov.decompose.router.webhistory/WebNavigationOwner?> = ...): com.arkivanov.decompose.router.webhistory/WebNavigation<*> // com.arkivanov.decompose.router.panels/childPanelsWebNavigation|childPanelsWebNavigation(com.arkivanov.decompose.router.panels.PanelsNavigator<0:0,0:2,0:4>;com.arkivanov.decompose.value.Value>;kotlinx.serialization.KSerializer<0:0>;kotlinx.serialization.KSerializer<0:2>;kotlinx.serialization.KSerializer<0:4>;kotlin.Function1,kotlin.String?>;kotlin.Function1,kotlin.collections.Map?>;kotlin.Function0;kotlin.Function1,com.arkivanov.decompose.router.webhistory.WebNavigationOwner?>){0§;1§;2§;3§;4§;5§}[0] +final fun <#A: kotlin/Any, #B: kotlin/Any, #C: kotlin/Any, #D: kotlin/Any, #E: kotlin/Any, #F: kotlin/Any> com.arkivanov.decompose.router.panels/childPanelsWebNavigation(com.arkivanov.decompose.router.panels/PanelsNavigator<#A, #C, #E>, com.arkivanov.decompose.value/Value>, kotlin/Triple, kotlinx.serialization/KSerializer<#C>, kotlinx.serialization/KSerializer<#E>>, kotlin/Function1, kotlin/String?> = ..., kotlin/Function1, kotlin.collections/Map?> = ..., kotlin/Function0 = ..., kotlin/Function1, com.arkivanov.decompose.router.webhistory/WebNavigationOwner?> = ...): com.arkivanov.decompose.router.webhistory/WebNavigation<*> // com.arkivanov.decompose.router.panels/childPanelsWebNavigation|childPanelsWebNavigation(com.arkivanov.decompose.router.panels.PanelsNavigator<0:0,0:2,0:4>;com.arkivanov.decompose.value.Value>;kotlin.Triple,kotlinx.serialization.KSerializer<0:2>,kotlinx.serialization.KSerializer<0:4>>;kotlin.Function1,kotlin.String?>;kotlin.Function1,kotlin.collections.Map?>;kotlin.Function0;kotlin.Function1,com.arkivanov.decompose.router.webhistory.WebNavigationOwner?>){0§;1§;2§;3§;4§;5§}[0] +final fun <#A: kotlin/Any, #B: kotlin/Any, #C: kotlin/Any, #D: kotlin/Any> com.arkivanov.decompose.router.panels/childPanelsWebNavigation(com.arkivanov.decompose.router.panels/PanelsNavigator<#A, #C, kotlin/Nothing>, com.arkivanov.decompose.value/Value>, kotlin/Pair, kotlinx.serialization/KSerializer<#C>>, kotlin/Function1, kotlin/String?> = ..., kotlin/Function1, kotlin.collections/Map?> = ..., kotlin/Function0 = ..., kotlin/Function1, com.arkivanov.decompose.router.webhistory/WebNavigationOwner?> = ...): com.arkivanov.decompose.router.webhistory/WebNavigation<*> // com.arkivanov.decompose.router.panels/childPanelsWebNavigation|childPanelsWebNavigation(com.arkivanov.decompose.router.panels.PanelsNavigator<0:0,0:2,kotlin.Nothing>;com.arkivanov.decompose.value.Value>;kotlin.Pair,kotlinx.serialization.KSerializer<0:2>>;kotlin.Function1,kotlin.String?>;kotlin.Function1,kotlin.collections.Map?>;kotlin.Function0;kotlin.Function1,com.arkivanov.decompose.router.webhistory.WebNavigationOwner?>){0§;1§;2§;3§}[0] final fun <#A: kotlin/Any, #B: kotlin/Any, #C: kotlin/Any> (com.arkivanov.decompose.router.panels/PanelsNavigator<#A, #B, #C>).com.arkivanov.decompose.router.panels/activateDetails(#B, kotlin/Function2, com.arkivanov.decompose.router.panels/Panels<#A, #B, #C>, kotlin/Unit> = ...) // com.arkivanov.decompose.router.panels/activateDetails|activateDetails@com.arkivanov.decompose.router.panels.PanelsNavigator<0:0,0:1,0:2>(0:1;kotlin.Function2,com.arkivanov.decompose.router.panels.Panels<0:0,0:1,0:2>,kotlin.Unit>){0§;1§;2§}[0] final fun <#A: kotlin/Any, #B: kotlin/Any, #C: kotlin/Any> (com.arkivanov.decompose.router.panels/PanelsNavigator<#A, #B, #C>).com.arkivanov.decompose.router.panels/activateExtra(#C, kotlin/Function2, com.arkivanov.decompose.router.panels/Panels<#A, #B, #C>, kotlin/Unit> = ...) // com.arkivanov.decompose.router.panels/activateExtra|activateExtra@com.arkivanov.decompose.router.panels.PanelsNavigator<0:0,0:1,0:2>(0:2;kotlin.Function2,com.arkivanov.decompose.router.panels.Panels<0:0,0:1,0:2>,kotlin.Unit>){0§;1§;2§}[0] final fun <#A: kotlin/Any, #B: kotlin/Any, #C: kotlin/Any> (com.arkivanov.decompose.router.panels/PanelsNavigator<#A, #B, #C>).com.arkivanov.decompose.router.panels/activateMain(#A, kotlin/Function2, com.arkivanov.decompose.router.panels/Panels<#A, #B, #C>, kotlin/Unit> = ...) // com.arkivanov.decompose.router.panels/activateMain|activateMain@com.arkivanov.decompose.router.panels.PanelsNavigator<0:0,0:1,0:2>(0:0;kotlin.Function2,com.arkivanov.decompose.router.panels.Panels<0:0,0:1,0:2>,kotlin.Unit>){0§;1§;2§}[0] diff --git a/decompose/api/jvm/decompose.api b/decompose/api/jvm/decompose.api index 33d26549..f2059c39 100644 --- a/decompose/api/jvm/decompose.api +++ b/decompose/api/jvm/decompose.api @@ -356,8 +356,10 @@ public final class com/arkivanov/decompose/router/panels/PanelsNavigatorExtKt { } public final class com/arkivanov/decompose/router/panels/PanelsWebNavigationKt { - public static final fun childPanelsWebNavigation (Lcom/arkivanov/decompose/router/panels/PanelsNavigator;Lcom/arkivanov/decompose/value/Value;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/router/webhistory/WebNavigation; - public static synthetic fun childPanelsWebNavigation$default (Lcom/arkivanov/decompose/router/panels/PanelsNavigator;Lcom/arkivanov/decompose/value/Value;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/arkivanov/decompose/router/webhistory/WebNavigation; + public static final fun childPanelsWebNavigation (Lcom/arkivanov/decompose/router/panels/PanelsNavigator;Lcom/arkivanov/decompose/value/Value;Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/router/webhistory/WebNavigation; + public static final fun childPanelsWebNavigation (Lcom/arkivanov/decompose/router/panels/PanelsNavigator;Lcom/arkivanov/decompose/value/Value;Lkotlin/Triple;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;)Lcom/arkivanov/decompose/router/webhistory/WebNavigation; + public static synthetic fun childPanelsWebNavigation$default (Lcom/arkivanov/decompose/router/panels/PanelsNavigator;Lcom/arkivanov/decompose/value/Value;Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/arkivanov/decompose/router/webhistory/WebNavigation; + public static synthetic fun childPanelsWebNavigation$default (Lcom/arkivanov/decompose/router/panels/PanelsNavigator;Lcom/arkivanov/decompose/value/Value;Lkotlin/Triple;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/arkivanov/decompose/router/webhistory/WebNavigation; } public final class com/arkivanov/decompose/router/slot/ChildSlot { diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/panels/ChildPanelsFactory.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/panels/ChildPanelsFactory.kt index fab9a708..6434103f 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/panels/ChildPanelsFactory.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/panels/ChildPanelsFactory.kt @@ -122,7 +122,7 @@ fun , MC : Any, MT : Any, DC : Any, DT : Any, * **It is strongly recommended to call this method on the Main thread.** * * @param source a source of navigation events. - * @param serializers an optional [Pair] of [KSerializer] (Main and Details) to be used for + * @param serializers an optional [Triple] of [KSerializer] (Main, Details and Extra) to be used for * serializing and deserializing configurations. If `null` then the navigation state will not be preserved. * @param initialPanels an initial state of Child Panels that should be set if there is no saved state. * See [Panels] for more information. diff --git a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/panels/PanelsWebNavigation.kt b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/panels/PanelsWebNavigation.kt index 0f54630a..2e7b9357 100644 --- a/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/panels/PanelsWebNavigation.kt +++ b/decompose/src/commonMain/kotlin/com/arkivanov/decompose/router/panels/PanelsWebNavigation.kt @@ -7,8 +7,10 @@ import com.arkivanov.decompose.router.webhistory.WebNavigation.HistoryItem import com.arkivanov.decompose.router.webhistory.WebNavigationOwner import com.arkivanov.decompose.value.Value import com.arkivanov.decompose.value.operator.map +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.NothingSerializer /** * Creates and returns an implementation of [WebNavigation] attached @@ -16,6 +18,9 @@ import kotlinx.serialization.Serializable * changes and manipulates the browser history to match the state. It also * observes the browser history changes and navigates the panels as needed. * + * This function overload is for Child Panels with just two panels: Main and + * Details. For Child Panels with all three panels, use the other overload. + * * The navigation replaces the current entry of the browser history when the * [ChildPanels] state changes. Multiple entries can still be pushed/popped * to/from the browser history at the same time if required to follow the nested @@ -25,9 +30,65 @@ import kotlinx.serialization.Serializable * the user presses the browser back and forward buttons. * @param panels an observable [ChildPanels] that should be bound to the browser * navigation history. - * @param mainSerializer a [KSerializer] of the `main` configuration [MC]. - * @param detailsSerializer a [KSerializer] of the `details` configuration [DC]. - * @param extraSerializer a [KSerializer] of the `extra` configuration [EC]. + * @param serializers a [Pair] of [KSerializer] (Main [MC] and Details [DC]) to be + * used for serializing and deserializing configurations. + * @param pathMapper an optional function that returns a part of the URL path for + * the provided [ChildPanels], or `null` (or empty string) if the path shouldn't be + * affected. The resulting URL path is constructed by concatenating all URL parts + * from every parent and child navigation, top to bottom. Default return value is `null`. + * @param parametersMapper an optional function that returns a [Map] containing all + * required URL parameters for the provided [ChildPanels], or `null` (or empty Map) + * if parameters are not required The resulting URL parameters string is constructed + * by merging all URL parameters from every parent and child navigation. Default + * return value is `null`. + * @param onBeforeNavigate an optional callback that can be used to allow (by + * returning `true`) or deny (by returning `false`) the browser-initiated navigation. + * Can be used e.g. to display an alert dialog before closing a screen. Returns `true` + * by default. + * @param childSelector an optional function that selects and returns a child + * [WebNavigationOwner] for the provided [ChildPanels]. + */ +@ExperimentalSerializationApi +@ExperimentalDecomposeApi +fun childPanelsWebNavigation( + navigator: PanelsNavigator, + panels: Value>, + serializers: Pair, KSerializer>, + pathMapper: (ChildPanels) -> String? = { null }, + parametersMapper: (ChildPanels) -> Map? = { null }, + onBeforeNavigate: () -> Boolean = { true }, + childSelector: (ChildPanels) -> WebNavigationOwner? = { null }, +): WebNavigation<*> = + childPanelsWebNavigation( + navigator = navigator, + panels = panels, + serializers = Triple(serializers.first, serializers.second, NothingSerializer()), + pathMapper = pathMapper, + parametersMapper = parametersMapper, + onBeforeNavigate = onBeforeNavigate, + childSelector = childSelector, + ) + +/** + * Creates and returns an implementation of [WebNavigation] attached + * to the provided [navigator] and [panels]. The navigation observes panel + * changes and manipulates the browser history to match the state. It also + * observes the browser history changes and navigates the panels as needed. + * + * This function overload is for Child Panels with all three panels: Main and + * Details. For Child Panels with just two panels, use the other overload. + * + * The navigation replaces the current entry of the browser history when the + * [ChildPanels] state changes. Multiple entries can still be pushed/popped + * to/from the browser history at the same time if required to follow the nested + * navigation. + * + * @param navigator a [PagesNavigator] that should be used for navigation when + * the user presses the browser back and forward buttons. + * @param panels an observable [ChildPanels] that should be bound to the browser + * navigation history. + * @param serializers a [Triple] of [KSerializer] (Main [MC], Details [DC] and + * Extra [EC]) to be used for serializing and deserializing configurations. * @param pathMapper an optional function that returns a part of the URL path for * the provided [ChildPanels], or `null` (or empty string) if the path shouldn't be * affected. The resulting URL path is constructed by concatenating all URL parts @@ -48,9 +109,7 @@ import kotlinx.serialization.Serializable fun childPanelsWebNavigation( navigator: PanelsNavigator, panels: Value>, - mainSerializer: KSerializer, - detailsSerializer: KSerializer, - extraSerializer: KSerializer, + serializers: Triple, KSerializer, KSerializer>, pathMapper: (ChildPanels) -> String? = { null }, parametersMapper: (ChildPanels) -> Map? = { null }, onBeforeNavigate: () -> Boolean = { true }, @@ -59,9 +118,9 @@ fun childPanelsWebN PanelsWebNavigation( navigator = navigator, panels = panels, - mainSerializer = mainSerializer, - detailsSerializer = detailsSerializer, - extraSerializer = extraSerializer, + mainSerializer = serializers.first, + detailsSerializer = serializers.second, + extraSerializer = serializers.third, pathMapper = pathMapper, parametersMapper = parametersMapper, onBeforeNavigate = onBeforeNavigate, diff --git a/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/DefaultMultiPaneComponent.kt b/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/DefaultMultiPaneComponent.kt index 8403cafd..ba407682 100644 --- a/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/DefaultMultiPaneComponent.kt +++ b/sample/shared/shared/src/commonMain/kotlin/com/arkivanov/sample/shared/multipane/DefaultMultiPaneComponent.kt @@ -47,7 +47,7 @@ internal class DefaultMultiPaneComponent( childPanels( source = navigation, initialPanels = { getInitialPanels(deepLinkUrl) }, - serializers = Triple(Unit.serializer(), Details.serializer(), Extra.serializer()), + serializers = SERIALIZERS, onStateChanged = { newState, _ -> _navState.onNext(newState) }, handleBackButton = true, mainFactory = { _, ctx -> listComponent(ctx) }, @@ -61,9 +61,7 @@ internal class DefaultMultiPaneComponent( childPanelsWebNavigation( navigator = navigation, panels = _panels, - mainSerializer = Unit.serializer(), - detailsSerializer = Details.serializer(), - extraSerializer = Extra.serializer(), + serializers = SERIALIZERS, parametersMapper = { panels -> panels.details?.let { mapOf(KEY_ARTICLE_ID to it.configuration.articleId.toString()) @@ -131,6 +129,7 @@ internal class DefaultMultiPaneComponent( private companion object { private const val KEY_ARTICLE_ID = "articleId" + private val SERIALIZERS = Triple(Unit.serializer(), Details.serializer(), Extra.serializer()) } @Serializable