From 222f8a7d7a62f294e3842d13e25d7fc975f8ba9e Mon Sep 17 00:00:00 2001 From: FourTOne5 <59261191+FourTOne5@users.noreply.github.com> Date: Mon, 2 May 2022 08:34:58 +0600 Subject: [PATCH] Make `SourceState` similar to `MigrateState` (#7053) * make `SourceState` similar to `MigrateState` * Review Changes (cherry picked from commit bd45bf7407716bceec5cc226d3680676bb211fea) # Conflicts: # app/src/main/java/eu/kanade/presentation/source/SourceScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcePresenter.kt --- .../presentation/source/SourceScreen.kt | 43 +++--- .../ui/browse/source/SourcePresenter.kt | 130 ++++++------------ app/src/main/res/values/strings.xml | 3 + 3 files changed, 70 insertions(+), 106 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/source/SourceScreen.kt b/app/src/main/java/eu/kanade/presentation/source/SourceScreen.kt index c0529369c..f9f39172f 100644 --- a/app/src/main/java/eu/kanade/presentation/source/SourceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/source/SourceScreen.kt @@ -47,7 +47,7 @@ import eu.kanade.presentation.util.horizontalPadding import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.ui.browse.source.SourcePresenter -import eu.kanade.tachiyomi.ui.browse.source.UiModel +import eu.kanade.tachiyomi.ui.browse.source.SourceState import eu.kanade.tachiyomi.util.system.LocaleHelper @Composable @@ -63,16 +63,15 @@ fun SourceScreen( ) { val state by presenter.state.collectAsState() - when { - state.isLoading -> LoadingScreen() - state.hasError -> Text(text = state.error!!.message!!) - state.isEmpty -> EmptyScreen(message = "") - else -> SourceList( + when (state) { + is SourceState.Loading -> LoadingScreen() + is SourceState.Error -> Text(text = (state as SourceState.Error).error.message!!) + is SourceState.Success -> SourceList( nestedScrollConnection = nestedScrollInterop, - list = state.sources, - categories = state.sourceCategories, - showPin = state.showPin, - showLatest = state.showLatest, + list = (state as SourceState.Success).uiModels, + categories = (state as SourceState.Success).sourceCategories, + showPin = (state as SourceState.Success).showPin, + showLatest = (state as SourceState.Success).showLatest, onClickItem = onClickItem, onClickDisable = onClickDisable, onClickLatest = onClickLatest, @@ -86,7 +85,7 @@ fun SourceScreen( @Composable fun SourceList( nestedScrollConnection: NestedScrollConnection, - list: List, + list: List, categories: List, showPin: Boolean, showLatest: Boolean, @@ -97,6 +96,11 @@ fun SourceList( onClickSetCategories: (Source, List) -> Unit, onClickToggleDataSaver: (Source) -> Unit ) { + if (list.isEmpty()) { + EmptyScreen(textResource = R.string.source_empty_screen) + return + } + val (sourceState, setSourceState) = remember { mutableStateOf(null) } // SY --> val (sourceCategoriesState, setSourceCategoriesState) = remember { mutableStateOf(null) } @@ -110,26 +114,26 @@ fun SourceList( items = list, contentType = { when (it) { - is UiModel.Header -> "header" - is UiModel.Item -> "item" + is SourceUiModel.Header -> "header" + is SourceUiModel.Item -> "item" } }, key = { when (it) { - is UiModel.Header -> it.hashCode() - is UiModel.Item -> it.source.key() + is SourceUiModel.Header -> it.hashCode() + is SourceUiModel.Item -> it.source.key() } } ) { model -> when (model) { - is UiModel.Header -> { + is SourceUiModel.Header -> { SourceHeader( modifier = Modifier.animateItemPlacement(), language = model.language, isCategory = model.isCategory ) } - is UiModel.Item -> SourceItem( + is SourceUiModel.Item -> SourceItem( modifier = Modifier.animateItemPlacement(), source = model.source, showLatest = showLatest, @@ -337,6 +341,11 @@ fun SourceOptionsDialog( ) } +sealed class SourceUiModel { + data class Item(val source: Source) : SourceUiModel() + data class Header(val language: String, val isCategory: Boolean) : SourceUiModel() +} + // SY --> @Composable fun SourceCategoriesDialog( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcePresenter.kt index 973f5edc7..9cb01685e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcePresenter.kt @@ -10,14 +10,17 @@ import eu.kanade.domain.source.interactor.ToggleSource import eu.kanade.domain.source.interactor.ToggleSourcePin import eu.kanade.domain.source.model.Pin import eu.kanade.domain.source.model.Source +import eu.kanade.presentation.source.SourceUiModel import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter -import eu.kanade.tachiyomi.util.lang.launchIO +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.update +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.util.TreeMap @@ -39,50 +42,28 @@ class SourcePresenter( // SY <-- ) : BasePresenter() { - private val _state: MutableStateFlow = MutableStateFlow(SourceState.EMPTY) + private val _state: MutableStateFlow = MutableStateFlow(SourceState.Loading) val state: StateFlow = _state.asStateFlow() override fun onCreate(savedState: Bundle?) { super.onCreate(savedState) - presenterScope.launchIO { - getEnabledSources.subscribe() - .catch { exception -> - _state.update { state -> - state.copy(sources = listOf(), error = exception) - } - } - .collectLatest(::collectLatestSources) - } // SY --> - presenterScope.launchIO { - getSourceCategories.subscribe() - .catch { exception -> - _state.update { state -> - state.copy(sources = listOf(), error = exception) - } - } - .collectLatest(::updateCategories) - } - presenterScope.launchIO { - _state.update { state -> - state.copy( - showPin = controllerMode == SourceController.Mode.CATALOGUE, - ) + combine( + getEnabledSources.subscribe(), + getSourceCategories.subscribe(), + getShowLatest.subscribe(controllerMode), + flowOf(controllerMode == SourceController.Mode.CATALOGUE), + ::collectLatestSources + ) + .catch { exception -> + _state.emit(SourceState.Error(exception)) } - } - presenterScope.launchIO { - getShowLatest.subscribe(mode = controllerMode) - .catch { exception -> - _state.update { state -> - state.copy(sources = listOf(), error = exception) - } - } - .collectLatest(::updateShowLatest) - } + .flowOn(Dispatchers.IO) + .launchIn(presenterScope) // SY <-- } - private fun collectLatestSources(sources: List) { + private suspend fun collectLatestSources(sources: List, categories: Set, showLatest: Boolean, showPin: Boolean) { val map = TreeMap> { d1, d2 -> // Catalogues without a lang defined will be placed at the end when { @@ -105,37 +86,24 @@ class SourcePresenter( else -> it.lang } } - _state.update { state -> - state.copy( - sources = byLang.flatMap { - listOf( - UiModel.Header(it.key, it.value.firstOrNull()?.category != null), - *it.value.map { source -> - UiModel.Item(source) - }.toTypedArray() - ) - }, - error = null - ) - } - } - // SY --> - private fun updateCategories(categories: Set) { - _state.update { state -> - state.copy( - sourceCategories = categories.sortedWith(compareByDescending(String.CASE_INSENSITIVE_ORDER) { it }) + val uiModels = byLang.flatMap { + listOf( + SourceUiModel.Header(it.key, it.value.firstOrNull()?.category != null), + *it.value.map { source -> + SourceUiModel.Item(source) + }.toTypedArray(), ) } - } - private fun updateShowLatest(showLatest: Boolean) { - _state.update { state -> - state.copy( - showLatest = showLatest + _state.emit( + SourceState.Success( + uiModels, + categories.sortedWith(compareByDescending(String.CASE_INSENSITIVE_ORDER) { it }), + showLatest, + showPin ) - } + ) } - // SY <-- fun toggleSource(source: Source) { toggleSource.await(source) @@ -159,29 +127,13 @@ class SourcePresenter( } } -sealed class UiModel { - data class Item(val source: Source) : UiModel() - data class Header(val language: String, val isCategory: Boolean) : UiModel() -} - -data class SourceState( - val sources: List, - val error: Throwable?, - val sourceCategories: List, - val showLatest: Boolean, - val showPin: Boolean -) { - - val isLoading: Boolean - get() = sources.isEmpty() && error == null - - val hasError: Boolean - get() = error != null - - val isEmpty: Boolean - get() = sources.isEmpty() - - companion object { - val EMPTY = SourceState(listOf(), null, emptyList(), true, true) - } +sealed class SourceState { + object Loading : SourceState() + data class Error(val error: Throwable) : SourceState() + data class Success( + val uiModels: List, + val sourceCategories: List, + val showLatest: Boolean, + val showPin: Boolean + ) : SourceState() } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6039b8607..7d650bafd 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -711,6 +711,9 @@ History deleted Are you sure? All history will be lost. + + No source found + No installed source found