diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt index 3d58ed5a6..62c380597 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/NiaNavHost.kt @@ -17,14 +17,20 @@ package com.google.samples.apps.nowinandroid.navigation import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier +import androidx.navigation.NavGraph.Companion.findStartDestination import androidx.navigation.compose.NavHost +import androidx.navigation.compose.rememberNavController +import androidx.navigation.navOptions import com.google.samples.apps.nowinandroid.feature.bookmarks.navigation.bookmarksScreen import com.google.samples.apps.nowinandroid.feature.foryou.navigation.forYouNavigationRoute import com.google.samples.apps.nowinandroid.feature.foryou.navigation.forYouScreen import com.google.samples.apps.nowinandroid.feature.interests.navigation.interestsGraph +import com.google.samples.apps.nowinandroid.feature.interests.navigation.navigateToInterestsGraph import com.google.samples.apps.nowinandroid.feature.search.navigation.searchScreen import com.google.samples.apps.nowinandroid.feature.topic.navigation.navigateToTopic +import com.google.samples.apps.nowinandroid.feature.topic.navigation.topicNavigationRoute import com.google.samples.apps.nowinandroid.feature.topic.navigation.topicScreen import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination.INTERESTS import com.google.samples.apps.nowinandroid.ui.NiaAppState @@ -49,23 +55,35 @@ fun NiaNavHost( startDestination = startDestination, modifier = modifier, ) { - forYouScreen(onTopicClick = navController::navigateToTopic) + forYouScreen(onTopicClick = navController::navigateToInterestsGraph) bookmarksScreen( - onTopicClick = navController::navigateToTopic, + onTopicClick = navController::navigateToInterestsGraph, onShowSnackbar = onShowSnackbar, ) searchScreen( onBackClick = navController::popBackStack, onInterestsClick = { appState.navigateToTopLevelDestination(INTERESTS) }, - onTopicClick = navController::navigateToTopic, + onTopicClick = navController::navigateToInterestsGraph, ) interestsGraph( - onTopicClick = navController::navigateToTopic, - nestedGraphs = { - topicScreen( - onBackClick = navController::popBackStack, - onTopicClick = navController::navigateToTopic, - ) + detailsPane = { topicId -> + val nestedNavController = rememberNavController() + NavHost( + navController = nestedNavController, + startDestination = topicNavigationRoute, + ) { + topicScreen(onTopicClick = nestedNavController::navigateToTopic) + } + LaunchedEffect(topicId) { + nestedNavController.navigateToTopic( + topicId, + navOptions { + popUpTo(nestedNavController.graph.findStartDestination().id) { + inclusive = true + } + }, + ) + } }, ) } diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt index 861252081..f075b468f 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt @@ -175,7 +175,7 @@ class NiaAppState( when (topLevelDestination) { FOR_YOU -> navController.navigateToForYou(topLevelNavOptions) BOOKMARKS -> navController.navigateToBookmarks(topLevelNavOptions) - INTERESTS -> navController.navigateToInterestsGraph(topLevelNavOptions) + INTERESTS -> navController.navigateToInterestsGraph(navOptions = topLevelNavOptions) } } } diff --git a/feature/interests/build.gradle.kts b/feature/interests/build.gradle.kts index 20b1ef1aa..f01c96163 100644 --- a/feature/interests/build.gradle.kts +++ b/feature/interests/build.gradle.kts @@ -22,3 +22,9 @@ plugins { android { namespace = "com.google.samples.apps.nowinandroid.feature.interests" } + +dependencies { + implementation(libs.androidx.compose.material3.adaptive) { + isTransitive = false + } +} diff --git a/feature/interests/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt b/feature/interests/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt index 492e91fa3..1de6fac15 100644 --- a/feature/interests/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt +++ b/feature/interests/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt @@ -74,7 +74,10 @@ class InterestsScreenTest { fun interestsWithTopics_whenTopicsFollowed_showFollowedAndUnfollowedTopicsWithInfo() { composeTestRule.setContent { InterestsScreen( - uiState = InterestsUiState.Interests(topics = followableTopicTestData), + uiState = InterestsUiState.Interests( + selectedTopicId = null, + topics = followableTopicTestData, + ), ) } @@ -108,8 +111,9 @@ class InterestsScreenTest { private fun InterestsScreen(uiState: InterestsUiState) { InterestsScreen( uiState = uiState, - followTopic = { _, _ -> }, - onTopicClick = {}, + followTopic = { _, _ -> /* no-op */ }, + onTopicClick = { /* no-op */ }, + detailsPane = { /* no-op */ }, ) } } diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt index e618c1c9f..e016861dd 100644 --- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt +++ b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsScreen.kt @@ -36,7 +36,7 @@ import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent @Composable internal fun InterestsRoute( - onTopicClick: (String) -> Unit, + detailsPane: @Composable (String) -> Unit, modifier: Modifier = Modifier, viewModel: InterestsViewModel = hiltViewModel(), ) { @@ -45,7 +45,8 @@ internal fun InterestsRoute( InterestsScreen( uiState = uiState, followTopic = viewModel::followTopic, - onTopicClick = onTopicClick, + onTopicClick = viewModel::onTopicClick, + detailsPane = detailsPane, modifier = modifier, ) } @@ -55,6 +56,7 @@ internal fun InterestsScreen( uiState: InterestsUiState, followTopic: (String, Boolean) -> Unit, onTopicClick: (String) -> Unit, + detailsPane: @Composable (String) -> Unit, modifier: Modifier = Modifier, ) { Column( @@ -67,13 +69,17 @@ internal fun InterestsScreen( modifier = modifier, contentDesc = stringResource(id = R.string.loading), ) + is InterestsUiState.Interests -> TopicsTabContent( topics = uiState.topics, + selectedTopicId = uiState.selectedTopicId, onTopicClick = onTopicClick, onFollowButtonClick = followTopic, + detailsPane = detailsPane, modifier = modifier, ) + is InterestsUiState.Empty -> InterestsEmptyScreen() } } @@ -96,9 +102,11 @@ fun InterestsScreenPopulated( InterestsScreen( uiState = InterestsUiState.Interests( topics = followableTopics, + selectedTopicId = followableTopics.first().topic.id, ), followTopic = { _, _ -> }, onTopicClick = {}, + detailsPane = {}, ) } } @@ -113,6 +121,7 @@ fun InterestsScreenLoading() { uiState = InterestsUiState.Loading, followTopic = { _, _ -> }, onTopicClick = {}, + detailsPane = {}, ) } } @@ -127,6 +136,7 @@ fun InterestsScreenEmpty() { uiState = InterestsUiState.Empty, followTopic = { _, _ -> }, onTopicClick = {}, + detailsPane = {}, ) } } diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsViewModel.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsViewModel.kt index 6d905a6d5..ad96732dd 100644 --- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsViewModel.kt +++ b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsViewModel.kt @@ -16,46 +16,57 @@ package com.google.samples.apps.nowinandroid.feature.interests +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCase 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.topicIdArg 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( + private val savedStateHandle: SavedStateHandle, val userDataRepository: UserDataRepository, getFollowableTopics: GetFollowableTopicsUseCase, ) : ViewModel() { - val uiState: StateFlow = - getFollowableTopics(sortBy = TopicSortField.NAME).map( - InterestsUiState::Interests, - ).stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5_000), - initialValue = InterestsUiState.Loading, - ) + val uiState: StateFlow = combine( + savedStateHandle.getStateFlow(topicIdArg, null), + 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) { + viewModelScope.launch { + savedStateHandle[topicIdArg] = topicId + } + } } sealed interface InterestsUiState { data object Loading : InterestsUiState data class Interests( + val selectedTopicId: String?, val topics: List, ) : InterestsUiState diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt index d865f5e1a..49e588b90 100644 --- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt +++ b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/TabContent.kt @@ -16,21 +16,27 @@ package com.google.samples.apps.nowinandroid.feature.interests +import androidx.activity.compose.BackHandler import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.windowInsetsBottomHeight import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi +import androidx.compose.material3.adaptive.ListDetailPaneScaffold +import androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole +import androidx.compose.material3.adaptive.rememberListDetailPaneScaffoldState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag @@ -40,44 +46,78 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollba import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.scrollbarState import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic +@OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable fun TopicsTabContent( + topics: List, + selectedTopicId: String?, + onTopicClick: (String) -> Unit, + onFollowButtonClick: (String, Boolean) -> Unit, + modifier: Modifier = Modifier, + detailsPane: @Composable (String) -> Unit, +) { + val listDetailPaneState = rememberListDetailPaneScaffoldState() + + BackHandler(enabled = listDetailPaneState.canNavigateBack()) { + listDetailPaneState.navigateBack() + } + + LaunchedEffect(selectedTopicId) { + if (selectedTopicId != null) { + listDetailPaneState.navigateTo(ListDetailPaneScaffoldRole.Detail) + } + } + + ListDetailPaneScaffold( + scaffoldState = listDetailPaneState, + listPane = { + ListPane( + topics = topics, + onTopicClick = onTopicClick, + onFollowButtonClick = onFollowButtonClick, + ) + }, + detailPane = { + if (selectedTopicId != null) { + detailsPane(selectedTopicId) + } + }, + modifier = modifier, + ) +} + +@Composable +private fun ListPane( topics: List, onTopicClick: (String) -> Unit, onFollowButtonClick: (String, Boolean) -> Unit, modifier: Modifier = Modifier, - withBottomSpacer: Boolean = true, ) { Box( - modifier = modifier - .fillMaxWidth(), + modifier = modifier.fillMaxSize(), ) { val scrollableState = rememberLazyListState() LazyColumn( - modifier = Modifier - .padding(horizontal = 24.dp) - .testTag("interests:topics"), - contentPadding = PaddingValues(vertical = 16.dp), + modifier = Modifier.testTag("interests:topics"), state = scrollableState, ) { - topics.forEach { followableTopic -> + items( + items = topics, + key = { followableTopic -> followableTopic.topic.id }, + ) { followableTopic -> val topicId = followableTopic.topic.id - item(key = topicId) { - InterestsItem( - name = followableTopic.topic.name, - following = followableTopic.isFollowed, - description = followableTopic.topic.shortDescription, - topicImageUrl = followableTopic.topic.imageUrl, - onClick = { onTopicClick(topicId) }, - onFollowButtonClick = { onFollowButtonClick(topicId, it) }, - ) - } + InterestsItem( + name = followableTopic.topic.name, + following = followableTopic.isFollowed, + description = followableTopic.topic.shortDescription, + topicImageUrl = followableTopic.topic.imageUrl, + onClick = { onTopicClick(topicId) }, + onFollowButtonClick = { onFollowButtonClick(topicId, it) }, + ) } - if (withBottomSpacer) { - item { - Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing)) - } + item { + Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.safeDrawing)) } } val scrollbarState = scrollableState.scrollbarState( diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/navigation/InterestsNavigation.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/navigation/InterestsNavigation.kt index 7558ec48d..0427d67e6 100644 --- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/navigation/InterestsNavigation.kt +++ b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/navigation/InterestsNavigation.kt @@ -16,31 +16,40 @@ package com.google.samples.apps.nowinandroid.feature.interests.navigation +import androidx.compose.runtime.Composable import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable -import androidx.navigation.navigation +import androidx.navigation.navArgument import com.google.samples.apps.nowinandroid.feature.interests.InterestsRoute -private const val INTERESTS_GRAPH_ROUTE_PATTERN = "interests_graph" -const val interestsRoute = "interests_route" +internal const val topicIdArg = "topicId" +const val interestsRoute = "interests_route?$topicIdArg={$topicIdArg}" -fun NavController.navigateToInterestsGraph(navOptions: NavOptions? = null) { - this.navigate(INTERESTS_GRAPH_ROUTE_PATTERN, navOptions) +fun NavController.navigateToInterestsGraph( + topicId: String? = null, + navOptions: NavOptions? = null, +) { + if (topicId == null) { + navigate("interests_route", navOptions) + } else { + navigate("interests_route?$topicIdArg=$topicId", navOptions) + } } fun NavGraphBuilder.interestsGraph( - onTopicClick: (String) -> Unit, - nestedGraphs: NavGraphBuilder.() -> Unit, + detailsPane: @Composable (String) -> Unit, ) { - navigation( - route = INTERESTS_GRAPH_ROUTE_PATTERN, - startDestination = interestsRoute, + composable( + route = interestsRoute, + arguments = listOf( + navArgument(topicIdArg) { + defaultValue = null + nullable = true + }, + ), ) { - composable(route = interestsRoute) { - InterestsRoute(onTopicClick) - } - nestedGraphs() + InterestsRoute(detailsPane) } } diff --git a/feature/interests/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsViewModelTest.kt b/feature/interests/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsViewModelTest.kt index c46cb7780..e98af543a 100644 --- a/feature/interests/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsViewModelTest.kt +++ b/feature/interests/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsViewModelTest.kt @@ -16,6 +16,7 @@ package com.google.samples.apps.nowinandroid.interests +import androidx.lifecycle.SavedStateHandle import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCase import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.Topic @@ -53,6 +54,7 @@ class InterestsViewModelTest { @Before fun setup() { viewModel = InterestsViewModel( + savedStateHandle = SavedStateHandle(), userDataRepository = userDataRepository, getFollowableTopics = getFollowableTopicsUseCase, ) @@ -93,7 +95,7 @@ class InterestsViewModelTest { ) assertEquals( - InterestsUiState.Interests(topics = testOutputTopics), + InterestsUiState.Interests(selectedTopicId = null, topics = testOutputTopics), viewModel.uiState.value, ) @@ -123,7 +125,7 @@ class InterestsViewModelTest { ) assertEquals( - InterestsUiState.Interests(topics = testInputTopics), + InterestsUiState.Interests(selectedTopicId = null, topics = testInputTopics), viewModel.uiState.value, ) diff --git a/feature/topic/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt b/feature/topic/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt index 94f86a8e4..0c1ef2da0 100644 --- a/feature/topic/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt +++ b/feature/topic/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreenTest.kt @@ -55,7 +55,6 @@ class TopicScreenTest { TopicScreen( topicUiState = TopicUiState.Loading, newsUiState = NewsUiState.Loading, - onBackClick = {}, onFollowClick = {}, onTopicClick = {}, onBookmarkChanged = { _, _ -> }, @@ -75,7 +74,6 @@ class TopicScreenTest { TopicScreen( topicUiState = TopicUiState.Success(testTopic), newsUiState = NewsUiState.Loading, - onBackClick = {}, onFollowClick = {}, onTopicClick = {}, onBookmarkChanged = { _, _ -> }, @@ -100,7 +98,6 @@ class TopicScreenTest { TopicScreen( topicUiState = TopicUiState.Loading, newsUiState = NewsUiState.Success(userNewsResourcesTestData), - onBackClick = {}, onFollowClick = {}, onTopicClick = {}, onBookmarkChanged = { _, _ -> }, @@ -123,7 +120,6 @@ class TopicScreenTest { newsUiState = NewsUiState.Success( userNewsResourcesTestData, ), - onBackClick = {}, onFollowClick = {}, onTopicClick = {}, onBookmarkChanged = { _, _ -> }, diff --git a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt index 3dbbe7da8..7d5e936c0 100644 --- a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt +++ b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt @@ -32,12 +32,9 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.windowInsetsBottomHeight import androidx.compose.foundation.layout.windowInsetsPadding -import androidx.compose.foundation.layout.windowInsetsTopHeight import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -57,7 +54,6 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaLoadi import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.DraggableScrollbar import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.rememberDraggableScroller import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.scrollbarState -import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource @@ -69,8 +65,7 @@ import com.google.samples.apps.nowinandroid.core.ui.userNewsResourceCardItems import com.google.samples.apps.nowinandroid.feature.topic.R.string @Composable -internal fun TopicRoute( - onBackClick: () -> Unit, +fun TopicRoute( onTopicClick: (String) -> Unit, modifier: Modifier = Modifier, viewModel: TopicViewModel = hiltViewModel(), @@ -83,7 +78,6 @@ internal fun TopicRoute( topicUiState = topicUiState, newsUiState = newsUiState, modifier = modifier, - onBackClick = onBackClick, onFollowClick = viewModel::followTopicToggle, onBookmarkChanged = viewModel::bookmarkNews, onNewsResourceViewed = { viewModel.setNewsResourceViewed(it, true) }, @@ -96,7 +90,6 @@ internal fun TopicRoute( internal fun TopicScreen( topicUiState: TopicUiState, newsUiState: NewsUiState, - onBackClick: () -> Unit, onFollowClick: (Boolean) -> Unit, onTopicClick: (String) -> Unit, onBookmarkChanged: (String, Boolean) -> Unit, @@ -112,9 +105,6 @@ internal fun TopicScreen( state = state, horizontalAlignment = Alignment.CenterHorizontally, ) { - item { - Spacer(Modifier.windowInsetsTopHeight(WindowInsets.safeDrawing)) - } when (topicUiState) { TopicUiState.Loading -> item { NiaLoadingWheel( @@ -127,7 +117,6 @@ internal fun TopicScreen( is TopicUiState.Success -> { item { TopicToolbar( - onBackClick = onBackClick, onFollowClick = onFollowClick, uiState = topicUiState.followableTopic, ) @@ -270,24 +259,12 @@ private fun TopicBodyPreview() { private fun TopicToolbar( uiState: FollowableTopic, modifier: Modifier = Modifier, - onBackClick: () -> Unit = {}, onFollowClick: (Boolean) -> Unit = {}, ) { Row( - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - modifier = modifier - .fillMaxWidth() - .padding(bottom = 32.dp), + horizontalArrangement = Arrangement.End, + modifier = modifier.fillMaxWidth(), ) { - IconButton(onClick = { onBackClick() }) { - Icon( - imageVector = NiaIcons.ArrowBack, - contentDescription = stringResource( - id = com.google.samples.apps.nowinandroid.core.ui.R.string.back, - ), - ) - } val selected = uiState.isFollowed NiaFilterChip( selected = selected, @@ -314,7 +291,6 @@ fun TopicScreenPopulated( TopicScreen( topicUiState = TopicUiState.Success(userNewsResources[0].followableTopics[0]), newsUiState = NewsUiState.Success(userNewsResources), - onBackClick = {}, onFollowClick = {}, onBookmarkChanged = { _, _ -> }, onNewsResourceViewed = {}, @@ -332,7 +308,6 @@ fun TopicScreenLoading() { TopicScreen( topicUiState = TopicUiState.Loading, newsUiState = NewsUiState.Loading, - onBackClick = {}, onFollowClick = {}, onBookmarkChanged = { _, _ -> }, onNewsResourceViewed = {}, diff --git a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/navigation/TopicNavigation.kt b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/navigation/TopicNavigation.kt index c29b57d47..8e226f963 100644 --- a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/navigation/TopicNavigation.kt +++ b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/navigation/TopicNavigation.kt @@ -20,6 +20,7 @@ import androidx.annotation.VisibleForTesting import androidx.lifecycle.SavedStateHandle import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavOptions import androidx.navigation.NavType import androidx.navigation.compose.composable import androidx.navigation.navArgument @@ -32,29 +33,27 @@ private val URL_CHARACTER_ENCODING = UTF_8.name() @VisibleForTesting internal const val topicIdArg = "topicId" +const val topicNavigationRoute = "topic_route/{$topicIdArg}" internal class TopicArgs(val topicId: String) { constructor(savedStateHandle: SavedStateHandle) : this(URLDecoder.decode(checkNotNull(savedStateHandle[topicIdArg]), URL_CHARACTER_ENCODING)) } -fun NavController.navigateToTopic(topicId: String) { +fun NavController.navigateToTopic(topicId: String, navOptions: NavOptions? = null) { val encodedId = URLEncoder.encode(topicId, URL_CHARACTER_ENCODING) - this.navigate("topic_route/$encodedId") { - launchSingleTop = true - } + navigate("topic_route/$encodedId", navOptions) } fun NavGraphBuilder.topicScreen( - onBackClick: () -> Unit, onTopicClick: (String) -> Unit, ) { composable( - route = "topic_route/{$topicIdArg}", + route = topicNavigationRoute, arguments = listOf( navArgument(topicIdArg) { type = NavType.StringType }, ), ) { - TopicRoute(onBackClick = onBackClick, onTopicClick = onTopicClick) + TopicRoute(onTopicClick = onTopicClick) } }