Prepare sample

Change-Id: I3870ea748e91795947f296ecf814dd5bc143f32e
experimental/uxr
Miłosz Moczkowski 1 year ago
parent 77356ebc63
commit 686178f2de

@ -16,56 +16,54 @@
package com.google.samples.apps.nowinandroid.ui
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.layout.Box
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.material3.Surface
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.navigation.NavType
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.navigation.interestsRoute
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(
ExperimentalLayoutApi::class,
)
@Composable
fun NiaApp() {
Scaffold { padding ->
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = interestsRoute,
modifier = Modifier
.fillMaxSize()
.padding(padding)
.consumeWindowInsets(padding)
.windowInsetsPadding(WindowInsets.safeDrawing),
) {
composable(route = interestsRoute) {
InterestsRoute(onTopicClick = navController::navigateToTopic)
}
composable(
route = "topic_route/{$topicIdArg}",
arguments = listOf(
navArgument(topicIdArg) { type = NavType.StringType },
),
Box(modifier = Modifier.padding(padding)) {
var selectedTopicId: String? by remember { mutableStateOf(null) }
var isTopicListVisible: Boolean by remember { mutableStateOf(false) }
InterestsRoute(
onTopicClick = { topicId ->
selectedTopicId = topicId
isTopicListVisible = true
},
)
AnimatedVisibility(
visible = isTopicListVisible,
enter = slideInHorizontally(),
exit = slideOutHorizontally()
) {
TopicRoute(
onBackClick = navController::popBackStack,
onTopicClick = navController::navigateToTopic
)
selectedTopicId?.let { id ->
Surface {
TopicRoute(
topicId = id,
onBackClick = {
isTopicListVisible = false
},
onTopicClick = { topicId ->
selectedTopicId = topicId
}
)
}
}
}
}
}

@ -36,6 +36,7 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -61,11 +62,16 @@ import com.google.samples.apps.nowinandroid.feature.topic.R.string
@Composable
fun TopicRoute(
topicId: String,
onBackClick: () -> Unit,
onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier,
viewModel: TopicViewModel = hiltViewModel(),
) {
LaunchedEffect(key1 = topicId) {
viewModel.setTopicId(topicId)
}
val topicUiState: TopicUiState by viewModel.topicUiState.collectAsStateWithLifecycle()
val newsUiState: NewsUiState by viewModel.newUiState.collectAsStateWithLifecycle()
@ -100,9 +106,6 @@ internal fun TopicScreen(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally,
) {
item {
Spacer(Modifier.windowInsetsTopHeight(WindowInsets.safeDrawing))
}
when (topicUiState) {
TopicUiState.Loading -> item {
NiaLoadingWheel(

@ -16,25 +16,25 @@
package com.google.samples.apps.nowinandroid.feature.topic
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery
import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository
import com.google.samples.apps.nowinandroid.core.decoder.StringDecoder
import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic
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 dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@ -42,30 +42,32 @@ import javax.inject.Inject
@HiltViewModel
class TopicViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,
stringDecoder: StringDecoder,
private val userDataRepository: UserDataRepository,
topicsRepository: TopicsRepository,
userNewsResourceRepository: UserNewsResourceRepository,
) : ViewModel() {
private val topicArgs: TopicArgs = TopicArgs(savedStateHandle, stringDecoder)
private val topicId: MutableStateFlow<String?> = MutableStateFlow(null)
val topicUiState: StateFlow<TopicUiState> = topicUiState(
topicId = topicArgs.topicId,
userDataRepository = userDataRepository,
topicsRepository = topicsRepository,
).stateIn(
val topicUiState: StateFlow<TopicUiState> = topicId.filterNotNull().flatMapLatest { id ->
topicUiState(
topicId = id,
userDataRepository = userDataRepository,
topicsRepository = topicsRepository,
)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = TopicUiState.Loading,
)
val newUiState: StateFlow<NewsUiState> = newsUiState(
topicId = topicArgs.topicId,
userDataRepository = userDataRepository,
userNewsResourceRepository = userNewsResourceRepository,
).stateIn(
val newUiState: StateFlow<NewsUiState> = topicId.filterNotNull().flatMapLatest { id ->
newsUiState(
topicId = id,
userNewsResourceRepository = userNewsResourceRepository,
userDataRepository = userDataRepository,
)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = NewsUiState.Loading,
@ -73,7 +75,9 @@ class TopicViewModel @Inject constructor(
fun followTopicToggle(followed: Boolean) {
viewModelScope.launch {
userDataRepository.toggleFollowedTopicId(topicArgs.topicId, followed)
topicId.value?.let { id ->
userDataRepository.toggleFollowedTopicId(id, followed)
}
}
}
@ -88,6 +92,10 @@ class TopicViewModel @Inject constructor(
userDataRepository.setNewsResourceViewed(newsResourceId, viewed)
}
}
fun setTopicId(id: String) {
topicId.value = id
}
}
private fun topicUiState(

Loading…
Cancel
Save