Migrate to ListDetailPaneScaffold

Change-Id: I5dfc70a595838ff39c40d7063f86bb8ceb810d4b
camal/after
Miłosz Moczkowski 1 year ago
parent 77356ebc63
commit 321d52cb1c

@ -73,6 +73,7 @@ dependencies {
implementation(project(":feature:interests")) implementation(project(":feature:interests"))
implementation(project(":feature:topic")) implementation(project(":feature:topic"))
implementation("androidx.compose.material3:material3-adaptive-android:1.0.0-SNAPSHOT")
implementation(libs.accompanist.systemuicontroller) implementation(libs.accompanist.systemuicontroller)
implementation(libs.androidx.activity.compose) implementation(libs.androidx.activity.compose)
implementation(libs.androidx.appcompat) implementation(libs.androidx.appcompat)

@ -16,58 +16,50 @@
package com.google.samples.apps.nowinandroid.ui package com.google.samples.apps.nowinandroid.ui
import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.material3.adaptive.ListDetailPaneScaffold
import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.material3.adaptive.ListDetailPaneScaffoldRole
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.adaptive.rememberListDetailPaneScaffoldState
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.navigation.NavType import androidx.compose.ui.unit.dp
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.google.samples.apps.nowinandroid.feature.interests.InterestsRoute import com.google.samples.apps.nowinandroid.feature.interests.InterestsRoute
import com.google.samples.apps.nowinandroid.feature.interests.navigation.interestsRoute
import com.google.samples.apps.nowinandroid.feature.topic.TopicRoute import com.google.samples.apps.nowinandroid.feature.topic.TopicRoute
import com.google.samples.apps.nowinandroid.feature.topic.navigation.navigateToTopic
import com.google.samples.apps.nowinandroid.feature.topic.navigation.topicIdArg
@OptIn( @OptIn(
ExperimentalLayoutApi::class, ExperimentalMaterial3AdaptiveApi::class,
) )
@Composable @Composable
fun NiaApp() { fun NiaApp() {
Scaffold { padding -> val layoutState = rememberListDetailPaneScaffoldState(
val navController = rememberNavController() initialFocus = ListDetailPaneScaffoldRole.List
NavHost( )
navController = navController, var selectedTopicId: String? by remember { mutableStateOf(null) }
startDestination = interestsRoute, ListDetailPaneScaffold(
modifier = Modifier layoutState = layoutState,
.fillMaxSize() listPane = {
.padding(padding) InterestsRoute(onTopicClick = { topicId ->
.consumeWindowInsets(padding) selectedTopicId = topicId
.windowInsetsPadding(WindowInsets.safeDrawing), layoutState.navigateTo(ListDetailPaneScaffoldRole.Detail)
) { })
composable(route = interestsRoute) { },
InterestsRoute(onTopicClick = navController::navigateToTopic) detailPane = {
} selectedTopicId?.let {
composable(
route = "topic_route/{$topicIdArg}",
arguments = listOf(
navArgument(topicIdArg) { type = NavType.StringType },
),
) {
TopicRoute( TopicRoute(
onBackClick = navController::popBackStack, topicId = it,
onTopicClick = navController::navigateToTopic onTopicClick = {},
onBackClick = {
layoutState.navigateBack(true)
layoutState.navigateTo(ListDetailPaneScaffoldRole.List)
},
//modifier = Modifier.preferredWidth(400.dp)
) )
} }
} },
} )
} }

@ -36,6 +36,7 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -61,11 +62,16 @@ import com.google.samples.apps.nowinandroid.feature.topic.R.string
@Composable @Composable
fun TopicRoute( fun TopicRoute(
topicId: String,
onBackClick: () -> Unit, onBackClick: () -> Unit,
onTopicClick: (String) -> Unit, onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
viewModel: TopicViewModel = hiltViewModel(), viewModel: TopicViewModel = hiltViewModel(),
) { ) {
LaunchedEffect(topicId) {
viewModel.onTopicIdChange(topicId)
}
val topicUiState: TopicUiState by viewModel.topicUiState.collectAsStateWithLifecycle() val topicUiState: TopicUiState by viewModel.topicUiState.collectAsStateWithLifecycle()
val newsUiState: NewsUiState by viewModel.newUiState.collectAsStateWithLifecycle() val newsUiState: NewsUiState by viewModel.newUiState.collectAsStateWithLifecycle()

@ -32,48 +32,67 @@ 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.TopicArgs
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class TopicViewModel @Inject constructor( class TopicViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
stringDecoder: StringDecoder, stringDecoder: StringDecoder,
private val userDataRepository: UserDataRepository, private val userDataRepository: UserDataRepository,
topicsRepository: TopicsRepository, topicsRepository: TopicsRepository,
userNewsResourceRepository: UserNewsResourceRepository, userNewsResourceRepository: UserNewsResourceRepository,
) : ViewModel() { ) : ViewModel() {
private val topicArgs: TopicArgs = TopicArgs(savedStateHandle, stringDecoder) private val _topicId: MutableStateFlow<String?> = MutableStateFlow(null)
private val topicId: SharedFlow<String> = _topicId.filterNotNull().shareIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
)
val topicUiState: StateFlow<TopicUiState> = topicUiState( val topicUiState: StateFlow<TopicUiState> = topicId.flatMapLatest { id ->
topicId = topicArgs.topicId, topicUiState(
topicId = id,
userDataRepository = userDataRepository, userDataRepository = userDataRepository,
topicsRepository = topicsRepository, topicsRepository = topicsRepository,
).stateIn( )
}.stateIn(
scope = viewModelScope, scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000), started = SharingStarted.WhileSubscribed(5_000),
initialValue = TopicUiState.Loading, initialValue = TopicUiState.Loading,
) )
val newUiState: StateFlow<NewsUiState> = newsUiState( val newUiState: StateFlow<NewsUiState> = topicId.flatMapLatest { id ->
topicId = topicArgs.topicId, newsUiState(
topicId = id,
userDataRepository = userDataRepository, userDataRepository = userDataRepository,
userNewsResourceRepository = userNewsResourceRepository, userNewsResourceRepository = userNewsResourceRepository,
).stateIn( )
}.stateIn(
scope = viewModelScope, scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000), started = SharingStarted.WhileSubscribed(5_000),
initialValue = NewsUiState.Loading, initialValue = NewsUiState.Loading,
) )
fun onTopicIdChange(topicId: String) {
viewModelScope.launch {
_topicId.value = topicId
}
}
fun followTopicToggle(followed: Boolean) { fun followTopicToggle(followed: Boolean) {
viewModelScope.launch { viewModelScope.launch {
userDataRepository.toggleFollowedTopicId(topicArgs.topicId, followed) userDataRepository.toggleFollowedTopicId(topicId.first(), followed)
} }
} }

@ -28,6 +28,7 @@ dependencyResolutionManagement {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
maven { url = uri("https://androidx.dev/snapshots/builds/10531429/artifacts/repository") }
} }
} }
rootProject.name = "nowinandroid" rootProject.name = "nowinandroid"

Loading…
Cancel
Save