From 595359d6f301e1adabbba3b618620a5ddcce8211 Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Tue, 15 Nov 2022 13:27:28 -0500 Subject: [PATCH 1/3] Fix resetting the TurboSessionNavHostFragment, since newer versions of the navigation library do not recreate/clear the backstack if the new nav graph is identical to the existing one --- .../hotwire/turbo/nav/TurboNavGraphBuilder.kt | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/turbo/src/main/kotlin/dev/hotwire/turbo/nav/TurboNavGraphBuilder.kt b/turbo/src/main/kotlin/dev/hotwire/turbo/nav/TurboNavGraphBuilder.kt index 3148c011..50206bbc 100644 --- a/turbo/src/main/kotlin/dev/hotwire/turbo/nav/TurboNavGraphBuilder.kt +++ b/turbo/src/main/kotlin/dev/hotwire/turbo/nav/TurboNavGraphBuilder.kt @@ -1,7 +1,6 @@ package dev.hotwire.turbo.nav import android.net.Uri -import androidx.annotation.IdRes import androidx.appcompat.app.AppCompatActivity import androidx.core.net.toUri import androidx.fragment.app.DialogFragment @@ -13,6 +12,7 @@ import androidx.navigation.fragment.FragmentNavigator import androidx.navigation.fragment.FragmentNavigatorDestinationBuilder import dev.hotwire.turbo.config.TurboPathConfiguration import dev.hotwire.turbo.config.uri +import kotlin.random.Random import kotlin.reflect.KClass import kotlin.reflect.full.findAnnotation import kotlin.reflect.full.isSubclassOf @@ -23,13 +23,13 @@ internal class TurboNavGraphBuilder( private val pathConfiguration: TurboPathConfiguration ) { private data class ActivityDestination( - val id: Int, + val route: String, val uri: Uri, val kClass: KClass ) private data class FragmentDestination( - val id: Int, + val route: String, val uri: Uri, val kClass: KClass ) @@ -38,11 +38,14 @@ internal class TurboNavGraphBuilder( registeredActivities: List>, registeredFragments: List> ): NavGraph { - var currentId = 1 + // Use a random number to start the nav graph, so the graph is unique every time + // and it can be reset/recreated on-demand. Updating an existing nav graph with + // an identical one would bypass recreating the nav stack from scratch. + var currentRoute = Random.nextInt() val activityDestinations = registeredActivities.map { ActivityDestination( - id = currentId.also { currentId++ }, + route = currentRoute.also { currentRoute++ }.toString(), uri = it.turboAnnotation().uri.toUri(), kClass = it ) @@ -50,7 +53,7 @@ internal class TurboNavGraphBuilder( val fragmentDestinations = registeredFragments.map { FragmentDestination( - id = currentId.also { currentId++ }, + route = currentRoute.also { currentRoute++ }.toString(), uri = it.turboAnnotation().uri.toUri(), kClass = it ) @@ -59,7 +62,7 @@ internal class TurboNavGraphBuilder( return createGraph( activityDestinations, fragmentDestinations, - fragmentDestinations.startDestination().id + fragmentDestinations.startDestination().route ) } @@ -67,24 +70,24 @@ internal class TurboNavGraphBuilder( private fun createGraph( activityDestinations: List, fragmentDestinations: List, - startDestinationId: Int + startDestinationRoute: String ): NavGraph { - return navController.createGraph(startDestination = startDestinationId) { + return navController.createGraph(startDestination = startDestinationRoute) { activityDestinations.forEach { - activity(it.id) { + activity(it.route) { activityClass = it.kClass deepLink(it.uri.toString()) } } fragmentDestinations.withoutDialogs().forEach { - fragment(it.id, it.kClass) { + fragment(it.route, it.kClass) { deepLink(it.uri.toString()) } } fragmentDestinations.dialogs().forEach { - dialog(it.id, it.kClass as KClass) { + dialog(it.route, it.kClass as KClass) { deepLink(it.uri.toString()) } } @@ -100,7 +103,7 @@ internal class TurboNavGraphBuilder( } private fun List.withoutDialogs(): List { - return minus(dialogs()) + return minus(dialogs().toSet()) } private fun List.startDestination(): FragmentDestination { @@ -118,26 +121,26 @@ internal class TurboNavGraphBuilder( // Modified from AndroidX FragmentNavigatorDestinationBuilder extensions private inline fun NavGraphBuilder.fragment( - @IdRes id: Int, + route: String, fragmentClass: KClass, builder: FragmentNavigatorDestinationBuilder.() -> Unit ) = destination( FragmentNavigatorDestinationBuilder( provider[FragmentNavigator::class], - id, + route, fragmentClass ).apply(builder) ) // Modified from AndroidX DialogFragmentNavigatorDestinationBuilder extensions private inline fun NavGraphBuilder.dialog( - @IdRes id: Int, + route: String, fragmentClass: KClass, builder: DialogFragmentNavigatorDestinationBuilder.() -> Unit ) = destination( DialogFragmentNavigatorDestinationBuilder( provider[DialogFragmentNavigator::class], - id, + route, fragmentClass ).apply(builder) ) From a98e6bc7d20eb44ce1382cf994df4905d92e1bdf Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Tue, 15 Nov 2022 18:26:14 -0500 Subject: [PATCH 2/3] Try a different approach in making the nav graph unique by using a `unique_instance` argument --- .../dev/hotwire/turbo/nav/TurboNavGraphBuilder.kt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/turbo/src/main/kotlin/dev/hotwire/turbo/nav/TurboNavGraphBuilder.kt b/turbo/src/main/kotlin/dev/hotwire/turbo/nav/TurboNavGraphBuilder.kt index 50206bbc..cfd51c5e 100644 --- a/turbo/src/main/kotlin/dev/hotwire/turbo/nav/TurboNavGraphBuilder.kt +++ b/turbo/src/main/kotlin/dev/hotwire/turbo/nav/TurboNavGraphBuilder.kt @@ -38,10 +38,7 @@ internal class TurboNavGraphBuilder( registeredActivities: List>, registeredFragments: List> ): NavGraph { - // Use a random number to start the nav graph, so the graph is unique every time - // and it can be reset/recreated on-demand. Updating an existing nav graph with - // an identical one would bypass recreating the nav stack from scratch. - var currentRoute = Random.nextInt() + var currentRoute = 1 val activityDestinations = registeredActivities.map { ActivityDestination( @@ -95,6 +92,15 @@ internal class TurboNavGraphBuilder( argument("location") { defaultValue = startLocation } + + // Use a random number to represent a unique instance of the graph, so the + // graph is unique every time. This lets it be reset/recreated on-demand from + // `TurboSessionNavHostFragment.reset()`. Replacing an existing nav graph with + // an identical one would bypass recreating the nav stack from scratch in + // `NavController.setGraph()`. + argument("unique_instance") { + defaultValue = Random.nextInt() + } } } From d1cc239d8330365466165409702350e4f51cece2 Mon Sep 17 00:00:00 2001 From: Jay Ohms Date: Wed, 16 Nov 2022 07:43:27 -0500 Subject: [PATCH 3/3] Use a random UUID instead of an Int --- .../kotlin/dev/hotwire/turbo/nav/TurboNavGraphBuilder.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/turbo/src/main/kotlin/dev/hotwire/turbo/nav/TurboNavGraphBuilder.kt b/turbo/src/main/kotlin/dev/hotwire/turbo/nav/TurboNavGraphBuilder.kt index cfd51c5e..5c733119 100644 --- a/turbo/src/main/kotlin/dev/hotwire/turbo/nav/TurboNavGraphBuilder.kt +++ b/turbo/src/main/kotlin/dev/hotwire/turbo/nav/TurboNavGraphBuilder.kt @@ -12,7 +12,7 @@ import androidx.navigation.fragment.FragmentNavigator import androidx.navigation.fragment.FragmentNavigatorDestinationBuilder import dev.hotwire.turbo.config.TurboPathConfiguration import dev.hotwire.turbo.config.uri -import kotlin.random.Random +import java.util.* import kotlin.reflect.KClass import kotlin.reflect.full.findAnnotation import kotlin.reflect.full.isSubclassOf @@ -93,13 +93,13 @@ internal class TurboNavGraphBuilder( defaultValue = startLocation } - // Use a random number to represent a unique instance of the graph, so the + // Use a random value to represent a unique instance of the graph, so the // graph is unique every time. This lets it be reset/recreated on-demand from // `TurboSessionNavHostFragment.reset()`. Replacing an existing nav graph with // an identical one would bypass recreating the nav stack from scratch in // `NavController.setGraph()`. argument("unique_instance") { - defaultValue = Random.nextInt() + defaultValue = UUID.randomUUID().toString() } } }