Use Stable interface for Browse screens (#7544)

This commit is contained in:
Andreas
2022-07-16 20:44:37 +02:00
committed by GitHub
parent 383f7089c4
commit 018ca71336
26 changed files with 505 additions and 307 deletions

View File

@@ -20,6 +20,9 @@ import eu.kanade.tachiyomi.util.preference.plusAssign
import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import logcat.LogPriority
import rx.Observable
import uy.kohesive.injekt.Injekt
@@ -63,9 +66,16 @@ class ExtensionManager(
var installedExtensions = emptyList<Extension.Installed>()
private set(value) {
field = value
installedExtensionsFlow.value = field
installedExtensionsRelay.call(value)
}
private val installedExtensionsFlow = MutableStateFlow(installedExtensions)
fun getInstalledExtensionsFlow(): StateFlow<List<Extension.Installed>> {
return installedExtensionsFlow.asStateFlow()
}
fun getAppIconForSource(source: Source): Drawable? {
return getAppIconForSource(source.id)
}

View File

@@ -17,9 +17,6 @@ class ExtensionFilterController : ComposeController<ExtensionFilterPresenter>()
ExtensionFilterScreen(
nestedScrollInterop = nestedScrollInterop,
presenter = presenter,
onClickLang = { language ->
presenter.toggleLanguage(language)
},
)
}
}

View File

@@ -3,32 +3,37 @@ 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.presentation.browse.ExtensionFilterState
import eu.kanade.presentation.browse.ExtensionFilterStateImpl
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 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: PreferencesHelper = Injekt.get(),
) : BasePresenter<ExtensionFilterController>() {
) : BasePresenter<ExtensionFilterController>(), ExtensionFilterState by state {
private val _state: MutableStateFlow<ExtensionFilterState> = MutableStateFlow(ExtensionFilterState.Loading)
val state: StateFlow<ExtensionFilterState> = _state.asStateFlow()
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 ->
_state.value = ExtensionFilterState.Error(exception)
logcat(LogPriority.ERROR, exception)
_events.send(Event.FailedFetchingLanguages)
}
.collectLatest(::collectLatestSourceLangMap)
}
@@ -36,19 +41,17 @@ class ExtensionFilterPresenter(
private fun collectLatestSourceLangMap(extLangs: List<String>) {
val enabledLanguages = preferences.enabledLanguages().get()
val uiModels = extLangs.map {
state.items = extLangs.map {
FilterUiModel(it, it in enabledLanguages)
}
_state.value = ExtensionFilterState.Success(uiModels)
state.isLoading = false
}
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()
sealed class Event {
object FailedFetchingLanguages : Event()
}
}

View File

@@ -3,11 +3,11 @@ package eu.kanade.tachiyomi.ui.browse.extension
import android.app.Application
import android.os.Bundle
import androidx.annotation.StringRes
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import eu.kanade.domain.extension.interactor.GetExtensionUpdates
import eu.kanade.domain.extension.interactor.GetExtensions
import eu.kanade.presentation.browse.ExtensionState
import eu.kanade.presentation.browse.ExtensionsState
import eu.kanade.presentation.browse.ExtensionsStateImpl
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.extension.model.Extension
@@ -17,8 +17,6 @@ import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.system.LocaleHelper
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.update
@@ -27,20 +25,16 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class ExtensionsPresenter(
private val state: ExtensionsStateImpl = ExtensionState() as ExtensionsStateImpl,
private val extensionManager: ExtensionManager = Injekt.get(),
private val getExtensionUpdates: GetExtensionUpdates = Injekt.get(),
private val getExtensions: GetExtensions = Injekt.get(),
) : BasePresenter<ExtensionsController>() {
) : BasePresenter<ExtensionsController>(), ExtensionsState by state {
private val _query: MutableStateFlow<String> = MutableStateFlow("")
private var _currentDownloads = MutableStateFlow<Map<String, InstallStep>>(hashMapOf())
private val _state: MutableStateFlow<ExtensionState> = MutableStateFlow(ExtensionState.Uninitialized)
val state: StateFlow<ExtensionState> = _state.asStateFlow()
var isRefreshing: Boolean by mutableStateOf(true)
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
@@ -86,8 +80,6 @@ class ExtensionsPresenter(
getExtensionUpdates.subscribe(),
_currentDownloads,
) { query, (installed, untrusted, available), updates, downloads ->
isRefreshing = false
val languagesWithExtensions = available
.filter(queryFilter(query))
.groupBy { LocaleHelper.getSourceDisplayName(it.lang, context) }
@@ -121,7 +113,9 @@ class ExtensionsPresenter(
items
}.collectLatest {
_state.value = ExtensionState.Initialized(it)
state.isRefreshing = false
state.isLoading = false
state.items = it
}
}
}
@@ -134,9 +128,9 @@ class ExtensionsPresenter(
fun updateAllExtensions() {
launchIO {
val state = _state.value
if (state !is ExtensionState.Initialized) return@launchIO
state.list.mapNotNull {
if (state.isEmpty) return@launchIO
val items = state.items
items.mapNotNull {
if (it !is ExtensionUiModel.Item) return@mapNotNull null
if (it.extension !is Extension.Installed) return@mapNotNull null
if (it.extension.hasUpdate.not()) return@mapNotNull null
@@ -189,7 +183,7 @@ class ExtensionsPresenter(
}
fun findAvailableExtensions() {
isRefreshing = true
state.isRefreshing = true
extensionManager.findAvailableExtensions()
}
@@ -217,8 +211,3 @@ sealed interface ExtensionUiModel {
}
}
}
sealed class ExtensionState {
object Uninitialized : ExtensionState()
data class Initialized(val list: List<ExtensionUiModel>) : ExtensionState()
}

View File

@@ -43,7 +43,6 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
nestedScrollInterop = nestedScrollInterop,
presenter = presenter,
onClickUninstall = { presenter.uninstallExtension() },
onClickAppInfo = { presenter.openInSettings() },
onClickSourcePreferences = { router.pushController(SourcePreferencesController(it)) },
onClickSource = { presenter.toggleSource(it) },
)

View File

@@ -1,48 +1,52 @@
package eu.kanade.tachiyomi.ui.browse.extension.details
import android.app.Application
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import eu.kanade.domain.extension.interactor.GetExtensionSources
import eu.kanade.domain.source.interactor.ToggleSource
import eu.kanade.presentation.browse.ExtensionDetailsState
import eu.kanade.presentation.browse.ExtensionDetailsStateImpl
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.system.LocaleHelper
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import rx.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.flow.take
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class ExtensionDetailsPresenter(
private val pkgName: String,
private val state: ExtensionDetailsStateImpl = ExtensionDetailsState() as ExtensionDetailsStateImpl,
private val context: Application = Injekt.get(),
private val getExtensionSources: GetExtensionSources = Injekt.get(),
private val toggleSource: ToggleSource = Injekt.get(),
private val extensionManager: ExtensionManager = Injekt.get(),
) : BasePresenter<ExtensionDetailsController>() {
val extension = extensionManager.installedExtensions.find { it.pkgName == pkgName }
private val _state: MutableStateFlow<List<ExtensionSourceItem>> = MutableStateFlow(emptyList())
val sourcesState: StateFlow<List<ExtensionSourceItem>> = _state.asStateFlow()
) : BasePresenter<ExtensionDetailsController>(), ExtensionDetailsState by state {
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
val extension = extension ?: return
presenterScope.launchIO {
extensionManager.getInstalledExtensionsFlow()
.map { it.firstOrNull { it.pkgName == pkgName } }
.collectLatest {
state.extension = it
fetchExtensionSources()
}
}
bindToUninstalledExtension()
}
presenterScope.launchIO {
getExtensionSources.subscribe(extension)
private fun CoroutineScope.fetchExtensionSources() {
launchIO {
getExtensionSources.subscribe(extension!!)
.map {
it.sortedWith(
compareBy(
@@ -51,20 +55,24 @@ class ExtensionDetailsPresenter(
),
)
}
.collectLatest { _state.value = it }
.collectLatest {
state.isLoading = false
state.sources = it
}
}
}
private fun bindToUninstalledExtension() {
extensionManager.getInstalledExtensionsObservable()
.skip(1)
.filter { extensions -> extensions.none { it.pkgName == pkgName } }
.map { }
.take(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst({ view, _ ->
view.onExtensionUninstalled()
},)
presenterScope.launchIO {
extensionManager.getInstalledExtensionsFlow()
.drop(1)
.filter { extensions -> extensions.none { it.pkgName == pkgName } }
.map { }
.take(1)
.collectLatest {
view?.onExtensionUninstalled()
}
}
}
fun uninstallExtension() {
@@ -72,13 +80,6 @@ class ExtensionDetailsPresenter(
extensionManager.uninstallExtension(extension.pkgName)
}
fun openInSettings() {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", pkgName, null)
}
view?.startActivity(intent)
}
fun toggleSource(sourceId: Long) {
toggleSource.await(sourceId)
}

View File

@@ -2,25 +2,29 @@ package eu.kanade.tachiyomi.ui.browse.migration.manga
import android.os.Bundle
import eu.kanade.domain.manga.interactor.GetFavorites
import eu.kanade.domain.manga.model.Manga
import eu.kanade.presentation.browse.MigrateMangaState
import eu.kanade.presentation.browse.MigrateMangaStateImpl
import eu.kanade.presentation.browse.MigrationMangaState
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 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.map
import kotlinx.coroutines.flow.receiveAsFlow
import logcat.LogPriority
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class MigrationMangaPresenter(
class MigrateMangaPresenter(
private val sourceId: Long,
private val state: MigrateMangaStateImpl = MigrationMangaState() as MigrateMangaStateImpl,
private val getFavorites: GetFavorites = Injekt.get(),
) : BasePresenter<MigrationMangaController>() {
) : BasePresenter<MigrationMangaController>(), MigrateMangaState by state {
private val _state: MutableStateFlow<MigrateMangaState> = MutableStateFlow(MigrateMangaState.Loading)
val state: StateFlow<MigrateMangaState> = _state.asStateFlow()
private val _events = Channel<Event>(Int.MAX_VALUE)
val events = _events.receiveAsFlow()
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
@@ -28,20 +32,20 @@ class MigrationMangaPresenter(
getFavorites
.subscribe(sourceId)
.catch { exception ->
_state.value = MigrateMangaState.Error(exception)
logcat(LogPriority.ERROR, exception)
_events.send(Event.FailedFetchingFavorites)
}
.map { list ->
list.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.title })
}
.collectLatest { sortedList ->
_state.value = MigrateMangaState.Success(sortedList)
state.isLoading = false
state.items = sortedList
}
}
}
}
sealed class MigrateMangaState {
object Loading : MigrateMangaState()
data class Error(val error: Throwable) : MigrateMangaState()
data class Success(val list: List<Manga>) : MigrateMangaState()
sealed class Event {
object FailedFetchingFavorites : Event()
}
}

View File

@@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.ui.base.controller.pushController
import eu.kanade.tachiyomi.ui.browse.migration.search.SearchController
import eu.kanade.tachiyomi.ui.manga.MangaController
class MigrationMangaController : ComposeController<MigrationMangaPresenter> {
class MigrationMangaController : ComposeController<MigrateMangaPresenter> {
constructor(sourceId: Long, sourceName: String?) : super(
bundleOf(
@@ -30,7 +30,7 @@ class MigrationMangaController : ComposeController<MigrationMangaPresenter> {
override fun getTitle(): String? = sourceName
override fun createPresenter(): MigrationMangaPresenter = MigrationMangaPresenter(sourceId)
override fun createPresenter(): MigrateMangaPresenter = MigrateMangaPresenter(sourceId)
@Composable
override fun ComposeContent(nestedScrollInterop: NestedScrollConnection) {

View File

@@ -10,7 +10,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.controller.ComposeController
import eu.kanade.tachiyomi.ui.base.controller.pushController
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrationMangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.openInBrowser
class MigrationSourcesController : ComposeController<MigrationSourcesPresenter>() {
@@ -34,10 +33,6 @@ class MigrationSourcesController : ComposeController<MigrationSourcesPresenter>(
),
)
},
onLongClickItem = { source ->
val sourceId = source.id.toString()
activity?.copyToClipboard(sourceId, sourceId)
},
)
}

View File

@@ -3,24 +3,27 @@ package eu.kanade.tachiyomi.ui.browse.migration.sources
import android.os.Bundle
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.domain.source.model.Source
import eu.kanade.presentation.browse.MigrateSourceState
import eu.kanade.presentation.browse.MigrateSourceStateImpl
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 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 MigrationSourcesPresenter(
private val state: MigrateSourceStateImpl = MigrateSourceState() as MigrateSourceStateImpl,
private val getSourcesWithFavoriteCount: GetSourcesWithFavoriteCount = Injekt.get(),
private val setMigrateSorting: SetMigrateSorting = Injekt.get(),
) : BasePresenter<MigrationSourcesController>() {
) : BasePresenter<MigrationSourcesController>(), MigrateSourceState by state {
private val _state: MutableStateFlow<MigrateSourceState> = MutableStateFlow(MigrateSourceState.Loading)
val state: StateFlow<MigrateSourceState> = _state.asStateFlow()
private val _channel = Channel<Event>(Int.MAX_VALUE)
val channel = _channel.receiveAsFlow()
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
@@ -28,10 +31,12 @@ class MigrationSourcesPresenter(
presenterScope.launchIO {
getSourcesWithFavoriteCount.subscribe()
.catch { exception ->
_state.value = MigrateSourceState.Error(exception)
logcat(LogPriority.ERROR, exception)
_channel.send(Event.FailedFetchingSourcesWithCount)
}
.collectLatest { sources ->
_state.value = MigrateSourceState.Success(sources)
state.items = sources
state.isLoading = false
}
}
}
@@ -43,10 +48,8 @@ class MigrationSourcesPresenter(
fun setTotalSorting(isAscending: Boolean) {
setMigrateSorting.await(SetMigrateSorting.Mode.TOTAL, isAscending)
}
}
sealed class MigrateSourceState {
object Loading : MigrateSourceState()
data class Error(val error: Throwable) : MigrateSourceState()
data class Success(val sources: List<Pair<Source, Long>>) : MigrateSourceState()
sealed class Event {
object FailedFetchingSourcesWithCount : Event()
}
}

View File

@@ -5,26 +5,30 @@ 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.presentation.browse.SourcesFilterState
import eu.kanade.presentation.browse.SourcesFilterStateImpl
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 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: PreferencesHelper = Injekt.get(),
) : BasePresenter<SourceFilterController>() {
) : BasePresenter<SourceFilterController>(), SourcesFilterState by state {
private val _state: MutableStateFlow<SourceFilterState> = MutableStateFlow(SourceFilterState.Loading)
val state: StateFlow<SourceFilterState> = _state.asStateFlow()
private val _events = Channel<Event>(Int.MAX_VALUE)
val events = _events.receiveAsFlow()
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
@@ -32,14 +36,15 @@ class SourcesFilterPresenter(
presenterScope.launchIO {
getLanguagesWithSources.subscribe()
.catch { exception ->
_state.value = SourceFilterState.Error(exception)
logcat(LogPriority.ERROR, exception)
_events.send(Event.FailedFetchingLanguages)
}
.collectLatest(::collectLatestSourceLangMap)
}
}
private fun collectLatestSourceLangMap(sourceLangMap: Map<String, List<Source>>) {
val uiModels = sourceLangMap.flatMap {
state.items = sourceLangMap.flatMap {
val isLangEnabled = it.key in preferences.enabledLanguages().get()
val header = listOf(FilterUiModel.Header(it.key, isLangEnabled))
@@ -51,7 +56,7 @@ class SourcesFilterPresenter(
)
}
}
_state.value = SourceFilterState.Success(uiModels)
state.isLoading = false
}
fun toggleSource(source: Source) {
@@ -61,10 +66,8 @@ class SourcesFilterPresenter(
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()
sealed class Event {
object FailedFetchingLanguages : Event()
}
}

View File

@@ -7,32 +7,37 @@ import eu.kanade.domain.source.interactor.ToggleSourcePin
import eu.kanade.domain.source.model.Pin
import eu.kanade.domain.source.model.Source
import eu.kanade.presentation.browse.SourceUiModel
import eu.kanade.presentation.browse.SourcesState
import eu.kanade.presentation.browse.SourcesStateImpl
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 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
import java.util.TreeMap
class SourcesPresenter(
private val state: SourcesStateImpl = SourcesState() as SourcesStateImpl,
private val getEnabledSources: GetEnabledSources = Injekt.get(),
private val toggleSource: ToggleSource = Injekt.get(),
private val toggleSourcePin: ToggleSourcePin = Injekt.get(),
) : BasePresenter<SourcesController>() {
) : BasePresenter<SourcesController>(), SourcesState by state {
private val _state: MutableStateFlow<SourceState> = MutableStateFlow(SourceState.Loading)
val state: StateFlow<SourceState> = _state.asStateFlow()
private val _events = Channel<Event>(Int.MAX_VALUE)
val events = _events.receiveAsFlow()
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
presenterScope.launchIO {
getEnabledSources.subscribe()
.catch { exception ->
_state.value = SourceState.Error(exception)
logcat(LogPriority.ERROR, exception)
_events.send(Event.FailedFetchingSources)
}
.collectLatest(::collectLatestSources)
}
@@ -67,7 +72,8 @@ class SourcesPresenter(
}.toTypedArray(),
)
}
_state.value = SourceState.Success(uiModels)
state.isLoading = false
state.items = uiModels
}
fun toggleSource(source: Source) {
@@ -78,14 +84,14 @@ class SourcesPresenter(
toggleSourcePin.await(source)
}
sealed class Event {
object FailedFetchingSources : Event()
}
data class Dialog(val source: Source)
companion object {
const val PINNED_KEY = "pinned"
const val LAST_USED_KEY = "last_used"
}
}
sealed class SourceState {
object Loading : SourceState()
data class Error(val error: Throwable) : SourceState()
data class Success(val uiModels: List<SourceUiModel>) : SourceState()
}