Use Voyager on Extension Filter screen (#8503)

- Use sealed class for state
- Minor changes
This commit is contained in:
Andreas
2022-11-11 22:57:31 +01:00
committed by GitHub
parent 6ada3c90ff
commit 0270878748
6 changed files with 150 additions and 127 deletions

View File

@@ -1,20 +1,17 @@
package eu.kanade.tachiyomi.ui.browse.extension
import androidx.compose.runtime.Composable
import eu.kanade.presentation.browse.ExtensionFilterScreen
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 ExtensionFilterController : FullComposeController<ExtensionFilterPresenter>() {
override fun createPresenter() = ExtensionFilterPresenter()
class ExtensionFilterController : BasicFullComposeController() {
@Composable
override fun ComposeContent() {
ExtensionFilterScreen(
navigateUp = router::popCurrentController,
presenter = presenter,
)
CompositionLocalProvider(LocalRouter provides router) {
Navigator(screen = ExtensionFilterScreen())
}
}
}
data class FilterUiModel(val lang: String, val enabled: Boolean)

View File

@@ -1,57 +0,0 @@
package eu.kanade.tachiyomi.ui.browse.extension
import android.os.Bundle
import eu.kanade.domain.extension.interactor.GetExtensionLanguages
import eu.kanade.domain.source.interactor.ToggleLanguage
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.presentation.browse.ExtensionFilterState
import eu.kanade.presentation.browse.ExtensionFilterStateImpl
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 ExtensionFilterPresenter(
private val state: ExtensionFilterStateImpl = ExtensionFilterState() as ExtensionFilterStateImpl,
private val getExtensionLanguages: GetExtensionLanguages = Injekt.get(),
private val toggleLanguage: ToggleLanguage = Injekt.get(),
private val preferences: SourcePreferences = Injekt.get(),
) : BasePresenter<ExtensionFilterController>(), ExtensionFilterState by state {
private val _events = Channel<Event>(Int.MAX_VALUE)
val events = _events.receiveAsFlow()
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
presenterScope.launchIO {
getExtensionLanguages.subscribe()
.catch { exception ->
logcat(LogPriority.ERROR, exception)
_events.send(Event.FailedFetchingLanguages)
}
.collectLatest(::collectLatestSourceLangMap)
}
}
private fun collectLatestSourceLangMap(extLangs: List<String>) {
val enabledLanguages = preferences.enabledLanguages().get()
state.items = extLangs.map {
FilterUiModel(it, it in enabledLanguages)
}
state.isLoading = false
}
fun toggleLanguage(language: String) {
toggleLanguage.await(language)
}
sealed class Event {
object FailedFetchingLanguages : Event()
}
}

View File

@@ -0,0 +1,50 @@
package eu.kanade.tachiyomi.ui.browse.extension
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.ExtensionFilterScreen
import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.util.LocalRouter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest
class ExtensionFilterScreen : Screen {
@Composable
override fun Content() {
val context = LocalContext.current
val router = LocalRouter.currentOrThrow
val screenModel = rememberScreenModel { ExtensionFilterScreenModel() }
val state by screenModel.state.collectAsState()
if (state is ExtensionFilterState.Loading) {
LoadingScreen()
return
}
val successState = state as ExtensionFilterState.Success
ExtensionFilterScreen(
navigateUp = router::popCurrentController,
state = successState,
onClickToggle = { screenModel.toggle(it) },
)
LaunchedEffect(Unit) {
screenModel.events.collectLatest {
when (it) {
ExtensionFilterEvent.FailedFetchingLanguages -> {
context.toast(R.string.internal_error)
}
}
}
}
}
}

View File

@@ -0,0 +1,75 @@
package eu.kanade.tachiyomi.ui.browse.extension
import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.extension.interactor.GetExtensionLanguages
import eu.kanade.domain.source.interactor.ToggleLanguage
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.util.system.logcat
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import logcat.LogPriority
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class ExtensionFilterScreenModel(
private val preferences: SourcePreferences = Injekt.get(),
private val getExtensionLanguages: GetExtensionLanguages = Injekt.get(),
private val toggleLanguage: ToggleLanguage = Injekt.get(),
) : StateScreenModel<ExtensionFilterState>(ExtensionFilterState.Loading) {
private val _events: Channel<ExtensionFilterEvent> = Channel()
val events: Flow<ExtensionFilterEvent> = _events.receiveAsFlow()
init {
coroutineScope.launch {
combine(
getExtensionLanguages.subscribe(),
preferences.enabledLanguages().changes(),
) { a, b -> a to b }
.catch { throwable ->
logcat(LogPriority.ERROR, throwable)
_events.send(ExtensionFilterEvent.FailedFetchingLanguages)
}
.collectLatest { (extensionLanguages, enabledLanguages) ->
mutableState.update {
ExtensionFilterState.Success(
languages = extensionLanguages,
enabledLanguages = enabledLanguages,
)
}
}
}
}
fun toggle(language: String) {
toggleLanguage.await(language)
}
}
sealed class ExtensionFilterEvent {
object FailedFetchingLanguages : ExtensionFilterEvent()
}
sealed class ExtensionFilterState {
@Immutable
object Loading : ExtensionFilterState()
@Immutable
data class Success(
val languages: List<String>,
val enabledLanguages: Set<String> = emptySet(),
) : ExtensionFilterState() {
val isEmpty: Boolean
get() = languages.isEmpty()
}
}