Minor cleanup

This commit is contained in:
arkon 2023-07-23 20:03:37 -04:00
parent 2556e9f08c
commit abae9bf37d
46 changed files with 341 additions and 331 deletions

View File

@ -72,9 +72,9 @@ class SetReadStatus(
suspend fun await(manga: Manga, read: Boolean) = suspend fun await(manga: Manga, read: Boolean) =
await(manga.id, read) await(manga.id, read)
sealed class Result { sealed interface Result {
data object Success : Result() data object Success : Result
data object NoChapters : Result() data object NoChapters : Result
data class InternalError(val error: Throwable) : Result() data class InternalError(val error: Throwable) : Result
} }
} }

View File

@ -53,7 +53,7 @@ import eu.kanade.presentation.more.settings.widget.TrailingWidgetBuffer
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsState import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsScreenModel
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.material.DIVIDER_ALPHA import tachiyomi.presentation.core.components.material.DIVIDER_ALPHA
@ -65,7 +65,7 @@ import tachiyomi.presentation.core.screens.EmptyScreen
@Composable @Composable
fun ExtensionDetailsScreen( fun ExtensionDetailsScreen(
navigateUp: () -> Unit, navigateUp: () -> Unit,
state: ExtensionDetailsState, state: ExtensionDetailsScreenModel.State,
onClickSourcePreferences: (sourceId: Long) -> Unit, onClickSourcePreferences: (sourceId: Long) -> Unit,
onClickWhatsNew: () -> Unit, onClickWhatsNew: () -> Unit,
onClickReadme: () -> Unit, onClickReadme: () -> Unit,

View File

@ -43,7 +43,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.extension.model.InstallStep import eu.kanade.tachiyomi.extension.model.InstallStep
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionUiModel import eu.kanade.tachiyomi.ui.browse.extension.ExtensionUiModel
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsState import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsScreenModel
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.material.PullRefresh import tachiyomi.presentation.core.components.material.PullRefresh
@ -57,7 +57,7 @@ import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable @Composable
fun ExtensionScreen( fun ExtensionScreen(
state: ExtensionsState, state: ExtensionsScreenModel.State,
contentPadding: PaddingValues, contentPadding: PaddingValues,
searchQuery: String?, searchQuery: String?,
onLongClickItem: (Extension) -> Unit, onLongClickItem: (Extension) -> Unit,
@ -108,7 +108,7 @@ fun ExtensionScreen(
@Composable @Composable
private fun ExtensionContent( private fun ExtensionContent(
state: ExtensionsState, state: ExtensionsScreenModel.State,
contentPadding: PaddingValues, contentPadding: PaddingValues,
onLongClickItem: (Extension) -> Unit, onLongClickItem: (Extension) -> Unit,
onClickItemCancel: (Extension) -> Unit, onClickItemCancel: (Extension) -> Unit,

View File

@ -8,7 +8,7 @@ import androidx.compose.ui.Modifier
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.manga.components.BaseMangaListItem import eu.kanade.presentation.manga.components.BaseMangaListItem
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaState import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaScreenModel
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
@ -18,7 +18,7 @@ import tachiyomi.presentation.core.screens.EmptyScreen
fun MigrateMangaScreen( fun MigrateMangaScreen(
navigateUp: () -> Unit, navigateUp: () -> Unit,
title: String?, title: String?,
state: MigrateMangaState, state: MigrateMangaScreenModel.State,
onClickItem: (Manga) -> Unit, onClickItem: (Manga) -> Unit,
onClickCover: (Manga) -> Unit, onClickCover: (Manga) -> Unit,
) { ) {
@ -51,7 +51,7 @@ fun MigrateMangaScreen(
@Composable @Composable
private fun MigrateMangaContent( private fun MigrateMangaContent(
contentPadding: PaddingValues, contentPadding: PaddingValues,
state: MigrateMangaState, state: MigrateMangaScreenModel.State,
onClickItem: (Manga) -> Unit, onClickItem: (Manga) -> Unit,
onClickCover: (Manga) -> Unit, onClickCover: (Manga) -> Unit,
) { ) {

View File

@ -26,7 +26,7 @@ import eu.kanade.domain.source.interactor.SetMigrateSorting
import eu.kanade.presentation.browse.components.BaseSourceItem import eu.kanade.presentation.browse.components.BaseSourceItem
import eu.kanade.presentation.browse.components.SourceIcon import eu.kanade.presentation.browse.components.SourceIcon
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrateSourceState import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrateSourceScreenModel
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.domain.source.model.Source import tachiyomi.domain.source.model.Source
import tachiyomi.presentation.core.components.Badge import tachiyomi.presentation.core.components.Badge
@ -43,7 +43,7 @@ import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable @Composable
fun MigrateSourceScreen( fun MigrateSourceScreen(
state: MigrateSourceState, state: MigrateSourceScreenModel.State,
contentPadding: PaddingValues, contentPadding: PaddingValues,
onClickItem: (Source) -> Unit, onClickItem: (Source) -> Unit,
onToggleSortingDirection: () -> Unit, onToggleSortingDirection: () -> Unit,

View File

@ -12,7 +12,7 @@ import eu.kanade.presentation.browse.components.BaseSourceItem
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterState import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterScreenModel
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.source.model.Source import tachiyomi.domain.source.model.Source
import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.FastScrollLazyColumn
@ -22,7 +22,7 @@ import tachiyomi.presentation.core.screens.EmptyScreen
@Composable @Composable
fun SourcesFilterScreen( fun SourcesFilterScreen(
navigateUp: () -> Unit, navigateUp: () -> Unit,
state: SourcesFilterState.Success, state: SourcesFilterScreenModel.State.Success,
onClickLanguage: (String) -> Unit, onClickLanguage: (String) -> Unit,
onClickSource: (Source) -> Unit, onClickSource: (Source) -> Unit,
) { ) {
@ -54,7 +54,7 @@ fun SourcesFilterScreen(
@Composable @Composable
private fun SourcesFilterContent( private fun SourcesFilterContent(
contentPadding: PaddingValues, contentPadding: PaddingValues,
state: SourcesFilterState.Success, state: SourcesFilterScreenModel.State.Success,
onClickLanguage: (String) -> Unit, onClickLanguage: (String) -> Unit,
onClickSource: (Source) -> Unit, onClickSource: (Source) -> Unit,
) { ) {

View File

@ -192,7 +192,7 @@ fun SourceOptionsDialog(
) )
} }
sealed class SourceUiModel { sealed interface SourceUiModel {
data class Item(val source: Source) : SourceUiModel() data class Item(val source: Source) : SourceUiModel
data class Header(val language: String) : SourceUiModel() data class Header(val language: String) : SourceUiModel
} }

View File

@ -20,7 +20,6 @@ import eu.kanade.presentation.components.SearchToolbar
import eu.kanade.presentation.history.components.HistoryItem import eu.kanade.presentation.history.components.HistoryItem
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.history.HistoryScreenModel import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
import eu.kanade.tachiyomi.ui.history.HistoryState
import tachiyomi.domain.history.model.HistoryWithRelations import tachiyomi.domain.history.model.HistoryWithRelations
import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.Scaffold
@ -33,7 +32,7 @@ import java.util.Date
@Composable @Composable
fun HistoryScreen( fun HistoryScreen(
state: HistoryState, state: HistoryScreenModel.State,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
onSearchQueryChange: (String?) -> Unit, onSearchQueryChange: (String?) -> Unit,
onClickCover: (mangaId: Long) -> Unit, onClickCover: (mangaId: Long) -> Unit,
@ -139,7 +138,7 @@ private fun HistoryScreenContent(
} }
} }
sealed class HistoryUiModel { sealed interface HistoryUiModel {
data class Header(val date: Date) : HistoryUiModel() data class Header(val date: Date) : HistoryUiModel
data class Item(val item: HistoryWithRelations) : HistoryUiModel() data class Item(val item: HistoryWithRelations) : HistoryUiModel
} }

View File

@ -62,7 +62,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.source.getNameForMangaInfo import eu.kanade.tachiyomi.source.getNameForMangaInfo
import eu.kanade.tachiyomi.ui.manga.ChapterItem import eu.kanade.tachiyomi.ui.manga.ChapterItem
import eu.kanade.tachiyomi.ui.manga.MangaScreenState import eu.kanade.tachiyomi.ui.manga.MangaScreenModel
import eu.kanade.tachiyomi.util.lang.toRelativeString import eu.kanade.tachiyomi.util.lang.toRelativeString
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter
@ -82,7 +82,7 @@ import java.util.Date
@Composable @Composable
fun MangaScreen( fun MangaScreen(
state: MangaScreenState.Success, state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
dateRelativeTime: Int, dateRelativeTime: Int,
dateFormat: DateFormat, dateFormat: DateFormat,
@ -210,7 +210,7 @@ fun MangaScreen(
@Composable @Composable
private fun MangaScreenSmallImpl( private fun MangaScreenSmallImpl(
state: MangaScreenState.Success, state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
dateRelativeTime: Int, dateRelativeTime: Int,
dateFormat: DateFormat, dateFormat: DateFormat,
@ -436,7 +436,7 @@ private fun MangaScreenSmallImpl(
@Composable @Composable
fun MangaScreenLargeImpl( fun MangaScreenLargeImpl(
state: MangaScreenState.Success, state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
dateRelativeTime: Int, dateRelativeTime: Int,
dateFormat: DateFormat, dateFormat: DateFormat,

View File

@ -19,6 +19,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
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.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@ -269,12 +270,15 @@ private class ClearDatabaseScreenModel : StateScreenModel<ClearDatabaseScreenMod
state.copy(showConfirmation = false) state.copy(showConfirmation = false)
} }
sealed class State { sealed interface State {
data object Loading : State() @Immutable
data object Loading : State
@Immutable
data class Ready( data class Ready(
val items: List<SourceWithCount>, val items: List<SourceWithCount>,
val selection: List<Long> = emptyList(), val selection: List<Long> = emptyList(),
val showConfirmation: Boolean = false, val showConfirmation: Boolean = false,
) : State() ) : State
} }
} }

View File

@ -3,9 +3,9 @@ package eu.kanade.presentation.more.stats
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import eu.kanade.presentation.more.stats.data.StatsData import eu.kanade.presentation.more.stats.data.StatsData
sealed class StatsScreenState { sealed interface StatsScreenState {
@Immutable @Immutable
data object Loading : StatsScreenState() data object Loading : StatsScreenState
@Immutable @Immutable
data class Success( data class Success(
@ -13,5 +13,5 @@ sealed class StatsScreenState {
val titles: StatsData.Titles, val titles: StatsData.Titles,
val chapters: StatsData.Chapters, val chapters: StatsData.Chapters,
val trackers: StatsData.Trackers, val trackers: StatsData.Trackers,
) : StatsScreenState() ) : StatsScreenState
} }

View File

@ -1,28 +1,28 @@
package eu.kanade.presentation.more.stats.data package eu.kanade.presentation.more.stats.data
sealed class StatsData { sealed interface StatsData {
data class Overview( data class Overview(
val libraryMangaCount: Int, val libraryMangaCount: Int,
val completedMangaCount: Int, val completedMangaCount: Int,
val totalReadDuration: Long, val totalReadDuration: Long,
) : StatsData() ) : StatsData
data class Titles( data class Titles(
val globalUpdateItemCount: Int, val globalUpdateItemCount: Int,
val startedMangaCount: Int, val startedMangaCount: Int,
val localMangaCount: Int, val localMangaCount: Int,
) : StatsData() ) : StatsData
data class Chapters( data class Chapters(
val totalChapterCount: Int, val totalChapterCount: Int,
val readChapterCount: Int, val readChapterCount: Int,
val downloadCount: Int, val downloadCount: Int,
) : StatsData() ) : StatsData
data class Trackers( data class Trackers(
val trackedTitleCount: Int, val trackedTitleCount: Int,
val meanScore: Double, val meanScore: Double,
val trackerCount: Int, val trackerCount: Int,
) : StatsData() ) : StatsData
} }

View File

@ -28,7 +28,7 @@ import eu.kanade.presentation.manga.components.MangaBottomActionMenu
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.updates.UpdatesItem import eu.kanade.tachiyomi.ui.updates.UpdatesItem
import eu.kanade.tachiyomi.ui.updates.UpdatesState import eu.kanade.tachiyomi.ui.updates.UpdatesScreenModel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.FastScrollLazyColumn
@ -40,7 +40,7 @@ import kotlin.time.Duration.Companion.seconds
@Composable @Composable
fun UpdateScreen( fun UpdateScreen(
state: UpdatesState, state: UpdatesScreenModel.State,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
lastUpdated: Long, lastUpdated: Long,
relativeTime: Int, relativeTime: Int,
@ -209,7 +209,7 @@ private fun UpdatesBottomBar(
) )
} }
sealed class UpdatesUiModel { sealed interface UpdatesUiModel {
data class Header(val date: String) : UpdatesUiModel() data class Header(val date: String) : UpdatesUiModel
data class Item(val item: UpdatesItem) : UpdatesUiModel() data class Item(val item: UpdatesItem) : UpdatesUiModel
} }

View File

@ -152,8 +152,8 @@ sealed class Image(
} }
} }
sealed class Location { sealed interface Location {
data class Pictures private constructor(val relativePath: String) : Location() { data class Pictures private constructor(val relativePath: String) : Location {
companion object { companion object {
fun create(relativePath: String = ""): Pictures { fun create(relativePath: String = ""): Pictures {
return Pictures(relativePath) return Pictures(relativePath)
@ -161,7 +161,7 @@ sealed class Location {
} }
} }
data object Cache : Location() data object Cache : Location
fun directory(context: Context): File { fun directory(context: Context): File {
return when (this) { return when (this) {

View File

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.extension.model package eu.kanade.tachiyomi.extension.model
sealed class LoadResult { sealed interface LoadResult {
data class Success(val extension: Extension.Installed) : LoadResult() data class Success(val extension: Extension.Installed) : LoadResult
data class Untrusted(val extension: Extension.Untrusted) : LoadResult() data class Untrusted(val extension: Extension.Untrusted) : LoadResult
data object Error : LoadResult() data object Error : LoadResult
} }

View File

@ -54,20 +54,20 @@ class ExtensionFilterScreenModel(
} }
} }
sealed class ExtensionFilterEvent { sealed interface ExtensionFilterEvent {
data object FailedFetchingLanguages : ExtensionFilterEvent() data object FailedFetchingLanguages : ExtensionFilterEvent
} }
sealed class ExtensionFilterState { sealed interface ExtensionFilterState {
@Immutable @Immutable
data object Loading : ExtensionFilterState() data object Loading : ExtensionFilterState
@Immutable @Immutable
data class Success( data class Success(
val languages: List<String>, val languages: List<String>,
val enabledLanguages: Set<String> = emptySet(), val enabledLanguages: Set<String> = emptySet(),
) : ExtensionFilterState() { ) : ExtensionFilterState {
val isEmpty: Boolean val isEmpty: Boolean
get() = languages.isEmpty() get() = languages.isEmpty()

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.browse.extension
import android.app.Application import android.app.Application
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.extension.interactor.GetExtensionsByType import eu.kanade.domain.extension.interactor.GetExtensionsByType
@ -35,7 +36,7 @@ class ExtensionsScreenModel(
preferences: SourcePreferences = Injekt.get(), preferences: SourcePreferences = Injekt.get(),
private val extensionManager: ExtensionManager = Injekt.get(), private val extensionManager: ExtensionManager = Injekt.get(),
private val getExtensions: GetExtensionsByType = Injekt.get(), private val getExtensions: GetExtensionsByType = Injekt.get(),
) : StateScreenModel<ExtensionsState>(ExtensionsState()) { ) : StateScreenModel<ExtensionsScreenModel.State>(State()) {
private var _currentDownloads = MutableStateFlow<Map<String, InstallStep>>(hashMapOf()) private var _currentDownloads = MutableStateFlow<Map<String, InstallStep>>(hashMapOf())
@ -190,16 +191,17 @@ class ExtensionsScreenModel(
fun trustSignature(signatureHash: String) { fun trustSignature(signatureHash: String) {
extensionManager.trustSignature(signatureHash) extensionManager.trustSignature(signatureHash)
} }
}
data class ExtensionsState( @Immutable
val isLoading: Boolean = true, data class State(
val isRefreshing: Boolean = false, val isLoading: Boolean = true,
val items: ItemGroups = mutableMapOf(), val isRefreshing: Boolean = false,
val updates: Int = 0, val items: ItemGroups = mutableMapOf(),
val searchQuery: String? = null, val updates: Int = 0,
) { val searchQuery: String? = null,
val isEmpty = items.isEmpty() ) {
val isEmpty = items.isEmpty()
}
} }
typealias ItemGroups = MutableMap<ExtensionUiModel.Header, List<ExtensionUiModel.Item>> typealias ItemGroups = MutableMap<ExtensionUiModel.Header, List<ExtensionUiModel.Item>>

View File

@ -38,7 +38,7 @@ class ExtensionDetailsScreenModel(
private val extensionManager: ExtensionManager = Injekt.get(), private val extensionManager: ExtensionManager = Injekt.get(),
private val getExtensionSources: GetExtensionSources = Injekt.get(), private val getExtensionSources: GetExtensionSources = Injekt.get(),
private val toggleSource: ToggleSource = Injekt.get(), private val toggleSource: ToggleSource = Injekt.get(),
) : StateScreenModel<ExtensionDetailsState>(ExtensionDetailsState()) { ) : StateScreenModel<ExtensionDetailsScreenModel.State>(State()) {
private val _events: Channel<ExtensionDetailsEvent> = Channel() private val _events: Channel<ExtensionDetailsEvent> = Channel()
val events: Flow<ExtensionDetailsEvent> = _events.receiveAsFlow() val events: Flow<ExtensionDetailsEvent> = _events.receiveAsFlow()
@ -160,21 +160,21 @@ class ExtensionDetailsScreenModel(
url + "/src/" + pkgName.replace(".", "/") + path url + "/src/" + pkgName.replace(".", "/") + path
} }
} }
@Immutable
data class State(
val extension: Extension.Installed? = null,
private val _sources: List<ExtensionSourceItem>? = null,
) {
val sources: List<ExtensionSourceItem>
get() = _sources.orEmpty()
val isLoading: Boolean
get() = extension == null || _sources == null
}
} }
sealed class ExtensionDetailsEvent { sealed interface ExtensionDetailsEvent {
data object Uninstalled : ExtensionDetailsEvent() data object Uninstalled : ExtensionDetailsEvent
}
@Immutable
data class ExtensionDetailsState(
val extension: Extension.Installed? = null,
private val _sources: List<ExtensionSourceItem>? = null,
) {
val sources: List<ExtensionSourceItem>
get() = _sources.orEmpty()
val isLoading: Boolean
get() = extension == null || _sources == null
} }

View File

@ -17,7 +17,7 @@ import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.screens.LoadingScreen
data class MigrationMangaScreen( data class MigrateMangaScreen(
private val sourceId: Long, private val sourceId: Long,
) : Screen() { ) : Screen() {
@ -25,7 +25,7 @@ data class MigrationMangaScreen(
override fun Content() { override fun Content() {
val context = LocalContext.current val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow val navigator = LocalNavigator.currentOrThrow
val screenModel = rememberScreenModel { MigrationMangaScreenModel(sourceId) } val screenModel = rememberScreenModel { MigrateMangaScreenModel(sourceId) }
val state by screenModel.state.collectAsState() val state by screenModel.state.collectAsState()

View File

@ -20,11 +20,11 @@ import tachiyomi.domain.source.service.SourceManager
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
class MigrationMangaScreenModel( class MigrateMangaScreenModel(
private val sourceId: Long, private val sourceId: Long,
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(),
private val getFavorites: GetFavorites = Injekt.get(), private val getFavorites: GetFavorites = Injekt.get(),
) : StateScreenModel<MigrateMangaState>(MigrateMangaState()) { ) : StateScreenModel<MigrateMangaScreenModel.State>(State()) {
private val _events: Channel<MigrationMangaEvent> = Channel() private val _events: Channel<MigrationMangaEvent> = Channel()
val events: Flow<MigrationMangaEvent> = _events.receiveAsFlow() val events: Flow<MigrationMangaEvent> = _events.receiveAsFlow()
@ -51,24 +51,24 @@ class MigrationMangaScreenModel(
} }
} }
} }
@Immutable
data class State(
val source: Source? = null,
private val titleList: List<Manga>? = null,
) {
val titles: List<Manga>
get() = titleList.orEmpty()
val isLoading: Boolean
get() = source == null || titleList == null
val isEmpty: Boolean
get() = titles.isEmpty()
}
} }
sealed class MigrationMangaEvent { sealed interface MigrationMangaEvent {
data object FailedFetchingFavorites : MigrationMangaEvent() data object FailedFetchingFavorites : MigrationMangaEvent
}
@Immutable
data class MigrateMangaState(
val source: Source? = null,
private val titleList: List<Manga>? = null,
) {
val titles: List<Manga>
get() = titleList.orEmpty()
val isLoading: Boolean
get() = source == null || titleList == null
val isEmpty: Boolean
get() = titles.isEmpty()
} }

View File

@ -37,7 +37,7 @@ class MigrateSearchScreenDialogScreenModel(
val dialog: Dialog? = null, val dialog: Dialog? = null,
) )
sealed class Dialog { sealed interface Dialog {
data class Migrate(val manga: Manga) : Dialog() data class Migrate(val manga: Manga) : Dialog
} }
} }

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.ui.browse.migration.sources package eu.kanade.tachiyomi.ui.browse.migration.sources
import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
@ -23,7 +24,7 @@ class MigrateSourceScreenModel(
preferences: SourcePreferences = Injekt.get(), preferences: SourcePreferences = Injekt.get(),
private val getSourcesWithFavoriteCount: GetSourcesWithFavoriteCount = Injekt.get(), private val getSourcesWithFavoriteCount: GetSourcesWithFavoriteCount = Injekt.get(),
private val setMigrateSorting: SetMigrateSorting = Injekt.get(), private val setMigrateSorting: SetMigrateSorting = Injekt.get(),
) : StateScreenModel<MigrateSourceState>(MigrateSourceState()) { ) : StateScreenModel<MigrateSourceScreenModel.State>(State()) {
private val _channel = Channel<Event>(Int.MAX_VALUE) private val _channel = Channel<Event>(Int.MAX_VALUE)
val channel = _channel.receiveAsFlow() val channel = _channel.receiveAsFlow()
@ -76,16 +77,17 @@ class MigrateSourceScreenModel(
} }
} }
sealed class Event { @Immutable
data object FailedFetchingSourcesWithCount : Event() data class State(
val isLoading: Boolean = true,
val items: List<Pair<Source, Long>> = emptyList(),
val sortingMode: SetMigrateSorting.Mode = SetMigrateSorting.Mode.ALPHABETICAL,
val sortingDirection: SetMigrateSorting.Direction = SetMigrateSorting.Direction.ASCENDING,
) {
val isEmpty = items.isEmpty()
}
sealed interface Event {
data object FailedFetchingSourcesWithCount : Event
} }
} }
data class MigrateSourceState(
val isLoading: Boolean = true,
val items: List<Pair<Source, Long>> = emptyList(),
val sortingMode: SetMigrateSorting.Mode = SetMigrateSorting.Mode.ALPHABETICAL,
val sortingDirection: SetMigrateSorting.Direction = SetMigrateSorting.Direction.ASCENDING,
) {
val isEmpty = items.isEmpty()
}

View File

@ -15,7 +15,7 @@ import eu.kanade.presentation.browse.MigrateSourceScreen
import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.TabContent import eu.kanade.presentation.components.TabContent
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrationMangaScreen import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaScreen
@Composable @Composable
fun Screen.migrateSourceTab(): TabContent { fun Screen.migrateSourceTab(): TabContent {
@ -40,7 +40,7 @@ fun Screen.migrateSourceTab(): TabContent {
state = state, state = state,
contentPadding = contentPadding, contentPadding = contentPadding,
onClickItem = { source -> onClickItem = { source ->
navigator.push(MigrationMangaScreen(source.id)) navigator.push(MigrateMangaScreen(source.id))
}, },
onToggleSortingDirection = screenModel::toggleSortingDirection, onToggleSortingDirection = screenModel::toggleSortingDirection,
onToggleSortingMode = screenModel::toggleSortingMode, onToggleSortingMode = screenModel::toggleSortingMode,

View File

@ -22,12 +22,12 @@ class SourcesFilterScreen : Screen() {
val screenModel = rememberScreenModel { SourcesFilterScreenModel() } val screenModel = rememberScreenModel { SourcesFilterScreenModel() }
val state by screenModel.state.collectAsState() val state by screenModel.state.collectAsState()
if (state is SourcesFilterState.Loading) { if (state is SourcesFilterScreenModel.State.Loading) {
LoadingScreen() LoadingScreen()
return return
} }
if (state is SourcesFilterState.Error) { if (state is SourcesFilterScreenModel.State.Error) {
val context = LocalContext.current val context = LocalContext.current
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
context.toast(R.string.internal_error) context.toast(R.string.internal_error)
@ -36,7 +36,7 @@ class SourcesFilterScreen : Screen() {
return return
} }
val successState = state as SourcesFilterState.Success val successState = state as SourcesFilterScreenModel.State.Success
SourcesFilterScreen( SourcesFilterScreen(
navigateUp = navigator::pop, navigateUp = navigator::pop,

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.ui.browse.source package eu.kanade.tachiyomi.ui.browse.source
import androidx.compose.runtime.Immutable
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.source.interactor.GetLanguagesWithSources import eu.kanade.domain.source.interactor.GetLanguagesWithSources
@ -21,7 +22,7 @@ class SourcesFilterScreenModel(
private val getLanguagesWithSources: GetLanguagesWithSources = Injekt.get(), private val getLanguagesWithSources: GetLanguagesWithSources = Injekt.get(),
private val toggleSource: ToggleSource = Injekt.get(), private val toggleSource: ToggleSource = Injekt.get(),
private val toggleLanguage: ToggleLanguage = Injekt.get(), private val toggleLanguage: ToggleLanguage = Injekt.get(),
) : StateScreenModel<SourcesFilterState>(SourcesFilterState.Loading) { ) : StateScreenModel<SourcesFilterScreenModel.State>(State.Loading) {
init { init {
coroutineScope.launch { coroutineScope.launch {
@ -32,14 +33,14 @@ class SourcesFilterScreenModel(
) { a, b, c -> Triple(a, b, c) } ) { a, b, c -> Triple(a, b, c) }
.catch { throwable -> .catch { throwable ->
mutableState.update { mutableState.update {
SourcesFilterState.Error( State.Error(
throwable = throwable, throwable = throwable,
) )
} }
} }
.collectLatest { (languagesWithSources, enabledLanguages, disabledSources) -> .collectLatest { (languagesWithSources, enabledLanguages, disabledSources) ->
mutableState.update { mutableState.update {
SourcesFilterState.Success( State.Success(
items = languagesWithSources, items = languagesWithSources,
enabledLanguages = enabledLanguages, enabledLanguages = enabledLanguages,
disabledSources = disabledSources, disabledSources = disabledSources,
@ -56,23 +57,26 @@ class SourcesFilterScreenModel(
fun toggleLanguage(language: String) { fun toggleLanguage(language: String) {
toggleLanguage.await(language) toggleLanguage.await(language)
} }
}
sealed class SourcesFilterState { sealed interface State {
data object Loading : SourcesFilterState() @Immutable
data object Loading : State
data class Error( @Immutable
val throwable: Throwable, data class Error(
) : SourcesFilterState() val throwable: Throwable,
) : State
data class Success( @Immutable
val items: SortedMap<String, List<Source>>, data class Success(
val enabledLanguages: Set<String>, val items: SortedMap<String, List<Source>>,
val disabledSources: Set<String>, val enabledLanguages: Set<String>,
) : SourcesFilterState() { val disabledSources: Set<String>,
) : State {
val isEmpty: Boolean val isEmpty: Boolean
get() = items.isEmpty() get() = items.isEmpty()
}
} }
} }

View File

@ -93,8 +93,8 @@ class SourcesScreenModel(
mutableState.update { it.copy(dialog = null) } mutableState.update { it.copy(dialog = null) }
} }
sealed class Event { sealed interface Event {
data object FailedFetchingSources : Event() data object FailedFetchingSources : Event
} }
data class Dialog(val source: Source) data class Dialog(val source: Source)

View File

@ -365,15 +365,15 @@ class BrowseSourceScreenModel(
} }
} }
sealed class Dialog { sealed interface Dialog {
data object Filter : Dialog() data object Filter : Dialog
data class RemoveManga(val manga: Manga) : Dialog() data class RemoveManga(val manga: Manga) : Dialog
data class AddDuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog() data class AddDuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog
data class ChangeMangaCategory( data class ChangeMangaCategory(
val manga: Manga, val manga: Manga,
val initialSelection: List<CheckboxState.State<Category>>, val initialSelection: List<CheckboxState.State<Category>>,
) : Dialog() ) : Dialog
data class Migrate(val newManga: Manga) : Dialog() data class Migrate(val newManga: Manga) : Dialog
} }
@Immutable @Immutable

View File

@ -190,16 +190,16 @@ enum class SourceFilter {
PinnedOnly, PinnedOnly,
} }
sealed class SearchItemResult { sealed interface SearchItemResult {
data object Loading : SearchItemResult() data object Loading : SearchItemResult
data class Error( data class Error(
val throwable: Throwable, val throwable: Throwable,
) : SearchItemResult() ) : SearchItemResult
data class Success( data class Success(
val result: List<Manga>, val result: List<Manga>,
) : SearchItemResult() { ) : SearchItemResult {
val isEmpty: Boolean val isEmpty: Boolean
get() = result.isEmpty() get() = result.isEmpty()
} }

View File

@ -107,27 +107,27 @@ class CategoryScreenModel(
} }
} }
sealed class CategoryDialog { sealed interface CategoryDialog {
data object Create : CategoryDialog() data object Create : CategoryDialog
data class Rename(val category: Category) : CategoryDialog() data class Rename(val category: Category) : CategoryDialog
data class Delete(val category: Category) : CategoryDialog() data class Delete(val category: Category) : CategoryDialog
} }
sealed class CategoryEvent { sealed interface CategoryEvent {
sealed class LocalizedMessage(@StringRes val stringRes: Int) : CategoryEvent() sealed class LocalizedMessage(@StringRes val stringRes: Int) : CategoryEvent
data object InternalError : LocalizedMessage(R.string.internal_error) data object InternalError : LocalizedMessage(R.string.internal_error)
} }
sealed class CategoryScreenState { sealed interface CategoryScreenState {
@Immutable @Immutable
data object Loading : CategoryScreenState() data object Loading : CategoryScreenState
@Immutable @Immutable
data class Success( data class Success(
val categories: List<Category>, val categories: List<Category>,
val dialog: CategoryDialog? = null, val dialog: CategoryDialog? = null,
) : CategoryScreenState() { ) : CategoryScreenState {
val isEmpty: Boolean val isEmpty: Boolean
get() = categories.isEmpty() get() = categories.isEmpty()

View File

@ -34,7 +34,7 @@ class HistoryScreenModel(
private val getHistory: GetHistory = Injekt.get(), private val getHistory: GetHistory = Injekt.get(),
private val getNextChapters: GetNextChapters = Injekt.get(), private val getNextChapters: GetNextChapters = Injekt.get(),
private val removeHistory: RemoveHistory = Injekt.get(), private val removeHistory: RemoveHistory = Injekt.get(),
) : StateScreenModel<HistoryState>(HistoryState()) { ) : StateScreenModel<HistoryScreenModel.State>(State()) {
private val _events: Channel<Event> = Channel(Channel.UNLIMITED) private val _events: Channel<Event> = Channel(Channel.UNLIMITED)
val events: Flow<Event> = _events.receiveAsFlow() val events: Flow<Event> = _events.receiveAsFlow()
@ -113,21 +113,21 @@ class HistoryScreenModel(
mutableState.update { it.copy(dialog = dialog) } mutableState.update { it.copy(dialog = dialog) }
} }
sealed class Dialog { @Immutable
data object DeleteAll : Dialog() data class State(
data class Delete(val history: HistoryWithRelations) : Dialog() val searchQuery: String? = null,
val list: List<HistoryUiModel>? = null,
val dialog: Dialog? = null,
)
sealed interface Dialog {
data object DeleteAll : Dialog
data class Delete(val history: HistoryWithRelations) : Dialog
} }
sealed class Event { sealed interface Event {
data class OpenChapter(val chapter: Chapter?) : Event() data class OpenChapter(val chapter: Chapter?) : Event
data object InternalError : Event() data object InternalError : Event
data object HistoryCleared : Event() data object HistoryCleared : Event
} }
} }
@Immutable
data class HistoryState(
val searchQuery: String? = null,
val list: List<HistoryUiModel>? = null,
val dialog: HistoryScreenModel.Dialog? = null,
)

View File

@ -291,11 +291,11 @@ object HomeScreen : Screen() {
showBottomNavEvent.send(show) showBottomNavEvent.send(show)
} }
sealed class Tab { sealed interface Tab {
data class Library(val mangaIdToOpen: Long? = null) : Tab() data class Library(val mangaIdToOpen: Long? = null) : Tab
data object Updates : Tab() data object Updates : Tab
data object History : Tab() data object History : Tab
data class Browse(val toExtensions: Boolean = false) : Tab() data class Browse(val toExtensions: Boolean = false) : Tab
data class More(val toDownloads: Boolean) : Tab() data class More(val toDownloads: Boolean) : Tab
} }
} }

View File

@ -657,10 +657,10 @@ class LibraryScreenModel(
mutableState.update { it.copy(dialog = null) } mutableState.update { it.copy(dialog = null) }
} }
sealed class Dialog { sealed interface Dialog {
data object SettingsSheet : Dialog() data object SettingsSheet : Dialog
data class ChangeCategory(val manga: List<Manga>, val initialSelection: List<CheckboxState<Category>>) : Dialog() data class ChangeCategory(val manga: List<Manga>, val initialSelection: List<CheckboxState<Category>>) : Dialog
data class DeleteManga(val manga: List<Manga>) : Dialog() data class DeleteManga(val manga: List<Manga>) : Dialog
} }
@Immutable @Immutable

View File

@ -75,12 +75,12 @@ class MangaScreen(
val state by screenModel.state.collectAsState() val state by screenModel.state.collectAsState()
if (state is MangaScreenState.Loading) { if (state is MangaScreenModel.State.Loading) {
LoadingScreen() LoadingScreen()
return return
} }
val successState = state as MangaScreenState.Success val successState = state as MangaScreenModel.State.Success
val isHttpSource = remember { successState.source is HttpSource } val isHttpSource = remember { successState.source is HttpSource }
LaunchedEffect(successState.manga, screenModel.source) { LaunchedEffect(successState.manga, screenModel.source) {

View File

@ -98,10 +98,10 @@ class MangaScreenModel(
private val getTracks: GetTracks = Injekt.get(), private val getTracks: GetTracks = Injekt.get(),
private val setMangaCategories: SetMangaCategories = Injekt.get(), private val setMangaCategories: SetMangaCategories = Injekt.get(),
val snackbarHostState: SnackbarHostState = SnackbarHostState(), val snackbarHostState: SnackbarHostState = SnackbarHostState(),
) : StateScreenModel<MangaScreenState>(MangaScreenState.Loading) { ) : StateScreenModel<MangaScreenModel.State>(State.Loading) {
private val successState: MangaScreenState.Success? private val successState: State.Success?
get() = state.value as? MangaScreenState.Success get() = state.value as? State.Success
private val loggedServices by lazy { trackManager.services.filter { it.isLogged } } private val loggedServices by lazy { trackManager.services.filter { it.isLogged } }
@ -133,11 +133,11 @@ class MangaScreenModel(
/** /**
* 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: (MangaScreenState.Success) -> MangaScreenState.Success) { private inline fun updateSuccessState(func: (State.Success) -> State.Success) {
mutableState.update { mutableState.update {
when (it) { when (it) {
MangaScreenState.Loading -> it State.Loading -> it
is MangaScreenState.Success -> func(it) is State.Success -> func(it)
} }
} }
} }
@ -175,7 +175,7 @@ class MangaScreenModel(
// Show what we have earlier // Show what we have earlier
mutableState.update { mutableState.update {
MangaScreenState.Success( State.Success(
manga = manga, manga = manga,
source = Injekt.get<SourceManager>().getOrStub(manga.source), source = Injekt.get<SourceManager>().getOrStub(manga.source),
isFromSource = isFromSource, isFromSource = isFromSource,
@ -334,8 +334,7 @@ class MangaScreenModel(
} }
fun promptChangeCategories() { fun promptChangeCategories() {
val state = successState ?: return val manga = successState?.manga ?: return
val manga = state.manga
coroutineScope.launch { coroutineScope.launch {
val categories = getCategories() val categories = getCategories()
val selection = getMangaCategoryIds(manga) val selection = getMangaCategoryIds(manga)
@ -662,9 +661,9 @@ class MangaScreenModel(
} }
fun markPreviousChapterRead(pointer: Chapter) { fun markPreviousChapterRead(pointer: Chapter) {
val successState = successState ?: return val manga = successState?.manga ?: return
val chapters = filteredChapters.orEmpty().map { it.chapter } val chapters = filteredChapters.orEmpty().map { it.chapter }
val prevChapters = if (successState.manga.sortDescending()) chapters.asReversed() else chapters val prevChapters = if (manga.sortDescending()) chapters.asReversed() else chapters
val pointerPos = prevChapters.indexOf(pointer) val pointerPos = prevChapters.indexOf(pointer)
if (pointerPos != -1) markChaptersRead(prevChapters.take(pointerPos), true) if (pointerPos != -1) markChaptersRead(prevChapters.take(pointerPos), true)
} }
@ -940,13 +939,13 @@ class MangaScreenModel(
// Track sheet - end // Track sheet - end
sealed class Dialog { sealed interface Dialog {
data class ChangeCategory(val manga: Manga, val initialSelection: List<CheckboxState<Category>>) : Dialog() data class ChangeCategory(val manga: Manga, val initialSelection: List<CheckboxState<Category>>) : Dialog
data class DeleteChapters(val chapters: List<Chapter>) : Dialog() data class DeleteChapters(val chapters: List<Chapter>) : Dialog
data class DuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog() data class DuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog
data object SettingsSheet : Dialog() data object SettingsSheet : Dialog
data object TrackSheet : Dialog() data object TrackSheet : Dialog
data object FullCover : Dialog() data object FullCover : Dialog
} }
fun dismissDialog() { fun dismissDialog() {
@ -968,48 +967,48 @@ class MangaScreenModel(
fun showCoverDialog() { fun showCoverDialog() {
updateSuccessState { it.copy(dialog = Dialog.FullCover) } updateSuccessState { it.copy(dialog = Dialog.FullCover) }
} }
}
sealed class MangaScreenState { sealed interface State {
@Immutable @Immutable
object Loading : MangaScreenState() object Loading : State
@Immutable @Immutable
data class Success( data class Success(
val manga: Manga, val manga: Manga,
val source: Source, val source: Source,
val isFromSource: Boolean, val isFromSource: Boolean,
val chapters: List<ChapterItem>, val chapters: List<ChapterItem>,
val trackItems: List<TrackItem> = emptyList(), val trackItems: List<TrackItem> = emptyList(),
val isRefreshingData: Boolean = false, val isRefreshingData: Boolean = false,
val dialog: MangaScreenModel.Dialog? = null, val dialog: Dialog? = null,
val hasPromptedToAddBefore: Boolean = false, val hasPromptedToAddBefore: Boolean = false,
) : MangaScreenState() { ) : State {
val processedChapters by lazy { val processedChapters by lazy {
chapters.applyFilters(manga).toList() chapters.applyFilters(manga).toList()
} }
val trackingAvailable: Boolean val trackingAvailable: Boolean
get() = trackItems.isNotEmpty() get() = trackItems.isNotEmpty()
val trackingCount: Int val trackingCount: Int
get() = trackItems.count { it.track != null } get() = trackItems.count { it.track != null }
/** /**
* Applies the view filters to the list of chapters obtained from the database. * Applies the view filters to the list of chapters obtained from the database.
* @return an observable of the list of chapters filtered and sorted. * @return an observable of the list of chapters filtered and sorted.
*/ */
private fun List<ChapterItem>.applyFilters(manga: Manga): Sequence<ChapterItem> { private fun List<ChapterItem>.applyFilters(manga: Manga): Sequence<ChapterItem> {
val isLocalManga = manga.isLocal() val isLocalManga = manga.isLocal()
val unreadFilter = manga.unreadFilter val unreadFilter = manga.unreadFilter
val downloadedFilter = manga.downloadedFilter val downloadedFilter = manga.downloadedFilter
val bookmarkedFilter = manga.bookmarkedFilter val bookmarkedFilter = manga.bookmarkedFilter
return asSequence() return asSequence()
.filter { (chapter) -> applyFilter(unreadFilter) { !chapter.read } } .filter { (chapter) -> applyFilter(unreadFilter) { !chapter.read } }
.filter { (chapter) -> applyFilter(bookmarkedFilter) { chapter.bookmark } } .filter { (chapter) -> applyFilter(bookmarkedFilter) { chapter.bookmark } }
.filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalManga } } .filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalManga } }
.sortedWith { (chapter1), (chapter2) -> getChapterSort(manga).invoke(chapter1, chapter2) } .sortedWith { (chapter1), (chapter2) -> getChapterSort(manga).invoke(chapter1, chapter2) }
}
} }
} }
} }

View File

@ -108,8 +108,8 @@ private class MoreScreenModel(
} }
} }
sealed class DownloadQueueState { sealed interface DownloadQueueState {
data object Stopped : DownloadQueueState() data object Stopped : DownloadQueueState
data class Paused(val pending: Int) : DownloadQueueState() data class Paused(val pending: Int) : DownloadQueueState
data class Downloading(val pending: Int) : DownloadQueueState() data class Downloading(val pending: Int) : DownloadQueueState
} }

View File

@ -798,9 +798,9 @@ class ReaderViewModel(
Error, Error,
} }
sealed class SaveImageResult { sealed interface SaveImageResult {
class Success(val uri: Uri) : SaveImageResult() class Success(val uri: Uri) : SaveImageResult
class Error(val error: Throwable) : SaveImageResult() class Error(val error: Throwable) : SaveImageResult
} }
/** /**
@ -860,18 +860,18 @@ class ReaderViewModel(
get() = viewerChapters?.currChapter?.pages?.size ?: -1 get() = viewerChapters?.currChapter?.pages?.size ?: -1
} }
sealed class Dialog { sealed interface Dialog {
data object Loading : Dialog() data object Loading : Dialog
data object Settings : Dialog() data object Settings : Dialog
data class PageActions(val page: ReaderPage) : Dialog() data class PageActions(val page: ReaderPage) : Dialog
} }
sealed class Event { sealed interface Event {
data object ReloadViewerChapters : Event() data object ReloadViewerChapters : Event
data class SetOrientation(val orientation: Int) : Event() data class SetOrientation(val orientation: Int) : Event
data class SetCoverResult(val result: SetAsCoverResult) : Event() data class SetCoverResult(val result: SetAsCoverResult) : Event
data class SavedImage(val result: SaveImageResult) : Event() data class SavedImage(val result: SaveImageResult) : Event
data class ShareImage(val uri: Uri, val page: ReaderPage) : Event() data class ShareImage(val uri: Uri, val page: ReaderPage) : Event
} }
} }

View File

@ -39,10 +39,10 @@ data class ReaderChapter(val chapter: Chapter) {
} }
} }
sealed class State { sealed interface State {
data object Wait : State() data object Wait : State
data object Loading : State() data object Loading : State
data class Error(val error: Throwable) : State() data class Error(val error: Throwable) : State
data class Loaded(val pages: List<ReaderPage>) : State() data class Loaded(val pages: List<ReaderPage>) : State
} }
} }

View File

@ -60,7 +60,7 @@ class UpdatesScreenModel(
private val libraryPreferences: LibraryPreferences = Injekt.get(), private val libraryPreferences: LibraryPreferences = Injekt.get(),
val snackbarHostState: SnackbarHostState = SnackbarHostState(), val snackbarHostState: SnackbarHostState = SnackbarHostState(),
uiPreferences: UiPreferences = Injekt.get(), uiPreferences: UiPreferences = Injekt.get(),
) : StateScreenModel<UpdatesState>(UpdatesState()) { ) : StateScreenModel<UpdatesScreenModel.State>(State()) {
private val _events: Channel<Event> = Channel(Int.MAX_VALUE) private val _events: Channel<Event> = Channel(Int.MAX_VALUE)
val events: Flow<Event> = _events.receiveAsFlow() val events: Flow<Event> = _events.receiveAsFlow()
@ -366,46 +366,46 @@ class UpdatesScreenModel(
libraryPreferences.newUpdatesCount().set(0) libraryPreferences.newUpdatesCount().set(0)
} }
sealed class Dialog { @Immutable
data class DeleteConfirmation(val toDelete: List<UpdatesItem>) : Dialog() data class State(
} val isLoading: Boolean = true,
val items: List<UpdatesItem> = emptyList(),
val dialog: UpdatesScreenModel.Dialog? = null,
) {
val selected = items.filter { it.selected }
val selectionMode = selected.isNotEmpty()
sealed class Event { fun getUiModel(context: Context, relativeTime: Int): List<UpdatesUiModel> {
data object InternalError : Event() val dateFormat by mutableStateOf(UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get()))
data class LibraryUpdateTriggered(val started: Boolean) : Event()
}
}
@Immutable return items
data class UpdatesState( .map { UpdatesUiModel.Item(it) }
val isLoading: Boolean = true, .insertSeparators { before, after ->
val items: List<UpdatesItem> = emptyList(), val beforeDate = before?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
val dialog: UpdatesScreenModel.Dialog? = null, val afterDate = after?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
) { when {
val selected = items.filter { it.selected } beforeDate.time != afterDate.time && afterDate.time != 0L -> {
val selectionMode = selected.isNotEmpty() val text = afterDate.toRelativeString(
context = context,
fun getUiModel(context: Context, relativeTime: Int): List<UpdatesUiModel> { range = relativeTime,
val dateFormat by mutableStateOf(UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get())) dateFormat = dateFormat,
)
return items UpdatesUiModel.Header(text)
.map { UpdatesUiModel.Item(it) } }
.insertSeparators { before, after -> // Return null to avoid adding a separator between two items.
val beforeDate = before?.item?.update?.dateFetch?.toDateKey() ?: Date(0) else -> null
val afterDate = after?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
when {
beforeDate.time != afterDate.time && afterDate.time != 0L -> {
val text = afterDate.toRelativeString(
context = context,
range = relativeTime,
dateFormat = dateFormat,
)
UpdatesUiModel.Header(text)
} }
// Return null to avoid adding a separator between two items.
else -> null
} }
} }
}
sealed interface Dialog {
data class DeleteConfirmation(val toDelete: List<UpdatesItem>) : Dialog
}
sealed interface Event {
data object InternalError : Event
data class LibraryUpdateTriggered(val started: Boolean) : Event
} }
} }

View File

@ -37,8 +37,8 @@ class CreateCategoryWithName(
} }
} }
sealed class Result { sealed interface Result {
data object Success : Result() data object Success : Result
data class InternalError(val error: Throwable) : Result() data class InternalError(val error: Throwable) : Result
} }
} }

View File

@ -35,8 +35,8 @@ class DeleteCategory(
} }
} }
sealed class Result { sealed interface Result {
data object Success : Result() data object Success : Result
data class InternalError(val error: Throwable) : Result() data class InternalError(val error: Throwable) : Result
} }
} }

View File

@ -28,8 +28,8 @@ class RenameCategory(
suspend fun await(category: Category, name: String) = await(category.id, name) suspend fun await(category: Category, name: String) = await(category.id, name)
sealed class Result { sealed interface Result {
data object Success : Result() data object Success : Result
data class InternalError(val error: Throwable) : Result() data class InternalError(val error: Throwable) : Result
} }
} }

View File

@ -57,10 +57,10 @@ class ReorderCategory(
} }
} }
sealed class Result { sealed interface Result {
data object Success : Result() data object Success : Result
data object Unchanged : Result() data object Unchanged : Result
data class InternalError(val error: Throwable) : Result() data class InternalError(val error: Throwable) : Result
} }
private enum class MoveTo { private enum class MoveTo {

View File

@ -17,8 +17,8 @@ class UpdateCategory(
} }
} }
sealed class Result { sealed interface Result {
data object Success : Result() data object Success : Result
data class Error(val error: Exception) : Result() data class Error(val error: Exception) : Result
} }
} }

View File

@ -1,11 +1,11 @@
package tachiyomi.domain.library.model package tachiyomi.domain.library.model
sealed class LibraryDisplayMode { sealed interface LibraryDisplayMode {
data object CompactGrid : LibraryDisplayMode() data object CompactGrid : LibraryDisplayMode
data object ComfortableGrid : LibraryDisplayMode() data object ComfortableGrid : LibraryDisplayMode
data object List : LibraryDisplayMode() data object List : LibraryDisplayMode
data object CoverOnlyGrid : LibraryDisplayMode() data object CoverOnlyGrid : LibraryDisplayMode
object Serializer { object Serializer {
fun deserialize(serialized: String): LibraryDisplayMode { fun deserialize(serialized: String): LibraryDisplayMode {

View File

@ -71,9 +71,9 @@ class GetApplicationRelease(
val forceCheck: Boolean = false, val forceCheck: Boolean = false,
) )
sealed class Result { sealed interface Result {
data class NewUpdate(val release: Release) : Result() data class NewUpdate(val release: Release) : Result
data object NoNewUpdate : Result() data object NoNewUpdate : Result
data object ThirdPartyInstallation : Result() data object ThirdPartyInstallation : Result
} }
} }

View File

@ -2,11 +2,11 @@ package tachiyomi.source.local.io
import java.io.File import java.io.File
sealed class Format { sealed interface Format {
data class Directory(val file: File) : Format() data class Directory(val file: File) : Format
data class Zip(val file: File) : Format() data class Zip(val file: File) : Format
data class Rar(val file: File) : Format() data class Rar(val file: File) : Format
data class Epub(val file: File) : Format() data class Epub(val file: File) : Format
class UnknownFormatException : Exception() class UnknownFormatException : Exception()