Replace StateViewModel with explicit backing fields (#3763)

Assisted-by: Claude:claude-opus-5
This commit is contained in:
AntsyLich
2026-08-15 01:53:06 +06:00
committed by GitHub
parent d00efa4bfc
commit b1efee47f5
21 changed files with 184 additions and 127 deletions
@@ -30,6 +30,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastMap
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
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.util.Screen
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.update
import mihon.core.viewmodel.StateViewModel
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.core.common.util.lang.launchUI
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 database: Database = Injekt.get()
@@ -228,7 +234,7 @@ class ClearDatabaseViewModel : StateViewModel<ClearDatabaseViewModel.State>(Stat
viewModelScope.launchIO {
getSourcesWithNonLibraryManga.subscribe()
.collectLatest { list ->
mutableState.update { old ->
state.update { old ->
val items = list.sortedBy { it.name }
when (old) {
State.Loading -> State.Ready(items)
@@ -245,7 +251,7 @@ class ClearDatabaseViewModel : StateViewModel<ClearDatabaseViewModel.State>(Stat
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
val mutableList = state.selection.toMutableList()
if (mutableList.contains(source.id)) {
@@ -256,17 +262,17 @@ class ClearDatabaseViewModel : StateViewModel<ClearDatabaseViewModel.State>(Stat
state.copy(selection = mutableList)
}
fun clearSelection() = mutableState.update { state ->
fun clearSelection() = state.update { state ->
if (state !is State.Ready) return@update state
state.copy(selection = emptyList())
}
fun selectAll() = mutableState.update { state ->
fun selectAll() = state.update { state ->
if (state !is State.Ready) return@update state
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
state.copy(
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
state.copy(showConfirmation = true)
}
fun hideConfirmation() = mutableState.update { state ->
fun hideConfirmation() = state.update { state ->
if (state !is State.Ready) return@update state
state.copy(showConfirmation = false)
}
@@ -11,6 +11,7 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import cafe.adriel.voyager.navigator.LocalNavigator
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.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import mihon.core.viewmodel.StateViewModel
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.LabeledCheckbox
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) {
mutableState.update {
state.update {
it.copy(
options = setter(it.options, enabled),
)
@@ -20,6 +20,7 @@ import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import androidx.core.net.toUri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.compose.viewModel
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.RestoreOptions
import eu.kanade.tachiyomi.util.system.DeviceUtil
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import mihon.core.viewmodel.StateViewModel
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.LabeledCheckbox
import tachiyomi.presentation.core.components.LazyColumnWithAction
@@ -176,7 +178,10 @@ class RestoreBackupScreen(
class RestoreBackupViewModel(
private val context: Context,
private val uri: String,
) : StateViewModel<RestoreBackupViewModel.State>(State()) {
) : ViewModel() {
val state: StateFlow<RestoreBackupViewModel.State>
field = MutableStateFlow<RestoreBackupViewModel.State>(State())
companion object {
val URI_KEY = CreationExtras.Key<String>()
@@ -196,7 +201,7 @@ class RestoreBackupViewModel(
}
fun toggle(setter: (RestoreOptions, Boolean) -> RestoreOptions, enabled: Boolean) {
mutableState.update {
state.update {
it.copy(
options = setter(it.options, enabled),
)
@@ -234,7 +239,7 @@ class RestoreBackupViewModel(
}
private fun setError(error: Any?, canRestore: Boolean) {
mutableState.update {
state.update {
it.copy(
error = error,
canRestore = canRestore,
@@ -8,7 +8,6 @@ import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchViewModel
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import tachiyomi.domain.manga.interactor.GetManga
import tachiyomi.domain.source.service.SourceManager
@@ -46,7 +45,7 @@ class MigrateSearchViewModel(
init {
viewModelScope.launch {
val manga = getManga.await(mangaId)!!
mutableState.update {
updateState {
it.copy(
from = manga,
searchQuery = manga.title,
@@ -6,6 +6,7 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
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.source.model.FilterList
import eu.kanade.tachiyomi.util.removeCovers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.firstOrNull
@@ -31,7 +34,6 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import mihon.core.viewmodel.StateViewModel
import tachiyomi.core.common.preference.CheckboxState
import tachiyomi.core.common.preference.mapAsCheckboxState
import tachiyomi.core.common.util.lang.launchIO
@@ -68,7 +70,10 @@ class BrowseSourceViewModel(
private val updateManga: UpdateManga = Injekt.get(),
private val addTracks: AddTracks = 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 {
val SOURCE_ID_KEY = CreationExtras.Key<Long>()
@@ -89,7 +94,7 @@ class BrowseSourceViewModel(
val source = sourceManager.getOrStub(sourceId)
init {
mutableState.update {
state.update {
var query: String? = null
var listing = it.listing
@@ -142,15 +147,15 @@ class BrowseSourceViewModel(
}
fun resetFilters() {
mutableState.update { it.copy(filters = source.getFilterList()) }
state.update { it.copy(filters = source.getFilterList()) }
}
fun setListing(listing: Listing) {
mutableState.update { it.copy(listing = listing, toolbarQuery = null) }
state.update { it.copy(listing = listing, toolbarQuery = null) }
}
fun setFilters(filters: FilterList) {
mutableState.update {
state.update {
it.copy(
filters = filters,
)
@@ -161,7 +166,7 @@ class BrowseSourceViewModel(
val input = state.value.listing as? Listing.Search
?: Listing.Search(query = null, filters = source.getFilterList())
mutableState.update {
state.update {
it.copy(
listing = input.copy(
query = query ?: input.query,
@@ -201,7 +206,7 @@ class BrowseSourceViewModel(
}
}
mutableState.update {
state.update {
val listing = if (genreExists) {
Listing.Search(query = null, filters = defaultFilters)
} else {
@@ -310,11 +315,11 @@ class BrowseSourceViewModel(
}
fun setDialog(dialog: Dialog?) {
mutableState.update { it.copy(dialog = dialog) }
state.update { it.copy(dialog = dialog) }
}
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) {
@@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.browse.source.globalsearch
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.produceState
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.extension.ExtensionManager
@@ -11,13 +12,14 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import mihon.core.viewmodel.StateViewModel
import mihon.domain.manga.model.toDomainManga
import tachiyomi.core.common.preference.toggle
import tachiyomi.core.common.util.lang.launchIO
@@ -37,7 +39,16 @@ abstract class SearchViewModel(
private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
private val getManga: GetManga = 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 var searchJob: Job? = null
@@ -61,8 +72,8 @@ abstract class SearchViewModel(
init {
viewModelScope.launch {
preferences.globalSearchFilterState.changes().collectLatest { state ->
mutableState.update { it.copy(onlyShowHasResults = state) }
preferences.globalSearchFilterState.changes().collectLatest { onlyShowHasResults ->
state.update { it.copy(onlyShowHasResults = onlyShowHasResults) }
}
}
}
@@ -104,11 +115,11 @@ abstract class SearchViewModel(
}
fun updateSearchQuery(query: String?) {
mutableState.update { it.copy(searchQuery = query) }
state.update { it.copy(searchQuery = query) }
}
fun setSourceFilter(filter: SourceFilter) {
mutableState.update { it.copy(sourceFilter = filter) }
state.update { it.copy(sourceFilter = filter) }
search()
}
@@ -178,7 +189,7 @@ abstract class SearchViewModel(
}
private fun updateItems(items: Map<Source, SearchItemResult>) {
mutableState.update {
state.update {
it.copy(
items = items
.toSortedMap(sortComparator(items)),
@@ -193,12 +204,12 @@ abstract class SearchViewModel(
fun setMigrateDialog(currentId: Long, target: Manga) {
viewModelScope.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() {
mutableState.update { it.copy(dialog = null) }
state.update { it.copy(dialog = null) }
}
@Immutable
@@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.ui.deeplink
import androidx.compose.runtime.Immutable
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
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.online.ResolvableSource
import eu.kanade.tachiyomi.source.online.UriType
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import mihon.core.viewmodel.StateViewModel
import mihon.domain.manga.model.toDomainManga
import mihon.domain.source.interactor.UpdateMangaFromRemote
import tachiyomi.core.common.util.lang.launchIO
@@ -28,7 +30,10 @@ class DeepLinkViewModel(
private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
private val getChapterByUrlAndMangaId: GetChapterByUrlAndMangaId = 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 {
val QUERY_KEY = CreationExtras.Key<String>()
@@ -58,7 +63,7 @@ class DeepLinkViewModel(
null
}
mutableState.update {
state.update {
if (manga == null) {
State.NoResults
} else {
@@ -8,6 +8,7 @@ import androidx.compose.material3.SnackbarResult
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.getValue
import androidx.compose.ui.util.fastAny
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
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.system.toast
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
@@ -50,7 +53,6 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import logcat.LogPriority
import mihon.core.viewmodel.StateViewModel
import mihon.domain.chapter.interactor.FilterChaptersForDownload
import mihon.domain.source.interactor.UpdateMangaFromRemote
import tachiyomi.core.common.i18n.stringResource
@@ -116,7 +118,10 @@ class MangaViewModel(
private val filterChaptersForDownload: FilterChaptersForDownload = Injekt.get(),
private val updateMangaFromRemote: UpdateMangaFromRemote = Injekt.get(),
val snackbarHostState: SnackbarHostState = SnackbarHostState(),
) : StateViewModel<MangaViewModel.State>(State.Loading) {
) : ViewModel() {
val state: StateFlow<State>
field = MutableStateFlow<State>(State.Loading)
companion object {
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
*/
private inline fun updateSuccessState(func: (State.Success) -> State.Success) {
mutableState.update {
state.update {
when (it) {
State.Loading -> it
is State.Success -> func(it)
@@ -228,7 +233,7 @@ class MangaViewModel(
val needRefreshChapter = chapters.isEmpty()
// Show what we have earlier
mutableState.update {
state.update {
State.Success(
manga = manga,
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.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.compose.viewModel
@@ -13,8 +14,9 @@ import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.manga.MangaNotesScreen
import eu.kanade.presentation.util.Screen
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import mihon.core.viewmodel.StateViewModel
import tachiyomi.core.common.util.lang.launchNonCancellable
import tachiyomi.domain.manga.interactor.UpdateMangaNotes
import tachiyomi.domain.manga.model.Manga
@@ -46,7 +48,10 @@ class MangaNotesScreen(
class Model(
private val manga: Manga,
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 {
val MANGA_KEY = CreationExtras.Key<Manga>()
@@ -63,7 +68,7 @@ class MangaNotesScreen(
fun updateNotes(content: String) {
if (content == state.value.notes) return
mutableState.update {
state.update {
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.openInBrowser
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.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -68,7 +70,6 @@ import kotlinx.coroutines.launch
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import logcat.LogPriority
import mihon.core.viewmodel.StateViewModel
import tachiyomi.core.common.i18n.stringResource
import tachiyomi.core.common.util.lang.launchNonCancellable
import tachiyomi.core.common.util.lang.withIOContext
@@ -205,7 +206,10 @@ data class TrackInfoDialogHomeScreen(
private val mangaId: Long,
private val sourceId: Long,
private val getTracks: GetTracks = Injekt.get(),
) : StateViewModel<Model.State>(State()) {
) : ViewModel() {
val state: StateFlow<Model.State>
field = MutableStateFlow<Model.State>(State())
companion object {
val MANGA_ID_KEY = CreationExtras.Key<Long>()
@@ -231,7 +235,7 @@ data class TrackInfoDialogHomeScreen(
.catch { logcat(LogPriority.ERROR, it) }
.distinctUntilChanged()
.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(
private val track: Track,
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 {
val TRACK_KEY = CreationExtras.Key<Track>()
@@ -345,7 +352,7 @@ private data class TrackStatusSelectorScreen(
}
fun setSelection(selection: Long) {
mutableState.update { it.copy(selection = selection) }
state.update { it.copy(selection = selection) }
}
fun setStatus() {
@@ -393,7 +400,10 @@ private data class TrackChapterSelectorScreen(
class Model(
private val track: Track,
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 {
val TRACK_KEY = CreationExtras.Key<Track>()
@@ -419,7 +429,7 @@ private data class TrackChapterSelectorScreen(
}
fun setSelection(selection: Int) {
mutableState.update { it.copy(selection = selection) }
state.update { it.copy(selection = selection) }
}
fun setChapter() {
@@ -467,7 +477,10 @@ private data class TrackScoreSelectorScreen(
class Model(
private val track: Track,
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 {
val TRACK_KEY = CreationExtras.Key<Track>()
@@ -488,7 +501,7 @@ private data class TrackScoreSelectorScreen(
}
fun setSelection(selection: String) {
mutableState.update { it.copy(selection = selection) }
state.update { it.copy(selection = selection) }
}
fun setScore() {
@@ -788,7 +801,10 @@ data class TrackerSearchScreen(
private val currentUrl: String?,
initialQuery: String,
private val tracker: Tracker,
) : StateViewModel<Model.State>(State()) {
) : ViewModel() {
val state: StateFlow<Model.State>
field = MutableStateFlow<Model.State>(State())
companion object {
val MANGA_ID_KEY = CreationExtras.Key<Long>()
@@ -820,7 +836,7 @@ data class TrackerSearchScreen(
fun trackingSearch(query: String) {
viewModelScope.launch {
// To show loading state
mutableState.update { it.copy(queryResult = null, selected = null) }
state.update { it.copy(queryResult = null, selected = null) }
val result = withIOContext {
try {
@@ -830,7 +846,7 @@ data class TrackerSearchScreen(
Result.failure(e)
}
}
mutableState.update { oldState ->
state.update { oldState ->
oldState.copy(
queryResult = result,
selected = result.getOrNull()?.find { it.tracking_url == currentUrl },
@@ -844,7 +860,7 @@ data class TrackerSearchScreen(
}
fun updateSelection(selected: TrackSearch) {
mutableState.update { it.copy(selected = selected) }
state.update { it.copy(selected = selected) }
}
@Immutable
@@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.more
import android.content.Context
import android.content.Intent
import androidx.compose.runtime.Immutable
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.initializer
@@ -17,10 +18,11 @@ import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.storage.saveTo
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import logcat.LogPriority
import mihon.core.viewmodel.StateViewModel
import tachiyomi.core.common.util.lang.withIOContext
import tachiyomi.core.common.util.system.logcat
import uy.kohesive.injekt.Injekt
@@ -32,7 +34,10 @@ class NewUpdateScreenModel(
private val downloadLink: String,
private val context: Context = 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
get() = File(context.externalCacheDir, "update.apk")
@@ -43,16 +48,16 @@ class NewUpdateScreenModel(
if (downloadJob?.isActive == true) return
downloadJob = viewModelScope.launch {
mutableState.update { it.copy(downloadProgress = 0, stage = Stage.Downloading) }
state.update { it.copy(downloadProgress = 0, stage = Stage.Downloading) }
try {
withIOContext { downloadApk() }
mutableState.update { it.copy(downloadProgress = 100, stage = Stage.Downloaded) }
state.update { it.copy(downloadProgress = 100, stage = Stage.Downloaded) }
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
logcat(LogPriority.ERROR, e)
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) {
savedProgress = progress
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.fastFilter
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import eu.kanade.core.util.fastCountNot
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.track.TrackerManager
import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import mihon.core.viewmodel.StateViewModel
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.domain.history.interactor.GetTotalReadDuration
import tachiyomi.domain.library.model.LibraryManga
@@ -32,7 +34,10 @@ class StatsViewModel(
private val getTracks: GetTracks = Injekt.get(),
private val preferences: LibraryPreferences = 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() }
@@ -73,7 +78,7 @@ class StatsViewModel(
trackerCount = loggedInTrackers.size,
)
mutableState.update {
state.update {
StatsScreenState.Success(
overview = overviewStatData,
titles = titlesStatData,
@@ -2,17 +2,16 @@ package eu.kanade.tachiyomi.ui.webview
import android.content.Context
import androidx.core.net.toUri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import eu.kanade.presentation.more.stats.StatsScreenState
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.system.openInBrowser
import eu.kanade.tachiyomi.util.system.toShareIntent
import eu.kanade.tachiyomi.util.system.toast
import logcat.LogPriority
import mihon.core.viewmodel.StateViewModel
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.source.service.SourceManager
@@ -23,7 +22,7 @@ class WebViewViewModel(
val sourceId: Long?,
private val sourceManager: SourceManager = Injekt.get(),
private val network: NetworkHelper = Injekt.get(),
) : StateViewModel<StatsScreenState>(StatsScreenState.Loading) {
) : ViewModel() {
companion object {
val SOURCE_ID_KEY = CreationExtras.Key<Long?>()