mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-21 20:47:03 -05:00
Migrate Source Filter Screen to Compose (#7031)
* Migrate Source Filter Screen to Compose * Changes from Review and some more fixes * Rename some variable and classes * Review Change * Ewbase and Review changes
This commit is contained in:
parent
cccd09fb5c
commit
23f8f35354
14 changed files with 324 additions and 123 deletions
|
@ -19,6 +19,12 @@ class SourceRepositoryImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getOnlineSources(): Flow<List<Source>> {
|
||||||
|
return sourceManager.onlineSources.map { sources ->
|
||||||
|
sources.map(sourceMapper)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun getSourcesWithFavoriteCount(): Flow<List<Pair<Source, Long>>> {
|
override fun getSourcesWithFavoriteCount(): Flow<List<Pair<Source, Long>>> {
|
||||||
val sourceIdWithFavoriteCount = handler.subscribeToList { mangasQueries.getSourceIdWithFavoriteCount() }
|
val sourceIdWithFavoriteCount = handler.subscribeToList { mangasQueries.getSourceIdWithFavoriteCount() }
|
||||||
return sourceIdWithFavoriteCount.map { sourceIdsWithCount ->
|
return sourceIdWithFavoriteCount.map { sourceIdsWithCount ->
|
||||||
|
|
|
@ -11,10 +11,12 @@ import eu.kanade.domain.history.interactor.RemoveHistoryByMangaId
|
||||||
import eu.kanade.domain.history.repository.HistoryRepository
|
import eu.kanade.domain.history.repository.HistoryRepository
|
||||||
import eu.kanade.domain.manga.interactor.GetFavoritesBySourceId
|
import eu.kanade.domain.manga.interactor.GetFavoritesBySourceId
|
||||||
import eu.kanade.domain.manga.repository.MangaRepository
|
import eu.kanade.domain.manga.repository.MangaRepository
|
||||||
import eu.kanade.domain.source.interactor.DisableSource
|
|
||||||
import eu.kanade.domain.source.interactor.GetEnabledSources
|
import eu.kanade.domain.source.interactor.GetEnabledSources
|
||||||
|
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
|
||||||
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
|
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
|
||||||
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
||||||
|
import eu.kanade.domain.source.interactor.ToggleLanguage
|
||||||
|
import eu.kanade.domain.source.interactor.ToggleSource
|
||||||
import eu.kanade.domain.source.interactor.ToggleSourcePin
|
import eu.kanade.domain.source.interactor.ToggleSourcePin
|
||||||
import eu.kanade.domain.source.repository.SourceRepository
|
import eu.kanade.domain.source.repository.SourceRepository
|
||||||
import uy.kohesive.injekt.api.InjektModule
|
import uy.kohesive.injekt.api.InjektModule
|
||||||
|
@ -37,10 +39,12 @@ class DomainModule : InjektModule {
|
||||||
addFactory { RemoveHistoryByMangaId(get()) }
|
addFactory { RemoveHistoryByMangaId(get()) }
|
||||||
|
|
||||||
addSingletonFactory<SourceRepository> { SourceRepositoryImpl(get(), get()) }
|
addSingletonFactory<SourceRepository> { SourceRepositoryImpl(get(), get()) }
|
||||||
|
addFactory { GetLanguagesWithSources(get(), get()) }
|
||||||
addFactory { GetEnabledSources(get(), get()) }
|
addFactory { GetEnabledSources(get(), get()) }
|
||||||
addFactory { DisableSource(get()) }
|
addFactory { ToggleSource(get()) }
|
||||||
addFactory { ToggleSourcePin(get()) }
|
addFactory { ToggleSourcePin(get()) }
|
||||||
addFactory { GetSourcesWithFavoriteCount(get(), get()) }
|
addFactory { GetSourcesWithFavoriteCount(get(), get()) }
|
||||||
addFactory { SetMigrateSorting(get()) }
|
addFactory { SetMigrateSorting(get()) }
|
||||||
|
addFactory { ToggleLanguage(get()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
package eu.kanade.domain.source.interactor
|
|
||||||
|
|
||||||
import eu.kanade.domain.source.model.Source
|
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|
||||||
import eu.kanade.tachiyomi.util.preference.plusAssign
|
|
||||||
|
|
||||||
class DisableSource(
|
|
||||||
private val preferences: PreferencesHelper
|
|
||||||
) {
|
|
||||||
|
|
||||||
fun await(source: Source) {
|
|
||||||
preferences.disabledSources() += source.id.toString()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.model.Source
|
||||||
|
import eu.kanade.domain.source.repository.SourceRepository
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
|
||||||
|
class GetLanguagesWithSources(
|
||||||
|
private val repository: SourceRepository,
|
||||||
|
private val preferences: PreferencesHelper,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun subscribe(): Flow<Map<String, List<Source>>> {
|
||||||
|
return combine(
|
||||||
|
preferences.enabledLanguages().asFlow(),
|
||||||
|
preferences.disabledSources().asFlow(),
|
||||||
|
repository.getOnlineSources()
|
||||||
|
) { enabledLanguage, disabledSource, onlineSources ->
|
||||||
|
val sortedSources = onlineSources.sortedWith(
|
||||||
|
compareBy<Source> { it.id.toString() in disabledSource }
|
||||||
|
.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name }
|
||||||
|
)
|
||||||
|
|
||||||
|
sortedSources.groupBy { it.lang }
|
||||||
|
.toSortedMap(
|
||||||
|
compareBy(
|
||||||
|
{ it !in enabledLanguage },
|
||||||
|
{ LocaleHelper.getDisplayName(it) }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.util.preference.minusAssign
|
||||||
|
import eu.kanade.tachiyomi.util.preference.plusAssign
|
||||||
|
|
||||||
|
class ToggleLanguage(
|
||||||
|
val preferences: PreferencesHelper
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun await(language: String) {
|
||||||
|
val isEnabled = language in preferences.enabledLanguages().get()
|
||||||
|
if (isEnabled) {
|
||||||
|
preferences.enabledLanguages() -= language
|
||||||
|
} else {
|
||||||
|
preferences.enabledLanguages() += language
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package eu.kanade.domain.source.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.model.Source
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.util.preference.minusAssign
|
||||||
|
import eu.kanade.tachiyomi.util.preference.plusAssign
|
||||||
|
|
||||||
|
class ToggleSource(
|
||||||
|
private val preferences: PreferencesHelper
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun await(source: Source) {
|
||||||
|
val isEnabled = source.id.toString() !in preferences.disabledSources().get()
|
||||||
|
if (isEnabled) {
|
||||||
|
preferences.disabledSources() += source.id.toString()
|
||||||
|
} else {
|
||||||
|
preferences.disabledSources() -= source.id.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,5 +7,7 @@ interface SourceRepository {
|
||||||
|
|
||||||
fun getSources(): Flow<List<Source>>
|
fun getSources(): Flow<List<Source>>
|
||||||
|
|
||||||
|
fun getOnlineSources(): Flow<List<Source>>
|
||||||
|
|
||||||
fun getSourcesWithFavoriteCount(): Flow<List<Pair<Source, Long>>>
|
fun getSourcesWithFavoriteCount(): Flow<List<Pair<Source, Long>>>
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
package eu.kanade.presentation.source
|
||||||
|
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.Checkbox
|
||||||
|
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.domain.source.model.Source
|
||||||
|
import eu.kanade.presentation.components.EmptyScreen
|
||||||
|
import eu.kanade.presentation.components.LoadingScreen
|
||||||
|
import eu.kanade.presentation.components.PreferenceRow
|
||||||
|
import eu.kanade.presentation.source.components.BaseSourceItem
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.ui.browse.source.FilterUiModel
|
||||||
|
import eu.kanade.tachiyomi.ui.browse.source.SourceFilterPresenter
|
||||||
|
import eu.kanade.tachiyomi.ui.browse.source.SourceFilterState
|
||||||
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SourceFilterScreen(
|
||||||
|
nestedScrollInterop: NestedScrollConnection,
|
||||||
|
presenter: SourceFilterPresenter,
|
||||||
|
onClickLang: (String) -> Unit,
|
||||||
|
onClickSource: (Source) -> Unit
|
||||||
|
) {
|
||||||
|
val state by presenter.state.collectAsState()
|
||||||
|
|
||||||
|
when (state) {
|
||||||
|
is SourceFilterState.Loading -> LoadingScreen()
|
||||||
|
is SourceFilterState.Error -> Text(text = (state as SourceFilterState.Error).error!!.message!!)
|
||||||
|
is SourceFilterState.Success ->
|
||||||
|
SourceFilterContent(
|
||||||
|
nestedScrollInterop = nestedScrollInterop,
|
||||||
|
items = (state as SourceFilterState.Success).models,
|
||||||
|
onClickLang = onClickLang,
|
||||||
|
onClickSource = onClickSource,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SourceFilterContent(
|
||||||
|
nestedScrollInterop: NestedScrollConnection,
|
||||||
|
items: List<FilterUiModel>,
|
||||||
|
onClickLang: (String) -> Unit,
|
||||||
|
onClickSource: (Source) -> Unit
|
||||||
|
) {
|
||||||
|
if (items.isEmpty()) {
|
||||||
|
EmptyScreen(textResource = R.string.source_filter_empty_screen)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.nestedScroll(nestedScrollInterop)
|
||||||
|
) {
|
||||||
|
items(
|
||||||
|
items = items,
|
||||||
|
contentType = {
|
||||||
|
when (it) {
|
||||||
|
is FilterUiModel.Header -> "header"
|
||||||
|
is FilterUiModel.Item -> "item"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
key = {
|
||||||
|
when (it) {
|
||||||
|
is FilterUiModel.Header -> it.hashCode()
|
||||||
|
is FilterUiModel.Item -> it.source.key()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) { model ->
|
||||||
|
when (model) {
|
||||||
|
is FilterUiModel.Header -> {
|
||||||
|
SourceFilterHeader(
|
||||||
|
modifier = Modifier.animateItemPlacement(),
|
||||||
|
language = model.language,
|
||||||
|
isEnabled = model.isEnabled,
|
||||||
|
onClickItem = onClickLang
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is FilterUiModel.Item -> SourceFilterItem(
|
||||||
|
modifier = Modifier.animateItemPlacement(),
|
||||||
|
source = model.source,
|
||||||
|
isEnabled = model.isEnabled,
|
||||||
|
onClickItem = onClickSource
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SourceFilterHeader(
|
||||||
|
modifier: Modifier,
|
||||||
|
language: String,
|
||||||
|
isEnabled: Boolean,
|
||||||
|
onClickItem: (String) -> Unit
|
||||||
|
) {
|
||||||
|
PreferenceRow(
|
||||||
|
modifier = modifier,
|
||||||
|
title = LocaleHelper.getSourceDisplayName(language, LocalContext.current),
|
||||||
|
action = {
|
||||||
|
Switch(checked = isEnabled, onCheckedChange = null)
|
||||||
|
},
|
||||||
|
onClick = { onClickItem(language) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SourceFilterItem(
|
||||||
|
modifier: Modifier,
|
||||||
|
source: Source,
|
||||||
|
isEnabled: Boolean,
|
||||||
|
onClickItem: (Source) -> Unit
|
||||||
|
) {
|
||||||
|
BaseSourceItem(
|
||||||
|
modifier = modifier,
|
||||||
|
source = source,
|
||||||
|
showLanguageInContent = false,
|
||||||
|
onClickItem = { onClickItem(source) },
|
||||||
|
action = {
|
||||||
|
Checkbox(checked = isEnabled, onCheckedChange = null)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
|
|
||||||
|
@ -18,6 +19,8 @@ open class SourceManager(private val context: Context) {
|
||||||
|
|
||||||
private val _catalogueSources: MutableStateFlow<List<CatalogueSource>> = MutableStateFlow(listOf())
|
private val _catalogueSources: MutableStateFlow<List<CatalogueSource>> = MutableStateFlow(listOf())
|
||||||
val catalogueSources: Flow<List<CatalogueSource>> = _catalogueSources
|
val catalogueSources: Flow<List<CatalogueSource>> = _catalogueSources
|
||||||
|
val onlineSources: Flow<List<HttpSource>> =
|
||||||
|
_catalogueSources.map { sources -> sources.filterIsInstance<HttpSource>() }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
createInternalSources().forEach { registerSource(it) }
|
createInternalSources().forEach { registerSource(it) }
|
||||||
|
|
|
@ -48,7 +48,7 @@ class SourceController : SearchableComposeController<SourcePresenter>() {
|
||||||
openSource(source, BrowseSourceController(source))
|
openSource(source, BrowseSourceController(source))
|
||||||
},
|
},
|
||||||
onClickDisable = { source ->
|
onClickDisable = { source ->
|
||||||
presenter.disableSource(source)
|
presenter.toggleSource(source)
|
||||||
},
|
},
|
||||||
onClickLatest = { source ->
|
onClickLatest = { source ->
|
||||||
openSource(source, LatestUpdatesController(source))
|
openSource(source, LatestUpdatesController(source))
|
||||||
|
|
|
@ -1,112 +1,34 @@
|
||||||
package eu.kanade.tachiyomi.ui.browse.source
|
package eu.kanade.tachiyomi.ui.browse.source
|
||||||
|
|
||||||
import android.graphics.drawable.Drawable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.preference.CheckBoxPreference
|
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||||
import androidx.preference.PreferenceGroup
|
import eu.kanade.domain.source.model.Source
|
||||||
import androidx.preference.PreferenceScreen
|
import eu.kanade.presentation.source.SourceFilterScreen
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.ui.base.controller.ComposeController
|
||||||
import eu.kanade.tachiyomi.source.getPreferenceKey
|
|
||||||
import eu.kanade.tachiyomi.source.icon
|
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
|
||||||
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.switchPreferenceCategory
|
|
||||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import java.util.TreeMap
|
|
||||||
|
|
||||||
class SourceFilterController : SettingsController() {
|
class SourceFilterController : ComposeController<SourceFilterPresenter>() {
|
||||||
|
|
||||||
private val onlineSources by lazy { Injekt.get<SourceManager>().getOnlineSources() }
|
override fun getTitle() = resources?.getString(R.string.label_sources)
|
||||||
|
|
||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
override fun createPresenter(): SourceFilterPresenter = SourceFilterPresenter()
|
||||||
titleRes = R.string.label_sources
|
|
||||||
|
|
||||||
// Get the list of active language codes.
|
@Composable
|
||||||
val activeLangsCodes = preferences.enabledLanguages().get()
|
override fun ComposeContent(nestedScrollInterop: NestedScrollConnection) {
|
||||||
|
SourceFilterScreen(
|
||||||
// Get a map of sources grouped by language.
|
nestedScrollInterop = nestedScrollInterop,
|
||||||
val sourcesByLang = onlineSources.groupByTo(TreeMap(), { it.lang })
|
presenter = presenter,
|
||||||
|
onClickLang = { language ->
|
||||||
// Order first by active languages, then inactive ones
|
presenter.toggleLanguage(language)
|
||||||
val orderedLangs = sourcesByLang.keys.sortedWith(
|
},
|
||||||
compareBy(
|
onClickSource = { source ->
|
||||||
{ it !in activeLangsCodes },
|
presenter.toggleSource(source)
|
||||||
{ LocaleHelper.getSourceDisplayName(it, context) },
|
},
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
orderedLangs.forEach { lang ->
|
|
||||||
val sources = sourcesByLang[lang].orEmpty().sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, { it.name }))
|
|
||||||
|
|
||||||
// Create a preference group and set initial state and change listener
|
|
||||||
switchPreferenceCategory {
|
|
||||||
this@apply.addPreference(this)
|
|
||||||
title = LocaleHelper.getSourceDisplayName(lang, context)
|
|
||||||
isPersistent = false
|
|
||||||
if (lang in activeLangsCodes) {
|
|
||||||
setChecked(true)
|
|
||||||
addLanguageSources(this, sources)
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange { newValue ->
|
|
||||||
val checked = newValue as Boolean
|
|
||||||
if (!checked) {
|
|
||||||
preferences.enabledLanguages() -= lang
|
|
||||||
removeAll()
|
|
||||||
} else {
|
|
||||||
preferences.enabledLanguages() += lang
|
|
||||||
addLanguageSources(this, sources)
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setDivider(divider: Drawable?) {
|
sealed class FilterUiModel {
|
||||||
super.setDivider(null)
|
data class Header(val language: String, val isEnabled: Boolean) : FilterUiModel()
|
||||||
}
|
data class Item(val source: Source, val isEnabled: Boolean) : FilterUiModel()
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the source list for the given group (language).
|
|
||||||
*
|
|
||||||
* @param group the language category.
|
|
||||||
*/
|
|
||||||
private fun addLanguageSources(group: PreferenceGroup, sources: List<HttpSource>) {
|
|
||||||
val disabledSourceIds = preferences.disabledSources().get()
|
|
||||||
|
|
||||||
sources
|
|
||||||
.sortedBy { it.id.toString() in disabledSourceIds }
|
|
||||||
.map { source ->
|
|
||||||
CheckBoxPreference(group.context).apply {
|
|
||||||
val id = source.id.toString()
|
|
||||||
title = source.name
|
|
||||||
key = source.getPreferenceKey()
|
|
||||||
isPersistent = false
|
|
||||||
isChecked = id !in disabledSourceIds
|
|
||||||
|
|
||||||
val sourceIcon = source.icon()
|
|
||||||
if (sourceIcon != null) {
|
|
||||||
icon = sourceIcon
|
|
||||||
}
|
|
||||||
|
|
||||||
onChange { newValue ->
|
|
||||||
val checked = newValue as Boolean
|
|
||||||
if (checked) {
|
|
||||||
preferences.disabledSources() -= id
|
|
||||||
} else {
|
|
||||||
preferences.disabledSources() += id
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.forEach { group.addPreference(it) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
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.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 SourceFilterPresenter(
|
||||||
|
private val getLanguagesWithSources: GetLanguagesWithSources = Injekt.get(),
|
||||||
|
private val toggleSource: ToggleSource = Injekt.get(),
|
||||||
|
private val toggleLanguage: ToggleLanguage = Injekt.get(),
|
||||||
|
private val preferences: PreferencesHelper = Injekt.get()
|
||||||
|
) : BasePresenter<SourceFilterController>() {
|
||||||
|
|
||||||
|
private val _state: MutableStateFlow<SourceFilterState> = MutableStateFlow(SourceFilterState.Loading)
|
||||||
|
val state: StateFlow<SourceFilterState> = _state.asStateFlow()
|
||||||
|
|
||||||
|
override fun onCreate(savedState: Bundle?) {
|
||||||
|
super.onCreate(savedState)
|
||||||
|
presenterScope.launchIO {
|
||||||
|
getLanguagesWithSources.subscribe()
|
||||||
|
.catch { exception ->
|
||||||
|
_state.emit(SourceFilterState.Error(exception))
|
||||||
|
}
|
||||||
|
.collectLatest { sourceLangMap ->
|
||||||
|
val uiModels = sourceLangMap.toFilterUiModels()
|
||||||
|
_state.emit(SourceFilterState.Success(uiModels))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Map<String, List<Source>>.toFilterUiModels(): List<FilterUiModel> {
|
||||||
|
return this.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()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggleSource(source: Source) {
|
||||||
|
toggleSource.await(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggleLanguage(language: String) {
|
||||||
|
toggleLanguage.await(language)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class SourceFilterState {
|
||||||
|
object Loading : SourceFilterState()
|
||||||
|
data class Error(val error: Throwable) : SourceFilterState()
|
||||||
|
data class Success(val models: List<FilterUiModel>) : SourceFilterState()
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
package eu.kanade.tachiyomi.ui.browse.source
|
package eu.kanade.tachiyomi.ui.browse.source
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import eu.kanade.domain.source.interactor.DisableSource
|
|
||||||
import eu.kanade.domain.source.interactor.GetEnabledSources
|
import eu.kanade.domain.source.interactor.GetEnabledSources
|
||||||
|
import eu.kanade.domain.source.interactor.ToggleSource
|
||||||
import eu.kanade.domain.source.interactor.ToggleSourcePin
|
import eu.kanade.domain.source.interactor.ToggleSourcePin
|
||||||
import eu.kanade.domain.source.model.Pin
|
import eu.kanade.domain.source.model.Pin
|
||||||
import eu.kanade.domain.source.model.Source
|
import eu.kanade.domain.source.model.Source
|
||||||
|
@ -24,7 +24,7 @@ import java.util.TreeMap
|
||||||
*/
|
*/
|
||||||
class SourcePresenter(
|
class SourcePresenter(
|
||||||
private val getEnabledSources: GetEnabledSources = Injekt.get(),
|
private val getEnabledSources: GetEnabledSources = Injekt.get(),
|
||||||
private val disableSource: DisableSource = Injekt.get(),
|
private val toggleSource: ToggleSource = Injekt.get(),
|
||||||
private val toggleSourcePin: ToggleSourcePin = Injekt.get()
|
private val toggleSourcePin: ToggleSourcePin = Injekt.get()
|
||||||
) : BasePresenter<SourceController>() {
|
) : BasePresenter<SourceController>() {
|
||||||
|
|
||||||
|
@ -79,8 +79,8 @@ class SourcePresenter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun disableSource(source: Source) {
|
fun toggleSource(source: Source) {
|
||||||
disableSource.await(source)
|
toggleSource.await(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun togglePin(source: Source) {
|
fun togglePin(source: Source) {
|
||||||
|
|
|
@ -711,6 +711,9 @@
|
||||||
<string name="clear_history_completed">History deleted</string>
|
<string name="clear_history_completed">History deleted</string>
|
||||||
<string name="clear_history_confirmation">Are you sure? All history will be lost.</string>
|
<string name="clear_history_confirmation">Are you sure? All history will be lost.</string>
|
||||||
|
|
||||||
|
<!-- Source Filter Screen -->
|
||||||
|
<string name="source_filter_empty_screen">No installed source found</string>
|
||||||
|
|
||||||
<!-- Source migration screen -->
|
<!-- Source migration screen -->
|
||||||
<string name="migration_help_guide">Source migration guide</string>
|
<string name="migration_help_guide">Source migration guide</string>
|
||||||
<string name="migration_dialog_what_to_include">Select data to include</string>
|
<string name="migration_dialog_what_to_include">Select data to include</string>
|
||||||
|
|
Loading…
Reference in a new issue