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) =
await(manga.id, read)
sealed class Result {
data object Success : Result()
data object NoChapters : Result()
data class InternalError(val error: Throwable) : Result()
sealed interface Result {
data object Success : Result
data object NoChapters : 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.extension.model.Extension
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 tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.material.DIVIDER_ALPHA
@ -65,7 +65,7 @@ import tachiyomi.presentation.core.screens.EmptyScreen
@Composable
fun ExtensionDetailsScreen(
navigateUp: () -> Unit,
state: ExtensionDetailsState,
state: ExtensionDetailsScreenModel.State,
onClickSourcePreferences: (sourceId: Long) -> Unit,
onClickWhatsNew: () -> 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.InstallStep
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 tachiyomi.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.material.PullRefresh
@ -57,7 +57,7 @@ import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable
fun ExtensionScreen(
state: ExtensionsState,
state: ExtensionsScreenModel.State,
contentPadding: PaddingValues,
searchQuery: String?,
onLongClickItem: (Extension) -> Unit,
@ -108,7 +108,7 @@ fun ExtensionScreen(
@Composable
private fun ExtensionContent(
state: ExtensionsState,
state: ExtensionsScreenModel.State,
contentPadding: PaddingValues,
onLongClickItem: (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.manga.components.BaseMangaListItem
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.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.material.Scaffold
@ -18,7 +18,7 @@ import tachiyomi.presentation.core.screens.EmptyScreen
fun MigrateMangaScreen(
navigateUp: () -> Unit,
title: String?,
state: MigrateMangaState,
state: MigrateMangaScreenModel.State,
onClickItem: (Manga) -> Unit,
onClickCover: (Manga) -> Unit,
) {
@ -51,7 +51,7 @@ fun MigrateMangaScreen(
@Composable
private fun MigrateMangaContent(
contentPadding: PaddingValues,
state: MigrateMangaState,
state: MigrateMangaScreenModel.State,
onClickItem: (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.SourceIcon
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 tachiyomi.domain.source.model.Source
import tachiyomi.presentation.core.components.Badge
@ -43,7 +43,7 @@ import tachiyomi.presentation.core.util.secondaryItemAlpha
@Composable
fun MigrateSourceScreen(
state: MigrateSourceState,
state: MigrateSourceScreenModel.State,
contentPadding: PaddingValues,
onClickItem: (Source) -> 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.more.settings.widget.SwitchPreferenceWidget
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 tachiyomi.domain.source.model.Source
import tachiyomi.presentation.core.components.FastScrollLazyColumn
@ -22,7 +22,7 @@ import tachiyomi.presentation.core.screens.EmptyScreen
@Composable
fun SourcesFilterScreen(
navigateUp: () -> Unit,
state: SourcesFilterState.Success,
state: SourcesFilterScreenModel.State.Success,
onClickLanguage: (String) -> Unit,
onClickSource: (Source) -> Unit,
) {
@ -54,7 +54,7 @@ fun SourcesFilterScreen(
@Composable
private fun SourcesFilterContent(
contentPadding: PaddingValues,
state: SourcesFilterState.Success,
state: SourcesFilterScreenModel.State.Success,
onClickLanguage: (String) -> Unit,
onClickSource: (Source) -> Unit,
) {

View file

@ -192,7 +192,7 @@ fun SourceOptionsDialog(
)
}
sealed class SourceUiModel {
data class Item(val source: Source) : SourceUiModel()
data class Header(val language: String) : SourceUiModel()
sealed interface SourceUiModel {
data class Item(val source: Source) : 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.tachiyomi.R
import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
import eu.kanade.tachiyomi.ui.history.HistoryState
import tachiyomi.domain.history.model.HistoryWithRelations
import tachiyomi.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.material.Scaffold
@ -33,7 +32,7 @@ import java.util.Date
@Composable
fun HistoryScreen(
state: HistoryState,
state: HistoryScreenModel.State,
snackbarHostState: SnackbarHostState,
onSearchQueryChange: (String?) -> Unit,
onClickCover: (mangaId: Long) -> Unit,
@ -139,7 +138,7 @@ private fun HistoryScreenContent(
}
}
sealed class HistoryUiModel {
data class Header(val date: Date) : HistoryUiModel()
data class Item(val item: HistoryWithRelations) : HistoryUiModel()
sealed interface HistoryUiModel {
data class Header(val date: Date) : 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.source.getNameForMangaInfo
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.system.copyToClipboard
import tachiyomi.domain.chapter.model.Chapter
@ -82,7 +82,7 @@ import java.util.Date
@Composable
fun MangaScreen(
state: MangaScreenState.Success,
state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
dateRelativeTime: Int,
dateFormat: DateFormat,
@ -210,7 +210,7 @@ fun MangaScreen(
@Composable
private fun MangaScreenSmallImpl(
state: MangaScreenState.Success,
state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
dateRelativeTime: Int,
dateFormat: DateFormat,
@ -436,7 +436,7 @@ private fun MangaScreenSmallImpl(
@Composable
fun MangaScreenLargeImpl(
state: MangaScreenState.Success,
state: MangaScreenModel.State.Success,
snackbarHostState: SnackbarHostState,
dateRelativeTime: Int,
dateFormat: DateFormat,

View file

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

View file

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

View file

@ -1,28 +1,28 @@
package eu.kanade.presentation.more.stats.data
sealed class StatsData {
sealed interface StatsData {
data class Overview(
val libraryMangaCount: Int,
val completedMangaCount: Int,
val totalReadDuration: Long,
) : StatsData()
) : StatsData
data class Titles(
val globalUpdateItemCount: Int,
val startedMangaCount: Int,
val localMangaCount: Int,
) : StatsData()
) : StatsData
data class Chapters(
val totalChapterCount: Int,
val readChapterCount: Int,
val downloadCount: Int,
) : StatsData()
) : StatsData
data class Trackers(
val trackedTitleCount: Int,
val meanScore: Double,
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.data.download.model.Download
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.launch
import tachiyomi.presentation.core.components.FastScrollLazyColumn
@ -40,7 +40,7 @@ import kotlin.time.Duration.Companion.seconds
@Composable
fun UpdateScreen(
state: UpdatesState,
state: UpdatesScreenModel.State,
snackbarHostState: SnackbarHostState,
lastUpdated: Long,
relativeTime: Int,
@ -209,7 +209,7 @@ private fun UpdatesBottomBar(
)
}
sealed class UpdatesUiModel {
data class Header(val date: String) : UpdatesUiModel()
data class Item(val item: UpdatesItem) : UpdatesUiModel()
sealed interface UpdatesUiModel {
data class Header(val date: String) : UpdatesUiModel
data class Item(val item: UpdatesItem) : UpdatesUiModel
}

View file

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

View file

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

View file

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

View file

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

View file

@ -38,7 +38,7 @@ class ExtensionDetailsScreenModel(
private val extensionManager: ExtensionManager = Injekt.get(),
private val getExtensionSources: GetExtensionSources = Injekt.get(),
private val toggleSource: ToggleSource = Injekt.get(),
) : StateScreenModel<ExtensionDetailsState>(ExtensionDetailsState()) {
) : StateScreenModel<ExtensionDetailsScreenModel.State>(State()) {
private val _events: Channel<ExtensionDetailsEvent> = Channel()
val events: Flow<ExtensionDetailsEvent> = _events.receiveAsFlow()
@ -160,21 +160,21 @@ class ExtensionDetailsScreenModel(
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 {
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
sealed interface ExtensionDetailsEvent {
data object Uninstalled : ExtensionDetailsEvent
}

View file

@ -17,7 +17,7 @@ import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.collectLatest
import tachiyomi.presentation.core.screens.LoadingScreen
data class MigrationMangaScreen(
data class MigrateMangaScreen(
private val sourceId: Long,
) : Screen() {
@ -25,7 +25,7 @@ data class MigrationMangaScreen(
override fun Content() {
val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow
val screenModel = rememberScreenModel { MigrationMangaScreenModel(sourceId) }
val screenModel = rememberScreenModel { MigrateMangaScreenModel(sourceId) }
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.api.get
class MigrationMangaScreenModel(
class MigrateMangaScreenModel(
private val sourceId: Long,
private val sourceManager: SourceManager = Injekt.get(),
private val getFavorites: GetFavorites = Injekt.get(),
) : StateScreenModel<MigrateMangaState>(MigrateMangaState()) {
) : StateScreenModel<MigrateMangaScreenModel.State>(State()) {
private val _events: Channel<MigrationMangaEvent> = Channel()
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 {
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()
sealed interface MigrationMangaEvent {
data object FailedFetchingFavorites : MigrationMangaEvent
}

View file

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

View file

@ -1,5 +1,6 @@
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.coroutineScope
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
@ -23,7 +24,7 @@ class MigrateSourceScreenModel(
preferences: SourcePreferences = Injekt.get(),
private val getSourcesWithFavoriteCount: GetSourcesWithFavoriteCount = Injekt.get(),
private val setMigrateSorting: SetMigrateSorting = Injekt.get(),
) : StateScreenModel<MigrateSourceState>(MigrateSourceState()) {
) : StateScreenModel<MigrateSourceScreenModel.State>(State()) {
private val _channel = Channel<Event>(Int.MAX_VALUE)
val channel = _channel.receiveAsFlow()
@ -76,16 +77,17 @@ class MigrateSourceScreenModel(
}
}
sealed class Event {
data object FailedFetchingSourcesWithCount : Event()
@Immutable
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.TabContent
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrationMangaScreen
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaScreen
@Composable
fun Screen.migrateSourceTab(): TabContent {
@ -40,7 +40,7 @@ fun Screen.migrateSourceTab(): TabContent {
state = state,
contentPadding = contentPadding,
onClickItem = { source ->
navigator.push(MigrationMangaScreen(source.id))
navigator.push(MigrateMangaScreen(source.id))
},
onToggleSortingDirection = screenModel::toggleSortingDirection,
onToggleSortingMode = screenModel::toggleSortingMode,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -34,7 +34,7 @@ class HistoryScreenModel(
private val getHistory: GetHistory = Injekt.get(),
private val getNextChapters: GetNextChapters = Injekt.get(),
private val removeHistory: RemoveHistory = Injekt.get(),
) : StateScreenModel<HistoryState>(HistoryState()) {
) : StateScreenModel<HistoryScreenModel.State>(State()) {
private val _events: Channel<Event> = Channel(Channel.UNLIMITED)
val events: Flow<Event> = _events.receiveAsFlow()
@ -113,21 +113,21 @@ class HistoryScreenModel(
mutableState.update { it.copy(dialog = dialog) }
}
sealed class Dialog {
data object DeleteAll : Dialog()
data class Delete(val history: HistoryWithRelations) : Dialog()
@Immutable
data class State(
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 {
data class OpenChapter(val chapter: Chapter?) : Event()
data object InternalError : Event()
data object HistoryCleared : Event()
sealed interface Event {
data class OpenChapter(val chapter: Chapter?) : Event
data object InternalError : 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)
}
sealed class Tab {
data class Library(val mangaIdToOpen: Long? = null) : Tab()
data object Updates : Tab()
data object History : Tab()
data class Browse(val toExtensions: Boolean = false) : Tab()
data class More(val toDownloads: Boolean) : Tab()
sealed interface Tab {
data class Library(val mangaIdToOpen: Long? = null) : Tab
data object Updates : Tab
data object History : Tab
data class Browse(val toExtensions: Boolean = false) : Tab
data class More(val toDownloads: Boolean) : Tab
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -60,7 +60,7 @@ class UpdatesScreenModel(
private val libraryPreferences: LibraryPreferences = Injekt.get(),
val snackbarHostState: SnackbarHostState = SnackbarHostState(),
uiPreferences: UiPreferences = Injekt.get(),
) : StateScreenModel<UpdatesState>(UpdatesState()) {
) : StateScreenModel<UpdatesScreenModel.State>(State()) {
private val _events: Channel<Event> = Channel(Int.MAX_VALUE)
val events: Flow<Event> = _events.receiveAsFlow()
@ -366,46 +366,46 @@ class UpdatesScreenModel(
libraryPreferences.newUpdatesCount().set(0)
}
sealed class Dialog {
data class DeleteConfirmation(val toDelete: List<UpdatesItem>) : Dialog()
}
@Immutable
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 {
data object InternalError : Event()
data class LibraryUpdateTriggered(val started: Boolean) : Event()
}
}
fun getUiModel(context: Context, relativeTime: Int): List<UpdatesUiModel> {
val dateFormat by mutableStateOf(UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get()))
@Immutable
data class UpdatesState(
val isLoading: Boolean = true,
val items: List<UpdatesItem> = emptyList(),
val dialog: UpdatesScreenModel.Dialog? = null,
) {
val selected = items.filter { it.selected }
val selectionMode = selected.isNotEmpty()
fun getUiModel(context: Context, relativeTime: Int): List<UpdatesUiModel> {
val dateFormat by mutableStateOf(UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get()))
return items
.map { UpdatesUiModel.Item(it) }
.insertSeparators { before, after ->
val beforeDate = before?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
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 items
.map { UpdatesUiModel.Item(it) }
.insertSeparators { before, after ->
val beforeDate = before?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
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
}
// 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 {
data object Success : Result()
data class InternalError(val error: Throwable) : Result()
sealed interface Result {
data object Success : Result
data class InternalError(val error: Throwable) : Result
}
}

View file

@ -35,8 +35,8 @@ class DeleteCategory(
}
}
sealed class Result {
data object Success : Result()
data class InternalError(val error: Throwable) : Result()
sealed interface Result {
data object Success : 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)
sealed class Result {
data object Success : Result()
data class InternalError(val error: Throwable) : Result()
sealed interface Result {
data object Success : Result
data class InternalError(val error: Throwable) : Result
}
}

View file

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

View file

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

View file

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

View file

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

View file

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