Replace LiveData with StateFlow

This commit is contained in:
Koitharu
2023-05-27 12:25:49 +03:00
parent 47f346b42c
commit 5a0c54e00f
147 changed files with 1047 additions and 1039 deletions

View File

@@ -18,6 +18,8 @@ import org.koitharu.kotatsu.core.logs.FileLogger
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.BasePreferenceFragment
import org.koitharu.kotatsu.core.util.ShareHelper
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
import javax.inject.Inject
@AndroidEntryPoint
@@ -45,7 +47,7 @@ class AboutSettingsFragment : BasePreferenceFragment(R.string.about) {
viewModel.isLoading.observe(viewLifecycleOwner) {
findPreference<Preference>(AppSettings.KEY_APP_UPDATE)?.isEnabled = !it
}
viewModel.onUpdateAvailable.observe(viewLifecycleOwner, ::onUpdateAvailable)
viewModel.onUpdateAvailable.observeEvent(viewLifecycleOwner, ::onUpdateAvailable)
}
override fun onPreferenceTreeClick(preference: Preference): Boolean {

View File

@@ -4,7 +4,8 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import org.koitharu.kotatsu.core.github.AppUpdateRepository
import org.koitharu.kotatsu.core.github.AppVersion
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.SingleLiveEvent
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call
import javax.inject.Inject
@HiltViewModel
@@ -13,7 +14,7 @@ class AboutSettingsViewModel @Inject constructor(
) : BaseViewModel() {
val isUpdateSupported = appUpdateRepository.isUpdateSupported()
val onUpdateAvailable = SingleLiveEvent<AppVersion?>()
val onUpdateAvailable = MutableEventFlow<AppVersion?>()
fun checkForUpdates() {
launchLoadingJob {

View File

@@ -13,6 +13,8 @@ import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.AlertDialogFragment
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.databinding.DialogProgressBinding
import java.io.File
import java.io.FileOutputStream
@@ -46,8 +48,8 @@ class BackupDialogFragment : AlertDialogFragment<DialogProgressBinding>() {
binding.textViewSubtitle.setText(R.string.processing_)
viewModel.progress.observe(viewLifecycleOwner, this::onProgressChanged)
viewModel.onBackupDone.observe(viewLifecycleOwner, this::onBackupDone)
viewModel.onError.observe(viewLifecycleOwner, this::onError)
viewModel.onBackupDone.observeEvent(viewLifecycleOwner, this::onBackupDone)
viewModel.onError.observeEvent(viewLifecycleOwner, this::onError)
}
override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {

View File

@@ -1,13 +1,14 @@
package org.koitharu.kotatsu.settings.backup
import android.content.Context
import androidx.lifecycle.MutableLiveData
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow
import org.koitharu.kotatsu.core.backup.BackupRepository
import org.koitharu.kotatsu.core.backup.BackupZipOutput
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.SingleLiveEvent
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call
import java.io.File
import javax.inject.Inject
@@ -17,8 +18,8 @@ class BackupViewModel @Inject constructor(
@ApplicationContext context: Context,
) : BaseViewModel() {
val progress = MutableLiveData(-1f)
val onBackupDone = SingleLiveEvent<File>()
val progress = MutableStateFlow(-1f)
val onBackupDone = MutableEventFlow<File>()
init {
launchLoadingJob {

View File

@@ -12,6 +12,8 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.backup.CompositeResult
import org.koitharu.kotatsu.core.ui.AlertDialogFragment
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.core.util.ext.withArgs
import org.koitharu.kotatsu.databinding.DialogProgressBinding
import kotlin.math.roundToInt
@@ -32,8 +34,8 @@ class RestoreDialogFragment : AlertDialogFragment<DialogProgressBinding>() {
binding.textViewSubtitle.setText(R.string.preparing_)
viewModel.progress.observe(viewLifecycleOwner, this::onProgressChanged)
viewModel.onRestoreDone.observe(viewLifecycleOwner, this::onRestoreDone)
viewModel.onError.observe(viewLifecycleOwner, this::onError)
viewModel.onRestoreDone.observeEvent(viewLifecycleOwner, this::onRestoreDone)
viewModel.onError.observeEvent(viewLifecycleOwner, this::onError)
}
override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {

View File

@@ -1,18 +1,19 @@
package org.koitharu.kotatsu.settings.backup
import android.content.Context
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.SavedStateHandle
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runInterruptible
import org.koitharu.kotatsu.core.backup.BackupEntry
import org.koitharu.kotatsu.core.backup.BackupRepository
import org.koitharu.kotatsu.core.backup.BackupZipInput
import org.koitharu.kotatsu.core.backup.CompositeResult
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.SingleLiveEvent
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.core.util.ext.toUriOrNull
import java.io.File
import java.io.FileNotFoundException
@@ -25,8 +26,8 @@ class RestoreViewModel @Inject constructor(
@ApplicationContext context: Context,
) : BaseViewModel() {
val progress = MutableLiveData(-1f)
val onRestoreDone = SingleLiveEvent<CompositeResult>()
val progress = MutableStateFlow(-1f)
val onRestoreDone = MutableEventFlow<CompositeResult>()
init {
launchLoadingJob {

View File

@@ -11,6 +11,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.AlertDialogFragment
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.databinding.DialogOnboardBinding
import org.koitharu.kotatsu.settings.sources.adapter.SourceConfigListener
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem

View File

@@ -1,8 +1,8 @@
package org.koitharu.kotatsu.settings.newsources
import androidx.core.os.LocaleListCompat
import androidx.lifecycle.MutableLiveData
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import org.koitharu.kotatsu.core.model.getLocaleTitle
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.BaseViewModel
@@ -15,13 +15,9 @@ class NewSourcesViewModel @Inject constructor(
private val settings: AppSettings,
) : BaseViewModel() {
val sources = MutableLiveData<List<SourceConfigItem>>()
val sources = MutableStateFlow<List<SourceConfigItem>>(buildList())
private val initialList = settings.newSources
init {
buildList()
}
fun onItemEnabledChanged(item: SourceConfigItem.SourceItem, isEnabled: Boolean) {
if (isEnabled) {
settings.hiddenSources -= item.source.name
@@ -34,10 +30,10 @@ class NewSourcesViewModel @Inject constructor(
settings.markKnownSources(initialList)
}
private fun buildList() {
private fun buildList(): List<SourceConfigItem.SourceItem> {
val locales = LocaleListCompat.getDefault().mapToSet { it.language }
val pendingHidden = HashSet<String>()
sources.value = initialList.map {
return initialList.map {
val locale = it.locale
val isEnabledByLocale = locale == null || locale in locales
if (!isEnabledByLocale) {
@@ -49,9 +45,10 @@ class NewSourcesViewModel @Inject constructor(
isEnabled = isEnabledByLocale,
isDraggable = false,
)
}
if (pendingHidden.isNotEmpty()) {
settings.hiddenSources += pendingHidden
}.also {
if (pendingHidden.isNotEmpty()) {
settings.hiddenSources += pendingHidden
}
}
}
}

View File

@@ -10,6 +10,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.AlertDialogFragment
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.showAllowStateLoss
import org.koitharu.kotatsu.core.util.ext.withArgs
import org.koitharu.kotatsu.databinding.DialogOnboardBinding

View File

@@ -1,8 +1,8 @@
package org.koitharu.kotatsu.settings.onboard
import androidx.core.os.LocaleListCompat
import androidx.lifecycle.MutableLiveData
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.BaseViewModel
@@ -26,7 +26,7 @@ class OnboardViewModel @Inject constructor(
private val selectedLocales = locales.keys.toMutableSet()
val list = MutableLiveData<List<SourceLocale>?>()
val list = MutableStateFlow<List<SourceLocale>?>(null)
init {
if (settings.isSourcesSelected) {

View File

@@ -18,6 +18,7 @@ import androidx.core.view.isVisible
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.BaseActivity
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.databinding.ActivitySetupProtectBinding
private const val MIN_PASSWORD_LENGTH = 4

View File

@@ -2,12 +2,16 @@ package org.koitharu.kotatsu.settings.protect
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.SingleLiveEvent
import org.koitharu.kotatsu.core.util.asFlowLiveData
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.parsers.util.md5
import javax.inject.Inject
@@ -20,10 +24,10 @@ class ProtectSetupViewModel @Inject constructor(
val isSecondStep = firstPassword.map {
it != null
}.asFlowLiveData(viewModelScope.coroutineContext, false)
val onPasswordSet = SingleLiveEvent<Unit>()
val onPasswordMismatch = SingleLiveEvent<Unit>()
val onClearText = SingleLiveEvent<Unit>()
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, false)
val onPasswordSet = MutableEventFlow<Unit>()
val onPasswordMismatch = MutableEventFlow<Unit>()
val onClearText = MutableEventFlow<Unit>()
val isBiometricEnabled
get() = settings.isBiometricProtectionEnabled

View File

@@ -21,6 +21,8 @@ import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
import org.koitharu.kotatsu.core.util.ext.addMenuProvider
import org.koitharu.kotatsu.core.util.ext.getItem
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.databinding.FragmentSettingsSourcesBinding
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
import org.koitharu.kotatsu.settings.SettingsActivity
@@ -64,7 +66,7 @@ class SourcesListFragment :
viewModel.items.observe(viewLifecycleOwner) {
sourcesAdapter.items = it
}
viewModel.onActionDone.observe(viewLifecycleOwner, ReversibleActionObserver(binding.recyclerView))
viewModel.onActionDone.observeEvent(viewLifecycleOwner, ReversibleActionObserver(binding.recyclerView))
addMenuProvider(SourcesMenuProvider())
}

View File

@@ -1,10 +1,10 @@
package org.koitharu.kotatsu.settings.sources
import androidx.core.os.LocaleListCompat
import androidx.lifecycle.MutableLiveData
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@@ -14,11 +14,12 @@ import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.ui.util.ReversibleAction
import org.koitharu.kotatsu.core.ui.util.ReversibleHandle
import org.koitharu.kotatsu.core.util.SingleLiveEvent
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.core.util.ext.map
import org.koitharu.kotatsu.core.util.ext.move
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.move
import org.koitharu.kotatsu.parsers.util.toTitleCase
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
import java.util.Locale
@@ -35,8 +36,8 @@ class SourcesListViewModel @Inject constructor(
private val settings: AppSettings,
) : BaseViewModel() {
val items = MutableLiveData<List<SourceConfigItem>>(emptyList())
val onActionDone = SingleLiveEvent<ReversibleAction>()
val items = MutableStateFlow<List<SourceConfigItem>>(emptyList())
val onActionDone = MutableEventFlow<ReversibleAction>()
private val mutex = Mutex()
private val expandedGroups = HashSet<String?>()
@@ -49,7 +50,7 @@ class SourcesListViewModel @Inject constructor(
}
fun reorderSources(oldPos: Int, newPos: Int): Boolean {
val snapshot = items.value?.toMutableList() ?: return false
val snapshot = items.value.toMutableList()
if ((snapshot[oldPos] as? SourceConfigItem.SourceItem)?.isEnabled != true) return false
if ((snapshot[newPos] as? SourceConfigItem.SourceItem)?.isEnabled != true) return false
launchAtomicJob(Dispatchers.Default) {
@@ -63,7 +64,7 @@ class SourcesListViewModel @Inject constructor(
}
fun canReorder(oldPos: Int, newPos: Int): Boolean {
val snapshot = items.value?.toMutableList() ?: return false
val snapshot = items.value.toMutableList()
if ((snapshot[oldPos] as? SourceConfigItem.SourceItem)?.isEnabled != true) return false
return (snapshot[newPos] as? SourceConfigItem.SourceItem)?.isEnabled == true
}
@@ -81,7 +82,7 @@ class SourcesListViewModel @Inject constructor(
val rollback = ReversibleHandle {
setEnabled(source, true)
}
onActionDone.emitCall(ReversibleAction(R.string.source_disabled, rollback))
onActionDone.call(ReversibleAction(R.string.source_disabled, rollback))
}
buildList()
}
@@ -126,21 +127,19 @@ class SourcesListViewModel @Inject constructor(
val hiddenSources = settings.hiddenSources
val query = searchQuery
if (!query.isNullOrEmpty()) {
items.postValue(
sources.mapNotNull {
if (!it.title.contains(query, ignoreCase = true)) {
return@mapNotNull null
}
SourceConfigItem.SourceItem(
source = it,
summary = it.getLocaleTitle(),
isEnabled = it.name !in hiddenSources,
isDraggable = false,
)
}.ifEmpty {
listOf(SourceConfigItem.EmptySearchResult)
},
)
items.value = sources.mapNotNull {
if (!it.title.contains(query, ignoreCase = true)) {
return@mapNotNull null
}
SourceConfigItem.SourceItem(
source = it,
summary = it.getLocaleTitle(),
isEnabled = it.name !in hiddenSources,
isDraggable = false,
)
}.ifEmpty {
listOf(SourceConfigItem.EmptySearchResult)
}
return@runInterruptible
}
val map = sources.groupByTo(TreeMap(LocaleKeyComparator())) {
@@ -188,7 +187,7 @@ class SourcesListViewModel @Inject constructor(
}
}
}
items.postValue(result)
items.value = result
}
private fun getLocaleTitle(localeKey: String?): String? {

View File

@@ -15,6 +15,7 @@ import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.github.AppVersion
import org.koitharu.kotatsu.core.ui.BaseFragment
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.setChecked
import org.koitharu.kotatsu.databinding.FragmentToolsBinding
import org.koitharu.kotatsu.download.ui.list.DownloadsActivity

View File

@@ -1,14 +1,16 @@
package org.koitharu.kotatsu.settings.tools
import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.core.github.AppUpdateRepository
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.observeAsLiveData
import org.koitharu.kotatsu.core.prefs.observeAsStateFlow
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.local.data.CacheDir
import org.koitharu.kotatsu.local.data.LocalStorageManager
@@ -18,21 +20,18 @@ import javax.inject.Inject
@HiltViewModel
class ToolsViewModel @Inject constructor(
private val storageManager: LocalStorageManager,
private val appUpdateRepository: AppUpdateRepository,
private val settings: AppSettings,
appUpdateRepository: AppUpdateRepository,
) : BaseViewModel() {
val appUpdate = appUpdateRepository.observeAvailableUpdate()
.asLiveData(viewModelScope.coroutineContext)
val storageUsage: LiveData<StorageUsage?> = liveData(
context = viewModelScope.coroutineContext + Dispatchers.Default,
) {
val storageUsage: StateFlow<StorageUsage?> = flow {
emit(collectStorageUsage())
}
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, null)
val isIncognitoModeEnabled = settings.observeAsLiveData(
context = viewModelScope.coroutineContext + Dispatchers.Default,
val isIncognitoModeEnabled = settings.observeAsStateFlow(
scope = viewModelScope + Dispatchers.Default,
key = AppSettings.KEY_INCOGNITO_MODE,
valueProducer = { isIncognitoModeEnabled },
)

View File

@@ -23,6 +23,7 @@ import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.BasePreferenceFragment
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.settings.tracker.categories.TrackerCategoriesConfigSheet
import org.koitharu.kotatsu.settings.utils.MultiSummaryProvider
import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels

View File

@@ -1,15 +1,14 @@
package org.koitharu.kotatsu.settings.tracker
import androidx.lifecycle.MutableLiveData
import androidx.room.InvalidationTracker
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import okio.Closeable
import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.db.TABLE_FAVOURITE_CATEGORIES
import org.koitharu.kotatsu.core.db.removeObserverAsync
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.ext.emitValue
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import javax.inject.Inject
@@ -19,7 +18,7 @@ class TrackerSettingsViewModel @Inject constructor(
private val database: MangaDatabase,
) : BaseViewModel() {
val categoriesCount = MutableLiveData<IntArray?>(null)
val categoriesCount = MutableStateFlow<IntArray?>(null)
init {
updateCategoriesCount()
@@ -32,7 +31,7 @@ class TrackerSettingsViewModel @Inject constructor(
private fun updateCategoriesCount() {
launchJob(Dispatchers.Default) {
categoriesCount.emitValue(repository.getCategoriesCount())
categoriesCount.value = repository.getCategoriesCount()
}
}

View File

@@ -12,6 +12,7 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.ui.BaseBottomSheet
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.databinding.SheetBaseBinding
@AndroidEntryPoint

View File

@@ -4,9 +4,11 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.asFlowLiveData
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import javax.inject.Inject
@@ -16,7 +18,7 @@ class TrackerCategoriesConfigViewModel @Inject constructor(
) : BaseViewModel() {
val content = favouritesRepository.observeCategories()
.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, emptyList())
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, emptyList())
private var updateJob: Job? = null