Make SourceState similar to MigrateState (#7053)

* make `SourceState` similar to `MigrateState`

* Review Changes
This commit is contained in:
FourTOne5 2022-05-02 08:34:58 +06:00 committed by GitHub
parent e3f6cfa2df
commit bd45bf7407
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 42 additions and 53 deletions

View File

@ -44,7 +44,7 @@ import eu.kanade.presentation.util.horizontalPadding
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.ui.browse.source.SourcePresenter 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 import eu.kanade.tachiyomi.util.system.LocaleHelper
@Composable @Composable
@ -58,13 +58,12 @@ fun SourceScreen(
) { ) {
val state by presenter.state.collectAsState() val state by presenter.state.collectAsState()
when { when (state) {
state.isLoading -> LoadingScreen() is SourceState.Loading -> LoadingScreen()
state.hasError -> Text(text = state.error!!.message!!) is SourceState.Error -> Text(text = (state as SourceState.Error).error.message!!)
state.isEmpty -> EmptyScreen(message = "") is SourceState.Success -> SourceList(
else -> SourceList(
nestedScrollConnection = nestedScrollInterop, nestedScrollConnection = nestedScrollInterop,
list = state.sources, list = (state as SourceState.Success).uiModels,
onClickItem = onClickItem, onClickItem = onClickItem,
onClickDisable = onClickDisable, onClickDisable = onClickDisable,
onClickLatest = onClickLatest, onClickLatest = onClickLatest,
@ -76,12 +75,17 @@ fun SourceScreen(
@Composable @Composable
fun SourceList( fun SourceList(
nestedScrollConnection: NestedScrollConnection, nestedScrollConnection: NestedScrollConnection,
list: List<UiModel>, list: List<SourceUiModel>,
onClickItem: (Source) -> Unit, onClickItem: (Source) -> Unit,
onClickDisable: (Source) -> Unit, onClickDisable: (Source) -> Unit,
onClickLatest: (Source) -> Unit, onClickLatest: (Source) -> Unit,
onClickPin: (Source) -> Unit, onClickPin: (Source) -> Unit,
) { ) {
if (list.isEmpty()) {
EmptyScreen(textResource = R.string.source_empty_screen)
return
}
val (sourceState, setSourceState) = remember { mutableStateOf<Source?>(null) } val (sourceState, setSourceState) = remember { mutableStateOf<Source?>(null) }
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier
@ -92,25 +96,25 @@ fun SourceList(
items = list, items = list,
contentType = { contentType = {
when (it) { when (it) {
is UiModel.Header -> "header" is SourceUiModel.Header -> "header"
is UiModel.Item -> "item" is SourceUiModel.Item -> "item"
} }
}, },
key = { key = {
when (it) { when (it) {
is UiModel.Header -> it.hashCode() is SourceUiModel.Header -> it.hashCode()
is UiModel.Item -> it.source.key() is SourceUiModel.Item -> it.source.key()
} }
} }
) { model -> ) { model ->
when (model) { when (model) {
is UiModel.Header -> { is SourceUiModel.Header -> {
SourceHeader( SourceHeader(
modifier = Modifier.animateItemPlacement(), modifier = Modifier.animateItemPlacement(),
language = model.language language = model.language
) )
} }
is UiModel.Item -> SourceItem( is SourceUiModel.Item -> SourceItem(
modifier = Modifier.animateItemPlacement(), modifier = Modifier.animateItemPlacement(),
source = model.source, source = model.source,
onClickItem = onClickItem, onClickItem = onClickItem,
@ -262,3 +266,8 @@ fun SourceOptionsDialog(
confirmButton = {}, confirmButton = {},
) )
} }
sealed class SourceUiModel {
data class Item(val source: Source) : SourceUiModel()
data class Header(val language: String) : SourceUiModel()
}

View File

@ -6,6 +6,7 @@ import eu.kanade.domain.source.interactor.ToggleSource
import eu.kanade.domain.source.interactor.ToggleSourcePin import eu.kanade.domain.source.interactor.ToggleSourcePin
import eu.kanade.domain.source.model.Pin import eu.kanade.domain.source.model.Pin
import eu.kanade.domain.source.model.Source 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.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -13,7 +14,6 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.update
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.TreeMap import java.util.TreeMap
@ -28,7 +28,7 @@ class SourcePresenter(
private val toggleSourcePin: ToggleSourcePin = Injekt.get() private val toggleSourcePin: ToggleSourcePin = Injekt.get()
) : BasePresenter<SourceController>() { ) : BasePresenter<SourceController>() {
private val _state: MutableStateFlow<SourceState> = MutableStateFlow(SourceState.EMPTY) private val _state: MutableStateFlow<SourceState> = MutableStateFlow(SourceState.Loading)
val state: StateFlow<SourceState> = _state.asStateFlow() val state: StateFlow<SourceState> = _state.asStateFlow()
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
@ -36,15 +36,13 @@ class SourcePresenter(
presenterScope.launchIO { presenterScope.launchIO {
getEnabledSources.subscribe() getEnabledSources.subscribe()
.catch { exception -> .catch { exception ->
_state.update { state -> _state.emit(SourceState.Error(exception))
state.copy(sources = listOf(), error = exception)
}
} }
.collectLatest(::collectLatestSources) .collectLatest(::collectLatestSources)
} }
} }
private fun collectLatestSources(sources: List<Source>) { private suspend fun collectLatestSources(sources: List<Source>) {
val map = TreeMap<String, MutableList<Source>> { d1, d2 -> val map = TreeMap<String, MutableList<Source>> { d1, d2 ->
// Catalogues without a lang defined will be placed at the end // Catalogues without a lang defined will be placed at the end
when { when {
@ -64,19 +62,16 @@ class SourcePresenter(
else -> it.lang else -> it.lang
} }
} }
_state.update { state ->
state.copy( val uiModels = byLang.flatMap {
sources = byLang.flatMap { listOf(
listOf( SourceUiModel.Header(it.key),
UiModel.Header(it.key), *it.value.map { source ->
*it.value.map { source -> SourceUiModel.Item(source)
UiModel.Item(source) }.toTypedArray(),
}.toTypedArray()
)
},
error = null
) )
} }
_state.emit(SourceState.Success(uiModels))
} }
fun toggleSource(source: Source) { fun toggleSource(source: Source) {
@ -93,26 +88,8 @@ class SourcePresenter(
} }
} }
sealed class UiModel { sealed class SourceState {
data class Item(val source: Source) : UiModel() object Loading : SourceState()
data class Header(val language: String) : UiModel() data class Error(val error: Throwable) : SourceState()
} data class Success(val uiModels: List<SourceUiModel>) : SourceState()
data class SourceState(
val sources: List<UiModel>,
val error: Throwable?
) {
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)
}
} }

View File

@ -711,6 +711,9 @@
<string name="clear_history_completed">History deleted</string> <string name="clear_history_completed">History deleted</string>
<string name="clear_history_confirmation">Are you sure? All history will be lost.</string> <string name="clear_history_confirmation">Are you sure? All history will be lost.</string>
<!-- Source Screen -->
<string name="source_empty_screen">No source found</string>
<!-- Source Filter Screen --> <!-- Source Filter Screen -->
<string name="source_filter_empty_screen">No installed source found</string> <string name="source_filter_empty_screen">No installed source found</string>