Refactor - using `StateInViewModelScope()` extension function inside ViewModels

This commit also includes refactoring for functions, making functions more clear by extracting them inside ViewModels.
pull/454/head
Mohsen Rzna 3 years ago
parent c19b8b9319
commit 01351f1ef0

@ -22,12 +22,11 @@ import com.google.samples.apps.nowinandroid.MainActivityUiState.Loading
import com.google.samples.apps.nowinandroid.MainActivityUiState.Success import com.google.samples.apps.nowinandroid.MainActivityUiState.Success
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.model.data.UserData import com.google.samples.apps.nowinandroid.core.model.data.UserData
import com.google.samples.apps.nowinandroid.core.ui.stateInViewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@HiltViewModel @HiltViewModel
class MainActivityViewModel @Inject constructor( class MainActivityViewModel @Inject constructor(
@ -35,11 +34,7 @@ class MainActivityViewModel @Inject constructor(
) : ViewModel() { ) : ViewModel() {
val uiState: StateFlow<MainActivityUiState> = userDataRepository.userDataStream.map { val uiState: StateFlow<MainActivityUiState> = userDataRepository.userDataStream.map {
Success(it) Success(it)
}.stateIn( }.stateInViewModelScope(viewModelScope, initialValue = Loading)
scope = viewModelScope,
initialValue = Loading,
started = SharingStarted.WhileSubscribed(5_000)
)
} }
sealed interface MainActivityUiState { sealed interface MainActivityUiState {

@ -27,17 +27,19 @@ import com.google.samples.apps.nowinandroid.core.domain.model.FollowableAuthor
import com.google.samples.apps.nowinandroid.core.domain.model.SaveableNewsResource import com.google.samples.apps.nowinandroid.core.domain.model.SaveableNewsResource
import com.google.samples.apps.nowinandroid.core.model.data.Author import com.google.samples.apps.nowinandroid.core.model.data.Author
import com.google.samples.apps.nowinandroid.core.result.Result import com.google.samples.apps.nowinandroid.core.result.Result
import com.google.samples.apps.nowinandroid.core.result.Result.Error
import com.google.samples.apps.nowinandroid.core.result.Result.Loading
import com.google.samples.apps.nowinandroid.core.result.Result.Success
import com.google.samples.apps.nowinandroid.core.result.asResult import com.google.samples.apps.nowinandroid.core.result.asResult
import com.google.samples.apps.nowinandroid.core.ui.stateInViewModelScope
import com.google.samples.apps.nowinandroid.feature.author.navigation.AuthorArgs import com.google.samples.apps.nowinandroid.feature.author.navigation.AuthorArgs
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
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.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel @HiltViewModel
class AuthorViewModel @Inject constructor( class AuthorViewModel @Inject constructor(
@ -54,33 +56,19 @@ class AuthorViewModel @Inject constructor(
authorId = authorArgs.authorId, authorId = authorArgs.authorId,
userDataRepository = userDataRepository, userDataRepository = userDataRepository,
authorsRepository = authorsRepository authorsRepository = authorsRepository
) ).stateInViewModelScope(viewModelScope, initialValue = AuthorUiState.Loading)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = AuthorUiState.Loading
)
val newsUiState: StateFlow<NewsUiState> = val newsUiState: StateFlow<NewsUiState> = getSaveableNewsResourcesStream
getSaveableNewsResourcesStream.newsUiStateStream(authorId = authorArgs.authorId) .newsUiStateStream(authorId = authorArgs.authorId)
.stateIn( .stateInViewModelScope(viewModelScope, initialValue = NewsUiState.Loading)
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = NewsUiState.Loading
)
fun followAuthorToggle(followed: Boolean) { fun followAuthorToggle(followed: Boolean) = viewModelScope.launch {
viewModelScope.launch {
userDataRepository.toggleFollowedAuthorId(authorArgs.authorId, followed) userDataRepository.toggleFollowedAuthorId(authorArgs.authorId, followed)
} }
}
fun bookmarkNews(newsResourceId: String, bookmarked: Boolean) { fun bookmarkNews(newsResourceId: String, bookmarked: Boolean) = viewModelScope.launch {
viewModelScope.launch {
userDataRepository.updateNewsResourceBookmark(newsResourceId, bookmarked) userDataRepository.updateNewsResourceBookmark(newsResourceId, bookmarked)
} }
}
}
private fun authorUiStateStream( private fun authorUiStateStream(
authorId: String, authorId: String,
@ -89,8 +77,7 @@ private fun authorUiStateStream(
): Flow<AuthorUiState> { ): Flow<AuthorUiState> {
// Observe the followed authors, as they could change over time. // Observe the followed authors, as they could change over time.
val followedAuthorIdsStream: Flow<Set<String>> = val followedAuthorIdsStream: Flow<Set<String>> =
userDataRepository.userDataStream userDataRepository.userDataStream.map { it.followedAuthors }
.map { it.followedAuthors }
// Observe author information // Observe author information
val authorStream: Flow<Author> = authorsRepository.getAuthorStream( val authorStream: Flow<Author> = authorsRepository.getAuthorStream(
@ -104,25 +91,32 @@ private fun authorUiStateStream(
) )
.asResult() .asResult()
.map { followedAuthorToAuthorResult -> .map { followedAuthorToAuthorResult ->
when (followedAuthorToAuthorResult) { handleToAuthorResult(followedAuthorToAuthorResult, authorId)
is Result.Success -> { }
}
private fun handleToAuthorResult(
followedAuthorToAuthorResult: Result<Pair<Set<String>, Author>>,
authorId: String
) = when (followedAuthorToAuthorResult) {
is Success -> onSuccessResult(followedAuthorToAuthorResult, authorId)
is Loading -> AuthorUiState.Loading
is Error -> AuthorUiState.Error
}
private fun onSuccessResult(
followedAuthorToAuthorResult: Success<Pair<Set<String>, Author>>,
authorId: String
): AuthorUiState.Success {
val (followedAuthors, author) = followedAuthorToAuthorResult.data val (followedAuthors, author) = followedAuthorToAuthorResult.data
val followed = followedAuthors.contains(authorId) val followed = followedAuthors.contains(authorId)
AuthorUiState.Success( return AuthorUiState.Success(
followableAuthor = FollowableAuthor( followableAuthor = FollowableAuthor(
author = author, author = author,
isFollowed = followed isFollowed = followed
) )
) )
} }
is Result.Loading -> {
AuthorUiState.Loading
}
is Result.Error -> {
AuthorUiState.Error
}
}
}
} }
private fun GetSaveableNewsResourcesStreamUseCase.newsUiStateStream( private fun GetSaveableNewsResourcesStreamUseCase.newsUiStateStream(
@ -134,9 +128,9 @@ private fun GetSaveableNewsResourcesStreamUseCase.newsUiStateStream(
).asResult() ).asResult()
.map { newsResult -> .map { newsResult ->
when (newsResult) { when (newsResult) {
is Result.Success -> NewsUiState.Success(newsResult.data) is Success -> NewsUiState.Success(newsResult.data)
is Result.Loading -> NewsUiState.Loading is Loading -> NewsUiState.Loading
is Result.Error -> NewsUiState.Error is Error -> NewsUiState.Error
} }
} }
} }

@ -23,14 +23,13 @@ import com.google.samples.apps.nowinandroid.core.domain.GetSaveableNewsResources
import com.google.samples.apps.nowinandroid.core.domain.model.SaveableNewsResource import com.google.samples.apps.nowinandroid.core.domain.model.SaveableNewsResource
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Loading import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Loading
import com.google.samples.apps.nowinandroid.core.ui.stateInViewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@HiltViewModel @HiltViewModel
@ -44,15 +43,9 @@ class BookmarksViewModel @Inject constructor(
.map { newsResources -> newsResources.filter(SaveableNewsResource::isSaved) } // Only show bookmarked news resources. .map { newsResources -> newsResources.filter(SaveableNewsResource::isSaved) } // Only show bookmarked news resources.
.map<List<SaveableNewsResource>, NewsFeedUiState>(NewsFeedUiState::Success) .map<List<SaveableNewsResource>, NewsFeedUiState>(NewsFeedUiState::Success)
.onStart { emit(Loading) } .onStart { emit(Loading) }
.stateIn( .stateInViewModelScope(viewModelScope, initialValue = Loading)
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = Loading
)
fun removeFromSavedResources(newsResourceId: String) { fun removeFromSavedResources(newsResourceId: String) = viewModelScope.launch {
viewModelScope.launch {
userDataRepository.updateNewsResourceBookmark(newsResourceId, false) userDataRepository.updateNewsResourceBookmark(newsResourceId, false)
} }
} }
}

@ -25,17 +25,16 @@ import com.google.samples.apps.nowinandroid.core.domain.GetSaveableNewsResources
import com.google.samples.apps.nowinandroid.core.domain.GetSortedFollowableAuthorsStreamUseCase import com.google.samples.apps.nowinandroid.core.domain.GetSortedFollowableAuthorsStreamUseCase
import com.google.samples.apps.nowinandroid.core.domain.model.SaveableNewsResource import com.google.samples.apps.nowinandroid.core.domain.model.SaveableNewsResource
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState
import com.google.samples.apps.nowinandroid.core.ui.stateInViewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
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.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@HiltViewModel @HiltViewModel
@ -50,12 +49,9 @@ class ForYouViewModel @Inject constructor(
private val shouldShowOnboarding: Flow<Boolean> = private val shouldShowOnboarding: Flow<Boolean> =
userDataRepository.userDataStream.map { !it.shouldHideOnboarding } userDataRepository.userDataStream.map { !it.shouldHideOnboarding }
val isSyncing = syncStatusMonitor.isSyncing val isSyncing = syncStatusMonitor
.stateIn( .isSyncing
scope = viewModelScope, .stateInViewModelScope(viewModelScope, initialValue = false)
started = SharingStarted.WhileSubscribed(5_000),
initialValue = false
)
val feedState: StateFlow<NewsFeedUiState> = val feedState: StateFlow<NewsFeedUiState> =
userDataRepository.userDataStream userDataRepository.userDataStream
@ -79,11 +75,7 @@ class ForYouViewModel @Inject constructor(
// As the selected topics and topic state changes, this will cancel the old feed // As the selected topics and topic state changes, this will cancel the old feed
// monitoring and start the new one. // monitoring and start the new one.
.flatMapLatest { it } .flatMapLatest { it }
.stateIn( .stateInViewModelScope(viewModelScope, initialValue = NewsFeedUiState.Loading)
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = NewsFeedUiState.Loading
)
val onboardingUiState: StateFlow<OnboardingUiState> = val onboardingUiState: StateFlow<OnboardingUiState> =
combine( combine(
@ -99,24 +91,15 @@ class ForYouViewModel @Inject constructor(
} else { } else {
OnboardingUiState.NotShown OnboardingUiState.NotShown
} }
} }.stateInViewModelScope(viewModelScope, initialValue = OnboardingUiState.Loading)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = OnboardingUiState.Loading
)
fun updateTopicSelection(topicId: String, isChecked: Boolean) { fun updateTopicSelection(topicId: String, isChecked: Boolean) = viewModelScope.launch {
viewModelScope.launch {
userDataRepository.toggleFollowedTopicId(topicId, isChecked) userDataRepository.toggleFollowedTopicId(topicId, isChecked)
} }
}
fun updateAuthorSelection(authorId: String, isChecked: Boolean) { fun updateAuthorSelection(authorId: String, isChecked: Boolean) = viewModelScope.launch {
viewModelScope.launch {
userDataRepository.toggleFollowedAuthorId(authorId, isChecked) userDataRepository.toggleFollowedAuthorId(authorId, isChecked)
} }
}
fun updateNewsResourceSaved(newsResourceId: String, isChecked: Boolean) { fun updateNewsResourceSaved(newsResourceId: String, isChecked: Boolean) {
viewModelScope.launch { viewModelScope.launch {
@ -124,12 +107,10 @@ class ForYouViewModel @Inject constructor(
} }
} }
fun dismissOnboarding() { fun dismissOnboarding() = viewModelScope.launch {
viewModelScope.launch {
userDataRepository.setShouldHideOnboarding(true) userDataRepository.setShouldHideOnboarding(true)
} }
} }
}
private fun Flow<List<SaveableNewsResource>>.mapToFeedState(): Flow<NewsFeedUiState> = private fun Flow<List<SaveableNewsResource>>.mapToFeedState(): Flow<NewsFeedUiState> =
map<List<SaveableNewsResource>, NewsFeedUiState>(NewsFeedUiState::Success) map<List<SaveableNewsResource>, NewsFeedUiState>(NewsFeedUiState::Success)

@ -24,14 +24,13 @@ import com.google.samples.apps.nowinandroid.core.domain.GetSortedFollowableAutho
import com.google.samples.apps.nowinandroid.core.domain.TopicSortField import com.google.samples.apps.nowinandroid.core.domain.TopicSortField
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableAuthor import com.google.samples.apps.nowinandroid.core.domain.model.FollowableAuthor
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.ui.stateInViewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -54,23 +53,15 @@ class InterestsViewModel @Inject constructor(
getSortedFollowableAuthorsStream(), getSortedFollowableAuthorsStream(),
getFollowableTopicsStream(sortBy = TopicSortField.NAME), getFollowableTopicsStream(sortBy = TopicSortField.NAME),
InterestsUiState::Interests InterestsUiState::Interests
).stateIn( ).stateInViewModelScope(viewModelScope, initialValue = InterestsUiState.Loading)
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = InterestsUiState.Loading
)
fun followTopic(followedTopicId: String, followed: Boolean) { fun followTopic(followedTopicId: String, followed: Boolean) = viewModelScope.launch {
viewModelScope.launch {
userDataRepository.toggleFollowedTopicId(followedTopicId, followed) userDataRepository.toggleFollowedTopicId(followedTopicId, followed)
} }
}
fun followAuthor(followedAuthorId: String, followed: Boolean) { fun followAuthor(followedAuthorId: String, followed: Boolean) = viewModelScope.launch {
viewModelScope.launch {
userDataRepository.toggleFollowedAuthorId(followedAuthorId, followed) userDataRepository.toggleFollowedAuthorId(followedAuthorId, followed)
} }
}
fun switchTab(newIndex: Int) { fun switchTab(newIndex: Int) {
if (newIndex != tabState.value.currentIndex) { if (newIndex != tabState.value.currentIndex) {

@ -21,6 +21,7 @@ import androidx.lifecycle.viewModelScope
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig
import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand
import com.google.samples.apps.nowinandroid.core.ui.stateInViewModelScope
import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading
import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Success import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Success
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
@ -28,7 +29,6 @@ import javax.inject.Inject
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@HiltViewModel @HiltViewModel
@ -44,31 +44,25 @@ class SettingsViewModel @Inject constructor(
darkThemeConfig = userData.darkThemeConfig darkThemeConfig = userData.darkThemeConfig
) )
) )
} }.stateInViewModelScope(viewModelScope, SharingStarted.Eagerly, Loading)
.stateIn(
scope = viewModelScope,
// Starting eagerly means the user data is ready when the SettingsDialog is laid out
// for the first time. Without this, due to b/221643630 the layout is done using the
// "Loading" text, then replaced with the user editable fields once loaded, however,
// the layout height doesn't change meaning all the fields are squashed into a small
// scrollable column.
// TODO: Change to SharingStarted.WhileSubscribed(5_000) when b/221643630 is fixed
started = SharingStarted.Eagerly,
initialValue = Loading
)
fun updateThemeBrand(themeBrand: ThemeBrand) { /**
viewModelScope.launch { * Starting eagerly means the user data is ready when the SettingsDialog is laid out
* for the first time. Without this, due to b/221643630 the layout is done using the
* "Loading" text, then replaced with the user editable fields once loaded, however,
* the layout height doesn't change meaning all the fields are squashed into a small
* scrollable column.
* TODO: Change to SharingStarted.WhileSubscribed(5_000) when b/221643630 is fixed
*/
fun updateThemeBrand(themeBrand: ThemeBrand) = viewModelScope.launch {
userDataRepository.setThemeBrand(themeBrand) userDataRepository.setThemeBrand(themeBrand)
} }
}
fun updateDarkThemeConfig(darkThemeConfig: DarkThemeConfig) { fun updateDarkThemeConfig(darkThemeConfig: DarkThemeConfig) = viewModelScope.launch {
viewModelScope.launch {
userDataRepository.setDarkThemeConfig(darkThemeConfig) userDataRepository.setDarkThemeConfig(darkThemeConfig)
} }
} }
}
/** /**
* Represents the settings which the user can edit within the app. * Represents the settings which the user can edit within the app.

@ -27,16 +27,18 @@ import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.SaveableNewsResource import com.google.samples.apps.nowinandroid.core.domain.model.SaveableNewsResource
import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.result.Result import com.google.samples.apps.nowinandroid.core.result.Result
import com.google.samples.apps.nowinandroid.core.result.Result.Error
import com.google.samples.apps.nowinandroid.core.result.Result.Loading
import com.google.samples.apps.nowinandroid.core.result.Result.Success
import com.google.samples.apps.nowinandroid.core.result.asResult import com.google.samples.apps.nowinandroid.core.result.asResult
import com.google.samples.apps.nowinandroid.core.ui.stateInViewModelScope
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 javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
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.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@HiltViewModel @HiltViewModel
@ -45,7 +47,6 @@ class TopicViewModel @Inject constructor(
stringDecoder: StringDecoder, stringDecoder: StringDecoder,
private val userDataRepository: UserDataRepository, private val userDataRepository: UserDataRepository,
topicsRepository: TopicsRepository, topicsRepository: TopicsRepository,
// newsRepository: NewsRepository,
getSaveableNewsResourcesStream: GetSaveableNewsResourcesStreamUseCase getSaveableNewsResourcesStream: GetSaveableNewsResourcesStreamUseCase
) : ViewModel() { ) : ViewModel() {
@ -55,36 +56,22 @@ class TopicViewModel @Inject constructor(
topicId = topicArgs.topicId, topicId = topicArgs.topicId,
userDataRepository = userDataRepository, userDataRepository = userDataRepository,
topicsRepository = topicsRepository topicsRepository = topicsRepository
) ).stateInViewModelScope(viewModelScope, initialValue = TopicUiState.Loading)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = TopicUiState.Loading
)
val newUiState: StateFlow<NewsUiState> = newsUiStateStream( val newUiState: StateFlow<NewsUiState> = newsUiStateStream(
topicId = topicArgs.topicId, topicId = topicArgs.topicId,
userDataRepository = userDataRepository, userDataRepository = userDataRepository,
getSaveableNewsResourcesStream = getSaveableNewsResourcesStream getSaveableNewsResourcesStream = getSaveableNewsResourcesStream
) ).stateInViewModelScope(viewModelScope, initialValue = NewsUiState.Loading)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = NewsUiState.Loading
)
fun followTopicToggle(followed: Boolean) { fun followTopicToggle(followed: Boolean) = viewModelScope.launch {
viewModelScope.launch {
userDataRepository.toggleFollowedTopicId(topicArgs.topicId, followed) userDataRepository.toggleFollowedTopicId(topicArgs.topicId, followed)
} }
}
fun bookmarkNews(newsResourceId: String, bookmarked: Boolean) { fun bookmarkNews(newsResourceId: String, bookmarked: Boolean) = viewModelScope.launch {
viewModelScope.launch {
userDataRepository.updateNewsResourceBookmark(newsResourceId, bookmarked) userDataRepository.updateNewsResourceBookmark(newsResourceId, bookmarked)
} }
} }
}
private fun topicUiStateStream( private fun topicUiStateStream(
topicId: String, topicId: String,
@ -92,8 +79,8 @@ private fun topicUiStateStream(
topicsRepository: TopicsRepository, topicsRepository: TopicsRepository,
): Flow<TopicUiState> { ): Flow<TopicUiState> {
// Observe the followed topics, as they could change over time. // Observe the followed topics, as they could change over time.
val followedTopicIdsStream: Flow<Set<String>> = val followedTopicIdsStream: Flow<Set<String>> = userDataRepository
userDataRepository.userDataStream .userDataStream
.map { it.followedTopics } .map { it.followedTopics }
// Observe topic information // Observe topic information
@ -108,8 +95,15 @@ private fun topicUiStateStream(
) )
.asResult() .asResult()
.map { followedTopicToTopicResult -> .map { followedTopicToTopicResult ->
when (followedTopicToTopicResult) { handleTopicResult(followedTopicToTopicResult, topicId)
is Result.Success -> { }
}
private fun handleTopicResult(
followedTopicToTopicResult: Result<Pair<Set<String>, Topic>>,
topicId: String
) = when (followedTopicToTopicResult) {
is Success -> {
val (followedTopics, topic) = followedTopicToTopicResult.data val (followedTopics, topic) = followedTopicToTopicResult.data
val followed = followedTopics.contains(topicId) val followed = followedTopics.contains(topicId)
TopicUiState.Success( TopicUiState.Success(
@ -119,14 +113,8 @@ private fun topicUiStateStream(
) )
) )
} }
is Result.Loading -> { is Loading -> TopicUiState.Loading
TopicUiState.Loading is Error -> TopicUiState.Error
}
is Result.Error -> {
TopicUiState.Error
}
}
}
} }
private fun newsUiStateStream( private fun newsUiStateStream(
@ -156,12 +144,8 @@ private fun newsUiStateStream(
val (news, bookmarks) = newsToBookmarksResult.data val (news, bookmarks) = newsToBookmarksResult.data
NewsUiState.Success(news) NewsUiState.Success(news)
} }
is Result.Loading -> { is Result.Loading -> NewsUiState.Loading
NewsUiState.Loading is Result.Error -> NewsUiState.Error
}
is Result.Error -> {
NewsUiState.Error
}
} }
} }
} }

Loading…
Cancel
Save