Migrate extensions language filter screen to compose (#7169)

This commit is contained in:
FourTOne5 2022-05-20 03:29:40 +06:00 committed by GitHub
parent 4be9b03ac6
commit fd9510e18f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 195 additions and 36 deletions

View file

@ -3,6 +3,7 @@ package eu.kanade.domain
import eu.kanade.data.history.HistoryRepositoryImpl
import eu.kanade.data.manga.MangaRepositoryImpl
import eu.kanade.data.source.SourceRepositoryImpl
import eu.kanade.domain.extension.interactor.GetExtensionLanguages
import eu.kanade.domain.extension.interactor.GetExtensionSources
import eu.kanade.domain.extension.interactor.GetExtensionUpdates
import eu.kanade.domain.extension.interactor.GetExtensions
@ -46,6 +47,7 @@ class DomainModule : InjektModule {
addFactory { GetExtensions(get(), get()) }
addFactory { GetExtensionSources(get()) }
addFactory { GetExtensionUpdates(get(), get()) }
addFactory { GetExtensionLanguages(get(), get()) }
addSingletonFactory<SourceRepository> { SourceRepositoryImpl(get(), get()) }
addFactory { GetLanguagesWithSources(get(), get()) }

View file

@ -0,0 +1,30 @@
package eu.kanade.domain.extension.interactor
import eu.kanade.core.util.asFlow
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.util.system.LocaleHelper
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
class GetExtensionLanguages(
private val preferences: PreferencesHelper,
private val extensionManager: ExtensionManager,
) {
fun subscribe(): Flow<List<String>> {
return combine(
preferences.enabledLanguages().asFlow(),
extensionManager.getAvailableExtensionsObservable().asFlow(),
) { enabledLanguage, availableExtensions ->
availableExtensions
.map { it.lang }
.distinct()
.sortedWith(
compareBy(
{ it !in enabledLanguage },
{ LocaleHelper.getDisplayName(it) },
),
)
}
}
}

View file

@ -0,0 +1,91 @@
package eu.kanade.presentation.browse
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.components.PreferenceRow
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionFilterPresenter
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionFilterState
import eu.kanade.tachiyomi.ui.browse.extension.FilterUiModel
import eu.kanade.tachiyomi.util.system.LocaleHelper
@Composable
fun ExtensionFilterScreen(
nestedScrollInterop: NestedScrollConnection,
presenter: ExtensionFilterPresenter,
onClickLang: (String) -> Unit,
) {
val state by presenter.state.collectAsState()
when (state) {
is ExtensionFilterState.Loading -> LoadingScreen()
is ExtensionFilterState.Error -> Text(text = (state as ExtensionFilterState.Error).error.message!!)
is ExtensionFilterState.Success ->
SourceFilterContent(
nestedScrollInterop = nestedScrollInterop,
items = (state as ExtensionFilterState.Success).models,
onClickLang = onClickLang,
)
}
}
@Composable
fun SourceFilterContent(
nestedScrollInterop: NestedScrollConnection,
items: List<FilterUiModel>,
onClickLang: (String) -> Unit,
) {
if (items.isEmpty()) {
EmptyScreen(textResource = R.string.empty_screen)
return
}
LazyColumn(
modifier = Modifier.nestedScroll(nestedScrollInterop),
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
) {
items(
items = items,
) { model ->
ExtensionFilterItem(
modifier = Modifier.animateItemPlacement(tween(1000, easing = LinearOutSlowInEasing)),
lang = model.lang,
isEnabled = model.isEnabled,
onClickItem = onClickLang,
)
}
}
}
@Composable
fun ExtensionFilterItem(
modifier: Modifier,
lang: String,
isEnabled: Boolean,
onClickItem: (String) -> Unit,
) {
PreferenceRow(
modifier = modifier,
title = LocaleHelper.getSourceDisplayName(lang, LocalContext.current),
action = {
Switch(checked = isEnabled, onCheckedChange = null)
},
onClick = { onClickItem(lang) },
)
}

View file

@ -1,45 +1,27 @@
package eu.kanade.tachiyomi.ui.browse.extension
import androidx.preference.PreferenceScreen
import androidx.compose.runtime.Composable
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import eu.kanade.presentation.browse.ExtensionFilterScreen
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.ui.setting.SettingsController
import eu.kanade.tachiyomi.util.preference.minusAssign
import eu.kanade.tachiyomi.util.preference.onChange
import eu.kanade.tachiyomi.util.preference.plusAssign
import eu.kanade.tachiyomi.util.preference.switchPreference
import eu.kanade.tachiyomi.util.preference.titleRes
import eu.kanade.tachiyomi.util.system.LocaleHelper
import uy.kohesive.injekt.injectLazy
import eu.kanade.tachiyomi.ui.base.controller.ComposeController
class ExtensionFilterController : SettingsController() {
class ExtensionFilterController : ComposeController<ExtensionFilterPresenter>() {
private val extensionManager: ExtensionManager by injectLazy()
override fun getTitle() = resources?.getString(R.string.label_extensions)
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.label_extensions
override fun createPresenter(): ExtensionFilterPresenter = ExtensionFilterPresenter()
val activeLangs = preferences.enabledLanguages().get()
val availableLangs = extensionManager.availableExtensions.groupBy { it.lang }.keys
.sortedWith(compareBy({ it !in activeLangs }, { LocaleHelper.getSourceDisplayName(it, context) }))
availableLangs.forEach {
switchPreference {
preferenceScreen.addPreference(this)
title = LocaleHelper.getSourceDisplayName(it, context)
isPersistent = false
isChecked = it in activeLangs
onChange { newValue ->
if (newValue as Boolean) {
preferences.enabledLanguages() += it
} else {
preferences.enabledLanguages() -= it
}
true
}
}
}
@Composable
override fun ComposeContent(nestedScrollInterop: NestedScrollConnection) {
ExtensionFilterScreen(
nestedScrollInterop = nestedScrollInterop,
presenter = presenter,
onClickLang = { language ->
presenter.toggleLanguage(language)
},
)
}
}
data class FilterUiModel(val lang: String, val isEnabled: Boolean)

View file

@ -0,0 +1,54 @@
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.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.lang.launchIO
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class ExtensionFilterPresenter(
private val getExtensionLanguages: GetExtensionLanguages = Injekt.get(),
private val toggleLanguage: ToggleLanguage = Injekt.get(),
private val preferences: PreferencesHelper = Injekt.get(),
) : BasePresenter<ExtensionFilterController>() {
private val _state: MutableStateFlow<ExtensionFilterState> = MutableStateFlow(ExtensionFilterState.Loading)
val state: StateFlow<ExtensionFilterState> = _state.asStateFlow()
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
presenterScope.launchIO {
getExtensionLanguages.subscribe()
.catch { exception ->
_state.value = ExtensionFilterState.Error(exception)
}
.collectLatest(::collectLatestSourceLangMap)
}
}
private fun collectLatestSourceLangMap(extLangs: List<String>) {
val enabledLanguages = preferences.enabledLanguages().get()
val uiModels = extLangs.map {
FilterUiModel(it, it in enabledLanguages)
}
_state.value = ExtensionFilterState.Success(uiModels)
}
fun toggleLanguage(language: String) {
toggleLanguage.await(language)
}
}
sealed class ExtensionFilterState {
object Loading : ExtensionFilterState()
data class Error(val error: Throwable) : ExtensionFilterState()
data class Success(val models: List<FilterUiModel>) : ExtensionFilterState()
}