arkon 1 жил өмнө
parent
commit
abae9bf37d
46 өөрчлөгдсөн 339 нэмэгдсэн , 329 устгасан
  1. 4 4
      app/src/main/java/eu/kanade/domain/chapter/interactor/SetReadStatus.kt
  2. 2 2
      app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt
  3. 3 3
      app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt
  4. 3 3
      app/src/main/java/eu/kanade/presentation/browse/MigrateMangaScreen.kt
  5. 2 2
      app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt
  6. 3 3
      app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt
  7. 3 3
      app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt
  8. 4 5
      app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt
  9. 4 4
      app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt
  10. 7 3
      app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt
  11. 3 3
      app/src/main/java/eu/kanade/presentation/more/stats/StatsScreenState.kt
  12. 5 5
      app/src/main/java/eu/kanade/presentation/more/stats/data/StatsData.kt
  13. 5 5
      app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt
  14. 3 3
      app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt
  15. 4 4
      app/src/main/java/eu/kanade/tachiyomi/extension/model/LoadResult.kt
  16. 5 5
      app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreenModel.kt
  17. 12 10
      app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt
  18. 14 14
      app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreenModel.kt
  19. 2 2
      app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreen.kt
  20. 17 17
      app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreenModel.kt
  21. 2 2
      app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreenDialogScreenModel.kt
  22. 13 11
      app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceScreenModel.kt
  23. 2 2
      app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceTab.kt
  24. 3 3
      app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesFilterScreen.kt
  25. 20 16
      app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesFilterScreenModel.kt
  26. 2 2
      app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesScreenModel.kt
  27. 6 6
      app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt
  28. 4 4
      app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchScreenModel.kt
  29. 9 9
      app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryScreenModel.kt
  30. 15 15
      app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt
  31. 6 6
      app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt
  32. 4 4
      app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt
  33. 2 2
      app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt
  34. 57 58
      app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt
  35. 4 4
      app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt
  36. 13 13
      app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
  37. 5 5
      app/src/main/java/eu/kanade/tachiyomi/ui/reader/model/ReaderChapter.kt
  38. 37 37
      app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt
  39. 3 3
      domain/src/main/java/tachiyomi/domain/category/interactor/CreateCategoryWithName.kt
  40. 3 3
      domain/src/main/java/tachiyomi/domain/category/interactor/DeleteCategory.kt
  41. 3 3
      domain/src/main/java/tachiyomi/domain/category/interactor/RenameCategory.kt
  42. 4 4
      domain/src/main/java/tachiyomi/domain/category/interactor/ReorderCategory.kt
  43. 3 3
      domain/src/main/java/tachiyomi/domain/category/interactor/UpdateCategory.kt
  44. 5 5
      domain/src/main/java/tachiyomi/domain/library/model/LibraryDisplayMode.kt
  45. 4 4
      domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt
  46. 5 5
      source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Format.kt

+ 4 - 4
app/src/main/java/eu/kanade/domain/chapter/interactor/SetReadStatus.kt

@@ -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
     }
 }

+ 2 - 2
app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt

@@ -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,

+ 3 - 3
app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt

@@ -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,

+ 3 - 3
app/src/main/java/eu/kanade/presentation/browse/MigrateMangaScreen.kt

@@ -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,
 ) {

+ 2 - 2
app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt

@@ -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,

+ 3 - 3
app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt

@@ -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,
 ) {

+ 3 - 3
app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt

@@ -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
 }

+ 4 - 5
app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt

@@ -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
 }

+ 4 - 4
app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt

@@ -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,

+ 7 - 3
app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt

@@ -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
     }
 }

+ 3 - 3
app/src/main/java/eu/kanade/presentation/more/stats/StatsScreenState.kt

@@ -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
 }

+ 5 - 5
app/src/main/java/eu/kanade/presentation/more/stats/data/StatsData.kt

@@ -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
 }

+ 5 - 5
app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt

@@ -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
 }

+ 3 - 3
app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt

@@ -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) {

+ 4 - 4
app/src/main/java/eu/kanade/tachiyomi/extension/model/LoadResult.kt

@@ -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
 }

+ 5 - 5
app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreenModel.kt

@@ -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()

+ 12 - 10
app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt

@@ -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>>

+ 14 - 14
app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreenModel.kt

@@ -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
         }
     }
-}
 
-sealed class ExtensionDetailsEvent {
-    data object Uninstalled : ExtensionDetailsEvent()
-}
+    @Immutable
+    data class State(
+        val extension: Extension.Installed? = null,
+        private val _sources: List<ExtensionSourceItem>? = null,
+    ) {
 
-@Immutable
-data class ExtensionDetailsState(
-    val extension: Extension.Installed? = null,
-    private val _sources: List<ExtensionSourceItem>? = null,
-) {
+        val sources: List<ExtensionSourceItem>
+            get() = _sources.orEmpty()
 
-    val sources: List<ExtensionSourceItem>
-        get() = _sources.orEmpty()
+        val isLoading: Boolean
+            get() = extension == null || _sources == null
+    }
+}
 
-    val isLoading: Boolean
-        get() = extension == null || _sources == null
+sealed interface ExtensionDetailsEvent {
+    data object Uninstalled : ExtensionDetailsEvent
 }

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaScreen.kt → app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreen.kt

@@ -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()
 

+ 17 - 17
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaScreenModel.kt → app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreenModel.kt

@@ -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(
                 }
         }
     }
-}
 
-sealed class MigrationMangaEvent {
-    data object FailedFetchingFavorites : MigrationMangaEvent()
-}
+    @Immutable
+    data class State(
+        val source: Source? = null,
+        private val titleList: List<Manga>? = null,
+    ) {
 
-@Immutable
-data class MigrateMangaState(
-    val source: Source? = null,
-    private val titleList: List<Manga>? = null,
-) {
+        val titles: List<Manga>
+            get() = titleList.orEmpty()
 
-    val titles: List<Manga>
-        get() = titleList.orEmpty()
+        val isLoading: Boolean
+            get() = source == null || titleList == null
 
-    val isLoading: Boolean
-        get() = source == null || titleList == null
+        val isEmpty: Boolean
+            get() = titles.isEmpty()
+    }
+}
 
-    val isEmpty: Boolean
-        get() = titles.isEmpty()
+sealed interface MigrationMangaEvent {
+    data object FailedFetchingFavorites : MigrationMangaEvent
 }

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreenDialogScreenModel.kt

@@ -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
     }
 }

+ 13 - 11
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceScreenModel.kt

@@ -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()
     }
-}
 
-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()
+    sealed interface Event {
+        data object FailedFetchingSourcesWithCount : Event
+    }
 }

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceTab.kt

@@ -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,

+ 3 - 3
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesFilterScreen.kt

@@ -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,

+ 20 - 16
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesFilterScreenModel.kt

@@ -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()
+        }
     }
 }

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesScreenModel.kt

@@ -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)

+ 6 - 6
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt

@@ -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

+ 4 - 4
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchScreenModel.kt

@@ -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()
     }

+ 9 - 9
app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryScreenModel.kt

@@ -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()

+ 15 - 15
app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt

@@ -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,
-)

+ 6 - 6
app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt

@@ -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
     }
 }

+ 4 - 4
app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt

@@ -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

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt

@@ -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) {

+ 57 - 58
app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt

@@ -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()
-
-    @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() {
-
-        val processedChapters by lazy {
-            chapters.applyFilters(manga).toList()
-        }
+    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: Dialog? = null,
+            val hasPromptedToAddBefore: Boolean = false,
+        ) : State {
+
+            val processedChapters by lazy {
+                chapters.applyFilters(manga).toList()
+            }
 
-        val trackingAvailable: Boolean
-            get() = trackItems.isNotEmpty()
-
-        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) }
+            val trackingAvailable: Boolean
+                get() = trackItems.isNotEmpty()
+
+            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) }
+            }
         }
     }
 }

+ 4 - 4
app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt

@@ -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
 }

+ 13 - 13
app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt

@@ -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
     }
 }

+ 5 - 5
app/src/main/java/eu/kanade/tachiyomi/ui/reader/model/ReaderChapter.kt

@@ -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
     }
 }

+ 37 - 37
app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt

@@ -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()
+
+        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 null to avoid adding a separator between two items.
+                        else -> null
+                    }
+                }
+        }
     }
 
-    sealed class Event {
-        data object InternalError : Event()
-        data class LibraryUpdateTriggered(val started: Boolean) : Event()
+    sealed interface Dialog {
+        data class DeleteConfirmation(val toDelete: List<UpdatesItem>) : Dialog
     }
-}
 
-@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 null to avoid adding a separator between two items.
-                    else -> null
-                }
-            }
+    sealed interface Event {
+        data object InternalError : Event
+        data class LibraryUpdateTriggered(val started: Boolean) : Event
     }
 }
 

+ 3 - 3
domain/src/main/java/tachiyomi/domain/category/interactor/CreateCategoryWithName.kt

@@ -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
     }
 }

+ 3 - 3
domain/src/main/java/tachiyomi/domain/category/interactor/DeleteCategory.kt

@@ -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
     }
 }

+ 3 - 3
domain/src/main/java/tachiyomi/domain/category/interactor/RenameCategory.kt

@@ -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
     }
 }

+ 4 - 4
domain/src/main/java/tachiyomi/domain/category/interactor/ReorderCategory.kt

@@ -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 {

+ 3 - 3
domain/src/main/java/tachiyomi/domain/category/interactor/UpdateCategory.kt

@@ -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
     }
 }

+ 5 - 5
domain/src/main/java/tachiyomi/domain/library/model/LibraryDisplayMode.kt

@@ -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 {

+ 4 - 4
domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt

@@ -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
     }
 }

+ 5 - 5
source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Format.kt

@@ -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()