Fix issue where selected topic in list was not showing as selected. Minor tidy ups.

Change-Id: Icfa79eac6f7327c365f79fd7d15dfa1f8c77184d
dt/nav-safe-args-android-dependency
Don Turner 5 months ago
parent 2264451bb8
commit 7ec21d9d2f

@ -0,0 +1,44 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.ui.interests2pane
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.navigation.toRoute
import com.google.samples.apps.nowinandroid.feature.interests.navigation.InterestsDestination
import com.google.samples.apps.nowinandroid.feature.interests.navigation.TOPIC_ID_KEY
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
const val TOPIC_ID_KEY = "selectedTopicId"
@HiltViewModel
class Interests2PaneViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle,
) : ViewModel() {
val destination = savedStateHandle.toRoute<InterestsDestination>()
val selectedTopicId: StateFlow<String?> = savedStateHandle.getStateFlow(
key = TOPIC_ID_KEY,
initialValue = destination.initialTopicId,
)
fun onTopicClick(topicId: String?) {
savedStateHandle[TOPIC_ID_KEY] = topicId
}
}

@ -26,14 +26,13 @@ import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaf
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.toRoute
import com.google.samples.apps.nowinandroid.feature.interests.InterestsRoute
import com.google.samples.apps.nowinandroid.feature.interests.navigation.InterestsDestination
import com.google.samples.apps.nowinandroid.feature.topic.TopicDetailPlaceholder
@ -46,13 +45,22 @@ import kotlinx.serialization.Serializable
@Serializable object DetailPaneNavHostDestination
fun NavGraphBuilder.interestsListDetailScreen() {
composable<InterestsDestination> { backStackEntry ->
val topicIdArgument = backStackEntry.toRoute<InterestsDestination>().topicId
var topicId: String? by rememberSaveable { mutableStateOf(topicIdArgument) }
InterestsListDetailScreen(selectedTopicId = topicId, onTopicClick = { topicId = it })
composable<InterestsDestination> {
InterestsListDetailScreen()
}
}
@Composable
internal fun InterestsListDetailScreen(
viewModel: Interests2PaneViewModel = hiltViewModel(),
) {
val selectedTopicId by viewModel.selectedTopicId.collectAsStateWithLifecycle()
InterestsListDetailScreen(
selectedTopicId = selectedTopicId,
onTopicClick = viewModel::onTopicClick,
)
}
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
internal fun InterestsListDetailScreen(
@ -86,7 +94,7 @@ internal fun InterestsListDetailScreen(
detailPane = {
NavHost(
navController = nestedNavController,
startDestination = TopicPlaceholderDestination::class,
startDestination = TopicPlaceholderDestination,
route = DetailPaneNavHostDestination::class,
) {
topicScreen(

@ -35,7 +35,7 @@ fun NavController.navigateToForYou(navOptions: NavOptions) = navigate(route = Fo
fun NavGraphBuilder.forYouScreen(onTopicClick: (String) -> Unit) {
composable<ForYouDestination>(
deepLinks = listOf(
navDeepLink { uriPattern = DEEP_LINK_URI_PATTERN },
navDeepLink<ForYouDestination>(basePath = DEEP_LINK_URI_PATTERN),
),
) {
ForYouRoute(onTopicClick)

@ -46,7 +46,10 @@ fun InterestsRoute(
InterestsScreen(
uiState = uiState,
followTopic = viewModel::followTopic,
onTopicClick = onTopicClick,
onTopicClick = {
viewModel.onTopicClick(it)
onTopicClick(it)
},
highlightSelectedTopic = highlightSelectedTopic,
modifier = modifier,
)

@ -25,37 +25,47 @@ import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCa
import com.google.samples.apps.nowinandroid.core.domain.TopicSortField
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
import com.google.samples.apps.nowinandroid.feature.interests.navigation.InterestsDestination
import com.google.samples.apps.nowinandroid.feature.interests.navigation.TOPIC_ID_KEY
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class InterestsViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
private val savedStateHandle: SavedStateHandle,
val userDataRepository: UserDataRepository,
getFollowableTopics: GetFollowableTopicsUseCase,
) : ViewModel() {
private val interestsDestination: InterestsDestination = savedStateHandle.toRoute()
private val selectedTopicId = savedStateHandle.getStateFlow(
key = TOPIC_ID_KEY,
initialValue = interestsDestination.initialTopicId,
)
val uiState: StateFlow<InterestsUiState> =
getFollowableTopics(sortBy = TopicSortField.NAME).map { topics ->
InterestsUiState.Interests(interestsDestination.topicId, topics)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = InterestsUiState.Loading,
)
val uiState: StateFlow<InterestsUiState> = combine(
selectedTopicId,
getFollowableTopics(sortBy = TopicSortField.NAME),
InterestsUiState::Interests,
).stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = InterestsUiState.Loading,
)
fun followTopic(followedTopicId: String, followed: Boolean) {
viewModelScope.launch {
userDataRepository.setTopicIdFollowed(followedTopicId, followed)
}
}
fun onTopicClick(topicId: String?) {
savedStateHandle[TOPIC_ID_KEY] = topicId
}
}
sealed interface InterestsUiState {

@ -20,10 +20,16 @@ import androidx.navigation.NavController
import androidx.navigation.NavOptions
import kotlinx.serialization.Serializable
const val TOPIC_ID_ARG = "topicId"
const val TOPIC_ID_KEY = "topicId"
@Serializable data class InterestsDestination(val topicId: String?)
@Serializable data class InterestsDestination(
// The ID of the topic which will be initially selected at this destination
val initialTopicId: String?,
)
fun NavController.navigateToInterests(topicId: String? = null, navOptions: NavOptions? = null) {
navigate(route = InterestsDestination(topicId), navOptions)
fun NavController.navigateToInterests(
initialTopicId: String? = null,
navOptions: NavOptions? = null,
) {
navigate(route = InterestsDestination(initialTopicId), navOptions)
}

@ -48,12 +48,10 @@ class TopicViewModel @Inject constructor(
userNewsResourceRepository: UserNewsResourceRepository,
) : ViewModel() {
private val topicDestination: TopicDestination = savedStateHandle.toRoute()
val topicId = topicDestination.id
val topicId = savedStateHandle.toRoute<TopicDestination>().id
val topicUiState: StateFlow<TopicUiState> = topicUiState(
topicId = topicDestination.id,
topicId = topicId,
userDataRepository = userDataRepository,
topicsRepository = topicsRepository,
)
@ -64,7 +62,7 @@ class TopicViewModel @Inject constructor(
)
val newsUiState: StateFlow<NewsUiState> = newsUiState(
topicId = topicDestination.id,
topicId = topicId,
userDataRepository = userDataRepository,
userNewsResourceRepository = userNewsResourceRepository,
)
@ -76,7 +74,7 @@ class TopicViewModel @Inject constructor(
fun followTopicToggle(followed: Boolean) {
viewModelScope.launch {
userDataRepository.setTopicIdFollowed(topicDestination.id, followed)
userDataRepository.setTopicIdFollowed(topicId, followed)
}
}

Loading…
Cancel
Save