Use SafeArgs navigation for Topic feature

Change-Id: Idf4386f10c780d3edc1f8aa11b428cb146e982c3
dt/nav-safe-args-android-dependency
Don Turner 8 months ago
parent b9935368e3
commit 6dc59e7016

@ -28,6 +28,10 @@ class AndroidFeatureConventionPlugin : Plugin<Project> {
pluginManager.apply {
apply("nowinandroid.android.library")
apply("nowinandroid.android.hilt")
// Serialization is used for type-safe navigation.
// TODO: Use the plugin ID from the version catalog when
// https://github.com/gradle/gradle/issues/15383# is resolved
apply("org.jetbrains.kotlin.plugin.serialization")
}
extensions.configure<LibraryExtension> {
defaultConfig {
@ -45,7 +49,9 @@ class AndroidFeatureConventionPlugin : Plugin<Project> {
add("implementation", libs.findLibrary("androidx.hilt.navigation.compose").get())
add("implementation", libs.findLibrary("androidx.lifecycle.runtimeCompose").get())
add("implementation", libs.findLibrary("androidx.lifecycle.viewModelCompose").get())
add("implementation", libs.findLibrary("androidx.navigation.compose").get())
add("implementation", libs.findLibrary("androidx.tracing.ktx").get())
add("implementation", libs.findLibrary("kotlinx.serialization.json").get())
add("androidTestImplementation", libs.findLibrary("androidx.lifecycle.runtimeTesting").get())
}

@ -28,7 +28,7 @@ import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource
import com.google.samples.apps.nowinandroid.core.result.Result
import com.google.samples.apps.nowinandroid.core.result.asResult
import com.google.samples.apps.nowinandroid.feature.topic.navigation.TopicArgs
import com.google.samples.apps.nowinandroid.feature.topic.navigation.TopicDestination
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
@ -47,12 +47,14 @@ class TopicViewModel @Inject constructor(
userNewsResourceRepository: UserNewsResourceRepository,
) : ViewModel() {
private val topicArgs: TopicArgs = TopicArgs(savedStateHandle)
// TODO: Remove when alpha08 is released
private val topicDestination = TopicDestination(savedStateHandle["id"]!!)
//private val topicDestination : TopicDestination = savedStateHandle.toRoute()
val topicId = topicArgs.topicId
val topicId = topicDestination.id
val topicUiState: StateFlow<TopicUiState> = topicUiState(
topicId = topicArgs.topicId,
topicId = topicDestination.id,
userDataRepository = userDataRepository,
topicsRepository = topicsRepository,
)
@ -63,7 +65,7 @@ class TopicViewModel @Inject constructor(
)
val newsUiState: StateFlow<NewsUiState> = newsUiState(
topicId = topicArgs.topicId,
topicId = topicDestination.id,
userDataRepository = userDataRepository,
userNewsResourceRepository = userNewsResourceRepository,
)
@ -75,7 +77,7 @@ class TopicViewModel @Inject constructor(
fun followTopicToggle(followed: Boolean) {
viewModelScope.launch {
userDataRepository.setTopicIdFollowed(topicArgs.topicId, followed)
userDataRepository.setTopicIdFollowed(topicDestination.id, followed)
}
}

@ -17,33 +17,23 @@
package com.google.samples.apps.nowinandroid.feature.topic.navigation
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.SavedStateHandle
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptionsBuilder
import androidx.navigation.NavType
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import com.google.samples.apps.nowinandroid.feature.topic.TopicRoute
import java.net.URLDecoder
import java.net.URLEncoder
import kotlin.text.Charsets.UTF_8
private val URL_CHARACTER_ENCODING = UTF_8.name()
import kotlinx.serialization.Serializable
// TODO: Remove
@VisibleForTesting
internal const val TOPIC_ID_ARG = "topicId"
const val TOPIC_ROUTE = "topic_route"
internal class TopicArgs(val topicId: String) {
constructor(savedStateHandle: SavedStateHandle) :
this(URLDecoder.decode(checkNotNull(savedStateHandle[TOPIC_ID_ARG]), URL_CHARACTER_ENCODING))
}
@Serializable
data class TopicDestination(val id: String)
fun NavController.navigateToTopic(topicId: String, navOptions: NavOptionsBuilder.() -> Unit = {}) {
val encodedId = URLEncoder.encode(topicId, URL_CHARACTER_ENCODING)
val newRoute = "$TOPIC_ROUTE/$encodedId"
navigate(newRoute) {
navigate(TopicDestination(topicId)) {
navOptions()
}
}
@ -53,12 +43,7 @@ fun NavGraphBuilder.topicScreen(
onBackClick: () -> Unit,
onTopicClick: (String) -> Unit,
) {
composable(
route = "topic_route/{$TOPIC_ID_ARG}",
arguments = listOf(
navArgument(TOPIC_ID_ARG) { type = NavType.StringType },
),
) {
composable<TopicDestination> {
TopicRoute(
showBackButton = showBackButton,
onBackClick = onBackClick,
@ -66,3 +51,4 @@ fun NavGraphBuilder.topicScreen(
)
}
}

@ -20,7 +20,8 @@ androidxHiltNavigationCompose = "1.2.0"
androidxLifecycle = "2.7.0"
androidxMacroBenchmark = "1.2.2"
androidxMetrics = "1.0.0-alpha04"
androidxNavigation = "2.7.4"
#androidxNavigation = "2.8.0-SNAPSHOT"
androidxNavigation = "2.8.0-alpha07"
androidxProfileinstaller = "1.3.1"
androidxTestCore = "1.5.0"
androidxTestExt = "1.1.5"

Loading…
Cancel
Save