Use Voyager on Source Filter screen (#8511)

This commit is contained in:
Andreas
2022-11-12 15:47:19 +01:00
committed by GitHub
parent 0270878748
commit bdf035d60a
6 changed files with 167 additions and 171 deletions

View File

@@ -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()
}

View File

@@ -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()
}
}

View File

@@ -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,
)
}
}

View File

@@ -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()
}
}