Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Fix resetting the TurboSessionNavHostFragment #251

Merged
merged 3 commits into from
Nov 16, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 26 additions & 17 deletions turbo/src/main/kotlin/dev/hotwire/turbo/nav/TurboNavGraphBuilder.kt
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 java.util.*
import kotlin.reflect.KClass
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.isSubclassOf
Expand All @@ -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<out AppCompatActivity>
)

private data class FragmentDestination(
val id: Int,
val route: String,
val uri: Uri,
val kClass: KClass<out Fragment>
)
Expand All @@ -38,19 +38,19 @@ internal class TurboNavGraphBuilder(
registeredActivities: List<KClass<out AppCompatActivity>>,
registeredFragments: List<KClass<out Fragment>>
): NavGraph {
var currentId = 1
var currentRoute = 1

val activityDestinations = registeredActivities.map {
ActivityDestination(
id = currentId.also { currentId++ },
route = currentRoute.also { currentRoute++ }.toString(),
uri = it.turboAnnotation().uri.toUri(),
kClass = it
)
}

val fragmentDestinations = registeredFragments.map {
FragmentDestination(
id = currentId.also { currentId++ },
route = currentRoute.also { currentRoute++ }.toString(),
uri = it.turboAnnotation().uri.toUri(),
kClass = it
)
Expand All @@ -59,39 +59,48 @@ internal class TurboNavGraphBuilder(
return createGraph(
activityDestinations,
fragmentDestinations,
fragmentDestinations.startDestination().id
fragmentDestinations.startDestination().route
)
}

@Suppress("UNCHECKED_CAST")
private fun createGraph(
activityDestinations: List<ActivityDestination>,
fragmentDestinations: List<FragmentDestination>,
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<out DialogFragment>) {
dialog(it.route, it.kClass as KClass<out DialogFragment>) {
deepLink(it.uri.toString())
}
}

argument("location") {
defaultValue = startLocation
}

// 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 = UUID.randomUUID().toString()
}
}
}

Expand All @@ -100,7 +109,7 @@ internal class TurboNavGraphBuilder(
}

private fun List<FragmentDestination>.withoutDialogs(): List<FragmentDestination> {
return minus(dialogs())
return minus(dialogs().toSet())
}

private fun List<FragmentDestination>.startDestination(): FragmentDestination {
Expand All @@ -118,26 +127,26 @@ internal class TurboNavGraphBuilder(

// Modified from AndroidX FragmentNavigatorDestinationBuilder extensions
private inline fun NavGraphBuilder.fragment(
@IdRes id: Int,
route: String,
fragmentClass: KClass<out Fragment>,
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<out DialogFragment>,
builder: DialogFragmentNavigatorDestinationBuilder.() -> Unit
) = destination(
DialogFragmentNavigatorDestinationBuilder(
provider[DialogFragmentNavigator::class],
id,
route,
fragmentClass
).apply(builder)
)
Expand Down