Replace StateViewModel with explicit backing fields (#3763)
Assisted-by: Claude:claude-opus-5
This commit is contained in:
@@ -209,7 +209,6 @@ dependencies {
|
|||||||
implementation(projects.i18n)
|
implementation(projects.i18n)
|
||||||
implementation(projects.core.archive)
|
implementation(projects.core.archive)
|
||||||
implementation(projects.core.common)
|
implementation(projects.core.common)
|
||||||
implementation(projects.core.viewmodel)
|
|
||||||
implementation(projects.coreMetadata)
|
implementation(projects.coreMetadata)
|
||||||
implementation(projects.sourceApi)
|
implementation(projects.sourceApi)
|
||||||
implementation(projects.sourceLocal)
|
implementation(projects.sourceLocal)
|
||||||
|
|||||||
+15
-9
@@ -30,6 +30,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.util.fastMap
|
import androidx.compose.ui.util.fastMap
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
@@ -39,9 +40,10 @@ import eu.kanade.presentation.components.AppBar
|
|||||||
import eu.kanade.presentation.components.AppBarActions
|
import eu.kanade.presentation.components.AppBarActions
|
||||||
import eu.kanade.presentation.util.Screen
|
import eu.kanade.presentation.util.Screen
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
import tachiyomi.core.common.util.lang.launchUI
|
import tachiyomi.core.common.util.lang.launchUI
|
||||||
import tachiyomi.core.common.util.lang.toLong
|
import tachiyomi.core.common.util.lang.toLong
|
||||||
@@ -220,7 +222,11 @@ class ClearDatabaseScreen : Screen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClearDatabaseViewModel : StateViewModel<ClearDatabaseViewModel.State>(State.Loading) {
|
class ClearDatabaseViewModel : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<ClearDatabaseViewModel.State>
|
||||||
|
field = MutableStateFlow<ClearDatabaseViewModel.State>(State.Loading)
|
||||||
|
|
||||||
private val getSourcesWithNonLibraryManga: GetSourcesWithNonLibraryManga = Injekt.get()
|
private val getSourcesWithNonLibraryManga: GetSourcesWithNonLibraryManga = Injekt.get()
|
||||||
private val database: Database = Injekt.get()
|
private val database: Database = Injekt.get()
|
||||||
|
|
||||||
@@ -228,7 +234,7 @@ class ClearDatabaseViewModel : StateViewModel<ClearDatabaseViewModel.State>(Stat
|
|||||||
viewModelScope.launchIO {
|
viewModelScope.launchIO {
|
||||||
getSourcesWithNonLibraryManga.subscribe()
|
getSourcesWithNonLibraryManga.subscribe()
|
||||||
.collectLatest { list ->
|
.collectLatest { list ->
|
||||||
mutableState.update { old ->
|
state.update { old ->
|
||||||
val items = list.sortedBy { it.name }
|
val items = list.sortedBy { it.name }
|
||||||
when (old) {
|
when (old) {
|
||||||
State.Loading -> State.Ready(items)
|
State.Loading -> State.Ready(items)
|
||||||
@@ -245,7 +251,7 @@ class ClearDatabaseViewModel : StateViewModel<ClearDatabaseViewModel.State>(Stat
|
|||||||
database.historyQueries.removeResettedHistory()
|
database.historyQueries.removeResettedHistory()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toggleSelection(source: Source) = mutableState.update { state ->
|
fun toggleSelection(source: Source) = state.update { state ->
|
||||||
if (state !is State.Ready) return@update state
|
if (state !is State.Ready) return@update state
|
||||||
val mutableList = state.selection.toMutableList()
|
val mutableList = state.selection.toMutableList()
|
||||||
if (mutableList.contains(source.id)) {
|
if (mutableList.contains(source.id)) {
|
||||||
@@ -256,17 +262,17 @@ class ClearDatabaseViewModel : StateViewModel<ClearDatabaseViewModel.State>(Stat
|
|||||||
state.copy(selection = mutableList)
|
state.copy(selection = mutableList)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearSelection() = mutableState.update { state ->
|
fun clearSelection() = state.update { state ->
|
||||||
if (state !is State.Ready) return@update state
|
if (state !is State.Ready) return@update state
|
||||||
state.copy(selection = emptyList())
|
state.copy(selection = emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun selectAll() = mutableState.update { state ->
|
fun selectAll() = state.update { state ->
|
||||||
if (state !is State.Ready) return@update state
|
if (state !is State.Ready) return@update state
|
||||||
state.copy(selection = state.items.fastMap { it.id })
|
state.copy(selection = state.items.fastMap { it.id })
|
||||||
}
|
}
|
||||||
|
|
||||||
fun invertSelection() = mutableState.update { state ->
|
fun invertSelection() = state.update { state ->
|
||||||
if (state !is State.Ready) return@update state
|
if (state !is State.Ready) return@update state
|
||||||
state.copy(
|
state.copy(
|
||||||
selection = state.items
|
selection = state.items
|
||||||
@@ -275,12 +281,12 @@ class ClearDatabaseViewModel : StateViewModel<ClearDatabaseViewModel.State>(Stat
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showConfirmation() = mutableState.update { state ->
|
fun showConfirmation() = state.update { state ->
|
||||||
if (state !is State.Ready) return@update state
|
if (state !is State.Ready) return@update state
|
||||||
state.copy(showConfirmation = true)
|
state.copy(showConfirmation = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hideConfirmation() = mutableState.update { state ->
|
fun hideConfirmation() = state.update { state ->
|
||||||
if (state !is State.Ready) return@update state
|
if (state !is State.Ready) return@update state
|
||||||
state.copy(showConfirmation = false)
|
state.copy(showConfirmation = false)
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-3
@@ -11,6 +11,7 @@ import androidx.compose.runtime.Immutable
|
|||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
@@ -22,8 +23,9 @@ import eu.kanade.tachiyomi.data.backup.create.BackupCreator
|
|||||||
import eu.kanade.tachiyomi.data.backup.create.BackupOptions
|
import eu.kanade.tachiyomi.data.backup.create.BackupOptions
|
||||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.LabeledCheckbox
|
import tachiyomi.presentation.core.components.LabeledCheckbox
|
||||||
import tachiyomi.presentation.core.components.LazyColumnWithAction
|
import tachiyomi.presentation.core.components.LazyColumnWithAction
|
||||||
@@ -119,10 +121,13 @@ class CreateBackupScreen : Screen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateBackupViewModel : StateViewModel<CreateBackupViewModel.State>(State()) {
|
class CreateBackupViewModel : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<CreateBackupViewModel.State>
|
||||||
|
field = MutableStateFlow<CreateBackupViewModel.State>(State())
|
||||||
|
|
||||||
fun toggle(setter: (BackupOptions, Boolean) -> BackupOptions, enabled: Boolean) {
|
fun toggle(setter: (BackupOptions, Boolean) -> BackupOptions, enabled: Boolean) {
|
||||||
mutableState.update {
|
state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
options = setter(it.options, enabled),
|
options = setter(it.options, enabled),
|
||||||
)
|
)
|
||||||
|
|||||||
+9
-4
@@ -20,6 +20,7 @@ import androidx.compose.ui.text.buildAnnotatedString
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.withStyle
|
import androidx.compose.ui.text.withStyle
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewmodel.CreationExtras
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.lifecycle.viewmodel.initializer
|
import androidx.lifecycle.viewmodel.initializer
|
||||||
@@ -33,8 +34,9 @@ import eu.kanade.tachiyomi.data.backup.BackupFileValidator
|
|||||||
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
|
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
|
||||||
import eu.kanade.tachiyomi.data.backup.restore.RestoreOptions
|
import eu.kanade.tachiyomi.data.backup.restore.RestoreOptions
|
||||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.LabeledCheckbox
|
import tachiyomi.presentation.core.components.LabeledCheckbox
|
||||||
import tachiyomi.presentation.core.components.LazyColumnWithAction
|
import tachiyomi.presentation.core.components.LazyColumnWithAction
|
||||||
@@ -176,7 +178,10 @@ class RestoreBackupScreen(
|
|||||||
class RestoreBackupViewModel(
|
class RestoreBackupViewModel(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val uri: String,
|
private val uri: String,
|
||||||
) : StateViewModel<RestoreBackupViewModel.State>(State()) {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<RestoreBackupViewModel.State>
|
||||||
|
field = MutableStateFlow<RestoreBackupViewModel.State>(State())
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val URI_KEY = CreationExtras.Key<String>()
|
val URI_KEY = CreationExtras.Key<String>()
|
||||||
@@ -196,7 +201,7 @@ class RestoreBackupViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun toggle(setter: (RestoreOptions, Boolean) -> RestoreOptions, enabled: Boolean) {
|
fun toggle(setter: (RestoreOptions, Boolean) -> RestoreOptions, enabled: Boolean) {
|
||||||
mutableState.update {
|
state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
options = setter(it.options, enabled),
|
options = setter(it.options, enabled),
|
||||||
)
|
)
|
||||||
@@ -234,7 +239,7 @@ class RestoreBackupViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setError(error: Any?, canRestore: Boolean) {
|
private fun setError(error: Any?, canRestore: Boolean) {
|
||||||
mutableState.update {
|
state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
error = error,
|
error = error,
|
||||||
canRestore = canRestore,
|
canRestore = canRestore,
|
||||||
|
|||||||
+1
-2
@@ -8,7 +8,6 @@ import eu.kanade.domain.source.service.SourcePreferences
|
|||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
|
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchViewModel
|
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchViewModel
|
||||||
import kotlinx.coroutines.flow.update
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import tachiyomi.domain.manga.interactor.GetManga
|
import tachiyomi.domain.manga.interactor.GetManga
|
||||||
import tachiyomi.domain.source.service.SourceManager
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
@@ -46,7 +45,7 @@ class MigrateSearchViewModel(
|
|||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val manga = getManga.await(mangaId)!!
|
val manga = getManga.await(mangaId)!!
|
||||||
mutableState.update {
|
updateState {
|
||||||
it.copy(
|
it.copy(
|
||||||
from = manga,
|
from = manga,
|
||||||
searchQuery = manga.title,
|
searchQuery = manga.title,
|
||||||
|
|||||||
+15
-10
@@ -6,6 +6,7 @@ import androidx.compose.runtime.Immutable
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.CreationExtras
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import androidx.lifecycle.viewmodel.initializer
|
import androidx.lifecycle.viewmodel.initializer
|
||||||
@@ -23,7 +24,9 @@ import eu.kanade.domain.track.interactor.AddTracks
|
|||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.util.removeCovers
|
import eu.kanade.tachiyomi.util.removeCovers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.emptyFlow
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
import kotlinx.coroutines.flow.firstOrNull
|
import kotlinx.coroutines.flow.firstOrNull
|
||||||
@@ -31,7 +34,6 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import tachiyomi.core.common.preference.CheckboxState
|
import tachiyomi.core.common.preference.CheckboxState
|
||||||
import tachiyomi.core.common.preference.mapAsCheckboxState
|
import tachiyomi.core.common.preference.mapAsCheckboxState
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
@@ -68,7 +70,10 @@ class BrowseSourceViewModel(
|
|||||||
private val updateManga: UpdateManga = Injekt.get(),
|
private val updateManga: UpdateManga = Injekt.get(),
|
||||||
private val addTracks: AddTracks = Injekt.get(),
|
private val addTracks: AddTracks = Injekt.get(),
|
||||||
getIncognitoState: GetIncognitoState = Injekt.get(),
|
getIncognitoState: GetIncognitoState = Injekt.get(),
|
||||||
) : StateViewModel<BrowseSourceViewModel.State>(State(Listing.valueOf(listingQuery))) {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<BrowseSourceViewModel.State>
|
||||||
|
field = MutableStateFlow<BrowseSourceViewModel.State>(State(Listing.valueOf(listingQuery)))
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val SOURCE_ID_KEY = CreationExtras.Key<Long>()
|
val SOURCE_ID_KEY = CreationExtras.Key<Long>()
|
||||||
@@ -89,7 +94,7 @@ class BrowseSourceViewModel(
|
|||||||
val source = sourceManager.getOrStub(sourceId)
|
val source = sourceManager.getOrStub(sourceId)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
mutableState.update {
|
state.update {
|
||||||
var query: String? = null
|
var query: String? = null
|
||||||
var listing = it.listing
|
var listing = it.listing
|
||||||
|
|
||||||
@@ -142,15 +147,15 @@ class BrowseSourceViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun resetFilters() {
|
fun resetFilters() {
|
||||||
mutableState.update { it.copy(filters = source.getFilterList()) }
|
state.update { it.copy(filters = source.getFilterList()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setListing(listing: Listing) {
|
fun setListing(listing: Listing) {
|
||||||
mutableState.update { it.copy(listing = listing, toolbarQuery = null) }
|
state.update { it.copy(listing = listing, toolbarQuery = null) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setFilters(filters: FilterList) {
|
fun setFilters(filters: FilterList) {
|
||||||
mutableState.update {
|
state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
filters = filters,
|
filters = filters,
|
||||||
)
|
)
|
||||||
@@ -161,7 +166,7 @@ class BrowseSourceViewModel(
|
|||||||
val input = state.value.listing as? Listing.Search
|
val input = state.value.listing as? Listing.Search
|
||||||
?: Listing.Search(query = null, filters = source.getFilterList())
|
?: Listing.Search(query = null, filters = source.getFilterList())
|
||||||
|
|
||||||
mutableState.update {
|
state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
listing = input.copy(
|
listing = input.copy(
|
||||||
query = query ?: input.query,
|
query = query ?: input.query,
|
||||||
@@ -201,7 +206,7 @@ class BrowseSourceViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutableState.update {
|
state.update {
|
||||||
val listing = if (genreExists) {
|
val listing = if (genreExists) {
|
||||||
Listing.Search(query = null, filters = defaultFilters)
|
Listing.Search(query = null, filters = defaultFilters)
|
||||||
} else {
|
} else {
|
||||||
@@ -310,11 +315,11 @@ class BrowseSourceViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setDialog(dialog: Dialog?) {
|
fun setDialog(dialog: Dialog?) {
|
||||||
mutableState.update { it.copy(dialog = dialog) }
|
state.update { it.copy(dialog = dialog) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setToolbarQuery(query: String?) {
|
fun setToolbarQuery(query: String?) {
|
||||||
mutableState.update { it.copy(toolbarQuery = query) }
|
state.update { it.copy(toolbarQuery = query) }
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class Listing(open val query: String?, open val filters: FilterList) {
|
sealed class Listing(open val query: String?, open val filters: FilterList) {
|
||||||
|
|||||||
+20
-9
@@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.browse.source.globalsearch
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.runtime.produceState
|
import androidx.compose.runtime.produceState
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||||
@@ -11,13 +12,14 @@ import kotlinx.coroutines.Job
|
|||||||
import kotlinx.coroutines.asCoroutineDispatcher
|
import kotlinx.coroutines.asCoroutineDispatcher
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import mihon.domain.manga.model.toDomainManga
|
import mihon.domain.manga.model.toDomainManga
|
||||||
import tachiyomi.core.common.preference.toggle
|
import tachiyomi.core.common.preference.toggle
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
@@ -37,7 +39,16 @@ abstract class SearchViewModel(
|
|||||||
private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
|
private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
|
||||||
private val getManga: GetManga = Injekt.get(),
|
private val getManga: GetManga = Injekt.get(),
|
||||||
private val preferences: SourcePreferences = Injekt.get(),
|
private val preferences: SourcePreferences = Injekt.get(),
|
||||||
) : StateViewModel<SearchViewModel.State>(initialState) {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<State>
|
||||||
|
field = MutableStateFlow<State>(initialState)
|
||||||
|
|
||||||
|
// Subclasses can't touch the backing field (Kotlin forbids a visibility modifier on one),
|
||||||
|
// so state writes from them go through here.
|
||||||
|
protected fun updateState(function: (State) -> State) {
|
||||||
|
state.update(function)
|
||||||
|
}
|
||||||
|
|
||||||
private val coroutineDispatcher = Executors.newFixedThreadPool(5).asCoroutineDispatcher()
|
private val coroutineDispatcher = Executors.newFixedThreadPool(5).asCoroutineDispatcher()
|
||||||
private var searchJob: Job? = null
|
private var searchJob: Job? = null
|
||||||
@@ -61,8 +72,8 @@ abstract class SearchViewModel(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
preferences.globalSearchFilterState.changes().collectLatest { state ->
|
preferences.globalSearchFilterState.changes().collectLatest { onlyShowHasResults ->
|
||||||
mutableState.update { it.copy(onlyShowHasResults = state) }
|
state.update { it.copy(onlyShowHasResults = onlyShowHasResults) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,11 +115,11 @@ abstract class SearchViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateSearchQuery(query: String?) {
|
fun updateSearchQuery(query: String?) {
|
||||||
mutableState.update { it.copy(searchQuery = query) }
|
state.update { it.copy(searchQuery = query) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSourceFilter(filter: SourceFilter) {
|
fun setSourceFilter(filter: SourceFilter) {
|
||||||
mutableState.update { it.copy(sourceFilter = filter) }
|
state.update { it.copy(sourceFilter = filter) }
|
||||||
search()
|
search()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +189,7 @@ abstract class SearchViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateItems(items: Map<Source, SearchItemResult>) {
|
private fun updateItems(items: Map<Source, SearchItemResult>) {
|
||||||
mutableState.update {
|
state.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
items = items
|
items = items
|
||||||
.toSortedMap(sortComparator(items)),
|
.toSortedMap(sortComparator(items)),
|
||||||
@@ -193,12 +204,12 @@ abstract class SearchViewModel(
|
|||||||
fun setMigrateDialog(currentId: Long, target: Manga) {
|
fun setMigrateDialog(currentId: Long, target: Manga) {
|
||||||
viewModelScope.launchIO {
|
viewModelScope.launchIO {
|
||||||
val current = getManga.await(currentId) ?: return@launchIO
|
val current = getManga.await(currentId) ?: return@launchIO
|
||||||
mutableState.update { it.copy(dialog = Dialog.Migrate(target, current)) }
|
state.update { it.copy(dialog = Dialog.Migrate(target, current)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearDialog() {
|
fun clearDialog() {
|
||||||
mutableState.update { it.copy(dialog = null) }
|
state.update { it.copy(dialog = null) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package eu.kanade.tachiyomi.ui.deeplink
|
package eu.kanade.tachiyomi.ui.deeplink
|
||||||
|
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.CreationExtras
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import androidx.lifecycle.viewmodel.initializer
|
import androidx.lifecycle.viewmodel.initializer
|
||||||
@@ -9,8 +10,9 @@ import eu.kanade.tachiyomi.source.Source
|
|||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.online.ResolvableSource
|
import eu.kanade.tachiyomi.source.online.ResolvableSource
|
||||||
import eu.kanade.tachiyomi.source.online.UriType
|
import eu.kanade.tachiyomi.source.online.UriType
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import mihon.domain.manga.model.toDomainManga
|
import mihon.domain.manga.model.toDomainManga
|
||||||
import mihon.domain.source.interactor.UpdateMangaFromRemote
|
import mihon.domain.source.interactor.UpdateMangaFromRemote
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
@@ -28,7 +30,10 @@ class DeepLinkViewModel(
|
|||||||
private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
|
private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
|
||||||
private val getChapterByUrlAndMangaId: GetChapterByUrlAndMangaId = Injekt.get(),
|
private val getChapterByUrlAndMangaId: GetChapterByUrlAndMangaId = Injekt.get(),
|
||||||
private val updateMangaFromRemote: UpdateMangaFromRemote = Injekt.get(),
|
private val updateMangaFromRemote: UpdateMangaFromRemote = Injekt.get(),
|
||||||
) : StateViewModel<DeepLinkViewModel.State>(State.Loading) {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<DeepLinkViewModel.State>
|
||||||
|
field = MutableStateFlow<DeepLinkViewModel.State>(State.Loading)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val QUERY_KEY = CreationExtras.Key<String>()
|
val QUERY_KEY = CreationExtras.Key<String>()
|
||||||
@@ -58,7 +63,7 @@ class DeepLinkViewModel(
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
mutableState.update {
|
state.update {
|
||||||
if (manga == null) {
|
if (manga == null) {
|
||||||
State.NoResults
|
State.NoResults
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import androidx.compose.material3.SnackbarResult
|
|||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.util.fastAny
|
import androidx.compose.ui.util.fastAny
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.CreationExtras
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import androidx.lifecycle.viewmodel.initializer
|
import androidx.lifecycle.viewmodel.initializer
|
||||||
@@ -41,6 +42,8 @@ import eu.kanade.tachiyomi.util.chapter.getNextUnread
|
|||||||
import eu.kanade.tachiyomi.util.removeCovers
|
import eu.kanade.tachiyomi.util.removeCovers
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
@@ -50,7 +53,6 @@ import kotlinx.coroutines.flow.update
|
|||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import mihon.domain.chapter.interactor.FilterChaptersForDownload
|
import mihon.domain.chapter.interactor.FilterChaptersForDownload
|
||||||
import mihon.domain.source.interactor.UpdateMangaFromRemote
|
import mihon.domain.source.interactor.UpdateMangaFromRemote
|
||||||
import tachiyomi.core.common.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
@@ -116,7 +118,10 @@ class MangaViewModel(
|
|||||||
private val filterChaptersForDownload: FilterChaptersForDownload = Injekt.get(),
|
private val filterChaptersForDownload: FilterChaptersForDownload = Injekt.get(),
|
||||||
private val updateMangaFromRemote: UpdateMangaFromRemote = Injekt.get(),
|
private val updateMangaFromRemote: UpdateMangaFromRemote = Injekt.get(),
|
||||||
val snackbarHostState: SnackbarHostState = SnackbarHostState(),
|
val snackbarHostState: SnackbarHostState = SnackbarHostState(),
|
||||||
) : StateViewModel<MangaViewModel.State>(State.Loading) {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<State>
|
||||||
|
field = MutableStateFlow<State>(State.Loading)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val MANGA_ID_KEY = CreationExtras.Key<Long>()
|
val MANGA_ID_KEY = CreationExtras.Key<Long>()
|
||||||
@@ -168,7 +173,7 @@ class MangaViewModel(
|
|||||||
* Helper function to update the UI state only if it's currently in success state
|
* Helper function to update the UI state only if it's currently in success state
|
||||||
*/
|
*/
|
||||||
private inline fun updateSuccessState(func: (State.Success) -> State.Success) {
|
private inline fun updateSuccessState(func: (State.Success) -> State.Success) {
|
||||||
mutableState.update {
|
state.update {
|
||||||
when (it) {
|
when (it) {
|
||||||
State.Loading -> it
|
State.Loading -> it
|
||||||
is State.Success -> func(it)
|
is State.Success -> func(it)
|
||||||
@@ -228,7 +233,7 @@ class MangaViewModel(
|
|||||||
val needRefreshChapter = chapters.isEmpty()
|
val needRefreshChapter = chapters.isEmpty()
|
||||||
|
|
||||||
// Show what we have earlier
|
// Show what we have earlier
|
||||||
mutableState.update {
|
state.update {
|
||||||
State.Success(
|
State.Success(
|
||||||
manga = manga,
|
manga = manga,
|
||||||
source = Injekt.get<SourceManager>().getOrStub(manga.source),
|
source = Injekt.get<SourceManager>().getOrStub(manga.source),
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.CreationExtras
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
@@ -13,8 +14,9 @@ import cafe.adriel.voyager.navigator.LocalNavigator
|
|||||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
import eu.kanade.presentation.manga.MangaNotesScreen
|
import eu.kanade.presentation.manga.MangaNotesScreen
|
||||||
import eu.kanade.presentation.util.Screen
|
import eu.kanade.presentation.util.Screen
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import tachiyomi.core.common.util.lang.launchNonCancellable
|
import tachiyomi.core.common.util.lang.launchNonCancellable
|
||||||
import tachiyomi.domain.manga.interactor.UpdateMangaNotes
|
import tachiyomi.domain.manga.interactor.UpdateMangaNotes
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
@@ -46,7 +48,10 @@ class MangaNotesScreen(
|
|||||||
class Model(
|
class Model(
|
||||||
private val manga: Manga,
|
private val manga: Manga,
|
||||||
private val updateMangaNotes: UpdateMangaNotes = Injekt.get(),
|
private val updateMangaNotes: UpdateMangaNotes = Injekt.get(),
|
||||||
) : StateViewModel<State>(State(manga, manga.notes)) {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<State>
|
||||||
|
field = MutableStateFlow<State>(State(manga, manga.notes))
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val MANGA_KEY = CreationExtras.Key<Manga>()
|
val MANGA_KEY = CreationExtras.Key<Manga>()
|
||||||
@@ -63,7 +68,7 @@ class MangaNotesScreen(
|
|||||||
fun updateNotes(content: String) {
|
fun updateNotes(content: String) {
|
||||||
if (content == state.value.notes) return
|
if (content == state.value.notes) return
|
||||||
|
|
||||||
mutableState.update {
|
state.update {
|
||||||
it.copy(notes = content)
|
it.copy(notes = content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone
|
|||||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
@@ -68,7 +70,6 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.datetime.TimeZone
|
import kotlinx.datetime.TimeZone
|
||||||
import kotlinx.datetime.toLocalDateTime
|
import kotlinx.datetime.toLocalDateTime
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import tachiyomi.core.common.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
import tachiyomi.core.common.util.lang.launchNonCancellable
|
import tachiyomi.core.common.util.lang.launchNonCancellable
|
||||||
import tachiyomi.core.common.util.lang.withIOContext
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
@@ -205,7 +206,10 @@ data class TrackInfoDialogHomeScreen(
|
|||||||
private val mangaId: Long,
|
private val mangaId: Long,
|
||||||
private val sourceId: Long,
|
private val sourceId: Long,
|
||||||
private val getTracks: GetTracks = Injekt.get(),
|
private val getTracks: GetTracks = Injekt.get(),
|
||||||
) : StateViewModel<Model.State>(State()) {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<Model.State>
|
||||||
|
field = MutableStateFlow<Model.State>(State())
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val MANGA_ID_KEY = CreationExtras.Key<Long>()
|
val MANGA_ID_KEY = CreationExtras.Key<Long>()
|
||||||
@@ -231,7 +235,7 @@ data class TrackInfoDialogHomeScreen(
|
|||||||
.catch { logcat(LogPriority.ERROR, it) }
|
.catch { logcat(LogPriority.ERROR, it) }
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.map { it.mapToTrackItem() }
|
.map { it.mapToTrackItem() }
|
||||||
.collectLatest { trackItems -> mutableState.update { it.copy(trackItems = trackItems) } }
|
.collectLatest { trackItems -> state.update { it.copy(trackItems = trackItems) } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,7 +328,10 @@ private data class TrackStatusSelectorScreen(
|
|||||||
class Model(
|
class Model(
|
||||||
private val track: Track,
|
private val track: Track,
|
||||||
private val tracker: Tracker,
|
private val tracker: Tracker,
|
||||||
) : StateViewModel<Model.State>(State(track.status)) {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<Model.State>
|
||||||
|
field = MutableStateFlow<Model.State>(State(track.status))
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TRACK_KEY = CreationExtras.Key<Track>()
|
val TRACK_KEY = CreationExtras.Key<Track>()
|
||||||
@@ -345,7 +352,7 @@ private data class TrackStatusSelectorScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setSelection(selection: Long) {
|
fun setSelection(selection: Long) {
|
||||||
mutableState.update { it.copy(selection = selection) }
|
state.update { it.copy(selection = selection) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setStatus() {
|
fun setStatus() {
|
||||||
@@ -393,7 +400,10 @@ private data class TrackChapterSelectorScreen(
|
|||||||
class Model(
|
class Model(
|
||||||
private val track: Track,
|
private val track: Track,
|
||||||
private val tracker: Tracker,
|
private val tracker: Tracker,
|
||||||
) : StateViewModel<Model.State>(State(track.lastChapterRead.toInt())) {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<Model.State>
|
||||||
|
field = MutableStateFlow<Model.State>(State(track.lastChapterRead.toInt()))
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TRACK_KEY = CreationExtras.Key<Track>()
|
val TRACK_KEY = CreationExtras.Key<Track>()
|
||||||
@@ -419,7 +429,7 @@ private data class TrackChapterSelectorScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setSelection(selection: Int) {
|
fun setSelection(selection: Int) {
|
||||||
mutableState.update { it.copy(selection = selection) }
|
state.update { it.copy(selection = selection) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setChapter() {
|
fun setChapter() {
|
||||||
@@ -467,7 +477,10 @@ private data class TrackScoreSelectorScreen(
|
|||||||
class Model(
|
class Model(
|
||||||
private val track: Track,
|
private val track: Track,
|
||||||
private val tracker: Tracker,
|
private val tracker: Tracker,
|
||||||
) : StateViewModel<Model.State>(State(tracker.displayScore(track))) {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<Model.State>
|
||||||
|
field = MutableStateFlow<Model.State>(State(tracker.displayScore(track)))
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TRACK_KEY = CreationExtras.Key<Track>()
|
val TRACK_KEY = CreationExtras.Key<Track>()
|
||||||
@@ -488,7 +501,7 @@ private data class TrackScoreSelectorScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun setSelection(selection: String) {
|
fun setSelection(selection: String) {
|
||||||
mutableState.update { it.copy(selection = selection) }
|
state.update { it.copy(selection = selection) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setScore() {
|
fun setScore() {
|
||||||
@@ -788,7 +801,10 @@ data class TrackerSearchScreen(
|
|||||||
private val currentUrl: String?,
|
private val currentUrl: String?,
|
||||||
initialQuery: String,
|
initialQuery: String,
|
||||||
private val tracker: Tracker,
|
private val tracker: Tracker,
|
||||||
) : StateViewModel<Model.State>(State()) {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<Model.State>
|
||||||
|
field = MutableStateFlow<Model.State>(State())
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val MANGA_ID_KEY = CreationExtras.Key<Long>()
|
val MANGA_ID_KEY = CreationExtras.Key<Long>()
|
||||||
@@ -820,7 +836,7 @@ data class TrackerSearchScreen(
|
|||||||
fun trackingSearch(query: String) {
|
fun trackingSearch(query: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
// To show loading state
|
// To show loading state
|
||||||
mutableState.update { it.copy(queryResult = null, selected = null) }
|
state.update { it.copy(queryResult = null, selected = null) }
|
||||||
|
|
||||||
val result = withIOContext {
|
val result = withIOContext {
|
||||||
try {
|
try {
|
||||||
@@ -830,7 +846,7 @@ data class TrackerSearchScreen(
|
|||||||
Result.failure(e)
|
Result.failure(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mutableState.update { oldState ->
|
state.update { oldState ->
|
||||||
oldState.copy(
|
oldState.copy(
|
||||||
queryResult = result,
|
queryResult = result,
|
||||||
selected = result.getOrNull()?.find { it.tracking_url == currentUrl },
|
selected = result.getOrNull()?.find { it.tracking_url == currentUrl },
|
||||||
@@ -844,7 +860,7 @@ data class TrackerSearchScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateSelection(selected: TrackSearch) {
|
fun updateSelection(selected: TrackSearch) {
|
||||||
mutableState.update { it.copy(selected = selected) }
|
state.update { it.copy(selected = selected) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.more
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.CreationExtras
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import androidx.lifecycle.viewmodel.initializer
|
import androidx.lifecycle.viewmodel.initializer
|
||||||
@@ -17,10 +18,11 @@ import eu.kanade.tachiyomi.util.storage.getUriCompat
|
|||||||
import eu.kanade.tachiyomi.util.storage.saveTo
|
import eu.kanade.tachiyomi.util.storage.saveTo
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import tachiyomi.core.common.util.lang.withIOContext
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
@@ -32,7 +34,10 @@ class NewUpdateScreenModel(
|
|||||||
private val downloadLink: String,
|
private val downloadLink: String,
|
||||||
private val context: Context = Injekt.get(),
|
private val context: Context = Injekt.get(),
|
||||||
private val network: NetworkHelper = Injekt.get(),
|
private val network: NetworkHelper = Injekt.get(),
|
||||||
) : StateViewModel<NewUpdateScreenModel.State>(State(changelogInfo = changelogInfo)) {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<NewUpdateScreenModel.State>
|
||||||
|
field = MutableStateFlow<NewUpdateScreenModel.State>(State(changelogInfo = changelogInfo))
|
||||||
|
|
||||||
private val apkFile: File
|
private val apkFile: File
|
||||||
get() = File(context.externalCacheDir, "update.apk")
|
get() = File(context.externalCacheDir, "update.apk")
|
||||||
@@ -43,16 +48,16 @@ class NewUpdateScreenModel(
|
|||||||
if (downloadJob?.isActive == true) return
|
if (downloadJob?.isActive == true) return
|
||||||
|
|
||||||
downloadJob = viewModelScope.launch {
|
downloadJob = viewModelScope.launch {
|
||||||
mutableState.update { it.copy(downloadProgress = 0, stage = Stage.Downloading) }
|
state.update { it.copy(downloadProgress = 0, stage = Stage.Downloading) }
|
||||||
try {
|
try {
|
||||||
withIOContext { downloadApk() }
|
withIOContext { downloadApk() }
|
||||||
mutableState.update { it.copy(downloadProgress = 100, stage = Stage.Downloaded) }
|
state.update { it.copy(downloadProgress = 100, stage = Stage.Downloaded) }
|
||||||
} catch (e: CancellationException) {
|
} catch (e: CancellationException) {
|
||||||
throw e
|
throw e
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(LogPriority.ERROR, e)
|
logcat(LogPriority.ERROR, e)
|
||||||
apkFile.delete()
|
apkFile.delete()
|
||||||
mutableState.update { it.copy(stage = Stage.Failed) }
|
state.update { it.copy(stage = Stage.Failed) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,7 +76,7 @@ class NewUpdateScreenModel(
|
|||||||
if (progress > savedProgress && currentTime - 200 > lastTick) {
|
if (progress > savedProgress && currentTime - 200 > lastTick) {
|
||||||
savedProgress = progress
|
savedProgress = progress
|
||||||
lastTick = currentTime
|
lastTick = currentTime
|
||||||
mutableState.update { it.copy(downloadProgress = progress) }
|
state.update { it.copy(downloadProgress = progress) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.stats
|
|||||||
|
|
||||||
import androidx.compose.ui.util.fastDistinctBy
|
import androidx.compose.ui.util.fastDistinctBy
|
||||||
import androidx.compose.ui.util.fastFilter
|
import androidx.compose.ui.util.fastFilter
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import eu.kanade.core.util.fastCountNot
|
import eu.kanade.core.util.fastCountNot
|
||||||
import eu.kanade.presentation.more.stats.StatsScreenState
|
import eu.kanade.presentation.more.stats.StatsScreenState
|
||||||
@@ -9,8 +10,9 @@ import eu.kanade.presentation.more.stats.data.StatsData
|
|||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.data.track.TrackerManager
|
import eu.kanade.tachiyomi.data.track.TrackerManager
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
import tachiyomi.domain.history.interactor.GetTotalReadDuration
|
import tachiyomi.domain.history.interactor.GetTotalReadDuration
|
||||||
import tachiyomi.domain.library.model.LibraryManga
|
import tachiyomi.domain.library.model.LibraryManga
|
||||||
@@ -32,7 +34,10 @@ class StatsViewModel(
|
|||||||
private val getTracks: GetTracks = Injekt.get(),
|
private val getTracks: GetTracks = Injekt.get(),
|
||||||
private val preferences: LibraryPreferences = Injekt.get(),
|
private val preferences: LibraryPreferences = Injekt.get(),
|
||||||
private val trackerManager: TrackerManager = Injekt.get(),
|
private val trackerManager: TrackerManager = Injekt.get(),
|
||||||
) : StateViewModel<StatsScreenState>(StatsScreenState.Loading) {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<StatsScreenState>
|
||||||
|
field = MutableStateFlow<StatsScreenState>(StatsScreenState.Loading)
|
||||||
|
|
||||||
private val loggedInTrackers by lazy { trackerManager.loggedInTrackers() }
|
private val loggedInTrackers by lazy { trackerManager.loggedInTrackers() }
|
||||||
|
|
||||||
@@ -73,7 +78,7 @@ class StatsViewModel(
|
|||||||
trackerCount = loggedInTrackers.size,
|
trackerCount = loggedInTrackers.size,
|
||||||
)
|
)
|
||||||
|
|
||||||
mutableState.update {
|
state.update {
|
||||||
StatsScreenState.Success(
|
StatsScreenState.Success(
|
||||||
overview = overviewStatData,
|
overview = overviewStatData,
|
||||||
titles = titlesStatData,
|
titles = titlesStatData,
|
||||||
|
|||||||
@@ -2,17 +2,16 @@ package eu.kanade.tachiyomi.ui.webview
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewmodel.CreationExtras
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import androidx.lifecycle.viewmodel.initializer
|
import androidx.lifecycle.viewmodel.initializer
|
||||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||||
import eu.kanade.presentation.more.stats.StatsScreenState
|
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||||
import eu.kanade.tachiyomi.util.system.toShareIntent
|
import eu.kanade.tachiyomi.util.system.toShareIntent
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import tachiyomi.domain.source.service.SourceManager
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
@@ -23,7 +22,7 @@ class WebViewViewModel(
|
|||||||
val sourceId: Long?,
|
val sourceId: Long?,
|
||||||
private val sourceManager: SourceManager = Injekt.get(),
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
private val network: NetworkHelper = Injekt.get(),
|
private val network: NetworkHelper = Injekt.get(),
|
||||||
) : StateViewModel<StatsScreenState>(StatsScreenState.Loading) {
|
) : ViewModel() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val SOURCE_ID_KEY = CreationExtras.Key<Long?>()
|
val SOURCE_ID_KEY = CreationExtras.Key<Long?>()
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.util.fastForEachIndexed
|
import androidx.compose.ui.util.fastForEachIndexed
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
@@ -48,8 +49,9 @@ import eu.kanade.presentation.util.Screen
|
|||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchScreen
|
import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchScreen
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import mihon.feature.migration.list.MigrationListScreen
|
import mihon.feature.migration.list.MigrationListScreen
|
||||||
import sh.calvin.reorderable.ReorderableCollectionItemScope
|
import sh.calvin.reorderable.ReorderableCollectionItemScope
|
||||||
import sh.calvin.reorderable.ReorderableItem
|
import sh.calvin.reorderable.ReorderableItem
|
||||||
@@ -309,7 +311,10 @@ class MigrationConfigScreen(private val mangaIds: Collection<Long>) : Screen() {
|
|||||||
class Model(
|
class Model(
|
||||||
val sourcePreferences: SourcePreferences = Injekt.get(),
|
val sourcePreferences: SourcePreferences = Injekt.get(),
|
||||||
private val sourceManager: SourceManager = Injekt.get(),
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
) : StateViewModel<Model.State>(State()) {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<Model.State>
|
||||||
|
field = MutableStateFlow<Model.State>(State())
|
||||||
|
|
||||||
private val sourcesComparator = { includedSources: List<Long> ->
|
private val sourcesComparator = { includedSources: List<Long> ->
|
||||||
compareBy<MigrationSource>(
|
compareBy<MigrationSource>(
|
||||||
@@ -322,12 +327,12 @@ class MigrationConfigScreen(private val mangaIds: Collection<Long>) : Screen() {
|
|||||||
init {
|
init {
|
||||||
viewModelScope.launchIO {
|
viewModelScope.launchIO {
|
||||||
initSources()
|
initSources()
|
||||||
mutableState.update { it.copy(isLoading = false) }
|
state.update { it.copy(isLoading = false) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateSources(action: (List<MigrationSource>) -> List<MigrationSource>) {
|
private fun updateSources(action: (List<MigrationSource>) -> List<MigrationSource>) {
|
||||||
mutableState.update { state ->
|
state.update { state ->
|
||||||
val updatedSources = action(state.sources)
|
val updatedSources = action(state.sources)
|
||||||
val includedSources = updatedSources.mapNotNull { if (!it.isSelected) null else it.id }
|
val includedSources = updatedSources.mapNotNull { if (!it.isSelected) null else it.id }
|
||||||
state.copy(sources = updatedSources.sortedWith(sourcesComparator(includedSources)))
|
state.copy(sources = updatedSources.sortedWith(sourcesComparator(includedSources)))
|
||||||
@@ -364,7 +369,7 @@ class MigrationConfigScreen(private val mangaIds: Collection<Long>) : Screen() {
|
|||||||
}
|
}
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
mutableState.update { state ->
|
state.update { state ->
|
||||||
state.copy(sources = sources.sortedWith(sourcesComparator(includedSources)))
|
state.copy(sources = sources.sortedWith(sourcesComparator(includedSources)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,16 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.util.fastForEach
|
import androidx.compose.ui.util.fastForEach
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
import cafe.adriel.voyager.core.screen.Screen
|
||||||
import eu.kanade.domain.manga.model.hasCustomCover
|
import eu.kanade.domain.manga.model.hasCustomCover
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import mihon.domain.migration.models.MigrationFlag
|
import mihon.domain.migration.models.MigrationFlag
|
||||||
import mihon.domain.migration.usecases.MigrateMangaUseCase
|
import mihon.domain.migration.usecases.MigrateMangaUseCase
|
||||||
import mihon.feature.common.utils.getLabel
|
import mihon.feature.common.utils.getLabel
|
||||||
@@ -128,7 +130,10 @@ class MigrateDialogViewModel(
|
|||||||
private val coverCache: CoverCache = Injekt.get(),
|
private val coverCache: CoverCache = Injekt.get(),
|
||||||
private val downloadManager: DownloadManager = Injekt.get(),
|
private val downloadManager: DownloadManager = Injekt.get(),
|
||||||
private val migrateManga: MigrateMangaUseCase = Injekt.get(),
|
private val migrateManga: MigrateMangaUseCase = Injekt.get(),
|
||||||
) : StateViewModel<MigrateDialogViewModel.State>(State()) {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<MigrateDialogViewModel.State>
|
||||||
|
field = MutableStateFlow<MigrateDialogViewModel.State>(State())
|
||||||
|
|
||||||
fun init(current: Manga, target: Manga) {
|
fun init(current: Manga, target: Manga) {
|
||||||
val applicableFlags = buildList {
|
val applicableFlags = buildList {
|
||||||
@@ -144,7 +149,7 @@ class MigrateDialogViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val selectedFlags = sourcePreference.migrationFlags.get()
|
val selectedFlags = sourcePreference.migrationFlags.get()
|
||||||
mutableState.update {
|
state.update {
|
||||||
State(
|
State(
|
||||||
current = current,
|
current = current,
|
||||||
target = target,
|
target = target,
|
||||||
@@ -155,7 +160,7 @@ class MigrateDialogViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun toggleSelection(flag: MigrationFlag) {
|
fun toggleSelection(flag: MigrationFlag) {
|
||||||
mutableState.update {
|
state.update {
|
||||||
val selectedFlags = it.selectedFlags.toMutableSet()
|
val selectedFlags = it.selectedFlags.toMutableSet()
|
||||||
.apply { if (contains(flag)) remove(flag) else add(flag) }
|
.apply { if (contains(flag)) remove(flag) else add(flag) }
|
||||||
.toSet()
|
.toSet()
|
||||||
@@ -164,13 +169,13 @@ class MigrateDialogViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun migrateManga(replace: Boolean) {
|
suspend fun migrateManga(replace: Boolean) {
|
||||||
val state = state.value
|
val currentState = state.value
|
||||||
val current = state.current ?: return
|
val current = currentState.current ?: return
|
||||||
val target = state.target ?: return
|
val target = currentState.target ?: return
|
||||||
sourcePreference.migrationFlags.set(state.selectedFlags)
|
sourcePreference.migrationFlags.set(currentState.selectedFlags)
|
||||||
mutableState.update { it.copy(isMigrating = true) }
|
state.update { it.copy(isMigrating = true) }
|
||||||
migrateManga(current, target, replace)
|
migrateManga(current, target, replace)
|
||||||
mutableState.update { it.copy(isMigrating = false, isMigrated = true) }
|
state.update { it.copy(isMigrating = false, isMigrated = true) }
|
||||||
}
|
}
|
||||||
|
|
||||||
data class State(
|
data class State(
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package mihon.feature.migration.list
|
package mihon.feature.migration.list
|
||||||
|
|
||||||
import androidx.annotation.FloatRange
|
import androidx.annotation.FloatRange
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.CreationExtras
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
import androidx.lifecycle.viewmodel.initializer
|
import androidx.lifecycle.viewmodel.initializer
|
||||||
@@ -16,13 +17,14 @@ import kotlinx.coroutines.cancel
|
|||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.currentCoroutineContext
|
import kotlinx.coroutines.currentCoroutineContext
|
||||||
import kotlinx.coroutines.ensureActive
|
import kotlinx.coroutines.ensureActive
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.receiveAsFlow
|
import kotlinx.coroutines.flow.receiveAsFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.sync.Semaphore
|
import kotlinx.coroutines.sync.Semaphore
|
||||||
import kotlinx.coroutines.sync.withPermit
|
import kotlinx.coroutines.sync.withPermit
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import mihon.core.viewmodel.StateViewModel
|
|
||||||
import mihon.domain.migration.usecases.MigrateMangaUseCase
|
import mihon.domain.migration.usecases.MigrateMangaUseCase
|
||||||
import mihon.domain.source.interactor.UpdateMangaFromRemote
|
import mihon.domain.source.interactor.UpdateMangaFromRemote
|
||||||
import mihon.feature.migration.list.models.MigratingManga
|
import mihon.feature.migration.list.models.MigratingManga
|
||||||
@@ -49,7 +51,10 @@ class MigrationListViewModel(
|
|||||||
private val getChaptersByMangaId: GetChaptersByMangaId = Injekt.get(),
|
private val getChaptersByMangaId: GetChaptersByMangaId = Injekt.get(),
|
||||||
private val migrateManga: MigrateMangaUseCase = Injekt.get(),
|
private val migrateManga: MigrateMangaUseCase = Injekt.get(),
|
||||||
private val updateMangaFromRemote: UpdateMangaFromRemote = Injekt.get(),
|
private val updateMangaFromRemote: UpdateMangaFromRemote = Injekt.get(),
|
||||||
) : StateViewModel<MigrationListViewModel.State>(State()) {
|
) : ViewModel() {
|
||||||
|
|
||||||
|
val state: StateFlow<MigrationListViewModel.State>
|
||||||
|
field = MutableStateFlow<MigrationListViewModel.State>(State())
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val MANGA_IDS_KEY = CreationExtras.Key<Collection<Long>>()
|
val MANGA_IDS_KEY = CreationExtras.Key<Collection<Long>>()
|
||||||
@@ -97,7 +102,7 @@ class MigrationListViewModel(
|
|||||||
}
|
}
|
||||||
.awaitAll()
|
.awaitAll()
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
mutableState.update { it.copy(items = manga) }
|
state.update { it.copy(items = manga) }
|
||||||
runMigrations(manga)
|
runMigrations(manga)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,7 +220,7 @@ class MigrationListViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun updateMigrationProgress() {
|
private suspend fun updateMigrationProgress() {
|
||||||
mutableState.update { state ->
|
state.update { state ->
|
||||||
state.copy(
|
state.copy(
|
||||||
finishedCount = items.count { it.searchResult.value != SearchResult.Searching },
|
finishedCount = items.count { it.searchResult.value != SearchResult.Searching },
|
||||||
migrationComplete = migrationComplete(),
|
migrationComplete = migrationComplete(),
|
||||||
@@ -265,7 +270,7 @@ class MigrationListViewModel(
|
|||||||
|
|
||||||
private fun migrateMangas(replace: Boolean) {
|
private fun migrateMangas(replace: Boolean) {
|
||||||
migrateJob = viewModelScope.launchIO {
|
migrateJob = viewModelScope.launchIO {
|
||||||
mutableState.update { it.copy(dialog = Dialog.Progress(0f)) }
|
state.update { it.copy(dialog = Dialog.Progress(0f)) }
|
||||||
val items = items
|
val items = items
|
||||||
try {
|
try {
|
||||||
items.forEachIndexed { index, manga ->
|
items.forEachIndexed { index, manga ->
|
||||||
@@ -285,14 +290,14 @@ class MigrationListViewModel(
|
|||||||
if (e is CancellationException) throw e
|
if (e is CancellationException) throw e
|
||||||
logcat(LogPriority.WARN, throwable = e)
|
logcat(LogPriority.WARN, throwable = e)
|
||||||
}
|
}
|
||||||
mutableState.update {
|
state.update {
|
||||||
it.copy(dialog = Dialog.Progress((index.toFloat() / items.size).coerceAtMost(1f)))
|
it.copy(dialog = Dialog.Progress((index.toFloat() / items.size).coerceAtMost(1f)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
navigateBack()
|
navigateBack()
|
||||||
} finally {
|
} finally {
|
||||||
mutableState.update { it.copy(dialog = null) }
|
state.update { it.copy(dialog = null) }
|
||||||
migrateJob = null
|
migrateJob = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,7 +332,7 @@ class MigrationListViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun removeManga(item: MigratingManga) {
|
private fun removeManga(item: MigratingManga) {
|
||||||
mutableState.update { it.copy(items = items.toMutableList().apply { remove(item) }) }
|
state.update { it.copy(items = items.toMutableList().apply { remove(item) }) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
@@ -337,7 +342,7 @@ class MigrationListViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun showMigrateDialog(copy: Boolean) {
|
fun showMigrateDialog(copy: Boolean) {
|
||||||
mutableState.update { state ->
|
state.update { state ->
|
||||||
state.copy(
|
state.copy(
|
||||||
dialog = Dialog.Migrate(
|
dialog = Dialog.Migrate(
|
||||||
copy = copy,
|
copy = copy,
|
||||||
@@ -349,13 +354,13 @@ class MigrationListViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun showExitDialog() {
|
fun showExitDialog() {
|
||||||
mutableState.update {
|
state.update {
|
||||||
it.copy(dialog = Dialog.Exit)
|
it.copy(dialog = Dialog.Exit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dismissDialog() {
|
fun dismissDialog() {
|
||||||
mutableState.update { it.copy(dialog = null) }
|
state.update { it.copy(dialog = null) }
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ChapterInfo(
|
data class ChapterInfo(
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
plugins {
|
|
||||||
alias(mihonx.plugins.android.library)
|
|
||||||
alias(mihonx.plugins.spotless)
|
|
||||||
|
|
||||||
alias(libs.plugins.kotlin.serialization)
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "mihon.core.viewmodel"
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(libs.androidx.lifecycle.viewmodel)
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest />
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package mihon.core.viewmodel
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
|
||||||
|
|
||||||
abstract class StateViewModel<S>(initialState: S) : ViewModel() {
|
|
||||||
protected val mutableState: MutableStateFlow<S> = MutableStateFlow(initialState)
|
|
||||||
val state: StateFlow<S> = mutableState.asStateFlow()
|
|
||||||
}
|
|
||||||
@@ -34,7 +34,6 @@ include(":baseline-profile")
|
|||||||
include(":core-metadata")
|
include(":core-metadata")
|
||||||
include(":core:archive")
|
include(":core:archive")
|
||||||
include(":core:common")
|
include(":core:common")
|
||||||
include(":core:viewmodel")
|
|
||||||
include(":data")
|
include(":data")
|
||||||
include(":domain")
|
include(":domain")
|
||||||
include(":i18n")
|
include(":i18n")
|
||||||
|
|||||||
Reference in New Issue
Block a user