mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-17 22:47:29 +01:00
Use Voyager on Source Filter screen (#8511)
This commit is contained in:
@@ -1,30 +1,17 @@
|
||||
package eu.kanade.tachiyomi.ui.browse.source
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import eu.kanade.domain.source.model.Source
|
||||
import eu.kanade.presentation.browse.SourcesFilterScreen
|
||||
import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import cafe.adriel.voyager.navigator.Navigator
|
||||
import eu.kanade.presentation.util.LocalRouter
|
||||
import eu.kanade.tachiyomi.ui.base.controller.BasicFullComposeController
|
||||
|
||||
class SourceFilterController : FullComposeController<SourcesFilterPresenter>() {
|
||||
|
||||
override fun createPresenter(): SourcesFilterPresenter = SourcesFilterPresenter()
|
||||
class SourceFilterController : BasicFullComposeController() {
|
||||
|
||||
@Composable
|
||||
override fun ComposeContent() {
|
||||
SourcesFilterScreen(
|
||||
navigateUp = router::popCurrentController,
|
||||
presenter = presenter,
|
||||
onClickLang = { language ->
|
||||
presenter.toggleLanguage(language)
|
||||
},
|
||||
onClickSource = { source ->
|
||||
presenter.toggleSource(source)
|
||||
},
|
||||
)
|
||||
CompositionLocalProvider(LocalRouter provides router) {
|
||||
Navigator(screen = SourcesFilterScreen())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class FilterUiModel {
|
||||
data class Header(val language: String, val enabled: Boolean) : FilterUiModel()
|
||||
data class Item(val source: Source, val enabled: Boolean) : FilterUiModel()
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.browse.source
|
||||
|
||||
import android.os.Bundle
|
||||
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
|
||||
import eu.kanade.domain.source.interactor.ToggleLanguage
|
||||
import eu.kanade.domain.source.interactor.ToggleSource
|
||||
import eu.kanade.domain.source.model.Source
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.presentation.browse.SourcesFilterState
|
||||
import eu.kanade.presentation.browse.SourcesFilterStateImpl
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import logcat.LogPriority
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class SourcesFilterPresenter(
|
||||
private val state: SourcesFilterStateImpl = SourcesFilterState() as SourcesFilterStateImpl,
|
||||
private val getLanguagesWithSources: GetLanguagesWithSources = Injekt.get(),
|
||||
private val toggleSource: ToggleSource = Injekt.get(),
|
||||
private val toggleLanguage: ToggleLanguage = Injekt.get(),
|
||||
private val preferences: SourcePreferences = Injekt.get(),
|
||||
) : BasePresenter<SourceFilterController>(), SourcesFilterState by state {
|
||||
|
||||
private val _events = Channel<Event>(Int.MAX_VALUE)
|
||||
val events = _events.receiveAsFlow()
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
|
||||
presenterScope.launchIO {
|
||||
getLanguagesWithSources.subscribe()
|
||||
.catch { exception ->
|
||||
logcat(LogPriority.ERROR, exception)
|
||||
_events.send(Event.FailedFetchingLanguages)
|
||||
}
|
||||
.collectLatest(::collectLatestSourceLangMap)
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectLatestSourceLangMap(sourceLangMap: Map<String, List<Source>>) {
|
||||
state.items = sourceLangMap.flatMap {
|
||||
val isLangEnabled = it.key in preferences.enabledLanguages().get()
|
||||
val header = listOf(FilterUiModel.Header(it.key, isLangEnabled))
|
||||
|
||||
if (isLangEnabled.not()) return@flatMap header
|
||||
header + it.value.map { source ->
|
||||
FilterUiModel.Item(
|
||||
source,
|
||||
source.id.toString() !in preferences.disabledSources().get(),
|
||||
)
|
||||
}
|
||||
}
|
||||
state.isLoading = false
|
||||
}
|
||||
|
||||
fun toggleSource(source: Source) {
|
||||
toggleSource.await(source)
|
||||
}
|
||||
|
||||
fun toggleLanguage(language: String) {
|
||||
toggleLanguage.await(language)
|
||||
}
|
||||
|
||||
sealed class Event {
|
||||
object FailedFetchingLanguages : Event()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package eu.kanade.tachiyomi.ui.browse.source
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.browse.SourcesFilterScreen
|
||||
import eu.kanade.presentation.components.LoadingScreen
|
||||
import eu.kanade.presentation.util.LocalRouter
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
|
||||
class SourcesFilterScreen : Screen {
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val router = LocalRouter.currentOrThrow
|
||||
val screenModel = rememberScreenModel { SourcesFilterScreenModel() }
|
||||
val state by screenModel.state.collectAsState()
|
||||
|
||||
if (state is SourcesFilterState.Loading) {
|
||||
LoadingScreen()
|
||||
return
|
||||
}
|
||||
|
||||
if (state is SourcesFilterState.Error) {
|
||||
val context = LocalContext.current
|
||||
LaunchedEffect(Unit) {
|
||||
context.toast(R.string.internal_error)
|
||||
router.popCurrentController()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val successState = state as SourcesFilterState.Success
|
||||
|
||||
SourcesFilterScreen(
|
||||
navigateUp = router::popCurrentController,
|
||||
state = successState,
|
||||
onClickLanguage = screenModel::toggleLanguage,
|
||||
onClickSource = screenModel::toggleSource,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package eu.kanade.tachiyomi.ui.browse.source
|
||||
|
||||
import cafe.adriel.voyager.core.model.StateScreenModel
|
||||
import cafe.adriel.voyager.core.model.coroutineScope
|
||||
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
|
||||
import eu.kanade.domain.source.interactor.ToggleLanguage
|
||||
import eu.kanade.domain.source.interactor.ToggleSource
|
||||
import eu.kanade.domain.source.model.Source
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class SourcesFilterScreenModel(
|
||||
private val preferences: SourcePreferences = Injekt.get(),
|
||||
private val getLanguagesWithSources: GetLanguagesWithSources = Injekt.get(),
|
||||
private val toggleSource: ToggleSource = Injekt.get(),
|
||||
private val toggleLanguage: ToggleLanguage = Injekt.get(),
|
||||
) : StateScreenModel<SourcesFilterState>(SourcesFilterState.Loading) {
|
||||
|
||||
init {
|
||||
coroutineScope.launch {
|
||||
combine(
|
||||
getLanguagesWithSources.subscribe(),
|
||||
preferences.enabledLanguages().changes(),
|
||||
preferences.disabledSources().changes(),
|
||||
) { a, b, c -> Triple(a, b, c) }
|
||||
.catch { throwable ->
|
||||
mutableState.update {
|
||||
SourcesFilterState.Error(
|
||||
throwable = throwable,
|
||||
)
|
||||
}
|
||||
}
|
||||
.collectLatest { (languagesWithSources, enabledLanguages, disabledSources) ->
|
||||
mutableState.update {
|
||||
SourcesFilterState.Success(
|
||||
items = languagesWithSources,
|
||||
enabledLanguages = enabledLanguages,
|
||||
disabledSources = disabledSources,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun toggleSource(source: Source) {
|
||||
toggleSource.await(source)
|
||||
}
|
||||
|
||||
fun toggleLanguage(language: String) {
|
||||
toggleLanguage.await(language)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SourcesFilterState {
|
||||
|
||||
object Loading : SourcesFilterState()
|
||||
|
||||
data class Error(
|
||||
val throwable: Throwable,
|
||||
) : SourcesFilterState()
|
||||
|
||||
data class Success(
|
||||
val items: Map<String, List<Source>>,
|
||||
val enabledLanguages: Set<String>,
|
||||
val disabledSources: Set<String>,
|
||||
) : SourcesFilterState() {
|
||||
|
||||
val isEmpty: Boolean
|
||||
get() = items.isEmpty()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user