diff --git a/gradle/tiamat.toml b/gradle/tiamat.toml index cd489f0e..29da277b 100644 --- a/gradle/tiamat.toml +++ b/gradle/tiamat.toml @@ -1,5 +1,5 @@ [versions] -tiamat = "2.2.0" +tiamat = "2.3.0" minSdk = "21" compileSdk = "36" diff --git a/sample/shared/src/commonMain/kotlin/composegears/tiamat/sample/App.kt b/sample/shared/src/commonMain/kotlin/composegears/tiamat/sample/App.kt index 9fe17bde..1d35c3eb 100644 --- a/sample/shared/src/commonMain/kotlin/composegears/tiamat/sample/App.kt +++ b/sample/shared/src/commonMain/kotlin/composegears/tiamat/sample/App.kt @@ -58,4 +58,4 @@ data class PlatformFeatures( @Composable private fun AppPreview() { App() -} +} \ No newline at end of file diff --git a/tiamat-destinations/tiamat-destinations/src/commonMain/kotlin/com/composegears/tiamat/destinations/TiamatGraph.kt b/tiamat-destinations/tiamat-destinations/src/commonMain/kotlin/com/composegears/tiamat/destinations/TiamatGraph.kt index b5c88212..f7de647d 100644 --- a/tiamat-destinations/tiamat-destinations/src/commonMain/kotlin/com/composegears/tiamat/destinations/TiamatGraph.kt +++ b/tiamat-destinations/tiamat-destinations/src/commonMain/kotlin/com/composegears/tiamat/destinations/TiamatGraph.kt @@ -1,5 +1,6 @@ package com.composegears.tiamat.destinations +import androidx.compose.runtime.Stable import com.composegears.tiamat.TiamatExperimentalApi import com.composegears.tiamat.navigation.NavDestination @@ -9,6 +10,7 @@ import com.composegears.tiamat.navigation.NavDestination * Graphs can be created by implementing this interface directly or by using * the annotation processor with the [InstallIn] annotation. */ +@Stable @SubclassOptInRequired(TiamatExperimentalApi::class) public interface TiamatGraph { /** diff --git a/tiamat/api/jvm/tiamat.api b/tiamat/api/jvm/tiamat.api index 79aafeba..ed5328db 100644 --- a/tiamat/api/jvm/tiamat.api +++ b/tiamat/api/jvm/tiamat.api @@ -1,6 +1,9 @@ public abstract interface annotation class com/composegears/tiamat/TiamatExperimentalApi : java/lang/annotation/Annotation { } +public abstract interface annotation class com/composegears/tiamat/TiamatUnsafeApi : java/lang/annotation/Annotation { +} + public final class com/composegears/tiamat/UtilsKt { public static final fun toHumanReadableString (Ljava/util/Map;Ljava/lang/String;I)Ljava/lang/String; public static synthetic fun toHumanReadableString$default (Ljava/util/Map;Ljava/lang/String;IILjava/lang/Object;)Ljava/lang/String; @@ -33,21 +36,21 @@ public final class com/composegears/tiamat/compose/ComposableNavDestinationKt { } public final class com/composegears/tiamat/compose/ComposableNavigationKt { - public static final fun Navigation (Lcom/composegears/tiamat/navigation/NavController;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/Modifier;ZLkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V + public static final fun Navigation (Lcom/composegears/tiamat/navigation/NavController;Lcom/composegears/tiamat/compose/DestinationLoader;Landroidx/compose/ui/Modifier;ZLkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V public static final fun Navigation (Lcom/composegears/tiamat/navigation/NavController;[Lcom/composegears/tiamat/navigation/NavDestination;Landroidx/compose/ui/Modifier;ZLkotlin/jvm/functions/Function1;Landroidx/compose/runtime/Composer;II)V - public static final fun NavigationScene (Lcom/composegears/tiamat/navigation/NavController;Lkotlin/jvm/functions/Function1;ZLkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V + public static final fun NavigationScene (Lcom/composegears/tiamat/navigation/NavController;Lcom/composegears/tiamat/compose/DestinationLoader;ZLkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V public static final fun NavigationScene (Lcom/composegears/tiamat/navigation/NavController;[Lcom/composegears/tiamat/navigation/NavDestination;ZLkotlin/jvm/functions/Function3;Landroidx/compose/runtime/Composer;II)V public static final fun getLocalNavAnimatedVisibilityScope ()Landroidx/compose/runtime/ProvidableCompositionLocal; } public final class com/composegears/tiamat/compose/ComposablePreviewKt { - public static final fun TiamatPreview (Lcom/composegears/tiamat/navigation/NavDestination;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Landroidx/compose/runtime/Composer;II)V + public static final fun TiamatPreview (Lcom/composegears/tiamat/navigation/NavDestination;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Landroidx/compose/ui/Modifier;Landroidx/compose/runtime/Composer;II)V } public final class com/composegears/tiamat/compose/ComposableSingletons$ComposableNavigationKt { public static final field INSTANCE Lcom/composegears/tiamat/compose/ComposableSingletons$ComposableNavigationKt; public fun ()V - public final fun getLambda$-1750091015$tiamat ()Lkotlin/jvm/functions/Function3; + public final fun getLambda$200833937$tiamat ()Lkotlin/jvm/functions/Function3; } public final class com/composegears/tiamat/compose/ComposeNavDestination : com/composegears/tiamat/navigation/NavDestination { @@ -72,6 +75,34 @@ public final class com/composegears/tiamat/compose/ContentExtension$Type : java/ public static fun values ()[Lcom/composegears/tiamat/compose/ContentExtension$Type; } +public abstract interface class com/composegears/tiamat/compose/DestinationLoader { + public static final field Companion Lcom/composegears/tiamat/compose/DestinationLoader$Companion; + public abstract fun load (Ljava/lang/String;)Lcom/composegears/tiamat/navigation/NavDestination; +} + +public final class com/composegears/tiamat/compose/DestinationLoader$ByKey : com/composegears/tiamat/compose/DestinationLoader { + public static final field $stable I + public fun (Lkotlin/jvm/functions/Function1;)V + public fun load (Ljava/lang/String;)Lcom/composegears/tiamat/navigation/NavDestination; +} + +public final class com/composegears/tiamat/compose/DestinationLoader$Companion { + public final fun byKey (Lkotlin/jvm/functions/Function1;)Lcom/composegears/tiamat/compose/DestinationLoader; + public final fun from ([Lcom/composegears/tiamat/navigation/NavDestination;)Lcom/composegears/tiamat/compose/DestinationLoader; +} + +public final class com/composegears/tiamat/compose/DestinationLoader$DoNotLoad : com/composegears/tiamat/compose/DestinationLoader { + public static final field $stable I + public static final field INSTANCE Lcom/composegears/tiamat/compose/DestinationLoader$DoNotLoad; + public fun load (Ljava/lang/String;)Lcom/composegears/tiamat/navigation/NavDestination; +} + +public final class com/composegears/tiamat/compose/DestinationLoader$FromArray : com/composegears/tiamat/compose/DestinationLoader { + public static final field $stable I + public fun ([Lcom/composegears/tiamat/navigation/NavDestination;)V + public fun load (Ljava/lang/String;)Lcom/composegears/tiamat/navigation/NavDestination; +} + public final class com/composegears/tiamat/compose/NavActionsKt { public static final fun back (Lcom/composegears/tiamat/navigation/NavController;Lcom/composegears/tiamat/navigation/NavDestination;Ljava/lang/Object;ZZLandroidx/compose/animation/ContentTransform;Lcom/composegears/tiamat/compose/TransitionController;)Z public static synthetic fun back$default (Lcom/composegears/tiamat/navigation/NavController;Lcom/composegears/tiamat/navigation/NavDestination;Ljava/lang/Object;ZZLandroidx/compose/animation/ContentTransform;Lcom/composegears/tiamat/compose/TransitionController;ILjava/lang/Object;)Z @@ -229,6 +260,7 @@ public abstract class com/composegears/tiamat/navigation/NavDestination : com/co public static final field $stable I public static final field Companion Lcom/composegears/tiamat/navigation/NavDestination$Companion; public fun (Ljava/lang/String;Lkotlin/reflect/KType;)V + public final fun getKey ()Ljava/lang/String; public final fun getName ()Ljava/lang/String; } diff --git a/tiamat/api/tiamat.klib.api b/tiamat/api/tiamat.klib.api index 844e8b5a..560aaf84 100644 --- a/tiamat/api/tiamat.klib.api +++ b/tiamat/api/tiamat.klib.api @@ -10,6 +10,10 @@ open annotation class com.composegears.tiamat/TiamatExperimentalApi : kotlin/Ann constructor () // com.composegears.tiamat/TiamatExperimentalApi.|(){}[0] } +open annotation class com.composegears.tiamat/TiamatUnsafeApi : kotlin/Annotation { // com.composegears.tiamat/TiamatUnsafeApi|null[0] + constructor () // com.composegears.tiamat/TiamatUnsafeApi.|(){}[0] +} + abstract interface <#A: in kotlin/Any> com.composegears.tiamat.compose/ContentExtension : com.composegears.tiamat.compose/NavExtension<#A> { // com.composegears.tiamat.compose/ContentExtension|null[0] abstract fun (com.composegears.tiamat.compose/NavDestinationScope).Content(androidx.compose.runtime/Composer?, kotlin/Int) // com.composegears.tiamat.compose/ContentExtension.Content|Content@com.composegears.tiamat.compose.NavDestinationScope(androidx.compose.runtime.Composer?;kotlin.Int){}[0] open fun getType(): com.composegears.tiamat.compose/ContentExtension.Type // com.composegears.tiamat.compose/ContentExtension.getType|getType(){}[0] @@ -30,11 +34,38 @@ abstract interface <#A: in kotlin/Any?> com.composegears.tiamat.compose/NavExten abstract interface com.composegears.tiamat.navigation/NavData // com.composegears.tiamat.navigation/NavData|null[0] +sealed interface com.composegears.tiamat.compose/DestinationLoader { // com.composegears.tiamat.compose/DestinationLoader|null[0] + abstract fun load(kotlin/String): com.composegears.tiamat.navigation/NavDestination<*>? // com.composegears.tiamat.compose/DestinationLoader.load|load(kotlin.String){}[0] + + final class ByKey : com.composegears.tiamat.compose/DestinationLoader { // com.composegears.tiamat.compose/DestinationLoader.ByKey|null[0] + constructor (kotlin/Function1?>) // com.composegears.tiamat.compose/DestinationLoader.ByKey.|(kotlin.Function1?>){}[0] + + final fun load(kotlin/String): com.composegears.tiamat.navigation/NavDestination<*>? // com.composegears.tiamat.compose/DestinationLoader.ByKey.load|load(kotlin.String){}[0] + } + + final class FromArray : com.composegears.tiamat.compose/DestinationLoader { // com.composegears.tiamat.compose/DestinationLoader.FromArray|null[0] + constructor (kotlin/Array>) // com.composegears.tiamat.compose/DestinationLoader.FromArray.|(kotlin.Array>){}[0] + + final fun load(kotlin/String): com.composegears.tiamat.navigation/NavDestination<*>? // com.composegears.tiamat.compose/DestinationLoader.FromArray.load|load(kotlin.String){}[0] + } + + final object Companion { // com.composegears.tiamat.compose/DestinationLoader.Companion|null[0] + final fun byKey(kotlin/Function1?>): com.composegears.tiamat.compose/DestinationLoader // com.composegears.tiamat.compose/DestinationLoader.Companion.byKey|byKey(kotlin.Function1?>){}[0] + final fun from(kotlin/Array>): com.composegears.tiamat.compose/DestinationLoader // com.composegears.tiamat.compose/DestinationLoader.Companion.from|from(kotlin.Array>){}[0] + } + + final object DoNotLoad : com.composegears.tiamat.compose/DestinationLoader { // com.composegears.tiamat.compose/DestinationLoader.DoNotLoad|null[0] + final fun load(kotlin/String): com.composegears.tiamat.navigation/NavDestination<*>? // com.composegears.tiamat.compose/DestinationLoader.DoNotLoad.load|load(kotlin.String){}[0] + } +} + sealed interface com.composegears.tiamat.navigation/RouteElement // com.composegears.tiamat.navigation/RouteElement|null[0] abstract class <#A: kotlin/Any> com.composegears.tiamat.navigation/NavDestination : com.composegears.tiamat.navigation/RouteElement { // com.composegears.tiamat.navigation/NavDestination|null[0] constructor (kotlin/String, kotlin.reflect/KType) // com.composegears.tiamat.navigation/NavDestination.|(kotlin.String;kotlin.reflect.KType){}[0] + final val key // com.composegears.tiamat.navigation/NavDestination.key|{}key[0] + final fun (): kotlin/String // com.composegears.tiamat.navigation/NavDestination.key.|(){}[0] final val name // com.composegears.tiamat.navigation/NavDestination.name|{}name[0] final fun (): kotlin/String // com.composegears.tiamat.navigation/NavDestination.name.|(){}[0] @@ -200,6 +231,9 @@ open class com.composegears.tiamat.compose/TransitionController { // com.compose final val com.composegears.tiamat.compose/LocalNavAnimatedVisibilityScope // com.composegears.tiamat.compose/LocalNavAnimatedVisibilityScope|{}LocalNavAnimatedVisibilityScope[0] final fun (): androidx.compose.runtime/ProvidableCompositionLocal // com.composegears.tiamat.compose/LocalNavAnimatedVisibilityScope.|(){}[0] final val com.composegears.tiamat.compose/com_composegears_tiamat_compose_ComposeNavDestination$stableprop // com.composegears.tiamat.compose/com_composegears_tiamat_compose_ComposeNavDestination$stableprop|#static{}com_composegears_tiamat_compose_ComposeNavDestination$stableprop[0] +final val com.composegears.tiamat.compose/com_composegears_tiamat_compose_DestinationLoader_ByKey$stableprop // com.composegears.tiamat.compose/com_composegears_tiamat_compose_DestinationLoader_ByKey$stableprop|#static{}com_composegears_tiamat_compose_DestinationLoader_ByKey$stableprop[0] +final val com.composegears.tiamat.compose/com_composegears_tiamat_compose_DestinationLoader_DoNotLoad$stableprop // com.composegears.tiamat.compose/com_composegears_tiamat_compose_DestinationLoader_DoNotLoad$stableprop|#static{}com_composegears_tiamat_compose_DestinationLoader_DoNotLoad$stableprop[0] +final val com.composegears.tiamat.compose/com_composegears_tiamat_compose_DestinationLoader_FromArray$stableprop // com.composegears.tiamat.compose/com_composegears_tiamat_compose_DestinationLoader_FromArray$stableprop|#static{}com_composegears_tiamat_compose_DestinationLoader_FromArray$stableprop[0] final val com.composegears.tiamat.compose/com_composegears_tiamat_compose_NavDestinationInstanceDelegate$stableprop // com.composegears.tiamat.compose/com_composegears_tiamat_compose_NavDestinationInstanceDelegate$stableprop|#static{}com_composegears_tiamat_compose_NavDestinationInstanceDelegate$stableprop[0] final val com.composegears.tiamat.compose/com_composegears_tiamat_compose_NavDestinationScope$stableprop // com.composegears.tiamat.compose/com_composegears_tiamat_compose_NavDestinationScope$stableprop|#static{}com_composegears_tiamat_compose_NavDestinationScope$stableprop[0] final val com.composegears.tiamat.compose/com_composegears_tiamat_compose_NavigationSceneScope$stableprop // com.composegears.tiamat.compose/com_composegears_tiamat_compose_NavigationSceneScope$stableprop|#static{}com_composegears_tiamat_compose_NavigationSceneScope$stableprop[0] @@ -236,15 +270,18 @@ final fun <#A: kotlin/Any> (com.composegears.tiamat.navigation/NavController).co final fun <#A: kotlin/Any> (com.composegears.tiamat.navigation/NavController).com.composegears.tiamat.compose/popToTop(com.composegears.tiamat.navigation/NavDestination<#A>, androidx.compose.animation/ContentTransform? = ..., com.composegears.tiamat.compose/TransitionController? = ..., kotlin/Function1 = ...) // com.composegears.tiamat.compose/popToTop|popToTop@com.composegears.tiamat.navigation.NavController(com.composegears.tiamat.navigation.NavDestination<0:0>;androidx.compose.animation.ContentTransform?;com.composegears.tiamat.compose.TransitionController?;kotlin.Function1){0§}[0] final fun <#A: kotlin/Any> (com.composegears.tiamat.navigation/NavController).com.composegears.tiamat.compose/replace(com.composegears.tiamat.navigation/NavDestination<#A>, #A? = ..., kotlin/Any? = ..., androidx.compose.animation/ContentTransform? = ..., com.composegears.tiamat.compose/TransitionController? = ...) // com.composegears.tiamat.compose/replace|replace@com.composegears.tiamat.navigation.NavController(com.composegears.tiamat.navigation.NavDestination<0:0>;0:0?;kotlin.Any?;androidx.compose.animation.ContentTransform?;com.composegears.tiamat.compose.TransitionController?){0§}[0] final fun <#A: kotlin/Any> (com.composegears.tiamat.navigation/NavController).com.composegears.tiamat.compose/replace(com.composegears.tiamat.navigation/NavEntry<#A>, androidx.compose.animation/ContentTransform? = ..., com.composegears.tiamat.compose/TransitionController? = ...) // com.composegears.tiamat.compose/replace|replace@com.composegears.tiamat.navigation.NavController(com.composegears.tiamat.navigation.NavEntry<0:0>;androidx.compose.animation.ContentTransform?;com.composegears.tiamat.compose.TransitionController?){0§}[0] -final fun <#A: kotlin/Any> com.composegears.tiamat.compose/TiamatPreview(com.composegears.tiamat.navigation/NavDestination<#A>, #A?, kotlin/Any?, kotlin/Any?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // com.composegears.tiamat.compose/TiamatPreview|TiamatPreview(com.composegears.tiamat.navigation.NavDestination<0:0>;0:0?;kotlin.Any?;kotlin.Any?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){0§}[0] +final fun <#A: kotlin/Any> com.composegears.tiamat.compose/TiamatPreview(com.composegears.tiamat.navigation/NavDestination<#A>, #A?, kotlin/Any?, kotlin/Any?, androidx.compose.ui/Modifier?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // com.composegears.tiamat.compose/TiamatPreview|TiamatPreview(com.composegears.tiamat.navigation.NavDestination<0:0>;0:0?;kotlin.Any?;kotlin.Any?;androidx.compose.ui.Modifier?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){0§}[0] final fun <#A: kotlin/Any> com.composegears.tiamat.compose/extension(kotlin/Function3, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>): com.composegears.tiamat.compose/NavExtension<#A> // com.composegears.tiamat.compose/extension|extension(kotlin.Function3,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>){0§}[0] final fun <#A: kotlin/Any?> com.composegears.tiamat.compose/produceRetainedState(#A, kotlin.coroutines/SuspendFunction1, kotlin/Unit>, androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.runtime/State<#A> // com.composegears.tiamat.compose/produceRetainedState|produceRetainedState(0:0;kotlin.coroutines.SuspendFunction1,kotlin.Unit>;androidx.compose.runtime.Composer?;kotlin.Int){0§}[0] final fun <#A: kotlin/Any?> com.composegears.tiamat.compose/produceRetainedState(#A, kotlin/Array..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>, androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.runtime/State<#A> // com.composegears.tiamat.compose/produceRetainedState|produceRetainedState(0:0;kotlin.Array...;kotlin.coroutines.SuspendFunction1,kotlin.Unit>;androidx.compose.runtime.Composer?;kotlin.Int){0§}[0] +final fun com.composegears.tiamat.compose/Navigation(com.composegears.tiamat.navigation/NavController, com.composegears.tiamat.compose/DestinationLoader, androidx.compose.ui/Modifier?, kotlin/Boolean, kotlin/Function1?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // com.composegears.tiamat.compose/Navigation|Navigation(com.composegears.tiamat.navigation.NavController;com.composegears.tiamat.compose.DestinationLoader;androidx.compose.ui.Modifier?;kotlin.Boolean;kotlin.Function1?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0] final fun com.composegears.tiamat.compose/Navigation(com.composegears.tiamat.navigation/NavController, kotlin/Array>, androidx.compose.ui/Modifier?, kotlin/Boolean, kotlin/Function1?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // com.composegears.tiamat.compose/Navigation|Navigation(com.composegears.tiamat.navigation.NavController;kotlin.Array>;androidx.compose.ui.Modifier?;kotlin.Boolean;kotlin.Function1?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0] -final fun com.composegears.tiamat.compose/Navigation(com.composegears.tiamat.navigation/NavController, kotlin/Function1?>, androidx.compose.ui/Modifier?, kotlin/Boolean, kotlin/Function1?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // com.composegears.tiamat.compose/Navigation|Navigation(com.composegears.tiamat.navigation.NavController;kotlin.Function1?>;androidx.compose.ui.Modifier?;kotlin.Boolean;kotlin.Function1?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0] +final fun com.composegears.tiamat.compose/NavigationScene(com.composegears.tiamat.navigation/NavController, com.composegears.tiamat.compose/DestinationLoader, kotlin/Boolean, kotlin/Function3, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // com.composegears.tiamat.compose/NavigationScene|NavigationScene(com.composegears.tiamat.navigation.NavController;com.composegears.tiamat.compose.DestinationLoader;kotlin.Boolean;kotlin.Function3;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0] final fun com.composegears.tiamat.compose/NavigationScene(com.composegears.tiamat.navigation/NavController, kotlin/Array>, kotlin/Boolean, kotlin/Function3, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // com.composegears.tiamat.compose/NavigationScene|NavigationScene(com.composegears.tiamat.navigation.NavController;kotlin.Array>;kotlin.Boolean;kotlin.Function3;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0] -final fun com.composegears.tiamat.compose/NavigationScene(com.composegears.tiamat.navigation/NavController, kotlin/Function1?>, kotlin/Boolean, kotlin/Function3, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // com.composegears.tiamat.compose/NavigationScene|NavigationScene(com.composegears.tiamat.navigation.NavController;kotlin.Function1?>;kotlin.Boolean;kotlin.Function3;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0] final fun com.composegears.tiamat.compose/com_composegears_tiamat_compose_ComposeNavDestination$stableprop_getter(): kotlin/Int // com.composegears.tiamat.compose/com_composegears_tiamat_compose_ComposeNavDestination$stableprop_getter|com_composegears_tiamat_compose_ComposeNavDestination$stableprop_getter(){}[0] +final fun com.composegears.tiamat.compose/com_composegears_tiamat_compose_DestinationLoader_ByKey$stableprop_getter(): kotlin/Int // com.composegears.tiamat.compose/com_composegears_tiamat_compose_DestinationLoader_ByKey$stableprop_getter|com_composegears_tiamat_compose_DestinationLoader_ByKey$stableprop_getter(){}[0] +final fun com.composegears.tiamat.compose/com_composegears_tiamat_compose_DestinationLoader_DoNotLoad$stableprop_getter(): kotlin/Int // com.composegears.tiamat.compose/com_composegears_tiamat_compose_DestinationLoader_DoNotLoad$stableprop_getter|com_composegears_tiamat_compose_DestinationLoader_DoNotLoad$stableprop_getter(){}[0] +final fun com.composegears.tiamat.compose/com_composegears_tiamat_compose_DestinationLoader_FromArray$stableprop_getter(): kotlin/Int // com.composegears.tiamat.compose/com_composegears_tiamat_compose_DestinationLoader_FromArray$stableprop_getter|com_composegears_tiamat_compose_DestinationLoader_FromArray$stableprop_getter(){}[0] final fun com.composegears.tiamat.compose/com_composegears_tiamat_compose_NavDestinationInstanceDelegate$stableprop_getter(): kotlin/Int // com.composegears.tiamat.compose/com_composegears_tiamat_compose_NavDestinationInstanceDelegate$stableprop_getter|com_composegears_tiamat_compose_NavDestinationInstanceDelegate$stableprop_getter(){}[0] final fun com.composegears.tiamat.compose/com_composegears_tiamat_compose_NavDestinationScope$stableprop_getter(): kotlin/Int // com.composegears.tiamat.compose/com_composegears_tiamat_compose_NavDestinationScope$stableprop_getter|com_composegears_tiamat_compose_NavDestinationScope$stableprop_getter(){}[0] final fun com.composegears.tiamat.compose/com_composegears_tiamat_compose_NavigationSceneScope$stableprop_getter(): kotlin/Int // com.composegears.tiamat.compose/com_composegears_tiamat_compose_NavigationSceneScope$stableprop_getter|com_composegears_tiamat_compose_NavigationSceneScope$stableprop_getter(){}[0] diff --git a/tiamat/src/commonMain/kotlin/com/composegears/tiamat/TiamatUnsafeApi.kt b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/TiamatUnsafeApi.kt new file mode 100644 index 00000000..512e4387 --- /dev/null +++ b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/TiamatUnsafeApi.kt @@ -0,0 +1,8 @@ +package com.composegears.tiamat + +@Retention(AnnotationRetention.BINARY) +@RequiresOptIn( + level = RequiresOptIn.Level.WARNING, + message = "Use of this API is unsafe and may lead to unexpected behavior or crashes. Use with caution." +) +public annotation class TiamatUnsafeApi \ No newline at end of file diff --git a/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/ComposableNavController.kt b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/ComposableNavController.kt index 82c8be12..912d2f06 100644 --- a/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/ComposableNavController.kt +++ b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/ComposableNavController.kt @@ -112,13 +112,18 @@ public fun rememberNavController( DisposableEffect(navController) { onDispose { - // Dispose called in 2 cases + // Dispose is called in 2 cases: // 1. NavEntry is closed due to navigation (detached from UI, attached to NC) - // 2. `rememberNavController` composable leave entry composition (eg: switch between 2 Nav-s) + // 2. `rememberNavController` composable leaves entry composition (e.g., switch between 2 NavControllers) + // Determine if NavController should be cleared or retained val shouldClear = when { - !isSaveable -> true // not saveable -> clear - navControllersStorage == null -> true // no storage + dispose means root NC leave composition -> clear - parentNavEntry.isAttachedToUI -> true // NC leave entry composition till entry on screen -> clear + // Case 1: Not saveable -> always clear resources + !isSaveable -> true + // Case 2: Root NavController + no storage + dispose = leaving root composition -> clear + navControllersStorage == null -> true + // Case 3: Parent entry still visible but this NavController left composition -> clear + parentNavEntry.isAttachedToUI -> true + // Case 4: Parent entry invisible, keep for later restore when entry re-appears else -> false } if (shouldClear) { diff --git a/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/ComposableNavigation.kt b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/ComposableNavigation.kt index 97ebc356..3812c50c 100644 --- a/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/ComposableNavigation.kt +++ b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/ComposableNavigation.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.navigationevent.NavigationEventInfo import androidx.navigationevent.compose.NavigationBackHandler import androidx.navigationevent.compose.rememberNavigationEventState +import com.composegears.tiamat.TiamatUnsafeApi import com.composegears.tiamat.compose.TransitionController.Event.* import com.composegears.tiamat.navigation.NavController import com.composegears.tiamat.navigation.NavDestination @@ -48,7 +49,7 @@ public fun Navigation( ) { Navigation( navController = navController, - destinationResolver = { name -> destinations.firstOrNull { it.name == name } }, + destinationLoader = DestinationLoader.from(destinations), modifier = modifier, handleSystemBackEvent = handleSystemBackEvent, contentTransformProvider = contentTransformProvider @@ -59,23 +60,27 @@ public fun Navigation( * The main navigation composable that displays the current destination and handles transitions. * * @param navController The NavController to use for navigation - * @param destinationResolver A function that resolves a destination by name (used during restoration from saved state) + * @param destinationLoader A [DestinationLoader] that resolves navigation destinations by their unique key. * @param modifier Modifier to apply to the navigation container * @param handleSystemBackEvent Whether to handle system back events (default: true) * @param contentTransformProvider Provider function for content transitions based on navigation direction + * + * @see [DestinationLoader.from] + * @see [DestinationLoader.byKey] + * @see [DestinationLoader.DoNotLoad] */ @Composable @Suppress("CognitiveComplexMethod", "CyclomaticComplexMethod") public fun Navigation( navController: NavController, - destinationResolver: (name: String) -> NavDestination<*>?, + destinationLoader: DestinationLoader, modifier: Modifier = Modifier, handleSystemBackEvent: Boolean = true, contentTransformProvider: (isForward: Boolean) -> ContentTransform = { navigationFadeInOut() }, ) { NavigationScene( navController = navController, - destinationResolver = destinationResolver, + destinationLoader = destinationLoader, handleSystemBackEvent = handleSystemBackEvent, ) { val stubEntry = remember { @@ -214,7 +219,7 @@ public fun NavigationScene( ) { NavigationScene( navController = navController, - destinationResolver = { name -> destinations.firstOrNull { it.name == name } }, + destinationLoader = DestinationLoader.from(destinations), handleSystemBackEvent = handleSystemBackEvent, scene = scene ) @@ -230,7 +235,7 @@ public fun NavigationScene( * * Example usage: * ``` - * NavigationScene(navController, destinations) { // this: NavigationSceneScope + * NavigationScene(navController, destinationLoader) { // this: NavigationSceneScope * val currentEntry by navController.currentNavEntryAsState() * AnimatedContent( * targetState = currentEntry, @@ -243,21 +248,27 @@ public fun NavigationScene( * ``` * * @param navController The NavController to use for navigation - * @param destinationResolver A function that resolves a destination by name (used during restoration from saved state) + * @param destinationLoader A [DestinationLoader] that resolves navigation destinations by their unique key. * @param handleSystemBackEvent Whether to handle system back events (default: true) * @param scene Scene builder composable function that defines how navigation entries are rendered + * + * @see [DestinationLoader.from] + * @see [DestinationLoader.byKey] + * @see [DestinationLoader.DoNotLoad] */ @Composable @Suppress("CognitiveComplexMethod", "MaximumLineLength", "MaxLineLength") public fun NavigationScene( navController: NavController, - destinationResolver: (name: String) -> NavDestination<*>?, + destinationLoader: DestinationLoader, handleSystemBackEvent: Boolean = true, scene: @Composable NavigationSceneScope.() -> Unit ) { - // resolve destinations in advance + // load destinations in advance LaunchedEffect(navController) { - navController.resolveNavDestinations(destinationResolver) + @OptIn(TiamatUnsafeApi::class) + if (destinationLoader != DestinationLoader.DoNotLoad) + navController.loadNavDestinations(destinationLoader) } // back handler if (handleSystemBackEvent) { @@ -275,15 +286,21 @@ public fun NavigationScene( CompositionLocalProvider(LocalNavController provides navController) { val navScope = remember { NavigationSceneScope { entry -> - if (!entry.isResolved) entry.resolveDestination(destinationResolver) + if (!entry.isLoaded) entry.load(destinationLoader) NavEntryContent(entry) DisposableEffect(entry) { - if (destinationResolver(entry.destination.name) == null) error( - "NavController: ${navController.key}, the destination (${entry.destination.name}) is not registered in Navigation" - ) - if (visibleEntries.contains(entry)) error( + // verify destination is loadable or loading is not required + @OptIn(TiamatUnsafeApi::class) + require( + destinationLoader == DestinationLoader.DoNotLoad || + destinationLoader.load(entry.destination.key) != null + ) { + "NavController: ${navController.key}, the destination (${entry.destination.name}) is not loadable with the provided DestinationLoader" + } + // verify the same entry is not displayed twice + require(!visibleEntries.contains(entry)) { "NavController: ${navController.key}, the same entry (${entry.destination.name}) should not be displayed twice" - ) + } visibleEntries.add(entry) onDispose { visibleEntries.remove(entry) diff --git a/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/ComposablePreview.kt b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/ComposablePreview.kt index 94d21073..e4800d95 100644 --- a/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/ComposablePreview.kt +++ b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/ComposablePreview.kt @@ -1,6 +1,8 @@ package com.composegears.tiamat.compose import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.composegears.tiamat.TiamatUnsafeApi import com.composegears.tiamat.navigation.NavDestination import com.composegears.tiamat.navigation.NavDestination.Companion.toNavEntry @@ -12,6 +14,7 @@ import com.composegears.tiamat.navigation.NavDestination.Companion.toNavEntry * @param freeArgs Optional free-form arguments that can be passed to the destination. * @param navResult Optional navigation result data that would normally be received from * a previous destination in the navigation stack. + * @param modifier Modifier to apply to the navigation container * * Example simple usage: * @@ -51,11 +54,13 @@ import com.composegears.tiamat.navigation.NavDestination.Companion.toNavEntry * @see NavDestination */ @Composable +@OptIn(TiamatUnsafeApi::class) public fun TiamatPreview( destination: NavDestination, navArgs: T? = null, freeArgs: Any? = null, - navResult: Any? = null + navResult: Any? = null, + modifier: Modifier = Modifier, ) { Navigation( navController = rememberNavController( @@ -65,6 +70,7 @@ public fun TiamatPreview( navResult = navResult ), ), - destinations = arrayOf(destination) + destinationLoader = DestinationLoader.DoNotLoad, + modifier = modifier, ) } diff --git a/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/DestinationLoader.kt b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/DestinationLoader.kt new file mode 100644 index 00000000..88f48cb3 --- /dev/null +++ b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/DestinationLoader.kt @@ -0,0 +1,81 @@ +package com.composegears.tiamat.compose + +import com.composegears.tiamat.TiamatUnsafeApi +import com.composegears.tiamat.navigation.NavDestination + +/** + * Interface for loading navigation destinations. + */ +public sealed interface DestinationLoader { + + public companion object { + + /** + * Creates a DestinationLoader that loads destinations from a provided array. + * + * @param destinations Array of navigation destinations to search through + * @return A DestinationLoader that finds destinations by key in the provided array + */ + public fun from( + destinations: Array> + ): DestinationLoader = FromArray(destinations) + + /** + * Creates a DestinationLoader with a custom loading function. + * + * @param loader Function that takes a destination key and returns the corresponding + * NavDestination, or null if not found + * @return A DestinationLoader that uses the provided function to load destinations + */ + public fun byKey( + loader: (key: String) -> NavDestination<*>? + ): DestinationLoader = ByKey(loader) + } + + /** + * Loads a navigation destination by its unique key. + * + * @param key The unique key of the destination to load + * @return The loaded NavDestination, or null if not found + */ + public fun load(key: String): NavDestination<*>? + + /** + * A DestinationLoader that never loads any destinations. + * + * This implementation always returns null for any destination key. + * Useful for scenarios where destination loading is not required. + */ + @TiamatUnsafeApi + public object DoNotLoad : DestinationLoader { + override fun load(key: String): NavDestination<*>? = null + } + + /** + * A DestinationLoader that loads destinations from a predefined array. + * + * This implementation searches through the provided array to find + * destinations that match the requested key. + * + * @property destinations Array of available navigation destinations + */ + public class FromArray( + private val destinations: Array> + ) : DestinationLoader { + override fun load(key: String): NavDestination<*>? = + destinations.firstOrNull { it.key == key } + } + + /** + * A DestinationLoader that uses a custom function to load destinations. + * + * This implementation delegates destination loading to the provided function. + * + * @property loader Function that loads a destination by key + */ + public class ByKey( + private val loader: (key: String) -> NavDestination<*>? + ) : DestinationLoader { + override fun load(key: String): NavDestination<*>? = loader(key) + } +} \ No newline at end of file diff --git a/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/TransitionController.kt b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/TransitionController.kt index 2f804db5..e81a7229 100644 --- a/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/TransitionController.kt +++ b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/compose/TransitionController.kt @@ -10,8 +10,16 @@ import kotlinx.coroutines.flow.update /** * A controller for managing a transition between two screens. * - * @param start The start value of the transition. - * @param end The end value of the transition. + * Once created, the controller is in an active state. Calling [update] allows programmatic + * control over the transition progress. The transition is completed by calling either [finish] + * or [cancel]. After either method is called, the controller becomes inactive and any further + * calls to [update], [cancel], or [finish] will throw an error. This is intentional to prevent + * stale transitions from affecting new navigation operations. + * + * @param start The start value of the transition (default: 0f, must be in 0..1) + * @param end The end value of the transition (default: 1f, must be in 0..1) + * + * @throws IllegalArgumentException if start or end values are outside 0..1 range or start > end */ public open class TransitionController( private val start: Float = 0f, diff --git a/tiamat/src/commonMain/kotlin/com/composegears/tiamat/navigation/NavController.kt b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/navigation/NavController.kt index d9c635e2..426ee100 100644 --- a/tiamat/src/commonMain/kotlin/com/composegears/tiamat/navigation/NavController.kt +++ b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/navigation/NavController.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModelStore import androidx.lifecycle.ViewModelStoreOwner import com.composegears.tiamat.ExcludeFromTests import com.composegears.tiamat.TiamatExperimentalApi +import com.composegears.tiamat.compose.DestinationLoader import com.composegears.tiamat.navigation.NavDestination.Companion.toNavEntry import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -69,7 +70,7 @@ public class NavController internal constructor( val navStack = navStackItems?.map { item -> NavEntry.restoreFromSavedState(navController, item) } - if (navStack != null && navStack.isNotEmpty()) { + if (!navStack.isNullOrEmpty()) { navController.editNavStack(null, TransitionType.Instant) { _ -> navStack } } navController.parent = parent @@ -340,7 +341,7 @@ public class NavController internal constructor( when (element) { is NavEntry<*> -> element is NavDestination<*> -> element.toNavEntry() - is Route.Destination -> NavDestination.Unresolved(element.name).toNavEntry() + is Route.Destination -> NavDestination.NotLoaded(element.name).toNavEntry() is Route.NavController -> null }?.also { entry: NavEntry<*> -> elements.removeAt(0) @@ -364,12 +365,12 @@ public class NavController internal constructor( // ----------- internal helpers methods ------------------------------------------------------------------------------------ - internal fun resolveNavDestinations( - destinationResolver: (name: String) -> NavDestination<*>?, + internal fun loadNavDestinations( + destinationLoader: DestinationLoader, ) { getNavStack().onEach { - if (!it.isResolved) - it.resolveDestination(destinationResolver) + if (!it.isLoaded) + it.load(destinationLoader) } } diff --git a/tiamat/src/commonMain/kotlin/com/composegears/tiamat/navigation/NavDestination.kt b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/navigation/NavDestination.kt index 5aa429c1..5d85199d 100644 --- a/tiamat/src/commonMain/kotlin/com/composegears/tiamat/navigation/NavDestination.kt +++ b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/navigation/NavDestination.kt @@ -42,7 +42,12 @@ public abstract class NavDestination( ) } - internal class Unresolved( + /** + * A unique key for this destination, used for saving and restoring destination. + */ + public val key: String = name + + internal class NotLoaded( name: String ) : NavDestination( name = name, diff --git a/tiamat/src/commonMain/kotlin/com/composegears/tiamat/navigation/NavEntry.kt b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/navigation/NavEntry.kt index 6efc6f87..10a97baa 100644 --- a/tiamat/src/commonMain/kotlin/com/composegears/tiamat/navigation/NavEntry.kt +++ b/tiamat/src/commonMain/kotlin/com/composegears/tiamat/navigation/NavEntry.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.* import androidx.savedstate.serialization.decodeFromSavedState import androidx.savedstate.serialization.encodeToSavedState import com.composegears.tiamat.ExcludeFromTests +import com.composegears.tiamat.compose.DestinationLoader import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.serializer @@ -59,7 +60,7 @@ public class NavEntry public constructor( val entrySavedState = savedState[KEY_SAVED_STATE] as? SavedState val navControllers = savedState[KEY_NAV_CONTROLLERS] as? SavedState return NavEntry( - destination = NavDestination.Unresolved(destination), + destination = NavDestination.NotLoaded(destination), navArgs = navArgs, freeArgs = freeArgs, navResult = navResult, @@ -79,7 +80,7 @@ public class NavEntry public constructor( private set internal var isAttachedToUI = false private set - internal var isResolved = destination !is NavDestination.Unresolved + internal var isLoaded = destination !is NavDestination.NotLoaded private set @OptIn(ExperimentalUuidApi::class) @@ -158,14 +159,15 @@ public class NavEntry public constructor( public fun contentKey(): String = "${destination.name}-$uuid" @Suppress("UNCHECKED_CAST") - internal fun resolveDestination(destinationResolver: (name: String) -> NavDestination<*>?) { - destination = destinationResolver(destination.name) + internal fun load(destinationLoader: DestinationLoader) { + if (isLoaded) return + destination = destinationLoader.load(destination.key) ?.let { it as? NavDestination } - ?: error("Unable to resolve destination: ${destination.name}") + ?: error("Unable to load destination: ${destination.name}") if (navArgs != null && navArgs is SavedStateX) navArgs ?.fromSavedState(destination.argsType) { navArgs = it as Args } ?: error("NavArgs type mismatch: expected ${destination.argsType} found: ${navArgs!!::class}") - isResolved = true + isLoaded = true } @PublishedApi @@ -201,7 +203,9 @@ public class NavEntry public constructor( } internal fun ensureDetachedAndAttach() { - if (isAttachedToNavController) error("NavEntry is already attached to a NavController") + require(!isAttachedToNavController) { + "NavEntry is already attached to a NavController" + } attachToNavController() } diff --git a/tiamat/src/commonTest/kotlin/com/composegears/tiamat/navigation/NavControllerTests.kt b/tiamat/src/commonTest/kotlin/com/composegears/tiamat/navigation/NavControllerTests.kt index 1190c975..199324a4 100644 --- a/tiamat/src/commonTest/kotlin/com/composegears/tiamat/navigation/NavControllerTests.kt +++ b/tiamat/src/commonTest/kotlin/com/composegears/tiamat/navigation/NavControllerTests.kt @@ -1,6 +1,7 @@ package com.composegears.tiamat.navigation import com.composegears.tiamat.TiamatExperimentalApi +import com.composegears.tiamat.compose.DestinationLoader import com.composegears.tiamat.compose.editNavStack import com.composegears.tiamat.compose.navDestination import com.composegears.tiamat.createTestNavController @@ -522,19 +523,20 @@ class NavControllerTests { } @Test - fun `resolveNavDestinations # resolve known destinations`() { - val destinations = listOf(Destination1, Destination2, Destination3) + fun `loadNavDestinations # load known destinations`() { + val destinations = arrayOf>(Destination1, Destination2, Destination3) val nc = createTestNavController() - nc.navigate(NavDestination.Unresolved(Destination1.name).toNavEntry()) - nc.navigate(NavDestination.Unresolved(Destination2.name).toNavEntry()) - nc.resolveNavDestinations { name -> destinations.find { it.name == name } } - assertTrue(nc.getNavStack()[0].isResolved) - assertTrue(nc.getNavStack()[1].isResolved) - nc.navigate(NavDestination.Unresolved("KeptUnresolved").toNavEntry()) + nc.navigate(NavDestination.NotLoaded(Destination1.name).toNavEntry()) + nc.navigate(NavDestination.NotLoaded(Destination2.name).toNavEntry()) + val loader = DestinationLoader.from(destinations) + nc.loadNavDestinations(loader) + assertTrue(nc.getNavStack()[0].isLoaded) + assertTrue(nc.getNavStack()[1].isLoaded) + nc.navigate(NavDestination.NotLoaded("KeptNotLoaded").toNavEntry()) assertFails { - nc.resolveNavDestinations { name -> destinations.find { it.name == name } } + nc.loadNavDestinations(loader) } - assertFalse(nc.getNavStack()[2].isResolved) + assertFalse(nc.getNavStack()[2].isLoaded) } @Test diff --git a/tiamat/src/commonTest/kotlin/com/composegears/tiamat/navigation/NavEntryTests.kt b/tiamat/src/commonTest/kotlin/com/composegears/tiamat/navigation/NavEntryTests.kt index 29131ea7..260dedab 100644 --- a/tiamat/src/commonTest/kotlin/com/composegears/tiamat/navigation/NavEntryTests.kt +++ b/tiamat/src/commonTest/kotlin/com/composegears/tiamat/navigation/NavEntryTests.kt @@ -2,6 +2,7 @@ package com.composegears.tiamat.navigation import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModel +import com.composegears.tiamat.compose.DestinationLoader import com.composegears.tiamat.compose.navDestination import com.composegears.tiamat.navigation.NavDestination.Companion.toNavEntry import kotlinx.serialization.Serializable @@ -41,15 +42,15 @@ class NavEntryTests { } @Test - fun `isResolved # returns false for unresolved destination`() { - val entry = NavEntry(destination = NavDestination.Unresolved("test")) - assertFalse(entry.isResolved) + fun `isLoaded # returns false for not loaded destination`() { + val entry = NavEntry(destination = NavDestination.NotLoaded("test")) + assertFalse(entry.isLoaded) } @Test - fun `isResolved # returns true for resolved destination`() { + fun `isLoaded # returns true for loaded destination`() { val entry = NavEntry(destination = TestDestination) - assertTrue(entry.isResolved) + assertTrue(entry.isLoaded) } @Test @@ -132,38 +133,41 @@ class NavEntryTests { } @Test - fun `resolveDestination # finds matching destination by name`() { + fun `load # finds matching destination by name`() { val destinationName = TestDestination.name - val entry = NavEntry(destination = NavDestination.Unresolved(destinationName)) + val entry = NavEntry(destination = NavDestination.NotLoaded(destinationName)) val destinations = arrayOf>( AnotherTestDestination, TestDestination ) - entry.resolveDestination(destinations) - assertTrue(entry.isResolved) + val loader = DestinationLoader.from(destinations) + entry.load(loader) + assertTrue(entry.isLoaded) assertEquals(destinationName, entry.destination.name) } @Test @Suppress("UNCHECKED_CAST") - fun `resolveDestination # deserialized nav args`() { + fun `load # deserializes nav args`() { val entry = SerializedNavArgsDestination.toNavEntry(navArgs = TestData("test")) val saved = entry.saveToSavedState() val restored = NavEntry.restoreFromSavedState(null, saved) - restored.resolveDestination(arrayOf(SerializedNavArgsDestination)) - assertTrue(restored.isResolved) + val loader = DestinationLoader.from(arrayOf(SerializedNavArgsDestination)) + restored.load(loader) + assertTrue(restored.isLoaded) assertEquals(SerializedNavArgsDestination.name, restored.destination.name) assertEquals("test", (restored as? NavEntry)?.getNavArgs()?.data) } @Test - fun `resolveDestination # throws error when destination not found`() { - val entry = NavEntry(destination = NavDestination.Unresolved("non_existent")) + fun `load # throws error when destination not found`() { + val entry = NavEntry(destination = NavDestination.NotLoaded("non_existent")) val destinations = arrayOf>( TestDestination, AnotherTestDestination ) - assertFails { entry.resolveDestination(destinations) } + val loader = DestinationLoader.from(destinations) + assertFails { entry.load(loader) } } @Test @@ -241,7 +245,7 @@ class NavEntryTests { assertEquals(entrySavedState, entry.savedState) assertEquals(1, entry.navControllerStore.navControllers.size) assertEquals(parentNC, entry.navControllerStore.navControllers[0].parent) - assertFalse(entry.isResolved) + assertFalse(entry.isLoaded) } @Test @@ -280,7 +284,8 @@ class NavEntryTests { ) val savedState = entry.saveToSavedState() val result = NavEntry.restoreFromSavedState(null, savedState) - result.resolveDestination(arrayOf(SerializedNavArgsDestination)) + val loader = DestinationLoader.from(arrayOf(SerializedNavArgsDestination)) + result.load(loader) assertEquals("args", result.getNavArgs().let { it as? TestData }?.data) assertEquals("free", result.getFreeArgs()?.data) assertEquals("result", result.getNavResult()?.data) @@ -295,8 +300,9 @@ class NavEntryTests { ) val savedState = entry.saveToSavedState() val result = NavEntry.restoreFromSavedState(null, savedState) + val loader = DestinationLoader.byKey { _ -> AnotherSerializedNavArgsDestination } assertFails { - result.resolveDestination { _ -> AnotherSerializedNavArgsDestination } + result.load(loader) } } @@ -422,8 +428,4 @@ class NavEntryTests { val entry = NavEntry(destination = TestDestination) assertEquals("TestDestination-${entry.uuid}", entry.contentKey()) } - - fun NavEntry<*>.resolveDestination(destinations: Array>) { - resolveDestination { name -> destinations.firstOrNull { it.name == name } } - } } \ No newline at end of file