Make SourceState
similar to MigrateState
(#7053)
* make `SourceState` similar to `MigrateState` * Review Changes
This commit is contained in:
parent
e3f6cfa2df
commit
bd45bf7407
@ -44,7 +44,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
|
||||
@ -58,13 +58,12 @@ 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,
|
||||
list = (state as SourceState.Success).uiModels,
|
||||
onClickItem = onClickItem,
|
||||
onClickDisable = onClickDisable,
|
||||
onClickLatest = onClickLatest,
|
||||
@ -76,12 +75,17 @@ fun SourceScreen(
|
||||
@Composable
|
||||
fun SourceList(
|
||||
nestedScrollConnection: NestedScrollConnection,
|
||||
list: List<UiModel>,
|
||||
list: List<SourceUiModel>,
|
||||
onClickItem: (Source) -> Unit,
|
||||
onClickDisable: (Source) -> Unit,
|
||||
onClickLatest: (Source) -> Unit,
|
||||
onClickPin: (Source) -> Unit,
|
||||
) {
|
||||
if (list.isEmpty()) {
|
||||
EmptyScreen(textResource = R.string.source_empty_screen)
|
||||
return
|
||||
}
|
||||
|
||||
val (sourceState, setSourceState) = remember { mutableStateOf<Source?>(null) }
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
@ -92,25 +96,25 @@ 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
|
||||
)
|
||||
}
|
||||
is UiModel.Item -> SourceItem(
|
||||
is SourceUiModel.Item -> SourceItem(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
source = model.source,
|
||||
onClickItem = onClickItem,
|
||||
@ -262,3 +266,8 @@ fun SourceOptionsDialog(
|
||||
confirmButton = {},
|
||||
)
|
||||
}
|
||||
|
||||
sealed class SourceUiModel {
|
||||
data class Item(val source: Source) : SourceUiModel()
|
||||
data class Header(val language: String) : SourceUiModel()
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ 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.flow.MutableStateFlow
|
||||
@ -13,7 +14,6 @@ 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 uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.TreeMap
|
||||
@ -28,7 +28,7 @@ class SourcePresenter(
|
||||
private val toggleSourcePin: ToggleSourcePin = Injekt.get()
|
||||
) : BasePresenter<SourceController>() {
|
||||
|
||||
private val _state: MutableStateFlow<SourceState> = MutableStateFlow(SourceState.EMPTY)
|
||||
private val _state: MutableStateFlow<SourceState> = MutableStateFlow(SourceState.Loading)
|
||||
val state: StateFlow<SourceState> = _state.asStateFlow()
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
@ -36,15 +36,13 @@ class SourcePresenter(
|
||||
presenterScope.launchIO {
|
||||
getEnabledSources.subscribe()
|
||||
.catch { exception ->
|
||||
_state.update { state ->
|
||||
state.copy(sources = listOf(), error = exception)
|
||||
}
|
||||
_state.emit(SourceState.Error(exception))
|
||||
}
|
||||
.collectLatest(::collectLatestSources)
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectLatestSources(sources: List<Source>) {
|
||||
private suspend fun collectLatestSources(sources: List<Source>) {
|
||||
val map = TreeMap<String, MutableList<Source>> { d1, d2 ->
|
||||
// Catalogues without a lang defined will be placed at the end
|
||||
when {
|
||||
@ -64,19 +62,16 @@ class SourcePresenter(
|
||||
else -> it.lang
|
||||
}
|
||||
}
|
||||
_state.update { state ->
|
||||
state.copy(
|
||||
sources = byLang.flatMap {
|
||||
listOf(
|
||||
UiModel.Header(it.key),
|
||||
*it.value.map { source ->
|
||||
UiModel.Item(source)
|
||||
}.toTypedArray()
|
||||
)
|
||||
},
|
||||
error = null
|
||||
|
||||
val uiModels = byLang.flatMap {
|
||||
listOf(
|
||||
SourceUiModel.Header(it.key),
|
||||
*it.value.map { source ->
|
||||
SourceUiModel.Item(source)
|
||||
}.toTypedArray(),
|
||||
)
|
||||
}
|
||||
_state.emit(SourceState.Success(uiModels))
|
||||
}
|
||||
|
||||
fun toggleSource(source: Source) {
|
||||
@ -93,26 +88,8 @@ class SourcePresenter(
|
||||
}
|
||||
}
|
||||
|
||||
sealed class UiModel {
|
||||
data class Item(val source: Source) : UiModel()
|
||||
data class Header(val language: String) : UiModel()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
sealed class SourceState {
|
||||
object Loading : SourceState()
|
||||
data class Error(val error: Throwable) : SourceState()
|
||||
data class Success(val uiModels: List<SourceUiModel>) : SourceState()
|
||||
}
|
||||
|
@ -711,6 +711,9 @@
|
||||
<string name="clear_history_completed">History deleted</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 -->
|
||||
<string name="source_filter_empty_screen">No installed source found</string>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user