From 645746aa431ed0cb5b718b81ce4f4b844e71c82f Mon Sep 17 00:00:00 2001 From: LuftVerbot <97435834+LuftVerbot@users.noreply.github.com> Date: Sun, 12 Nov 2023 15:57:35 +0100 Subject: [PATCH] merge21 Last commit merged: https://github.com/tachiyomiorg/tachiyomi/commit/7a4680603db7d80cd8a52ce83e7a8e2eef317d29 --- .../base/ExtensionInstallerPreference.kt | 2 +- .../items/chapter/interactor/SetReadStatus.kt | 4 +- .../items/episode/interactor/SetSeenStatus.kt | 4 +- .../anime/AnimeExtensionFilterScreen.kt | 4 +- .../browse/anime/GlobalAnimeSearchScreen.kt | 4 +- .../browse/anime/MigrateAnimeSearchScreen.kt | 7 +- .../anime/components/BrowseAnimeIcons.kt | 4 +- .../components/GlobalAnimeSearchCardRow.kt | 1 + .../components/GlobalAnimeSearchToolbar.kt | 2 +- .../browse/manga/GlobalMangaSearchScreen.kt | 4 +- .../manga/MangaExtensionFilterScreen.kt | 4 +- .../browse/manga/MigrateMangaSearchScreen.kt | 7 +- .../manga/components/BrowseMangaIcons.kt | 4 +- .../components/GlobalMangaSearchCardRow.kt | 1 + .../components/GlobalMangaSearchToolbar.kt | 2 +- .../entries/EntryBottomActionMenu.kt | 4 +- .../presentation/library/CommonEntryItem.kt | 5 +- .../screen/SettingsAppearanceScreen.kt | 4 +- .../settings/screen/SettingsReaderScreen.kt | 4 +- .../settings/screen/SettingsSecurityScreen.kt | 2 +- .../advanced/ClearAnimeDatabaseScreen.kt | 6 +- .../screen/advanced/ClearDatabaseScreen.kt | 6 +- .../widget/AppThemePreferenceWidget.kt | 2 +- .../more/stats/StatsScreenState.kt | 2 +- .../reader/settings/ReadingModePage.kt | 6 +- .../main/java/eu/kanade/tachiyomi/AppInfo.kt | 2 +- .../kanade/tachiyomi/data/saver/ImageSaver.kt | 2 +- .../data/track/bangumi/BangumiInterceptor.kt | 2 +- .../extension/anime/model/AnimeLoadResult.kt | 6 +- .../extension/manga/model/MangaLoadResult.kt | 6 +- .../AnimeExtensionFilterScreenModel.kt | 4 +- .../AnimeExtensionDetailsScreenModel.kt | 2 +- .../anime/MigrationAnimeScreenModel.kt | 2 +- ...imeMigrateSearchScreenDialogScreenModel.kt | 43 ++++++ .../search/MigrateAnimeSearchScreen.kt | 19 ++- .../search/MigrateAnimeSearchScreenModel.kt | 68 +-------- .../sources/MigrateAnimeSourceScreenModel.kt | 2 +- .../anime/source/AnimeSourcesScreenModel.kt | 2 +- .../browse/BrowseAnimeSourceScreenModel.kt | 6 +- .../globalsearch/AnimeSearchScreenModel.kt | 142 +++++++++++------- .../globalsearch/GlobalAnimeSearchScreen.kt | 2 +- .../GlobalAnimeSearchScreenModel.kt | 50 +----- .../MangaExtensionFilterScreenModel.kt | 4 +- .../MangaExtensionDetailsScreenModel.kt | 2 +- .../manga/MigrationMangaScreenModel.kt | 2 +- ...ngaMigrateSearchScreenDialogScreenModel.kt | 43 ++++++ .../search/MigrateMangaSearchScreen.kt | 19 ++- .../search/MigrateMangaSearchScreenModel.kt | 68 +-------- .../sources/MigrateMangaSourceScreenModel.kt | 2 +- .../manga/source/MangaSourcesScreenModel.kt | 2 +- .../browse/BrowseMangaSourceScreenModel.kt | 6 +- .../globalsearch/GlobalMangaSearchScreen.kt | 2 +- .../GlobalMangaSearchScreenModel.kt | 50 +----- .../globalsearch/MangaSearchScreenModel.kt | 141 ++++++++++------- .../anime/AnimeCategoryScreenModel.kt | 6 +- .../manga/MangaCategoryScreenModel.kt | 6 +- .../ui/entries/anime/AnimeScreenModel.kt | 12 +- .../ui/entries/manga/MangaScreenModel.kt | 10 +- .../history/anime/AnimeHistoryScreenModel.kt | 6 +- .../history/manga/MangaHistoryScreenModel.kt | 6 +- .../eu/kanade/tachiyomi/ui/home/HomeScreen.kt | 4 +- .../library/anime/AnimeLibraryScreenModel.kt | 2 +- .../library/manga/MangaLibraryScreenModel.kt | 2 +- .../eu/kanade/tachiyomi/ui/more/MoreTab.kt | 2 +- .../tachiyomi/ui/reader/ReaderActivity.kt | 4 +- .../tachiyomi/ui/reader/ReaderViewModel.kt | 6 +- .../ui/reader/model/ReaderChapter.kt | 8 +- .../ui/reader/setting/OrientationType.kt | 2 +- .../ui/reader/setting/ReadingModeType.kt | 2 +- .../ui/reader/viewer/ViewerNavigation.kt | 10 +- .../updates/anime/AnimeUpdatesScreenModel.kt | 6 +- .../updates/manga/MangaUpdatesScreenModel.kt | 6 +- .../eu/kanade/tachiyomi/util/system/GLUtil.kt | 2 +- .../core/metadata/comicinfo/ComicInfo.kt | 4 +- .../tachiyomi/core/util/system/ImageUtil.kt | 4 +- .../java/tachiyomi/data/DatabaseAdapter.kt | 4 +- .../interactor/CreateAnimeCategoryWithName.kt | 2 +- .../anime/interactor/DeleteAnimeCategory.kt | 2 +- .../anime/interactor/ReorderAnimeCategory.kt | 4 +- .../anime/interactor/UpdateAnimeCategory.kt | 2 +- .../interactor/CreateMangaCategoryWithName.kt | 2 +- .../manga/interactor/DeleteMangaCategory.kt | 2 +- .../manga/interactor/ReorderMangaCategory.kt | 4 +- .../manga/interactor/UpdateMangaCategory.kt | 2 +- .../anime/model/AnimeLibrarySortMode.kt | 22 +-- .../manga/model/MangaLibrarySortMode.kt | 20 +-- .../library/model/LibraryDisplayMode.kt | 8 +- .../interactor/GetApplicationRelease.kt | 6 +- .../domain/source/manga/model/Pin.kt | 6 +- gradle/compose.versions.toml | 2 +- gradle/kotlinx.versions.toml | 2 +- gradle/libs.versions.toml | 8 +- .../core/components/VerticalFastScroller.kt | 2 +- .../components/manga/UpdatesMangaWidget.kt | 4 +- 94 files changed, 499 insertions(+), 511 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/AnimeMigrateSearchScreenDialogScreenModel.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MangaMigrateSearchScreenDialogScreenModel.kt diff --git a/app/src/main/java/eu/kanade/domain/base/ExtensionInstallerPreference.kt b/app/src/main/java/eu/kanade/domain/base/ExtensionInstallerPreference.kt index 03c63dd2b..33f148e95 100644 --- a/app/src/main/java/eu/kanade/domain/base/ExtensionInstallerPreference.kt +++ b/app/src/main/java/eu/kanade/domain/base/ExtensionInstallerPreference.kt @@ -18,7 +18,7 @@ class ExtensionInstallerPreference( override fun key() = "extension_installer" - val entries get() = ExtensionInstaller.values().run { + val entries get() = ExtensionInstaller.entries.run { if (context.hasMiuiPackageInstaller) { filter { it != ExtensionInstaller.PACKAGEINSTALLER } } else { diff --git a/app/src/main/java/eu/kanade/domain/items/chapter/interactor/SetReadStatus.kt b/app/src/main/java/eu/kanade/domain/items/chapter/interactor/SetReadStatus.kt index 6e07301dc..27cea7125 100644 --- a/app/src/main/java/eu/kanade/domain/items/chapter/interactor/SetReadStatus.kt +++ b/app/src/main/java/eu/kanade/domain/items/chapter/interactor/SetReadStatus.kt @@ -73,8 +73,8 @@ class SetReadStatus( await(manga.id, read) sealed class Result { - object Success : Result() - object NoChapters : Result() + data object Success : Result() + data object NoChapters : Result() data class InternalError(val error: Throwable) : Result() } } diff --git a/app/src/main/java/eu/kanade/domain/items/episode/interactor/SetSeenStatus.kt b/app/src/main/java/eu/kanade/domain/items/episode/interactor/SetSeenStatus.kt index bfc181df9..fbe5cd307 100644 --- a/app/src/main/java/eu/kanade/domain/items/episode/interactor/SetSeenStatus.kt +++ b/app/src/main/java/eu/kanade/domain/items/episode/interactor/SetSeenStatus.kt @@ -73,8 +73,8 @@ class SetSeenStatus( await(anime.id, seen) sealed class Result { - object Success : Result() - object NoEpisodes : Result() + data object Success : Result() + data object NoEpisodes : Result() data class InternalError(val error: Throwable) : Result() } } diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionFilterScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionFilterScreen.kt index 24f97e501..ec8eac285 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionFilterScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionFilterScreen.kt @@ -2,6 +2,7 @@ package eu.kanade.presentation.browse.anime import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -12,7 +13,6 @@ import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.browse.anime.extension.AnimeExtensionFilterState import eu.kanade.tachiyomi.util.system.LocaleHelper -import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.screens.EmptyScreen @@ -53,7 +53,7 @@ private fun AnimeExtensionFilterContent( onClickLang: (String) -> Unit, ) { val context = LocalContext.current - FastScrollLazyColumn( + LazyColumn( contentPadding = contentPadding, ) { items(state.languages) { language -> diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/GlobalAnimeSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/GlobalAnimeSearchScreen.kt index 22258b9c2..3c0abdc58 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/anime/GlobalAnimeSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/anime/GlobalAnimeSearchScreen.kt @@ -11,15 +11,15 @@ import eu.kanade.presentation.browse.anime.components.GlobalAnimeSearchCardRow import eu.kanade.presentation.browse.anime.components.GlobalAnimeSearchToolbar import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.AnimeSearchItemResult +import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.AnimeSearchScreenModel import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.AnimeSourceFilter -import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.GlobalAnimeSearchScreenModel import eu.kanade.tachiyomi.util.system.LocaleHelper import tachiyomi.domain.entries.anime.model.Anime import tachiyomi.presentation.core.components.material.Scaffold @Composable fun GlobalAnimeSearchScreen( - state: GlobalAnimeSearchScreenModel.State, + state: AnimeSearchScreenModel.State, navigateUp: () -> Unit, onChangeSearchQuery: (String?) -> Unit, onSearch: (String) -> Unit, diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/MigrateAnimeSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/MigrateAnimeSearchScreen.kt index 45c989b0c..e02981c07 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/anime/MigrateAnimeSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/anime/MigrateAnimeSearchScreen.kt @@ -4,14 +4,15 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.State import eu.kanade.presentation.browse.anime.components.GlobalAnimeSearchToolbar import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource -import eu.kanade.tachiyomi.ui.browse.anime.migration.search.MigrateAnimeSearchScreenModel +import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.AnimeSearchScreenModel import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.AnimeSourceFilter import tachiyomi.domain.entries.anime.model.Anime import tachiyomi.presentation.core.components.material.Scaffold @Composable fun MigrateAnimeSearchScreen( - state: MigrateAnimeSearchScreenModel.State, + state: AnimeSearchScreenModel.State, + fromSourceId: Long?, navigateUp: () -> Unit, onChangeSearchQuery: (String?) -> Unit, onSearch: (String) -> Unit, @@ -40,7 +41,7 @@ fun MigrateAnimeSearchScreen( }, ) { paddingValues -> GlobalSearchContent( - fromSourceId = state.anime?.source ?: -1, + fromSourceId = fromSourceId, items = state.filteredItems, contentPadding = paddingValues, getAnime = getAnime, diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/components/BrowseAnimeIcons.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/components/BrowseAnimeIcons.kt index 645ccc3ff..81ea0a2fd 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/anime/components/BrowseAnimeIcons.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/anime/components/BrowseAnimeIcons.kt @@ -142,7 +142,7 @@ private fun AnimeExtension.getIcon(density: Int = DisplayMetrics.DENSITY_DEFAULT } sealed class Result { - object Loading : Result() - object Error : Result() + data object Loading : Result() + data object Error : Result() data class Success(val value: T) : Result() } diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/components/GlobalAnimeSearchCardRow.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/components/GlobalAnimeSearchCardRow.kt index 9a32dc568..e44d0878e 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/anime/components/GlobalAnimeSearchCardRow.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/anime/components/GlobalAnimeSearchCardRow.kt @@ -64,6 +64,7 @@ private fun AnimeItem( Box(modifier = Modifier.width(96.dp)) { EntryComfortableGridItem( title = title, + titleMaxLines = 3, coverData = cover, coverBadgeStart = { InLibraryBadge(enabled = isFavorite) diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/components/GlobalAnimeSearchToolbar.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/components/GlobalAnimeSearchToolbar.kt index 8e53b50ed..108a40aa8 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/anime/components/GlobalAnimeSearchToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/anime/components/GlobalAnimeSearchToolbar.kt @@ -56,7 +56,7 @@ fun GlobalAnimeSearchToolbar( navigateUp = navigateUp, scrollBehavior = scrollBehavior, ) - if (progress in 1 until total) { + if (progress in 1..< total) { LinearProgressIndicator( progress = progress / total.toFloat(), modifier = Modifier diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/GlobalMangaSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/GlobalMangaSearchScreen.kt index de6ad0b5d..df4f7ea35 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/manga/GlobalMangaSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/manga/GlobalMangaSearchScreen.kt @@ -10,8 +10,8 @@ import eu.kanade.presentation.browse.GlobalSearchResultItem import eu.kanade.presentation.browse.manga.components.GlobalMangaSearchCardRow import eu.kanade.presentation.browse.manga.components.GlobalMangaSearchToolbar import eu.kanade.tachiyomi.source.CatalogueSource -import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.GlobalMangaSearchScreenModel import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.MangaSearchItemResult +import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.MangaSearchScreenModel import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.MangaSourceFilter import eu.kanade.tachiyomi.util.system.LocaleHelper import tachiyomi.domain.entries.manga.model.Manga @@ -19,7 +19,7 @@ import tachiyomi.presentation.core.components.material.Scaffold @Composable fun GlobalMangaSearchScreen( - state: GlobalMangaSearchScreenModel.State, + state: MangaSearchScreenModel.State, navigateUp: () -> Unit, onChangeSearchQuery: (String?) -> Unit, onSearch: (String) -> Unit, diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/MangaExtensionFilterScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/MangaExtensionFilterScreen.kt index b095f5e8d..875aaa2a7 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/manga/MangaExtensionFilterScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/manga/MangaExtensionFilterScreen.kt @@ -2,6 +2,7 @@ package eu.kanade.presentation.browse.manga import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -12,7 +13,6 @@ import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.browse.manga.extension.MangaExtensionFilterState import eu.kanade.tachiyomi.util.system.LocaleHelper -import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.screens.EmptyScreen @@ -53,7 +53,7 @@ private fun ExtensionFilterContent( onClickLang: (String) -> Unit, ) { val context = LocalContext.current - FastScrollLazyColumn( + LazyColumn( contentPadding = contentPadding, ) { items(state.languages) { language -> diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/MigrateMangaSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/MigrateMangaSearchScreen.kt index cad96d433..a44e52b9d 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/manga/MigrateMangaSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/manga/MigrateMangaSearchScreen.kt @@ -4,14 +4,15 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.State import eu.kanade.presentation.browse.manga.components.GlobalMangaSearchToolbar import eu.kanade.tachiyomi.source.CatalogueSource -import eu.kanade.tachiyomi.ui.browse.manga.migration.search.MigrateSearchScreenModel +import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.MangaSearchScreenModel import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.MangaSourceFilter import tachiyomi.domain.entries.manga.model.Manga import tachiyomi.presentation.core.components.material.Scaffold @Composable fun MigrateMangaSearchScreen( - state: MigrateSearchScreenModel.State, + state: MangaSearchScreenModel.State, + fromSourceId: Long?, navigateUp: () -> Unit, onChangeSearchQuery: (String?) -> Unit, onSearch: (String) -> Unit, @@ -40,7 +41,7 @@ fun MigrateMangaSearchScreen( }, ) { paddingValues -> GlobalSearchContent( - fromSourceId = state.manga?.source, + fromSourceId = fromSourceId, items = state.filteredItems, contentPadding = paddingValues, getManga = getManga, diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/components/BrowseMangaIcons.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/components/BrowseMangaIcons.kt index ec6cf3562..ecc1e111f 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/manga/components/BrowseMangaIcons.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/manga/components/BrowseMangaIcons.kt @@ -142,7 +142,7 @@ private fun MangaExtension.getIcon(density: Int = DisplayMetrics.DENSITY_DEFAULT } sealed class Result { - object Loading : Result() - object Error : Result() + data object Loading : Result() + data object Error : Result() data class Success(val value: T) : Result() } diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/components/GlobalMangaSearchCardRow.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/components/GlobalMangaSearchCardRow.kt index 6e055f3d2..ba4f59e3f 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/manga/components/GlobalMangaSearchCardRow.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/manga/components/GlobalMangaSearchCardRow.kt @@ -64,6 +64,7 @@ private fun MangaItem( Box(modifier = Modifier.width(96.dp)) { EntryComfortableGridItem( title = title, + titleMaxLines = 3, coverData = cover, coverBadgeStart = { InLibraryBadge(enabled = isFavorite) diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/components/GlobalMangaSearchToolbar.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/components/GlobalMangaSearchToolbar.kt index 81d14d992..5b993be5b 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/manga/components/GlobalMangaSearchToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/manga/components/GlobalMangaSearchToolbar.kt @@ -56,7 +56,7 @@ fun GlobalMangaSearchToolbar( navigateUp = navigateUp, scrollBehavior = scrollBehavior, ) - if (progress in 1 until total) { + if (progress in 1.. Unit = { toConfirmIndex -> haptic.performHapticFeedback(HapticFeedbackType.LongPress) - (0 until 9).forEach { i -> confirm[i] = i == toConfirmIndex } + (0..<9).forEach { i -> confirm[i] = i == toConfirmIndex } resetJob?.cancel() resetJob = scope.launch { delay(1.seconds) @@ -273,7 +273,7 @@ fun LibraryBottomActionMenu( var resetJob: Job? = remember { null } val onLongClickItem: (Int) -> Unit = { toConfirmIndex -> haptic.performHapticFeedback(HapticFeedbackType.LongPress) - (0 until 5).forEach { i -> confirm[i] = i == toConfirmIndex } + (0 ..<5).forEach { i -> confirm[i] = i == toConfirmIndex } resetJob?.cancel() resetJob = scope.launch { delay(1.seconds) diff --git a/app/src/main/java/eu/kanade/presentation/library/CommonEntryItem.kt b/app/src/main/java/eu/kanade/presentation/library/CommonEntryItem.kt index 1585cda62..691486449 100644 --- a/app/src/main/java/eu/kanade/presentation/library/CommonEntryItem.kt +++ b/app/src/main/java/eu/kanade/presentation/library/CommonEntryItem.kt @@ -164,6 +164,7 @@ private fun BoxScope.CoverTextOverlay( fun EntryComfortableGridItem( isSelected: Boolean = false, title: String, + titleMaxLines: Int = 2, coverData: EntryCover, coverAlpha: Float = 1f, coverBadgeStart: (@Composable RowScope.() -> Unit)? = null, @@ -205,6 +206,7 @@ fun EntryComfortableGridItem( title = title, style = MaterialTheme.typography.titleSmall, minLines = 2, + maxLines = titleMaxLines, ) } } @@ -254,6 +256,7 @@ private fun GridItemTitle( title: String, style: TextStyle, minLines: Int, + maxLines: Int = 2, ) { Text( modifier = modifier, @@ -261,7 +264,7 @@ private fun GridItemTitle( fontSize = 12.sp, lineHeight = 18.sp, minLines = minLines, - maxLines = 2, + maxLines = maxLines, overflow = TextOverflow.Ellipsis, style = style, ) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt index c28ad702c..19f672583 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt @@ -169,7 +169,7 @@ object SettingsAppearanceScreen : SearchableSettings { Preference.PreferenceItem.ListPreference( pref = uiPreferences.tabletUiMode(), title = stringResource(R.string.pref_tablet_ui_mode), - entries = TabletUiMode.values().associateWith { stringResource(it.titleResId) }, + entries = TabletUiMode.entries.associateWith { stringResource(it.titleResId) }, onValueChanged = { context.toast(R.string.requires_app_restart) true @@ -213,7 +213,7 @@ object SettingsAppearanceScreen : SearchableSettings { var eventType = parser.eventType while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_TAG && parser.name == "locale") { - for (i in 0 until parser.attributeCount) { + for (i in 0.. @@ -270,7 +270,7 @@ private class ClearAnimeDatabaseScreenModel : StateScreenModel, val selection: List = emptyList(), diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt index c567c4346..f278c0bb7 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.FlipToBack @@ -47,7 +48,6 @@ import tachiyomi.data.Database import tachiyomi.domain.source.manga.interactor.GetMangaSourcesWithNonLibraryManga import tachiyomi.domain.source.manga.model.MangaSourceWithCount import tachiyomi.domain.source.manga.model.Source -import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.material.Divider import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.screens.EmptyScreen @@ -135,7 +135,7 @@ class ClearDatabaseScreen : Screen() { .padding(contentPadding) .fillMaxSize(), ) { - FastScrollLazyColumn( + LazyColumn( modifier = Modifier.weight(1f), ) { items(s.items) { sourceWithCount -> @@ -270,7 +270,7 @@ private class ClearDatabaseScreenModel : StateScreenModel, val selection: List = emptyList(), diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt index 3475ba292..60d679386 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt @@ -75,7 +75,7 @@ private fun AppThemesList( onItemClick: (AppTheme) -> Unit, ) { val appThemes = remember { - AppTheme.values() + AppTheme.entries .filterNot { it.titleResId == null || (it == AppTheme.MONET && !DeviceUtil.isDynamicColorAvailable) } } LazyRow( diff --git a/app/src/main/java/eu/kanade/presentation/more/stats/StatsScreenState.kt b/app/src/main/java/eu/kanade/presentation/more/stats/StatsScreenState.kt index cbb776d0e..e120cf51b 100644 --- a/app/src/main/java/eu/kanade/presentation/more/stats/StatsScreenState.kt +++ b/app/src/main/java/eu/kanade/presentation/more/stats/StatsScreenState.kt @@ -5,7 +5,7 @@ import eu.kanade.presentation.more.stats.data.StatsData sealed class StatsScreenState { @Immutable - object Loading : StatsScreenState() + data object Loading : StatsScreenState() @Immutable data class SuccessManga( diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt index dafd3b032..145ab144f 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt @@ -24,9 +24,9 @@ import tachiyomi.presentation.core.components.SettingsChipRow import tachiyomi.presentation.core.components.SliderItem import java.text.NumberFormat -private val readingModeOptions = ReadingModeType.values().map { it.stringRes to it } -private val orientationTypeOptions = OrientationType.values().map { it.stringRes to it } -private val tappingInvertModeOptions = ReaderPreferences.TappingInvertMode.values().map { it.titleResId to it } +private val readingModeOptions = ReadingModeType.entries.map { it.stringRes to it } +private val orientationTypeOptions = OrientationType.entries.map { it.stringRes to it } +private val tappingInvertModeOptions = ReaderPreferences.TappingInvertMode.entries.map { it.titleResId to it } @Composable internal fun ColumnScope.ReadingModePage(screenModel: ReaderSettingsScreenModel) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/AppInfo.kt b/app/src/main/java/eu/kanade/tachiyomi/AppInfo.kt index 8eeda9ef0..322983606 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/AppInfo.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/AppInfo.kt @@ -29,5 +29,5 @@ object AppInfo { * * @since extension-lib 1.5 */ - fun getSupportedImageMimeTypes(): List = ImageUtil.ImageType.values().map { it.mime } + fun getSupportedImageMimeTypes(): List = ImageUtil.ImageType.entries.map { it.mime } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt index 2610db565..b0ce4e3f8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt @@ -161,7 +161,7 @@ sealed class Location { } } - object Cache : Location() + data object Cache : Location() fun directory(context: Context): File { return when (this) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt index 8458a801a..17db81887 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt @@ -67,7 +67,7 @@ class BangumiInterceptor(val bangumi: Bangumi) : Interceptor { private fun addToken(token: String, oidFormBody: FormBody): FormBody { val newFormBody = FormBody.Builder() - for (i in 0 until oidFormBody.size) { + for (i in 0..(State()) { + + init { + coroutineScope.launch { + val anime = getAnime.await(animeId)!! + + mutableState.update { + it.copy(anime = anime) + } + } + } + + fun setDialog(dialog: Dialog?) { + mutableState.update { + it.copy(dialog = dialog) + } + } + + @Immutable + data class State( + val anime: Anime? = null, + val dialog: Dialog? = null, + ) + + sealed class Dialog { + data class Migrate(val anime: Anime) : Dialog() + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/MigrateAnimeSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/MigrateAnimeSearchScreen.kt index a182553d9..1d946c895 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/MigrateAnimeSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/MigrateAnimeSearchScreen.kt @@ -10,7 +10,6 @@ import eu.kanade.presentation.browse.anime.MigrateAnimeSearchScreen import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.ui.entries.anime.AnimeScreen -// TODO: this should probably be merged with GlobalSearchScreen somehow to dedupe logic class MigrateAnimeSearchScreen(private val animeId: Long) : Screen() { @Composable @@ -20,28 +19,32 @@ class MigrateAnimeSearchScreen(private val animeId: Long) : Screen() { val screenModel = rememberScreenModel { MigrateAnimeSearchScreenModel(animeId = animeId) } val state by screenModel.state.collectAsState() + val dialogScreenModel = rememberScreenModel { AnimeMigrateSearchScreenDialogScreenModel(animeId = animeId) } + val dialogState by dialogScreenModel.state.collectAsState() + MigrateAnimeSearchScreen( state = state, + fromSourceId = dialogState.anime?.source, navigateUp = navigator::pop, onChangeSearchQuery = screenModel::updateSearchQuery, - onSearch = screenModel::search, + onSearch = { screenModel.search() }, getAnime = { screenModel.getAnime(it) }, onChangeSearchFilter = screenModel::setSourceFilter, onToggleResults = screenModel::toggleFilterResults, onClickSource = { - navigator.push(AnimeSourceSearchScreen(state.anime!!, it.id, state.searchQuery)) + navigator.push(AnimeSourceSearchScreen(dialogState.anime!!, it.id, state.searchQuery)) }, - onClickItem = { screenModel.setDialog((MigrateAnimeSearchScreenModel.Dialog.Migrate(it))) }, + onClickItem = { dialogScreenModel.setDialog((AnimeMigrateSearchScreenDialogScreenModel.Dialog.Migrate(it))) }, onLongClickItem = { navigator.push(AnimeScreen(it.id, true)) }, ) - when (val dialog = state.dialog) { - is MigrateAnimeSearchScreenModel.Dialog.Migrate -> { + when (val dialog = dialogState.dialog) { + is AnimeMigrateSearchScreenDialogScreenModel.Dialog.Migrate -> { MigrateAnimeDialog( - oldAnime = state.anime!!, + oldAnime = dialogState.anime!!, newAnime = dialog.anime, screenModel = rememberScreenModel { MigrateAnimeDialogScreenModel() }, - onDismissRequest = { screenModel.setDialog(null) }, + onDismissRequest = { dialogScreenModel.setDialog(null) }, onClickTitle = { navigator.push(AnimeScreen(dialog.anime.id, true)) }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/MigrateAnimeSearchScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/MigrateAnimeSearchScreenModel.kt index 2fb5e9493..79ae7cad0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/MigrateAnimeSearchScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/MigrateAnimeSearchScreenModel.kt @@ -1,15 +1,12 @@ package eu.kanade.tachiyomi.ui.browse.anime.migration.search -import androidx.compose.runtime.Immutable import cafe.adriel.voyager.core.model.coroutineScope import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource -import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.AnimeSearchItemResult import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.AnimeSearchScreenModel import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.AnimeSourceFilter import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import tachiyomi.domain.entries.anime.interactor.GetAnime -import tachiyomi.domain.entries.anime.model.Anime import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -17,81 +14,32 @@ class MigrateAnimeSearchScreenModel( val animeId: Long, initialExtensionFilter: String = "", getAnime: GetAnime = Injekt.get(), -) : AnimeSearchScreenModel(State()) { +) : AnimeSearchScreenModel() { init { extensionFilter = initialExtensionFilter coroutineScope.launch { val anime = getAnime.await(animeId)!! - mutableState.update { - it.copy(anime = anime, searchQuery = anime.title) + it.copy( + fromSourceId = anime.source, + searchQuery = anime.title, + ) } - search(anime.title) + search() } } override fun getEnabledSources(): List { return super.getEnabledSources() - .filter { mutableState.value.sourceFilter != AnimeSourceFilter.PinnedOnly || "${it.id}" in pinnedSources } + .filter { state.value.sourceFilter != AnimeSourceFilter.PinnedOnly || "${it.id}" in pinnedSources } .sortedWith( compareBy( - { it.id != state.value.anime!!.source }, + { it.id != state.value.fromSourceId }, { "${it.id}" !in pinnedSources }, { "${it.name.lowercase()} (${it.lang})" }, ), ) } - - override fun updateSearchQuery(query: String?) { - mutableState.update { - it.copy(searchQuery = query) - } - } - - override fun updateItems(items: Map) { - mutableState.update { - it.copy(items = items) - } - } - - override fun getItems(): Map { - return mutableState.value.items - } - - override fun setSourceFilter(filter: AnimeSourceFilter) { - mutableState.update { it.copy(sourceFilter = filter) } - } - - override fun toggleFilterResults() { - mutableState.update { - it.copy(onlyShowHasResults = !it.onlyShowHasResults) - } - } - - fun setDialog(dialog: Dialog?) { - mutableState.update { - it.copy(dialog = dialog) - } - } - - @Immutable - data class State( - val anime: Anime? = null, - val dialog: Dialog? = null, - - val searchQuery: String? = null, - val sourceFilter: AnimeSourceFilter = AnimeSourceFilter.PinnedOnly, - val onlyShowHasResults: Boolean = false, - val items: Map = emptyMap(), - ) { - val progress: Int = items.count { it.value !is AnimeSearchItemResult.Loading } - val total: Int = items.size - val filteredItems = items.filter { (_, result) -> result.isVisible(onlyShowHasResults) } - } - - sealed class Dialog { - data class Migrate(val anime: Anime) : Dialog() - } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/sources/MigrateAnimeSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/sources/MigrateAnimeSourceScreenModel.kt index 3916b1546..1c9763684 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/sources/MigrateAnimeSourceScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/sources/MigrateAnimeSourceScreenModel.kt @@ -77,7 +77,7 @@ class MigrateAnimeSourceScreenModel( } sealed class Event { - object FailedFetchingSourcesWithCount : Event() + data object FailedFetchingSourcesWithCount : Event() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/AnimeSourcesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/AnimeSourcesScreenModel.kt index d096b9b3a..2495ac6df 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/AnimeSourcesScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/AnimeSourcesScreenModel.kt @@ -100,7 +100,7 @@ class AnimeSourcesScreenModel( } sealed class Event { - object FailedFetchingSources : Event() + data object FailedFetchingSources : Event() } data class Dialog(val source: AnimeSource) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreenModel.kt index 85dccf9a5..e7178189d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreenModel.kt @@ -358,8 +358,8 @@ class BrowseAnimeSourceScreenModel( } sealed class Listing(open val query: String?, open val filters: AnimeFilterList) { - object Popular : Listing(query = GetRemoteAnime.QUERY_POPULAR, filters = AnimeFilterList()) - object Latest : Listing(query = GetRemoteAnime.QUERY_LATEST, filters = AnimeFilterList()) + data object Popular : Listing(query = GetRemoteAnime.QUERY_POPULAR, filters = AnimeFilterList()) + data object Latest : Listing(query = GetRemoteAnime.QUERY_LATEST, filters = AnimeFilterList()) data class Search(override val query: String?, override val filters: AnimeFilterList) : Listing(query = query, filters = filters) companion object { @@ -374,7 +374,7 @@ class BrowseAnimeSourceScreenModel( } sealed class Dialog { - object Filter : Dialog() + data object Filter : Dialog() data class RemoveAnime(val anime: Anime) : Dialog() data class AddDuplicateAnime(val anime: Anime, val duplicate: Anime) : Dialog() data class ChangeAnimeCategory( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/AnimeSearchScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/AnimeSearchScreenModel.kt index 48fdf7268..2de41158a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/AnimeSearchScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/AnimeSearchScreenModel.kt @@ -1,10 +1,9 @@ package eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch import androidx.compose.runtime.Composable -import androidx.compose.runtime.State +import androidx.compose.runtime.Immutable import androidx.compose.runtime.produceState import cafe.adriel.voyager.core.model.StateScreenModel -import eu.kanade.domain.entries.anime.interactor.UpdateAnime import eu.kanade.domain.entries.anime.model.toDomainAnime import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.presentation.util.ioCoroutineScope @@ -16,6 +15,8 @@ import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import tachiyomi.core.util.lang.awaitSingle @@ -27,25 +28,27 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.util.concurrent.Executors -abstract class AnimeSearchScreenModel( - initialState: T, - private val sourcePreferences: SourcePreferences = Injekt.get(), +abstract class AnimeSearchScreenModel( + initialState: State = State(), + sourcePreferences: SourcePreferences = Injekt.get(), private val sourceManager: AnimeSourceManager = Injekt.get(), private val extensionManager: AnimeExtensionManager = Injekt.get(), private val networkToLocalAnime: NetworkToLocalAnime = Injekt.get(), private val getAnime: GetAnime = Injekt.get(), - private val updateAnime: UpdateAnime = Injekt.get(), -) : StateScreenModel(initialState) { +) : StateScreenModel(initialState) { private val coroutineDispatcher = Executors.newFixedThreadPool(5).asCoroutineDispatcher() private var searchJob: Job? = null - protected var query: String? = null - protected var extensionFilter: String? = null - - private val sources by lazy { getSelectedSources() } + private val enabledLanguages = sourcePreferences.enabledLanguages().get() + private val disabledSources = sourcePreferences.disabledAnimeSources().get() protected val pinnedSources = sourcePreferences.pinnedAnimeSources().get() + private var lastQuery: String? = null + private var lastSourceFilter: AnimeSourceFilter? = null + + protected var extensionFilter: String? = null + private val sortComparator = { map: Map -> compareBy( { (map[it] as? AnimeSearchItemResult.Success)?.isEmpty ?: true }, @@ -55,7 +58,7 @@ abstract class AnimeSearchScreenModel( } @Composable - fun getAnime(initialAnime: Anime): State { + fun getAnime(initialAnime: Anime): androidx.compose.runtime.State { return produceState(initialValue = initialAnime) { getAnime.subscribe(initialAnime.url, initialAnime.source) .filterNotNull() @@ -66,10 +69,6 @@ abstract class AnimeSearchScreenModel( } open fun getEnabledSources(): List { - val enabledLanguages = sourcePreferences.enabledLanguages().get() - val disabledSources = sourcePreferences.disabledAnimeSources().get() - val pinnedSources = sourcePreferences.pinnedAnimeSources().get() - return sourceManager.getCatalogueSources() .filter { it.lang in enabledLanguages && "${it.id}" !in disabledSources } .sortedWith( @@ -95,56 +94,91 @@ abstract class AnimeSearchScreenModel( .filter { it in enabledSources } } - abstract fun updateSearchQuery(query: String?) - - abstract fun updateItems(items: Map) - - abstract fun getItems(): Map - - private fun getAndUpdateItems(function: (Map) -> Map) { - updateItems(function(getItems())) + fun updateSearchQuery(query: String?) { + mutableState.update { it.copy(searchQuery = query) } } - abstract fun setSourceFilter(filter: AnimeSourceFilter) + fun setSourceFilter(filter: AnimeSourceFilter) { + mutableState.update { it.copy(sourceFilter = filter) } + search() + } - abstract fun toggleFilterResults() + fun toggleFilterResults() { + mutableState.update { it.copy(onlyShowHasResults = !it.onlyShowHasResults) } + } - fun search(query: String) { - if (this.query == query) return + fun search() { + val query = state.value.searchQuery + val sourceFilter = state.value.sourceFilter - this.query = query + if (query.isNullOrBlank()) return + val sameQuery = this.lastQuery == query + if (sameQuery && this.lastSourceFilter == sourceFilter) return + + this.lastQuery = query + this.lastSourceFilter = sourceFilter + + val sources = getSelectedSources() + + // Reuse previous results if possible + if (sameQuery) { + val existingResults = state.value.items + updateItems(sources.associateWith { existingResults[it] ?: AnimeSearchItemResult.Loading }) + } else { + updateItems(sources.associateWith { AnimeSearchItemResult.Loading }) + } - val initialItems = getSelectedSources().associateWith { AnimeSearchItemResult.Loading } - updateItems(initialItems) searchJob = ioCoroutineScope.launch { - sources - .map { source -> - async { - try { - val page = withContext(coroutineDispatcher) { - source.fetchSearchAnime(1, query, source.getFilterList()).awaitSingle() - } + sources.map { source -> + async { + if (state.value.items[source] !is AnimeSearchItemResult.Loading) { + return@async + } + try { + val page = withContext(coroutineDispatcher) { + source.fetchSearchAnime(1, query, source.getFilterList()).awaitSingle() + } - val titles = page.animes.map { - networkToLocalAnime.await(it.toDomainAnime(source.id)) - } + val titles = page.animes.map { + networkToLocalAnime.await(it.toDomainAnime(source.id)) + } - getAndUpdateItems { items -> - val mutableMap = items.toMutableMap() - mutableMap[source] = AnimeSearchItemResult.Success(titles) - mutableMap.toSortedMap(sortComparator(mutableMap)) - } - } catch (e: Exception) { - getAndUpdateItems { items -> - val mutableMap = items.toMutableMap() - mutableMap[source] = AnimeSearchItemResult.Error(e) - mutableMap.toSortedMap(sortComparator(mutableMap)) - } + if (isActive) { + updateItem(source, AnimeSearchItemResult.Success(titles)) + } + } catch (e: Exception) { + if (isActive) { + updateItem(source, AnimeSearchItemResult.Error(e)) } } - }.awaitAll() + } + } + .awaitAll() } } + + private fun updateItems(items: Map) { + mutableState.update { it.copy(items = items.toSortedMap(sortComparator(items))) } + } + + private fun updateItem(source: AnimeCatalogueSource, result: AnimeSearchItemResult) { + val mutableItems = state.value.items.toMutableMap() + mutableItems[source] = result + updateItems(mutableItems) + } + + @Immutable + data class State( + val fromSourceId: Long? = null, + val searchQuery: String? = null, + val sourceFilter: AnimeSourceFilter = AnimeSourceFilter.PinnedOnly, + val onlyShowHasResults: Boolean = false, + val items: Map = emptyMap(), + ) { + val progress: Int = items.count { it.value !is AnimeSearchItemResult.Loading } + val total: Int = items.size + val filteredItems = items.filter { (_, result) -> result.isVisible(onlyShowHasResults) } + } } enum class AnimeSourceFilter { @@ -153,7 +187,7 @@ enum class AnimeSourceFilter { } sealed class AnimeSearchItemResult { - object Loading : AnimeSearchItemResult() + data object Loading : AnimeSearchItemResult() data class Error( val throwable: Throwable, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/GlobalAnimeSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/GlobalAnimeSearchScreen.kt index 8b465694c..fa79b4ee8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/GlobalAnimeSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/GlobalAnimeSearchScreen.kt @@ -59,7 +59,7 @@ class GlobalAnimeSearchScreen( state = state, navigateUp = navigator::pop, onChangeSearchQuery = screenModel::updateSearchQuery, - onSearch = screenModel::search, + onSearch = { screenModel.search() }, getAnime = { screenModel.getAnime(it) }, onChangeSearchFilter = screenModel::setSourceFilter, onToggleResults = screenModel::toggleFilterResults, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/GlobalAnimeSearchScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/GlobalAnimeSearchScreenModel.kt index 7a079c308..52194462c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/GlobalAnimeSearchScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/GlobalAnimeSearchScreenModel.kt @@ -1,14 +1,12 @@ package eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch -import androidx.compose.runtime.Immutable import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource -import kotlinx.coroutines.flow.update import uy.kohesive.injekt.api.get class GlobalAnimeSearchScreenModel( initialQuery: String = "", initialExtensionFilter: String? = null, -) : AnimeSearchScreenModel( +) : AnimeSearchScreenModel( State( searchQuery = initialQuery, ), @@ -17,50 +15,16 @@ class GlobalAnimeSearchScreenModel( init { extensionFilter = initialExtensionFilter if (initialQuery.isNotBlank() || !initialExtensionFilter.isNullOrBlank()) { - search(initialQuery) + if (extensionFilter != null) { + // we're going to use custom extension filter instead + setSourceFilter(AnimeSourceFilter.All) + } + search() } } override fun getEnabledSources(): List { return super.getEnabledSources() - .filter { mutableState.value.sourceFilter != AnimeSourceFilter.PinnedOnly || "${it.id}" in pinnedSources } - } - - override fun updateSearchQuery(query: String?) { - mutableState.update { - it.copy(searchQuery = query) - } - } - - override fun updateItems(items: Map) { - mutableState.update { - it.copy(items = items) - } - } - - override fun getItems(): Map { - return mutableState.value.items - } - - override fun setSourceFilter(filter: AnimeSourceFilter) { - mutableState.update { it.copy(sourceFilter = filter) } - } - - override fun toggleFilterResults() { - mutableState.update { - it.copy(onlyShowHasResults = !it.onlyShowHasResults) - } - } - - @Immutable - data class State( - val searchQuery: String? = null, - val sourceFilter: AnimeSourceFilter = AnimeSourceFilter.PinnedOnly, - val onlyShowHasResults: Boolean = false, - val items: Map = emptyMap(), - ) { - val progress: Int = items.count { it.value !is AnimeSearchItemResult.Loading } - val total: Int = items.size - val filteredItems = items.filter { (_, result) -> result.isVisible(onlyShowHasResults) } + .filter { state.value.sourceFilter != AnimeSourceFilter.PinnedOnly || "${it.id}" in pinnedSources } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/MangaExtensionFilterScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/MangaExtensionFilterScreenModel.kt index 4ec195d4d..2378cd6b8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/MangaExtensionFilterScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/MangaExtensionFilterScreenModel.kt @@ -55,13 +55,13 @@ class MangaExtensionFilterScreenModel( } sealed class MangaExtensionFilterEvent { - object FailedFetchingLanguages : MangaExtensionFilterEvent() + data object FailedFetchingLanguages : MangaExtensionFilterEvent() } sealed class MangaExtensionFilterState { @Immutable - object Loading : MangaExtensionFilterState() + data object Loading : MangaExtensionFilterState() @Immutable data class Success( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/details/MangaExtensionDetailsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/details/MangaExtensionDetailsScreenModel.kt index 5061eb811..91be7d00b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/details/MangaExtensionDetailsScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/details/MangaExtensionDetailsScreenModel.kt @@ -163,7 +163,7 @@ class MangaExtensionDetailsScreenModel( } sealed class MangaExtensionDetailsEvent { - object Uninstalled : MangaExtensionDetailsEvent() + data object Uninstalled : MangaExtensionDetailsEvent() } @Immutable diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/manga/MigrationMangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/manga/MigrationMangaScreenModel.kt index b4fad5713..aa181fb5f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/manga/MigrationMangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/manga/MigrationMangaScreenModel.kt @@ -54,7 +54,7 @@ class MigrationMangaScreenModel( } sealed class MigrationMangaEvent { - object FailedFetchingFavorites : MigrationMangaEvent() + data object FailedFetchingFavorites : MigrationMangaEvent() } @Immutable diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MangaMigrateSearchScreenDialogScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MangaMigrateSearchScreenDialogScreenModel.kt new file mode 100644 index 000000000..e6577b09e --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MangaMigrateSearchScreenDialogScreenModel.kt @@ -0,0 +1,43 @@ +package eu.kanade.tachiyomi.ui.browse.manga.migration.search + +import androidx.compose.runtime.Immutable +import cafe.adriel.voyager.core.model.StateScreenModel +import cafe.adriel.voyager.core.model.coroutineScope +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import tachiyomi.domain.entries.manga.interactor.GetManga +import tachiyomi.domain.entries.manga.model.Manga +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +class MangaMigrateSearchScreenDialogScreenModel( + val mangaId: Long, + getManga: GetManga = Injekt.get(), +) : StateScreenModel(State()) { + + init { + coroutineScope.launch { + val manga = getManga.await(mangaId)!! + + mutableState.update { + it.copy(manga = manga) + } + } + } + + fun setDialog(dialog: Dialog?) { + mutableState.update { + it.copy(dialog = dialog) + } + } + + @Immutable + data class State( + val manga: Manga? = null, + val dialog: Dialog? = null, + ) + + sealed class Dialog { + data class Migrate(val manga: Manga) : Dialog() + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MigrateMangaSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MigrateMangaSearchScreen.kt index e6b5c6cd1..e467aa48a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MigrateMangaSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MigrateMangaSearchScreen.kt @@ -10,7 +10,6 @@ import eu.kanade.presentation.browse.manga.MigrateMangaSearchScreen import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.ui.entries.manga.MangaScreen -// TODO: this should probably be merged with GlobalSearchScreen somehow to dedupe logic class MigrateSearchScreen(private val mangaId: Long) : Screen() { @Composable @@ -20,28 +19,32 @@ class MigrateSearchScreen(private val mangaId: Long) : Screen() { val screenModel = rememberScreenModel { MigrateSearchScreenModel(mangaId = mangaId) } val state by screenModel.state.collectAsState() + val dialogScreenModel = rememberScreenModel { MangaMigrateSearchScreenDialogScreenModel(mangaId = mangaId) } + val dialogState by dialogScreenModel.state.collectAsState() + MigrateMangaSearchScreen( state = state, + fromSourceId = dialogState.manga?.source, navigateUp = navigator::pop, onChangeSearchQuery = screenModel::updateSearchQuery, - onSearch = screenModel::search, + onSearch = { screenModel.search() }, getManga = { screenModel.getManga(it) }, onChangeSearchFilter = screenModel::setSourceFilter, onToggleResults = screenModel::toggleFilterResults, onClickSource = { - navigator.push(MangaSourceSearchScreen(state.manga!!, it.id, state.searchQuery)) + navigator.push(MangaSourceSearchScreen(dialogState.manga!!, it.id, state.searchQuery)) }, - onClickItem = { screenModel.setDialog(MigrateSearchScreenModel.Dialog.Migrate(it)) }, + onClickItem = { dialogScreenModel.setDialog(MangaMigrateSearchScreenDialogScreenModel.Dialog.Migrate(it)) }, onLongClickItem = { navigator.push(MangaScreen(it.id, true)) }, ) - when (val dialog = state.dialog) { - is MigrateSearchScreenModel.Dialog.Migrate -> { + when (val dialog = dialogState.dialog) { + is MangaMigrateSearchScreenDialogScreenModel.Dialog.Migrate -> { MigrateMangaDialog( - oldManga = state.manga!!, + oldManga = dialogState.manga!!, newManga = dialog.manga, screenModel = rememberScreenModel { MigrateMangaDialogScreenModel() }, - onDismissRequest = { screenModel.setDialog(null) }, + onDismissRequest = { dialogScreenModel.setDialog(null) }, onClickTitle = { navigator.push(MangaScreen(dialog.manga.id, true)) }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MigrateMangaSearchScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MigrateMangaSearchScreenModel.kt index 38bdc86af..93102a5ca 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MigrateMangaSearchScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MigrateMangaSearchScreenModel.kt @@ -1,15 +1,12 @@ package eu.kanade.tachiyomi.ui.browse.manga.migration.search -import androidx.compose.runtime.Immutable import cafe.adriel.voyager.core.model.coroutineScope import eu.kanade.tachiyomi.source.CatalogueSource -import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.MangaSearchItemResult import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.MangaSearchScreenModel import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.MangaSourceFilter import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import tachiyomi.domain.entries.manga.interactor.GetManga -import tachiyomi.domain.entries.manga.model.Manga import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -17,81 +14,32 @@ class MigrateSearchScreenModel( val mangaId: Long, initialExtensionFilter: String = "", getManga: GetManga = Injekt.get(), -) : MangaSearchScreenModel(State()) { +) : MangaSearchScreenModel() { init { extensionFilter = initialExtensionFilter coroutineScope.launch { val manga = getManga.await(mangaId)!! - mutableState.update { - it.copy(manga = manga, searchQuery = manga.title) + it.copy( + fromSourceId = manga.source, + searchQuery = manga.title, + ) } - search(manga.title) + search() } } override fun getEnabledSources(): List { return super.getEnabledSources() - .filter { mutableState.value.sourceFilter != MangaSourceFilter.PinnedOnly || "${it.id}" in pinnedSources } + .filter { state.value.sourceFilter != MangaSourceFilter.PinnedOnly || "${it.id}" in pinnedSources } .sortedWith( compareBy( - { it.id != state.value.manga!!.source }, + { it.id != state.value.fromSourceId }, { "${it.id}" !in pinnedSources }, { "${it.name.lowercase()} (${it.lang})" }, ), ) } - - override fun updateSearchQuery(query: String?) { - mutableState.update { - it.copy(searchQuery = query) - } - } - - override fun updateItems(items: Map) { - mutableState.update { - it.copy(items = items) - } - } - - override fun getItems(): Map { - return mutableState.value.items - } - - override fun setSourceFilter(filter: MangaSourceFilter) { - mutableState.update { it.copy(sourceFilter = filter) } - } - - override fun toggleFilterResults() { - mutableState.update { - it.copy(onlyShowHasResults = !it.onlyShowHasResults) - } - } - - fun setDialog(dialog: Dialog?) { - mutableState.update { - it.copy(dialog = dialog) - } - } - - @Immutable - data class State( - val manga: Manga? = null, - val dialog: Dialog? = null, - - val searchQuery: String? = null, - val sourceFilter: MangaSourceFilter = MangaSourceFilter.PinnedOnly, - val onlyShowHasResults: Boolean = false, - val items: Map = emptyMap(), - ) { - val progress: Int = items.count { it.value !is MangaSearchItemResult.Loading } - val total: Int = items.size - val filteredItems = items.filter { (_, result) -> result.isVisible(onlyShowHasResults) } - } - - sealed class Dialog { - data class Migrate(val manga: Manga) : Dialog() - } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/sources/MigrateMangaSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/sources/MigrateMangaSourceScreenModel.kt index 03df6ca64..f3b2bcbc3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/sources/MigrateMangaSourceScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/sources/MigrateMangaSourceScreenModel.kt @@ -77,7 +77,7 @@ class MigrateSourceScreenModel( } sealed class Event { - object FailedFetchingSourcesWithCount : Event() + data object FailedFetchingSourcesWithCount : Event() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/MangaSourcesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/MangaSourcesScreenModel.kt index ad48f605e..04bc7d172 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/MangaSourcesScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/MangaSourcesScreenModel.kt @@ -124,7 +124,7 @@ class MangaSourcesScreenModel( } sealed class Event { - object FailedFetchingSources : Event() + data object FailedFetchingSources : Event() } data class Dialog(val source: Source) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreenModel.kt index 8d3bbadcb..ef6c3faf3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreenModel.kt @@ -352,8 +352,8 @@ class BrowseMangaSourceScreenModel( } sealed class Listing(open val query: String?, open val filters: FilterList) { - object Popular : Listing(query = GetRemoteManga.QUERY_POPULAR, filters = FilterList()) - object Latest : Listing(query = GetRemoteManga.QUERY_LATEST, filters = FilterList()) + data object Popular : Listing(query = GetRemoteManga.QUERY_POPULAR, filters = FilterList()) + data object Latest : Listing(query = GetRemoteManga.QUERY_LATEST, filters = FilterList()) data class Search(override val query: String?, override val filters: FilterList) : Listing(query = query, filters = filters) companion object { @@ -368,7 +368,7 @@ class BrowseMangaSourceScreenModel( } sealed class Dialog { - object Filter : 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( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/GlobalMangaSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/GlobalMangaSearchScreen.kt index d25756ec3..6c0df3958 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/GlobalMangaSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/GlobalMangaSearchScreen.kt @@ -59,7 +59,7 @@ class GlobalMangaSearchScreen( state = state, navigateUp = navigator::pop, onChangeSearchQuery = screenModel::updateSearchQuery, - onSearch = screenModel::search, + onSearch = { screenModel.search() }, getManga = { screenModel.getManga(it) }, onChangeSearchFilter = screenModel::setSourceFilter, onToggleResults = screenModel::toggleFilterResults, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/GlobalMangaSearchScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/GlobalMangaSearchScreenModel.kt index c1a4d52b4..5a6489ddd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/GlobalMangaSearchScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/GlobalMangaSearchScreenModel.kt @@ -1,14 +1,12 @@ package eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch -import androidx.compose.runtime.Immutable import eu.kanade.tachiyomi.source.CatalogueSource -import kotlinx.coroutines.flow.update import uy.kohesive.injekt.api.get class GlobalMangaSearchScreenModel( initialQuery: String = "", initialExtensionFilter: String? = null, -) : MangaSearchScreenModel( +) : MangaSearchScreenModel( State( searchQuery = initialQuery, ), @@ -17,50 +15,16 @@ class GlobalMangaSearchScreenModel( init { extensionFilter = initialExtensionFilter if (initialQuery.isNotBlank() || !initialExtensionFilter.isNullOrBlank()) { - search(initialQuery) + if (extensionFilter != null) { + // we're going to use custom extension filter instead + setSourceFilter(MangaSourceFilter.All) + } + search() } } override fun getEnabledSources(): List { return super.getEnabledSources() - .filter { mutableState.value.sourceFilter != MangaSourceFilter.PinnedOnly || "${it.id}" in pinnedSources } - } - - override fun updateSearchQuery(query: String?) { - mutableState.update { - it.copy(searchQuery = query) - } - } - - override fun updateItems(items: Map) { - mutableState.update { - it.copy(items = items) - } - } - - override fun getItems(): Map { - return mutableState.value.items - } - - override fun setSourceFilter(filter: MangaSourceFilter) { - mutableState.update { it.copy(sourceFilter = filter) } - } - - override fun toggleFilterResults() { - mutableState.update { - it.copy(onlyShowHasResults = !it.onlyShowHasResults) - } - } - - @Immutable - data class State( - val searchQuery: String? = null, - val sourceFilter: MangaSourceFilter = MangaSourceFilter.PinnedOnly, - val onlyShowHasResults: Boolean = false, - val items: Map = emptyMap(), - ) { - val progress: Int = items.count { it.value !is MangaSearchItemResult.Loading } - val total: Int = items.size - val filteredItems = items.filter { (_, result) -> result.isVisible(onlyShowHasResults) } + .filter { state.value.sourceFilter != MangaSourceFilter.PinnedOnly || "${it.id}" in pinnedSources } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/MangaSearchScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/MangaSearchScreenModel.kt index bbb0d65e5..dfdedbea6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/MangaSearchScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/MangaSearchScreenModel.kt @@ -1,10 +1,9 @@ package eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch import androidx.compose.runtime.Composable -import androidx.compose.runtime.State +import androidx.compose.runtime.Immutable import androidx.compose.runtime.produceState import cafe.adriel.voyager.core.model.StateScreenModel -import eu.kanade.domain.entries.manga.interactor.UpdateManga import eu.kanade.domain.entries.manga.model.toDomainManga import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.presentation.util.ioCoroutineScope @@ -16,6 +15,8 @@ import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import tachiyomi.core.util.lang.awaitSingle @@ -27,25 +28,27 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.util.concurrent.Executors -abstract class MangaSearchScreenModel( - initialState: T, - private val sourcePreferences: SourcePreferences = Injekt.get(), +abstract class MangaSearchScreenModel( + initialState: State = State(), + sourcePreferences: SourcePreferences = Injekt.get(), private val sourceManager: MangaSourceManager = Injekt.get(), private val extensionManager: MangaExtensionManager = Injekt.get(), private val networkToLocalManga: NetworkToLocalManga = Injekt.get(), private val getManga: GetManga = Injekt.get(), - private val updateManga: UpdateManga = Injekt.get(), -) : StateScreenModel(initialState) { +) : StateScreenModel(initialState) { private val coroutineDispatcher = Executors.newFixedThreadPool(5).asCoroutineDispatcher() private var searchJob: Job? = null - protected var query: String? = null - protected var extensionFilter: String? = null - - private val sources by lazy { getSelectedSources() } + private val enabledLanguages = sourcePreferences.enabledLanguages().get() + private val disabledSources = sourcePreferences.disabledMangaSources().get() protected val pinnedSources = sourcePreferences.pinnedMangaSources().get() + private var lastQuery: String? = null + private var lastSourceFilter: MangaSourceFilter? = null + + protected var extensionFilter: String? = null + private val sortComparator = { map: Map -> compareBy( { (map[it] as? MangaSearchItemResult.Success)?.isEmpty ?: true }, @@ -55,7 +58,7 @@ abstract class MangaSearchScreenModel( } @Composable - fun getManga(initialManga: Manga): State { + fun getManga(initialManga: Manga): androidx.compose.runtime.State { return produceState(initialValue = initialManga) { getManga.subscribe(initialManga.url, initialManga.source) .filterNotNull() @@ -66,10 +69,6 @@ abstract class MangaSearchScreenModel( } open fun getEnabledSources(): List { - val enabledLanguages = sourcePreferences.enabledLanguages().get() - val disabledSources = sourcePreferences.disabledMangaSources().get() - val pinnedSources = sourcePreferences.pinnedMangaSources().get() - return sourceManager.getCatalogueSources() .filter { it.lang in enabledLanguages && "${it.id}" !in disabledSources } .sortedWith( @@ -95,57 +94,91 @@ abstract class MangaSearchScreenModel( .filter { it in enabledSources } } - abstract fun updateSearchQuery(query: String?) - - abstract fun updateItems(items: Map) - - abstract fun getItems(): Map - - private fun getAndUpdateItems(function: (Map) -> Map) { - updateItems(function(getItems())) + fun updateSearchQuery(query: String?) { + mutableState.update { it.copy(searchQuery = query) } } - abstract fun setSourceFilter(filter: MangaSourceFilter) + fun setSourceFilter(filter: MangaSourceFilter) { + mutableState.update { it.copy(sourceFilter = filter) } + search() + } - abstract fun toggleFilterResults() + fun toggleFilterResults() { + mutableState.update { it.copy(onlyShowHasResults = !it.onlyShowHasResults) } + } - fun search(query: String) { - if (this.query == query) return + fun search() { + val query = state.value.searchQuery + val sourceFilter = state.value.sourceFilter - this.query = query + if (query.isNullOrBlank()) return + val sameQuery = this.lastQuery == query + if (sameQuery && this.lastSourceFilter == sourceFilter) return + + this.lastQuery = query + this.lastSourceFilter = sourceFilter searchJob?.cancel() - val initialItems = getSelectedSources().associateWith { MangaSearchItemResult.Loading } - updateItems(initialItems) + val sources = getSelectedSources() + + // Reuse previous results if possible + if (sameQuery) { + val existingResults = state.value.items + updateItems(sources.associateWith { existingResults[it] ?: MangaSearchItemResult.Loading }) + } else { + updateItems(sources.associateWith { MangaSearchItemResult.Loading }) + } searchJob = ioCoroutineScope.launch { - sources - .map { source -> - async { - try { - val page = withContext(coroutineDispatcher) { - source.fetchSearchManga(1, query, source.getFilterList()).awaitSingle() - } + sources.map { source -> + async { + if (state.value.items[source] !is MangaSearchItemResult.Loading) { + return@async + } + try { + val page = withContext(coroutineDispatcher) { + source.fetchSearchManga(1, query, source.getFilterList()).awaitSingle() + } - val titles = page.mangas.map { - networkToLocalManga.await(it.toDomainManga(source.id)) - } + val titles = page.mangas.map { + networkToLocalManga.await(it.toDomainManga(source.id)) + } - getAndUpdateItems { items -> - val mutableMap = items.toMutableMap() - mutableMap[source] = MangaSearchItemResult.Success(titles) - mutableMap.toSortedMap(sortComparator(mutableMap)) - } - } catch (e: Exception) { - getAndUpdateItems { items -> - val mutableMap = items.toMutableMap() - mutableMap[source] = MangaSearchItemResult.Error(e) - mutableMap.toSortedMap(sortComparator(mutableMap)) - } + if (isActive) { + updateItem(source, MangaSearchItemResult.Success(titles)) + } + } catch (e: Exception) { + if (isActive) { + updateItem(source, MangaSearchItemResult.Error(e)) } } - }.awaitAll() + } + } + .awaitAll() } } + + private fun updateItems(items: Map) { + mutableState.update { it.copy(items = items.toSortedMap(sortComparator(items))) } + } + + private fun updateItem(source: CatalogueSource, result: MangaSearchItemResult) { + val mutableItems = state.value.items.toMutableMap() + mutableItems[source] = result + updateItems(mutableItems) + } + + @Immutable + data class State( + val fromSourceId: Long? = null, + val searchQuery: String? = null, + val sourceFilter: MangaSourceFilter = MangaSourceFilter.PinnedOnly, + val onlyShowHasResults: Boolean = false, + val items: Map = emptyMap(), + ) { + val progress: Int = items.count { it.value !is MangaSearchItemResult.Loading } + val total: Int = items.size + val filteredItems = items.filter { (_, result) -> result.isVisible(onlyShowHasResults) } + } } enum class MangaSourceFilter { @@ -154,7 +187,7 @@ enum class MangaSourceFilter { } sealed class MangaSearchItemResult { - object Loading : MangaSearchItemResult() + data object Loading : MangaSearchItemResult() data class Error( val throwable: Throwable, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/anime/AnimeCategoryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/anime/AnimeCategoryScreenModel.kt index 0c2c3640e..fdca78633 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/anime/AnimeCategoryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/category/anime/AnimeCategoryScreenModel.kt @@ -131,20 +131,20 @@ class AnimeCategoryScreenModel( } sealed class AnimeCategoryDialog { - object Create : AnimeCategoryDialog() + data object Create : AnimeCategoryDialog() data class Rename(val category: Category) : AnimeCategoryDialog() data class Delete(val category: Category) : AnimeCategoryDialog() } sealed class AnimeCategoryEvent { sealed class LocalizedMessage(@StringRes val stringRes: Int) : AnimeCategoryEvent() - object InternalError : LocalizedMessage(R.string.internal_error) + data object InternalError : LocalizedMessage(R.string.internal_error) } sealed class AnimeCategoryScreenState { @Immutable - object Loading : AnimeCategoryScreenState() + data object Loading : AnimeCategoryScreenState() @Immutable data class Success( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/manga/MangaCategoryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/manga/MangaCategoryScreenModel.kt index 8991d53fb..4a6799124 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/manga/MangaCategoryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/category/manga/MangaCategoryScreenModel.kt @@ -131,20 +131,20 @@ class MangaCategoryScreenModel( } sealed class MangaCategoryDialog { - object Create : MangaCategoryDialog() + data object Create : MangaCategoryDialog() data class Rename(val category: Category) : MangaCategoryDialog() data class Delete(val category: Category) : MangaCategoryDialog() } sealed class MangaCategoryEvent { sealed class LocalizedMessage(@StringRes val stringRes: Int) : MangaCategoryEvent() - object InternalError : LocalizedMessage(R.string.internal_error) + data object InternalError : LocalizedMessage(R.string.internal_error) } sealed class MangaCategoryScreenState { @Immutable - object Loading : MangaCategoryScreenState() + data object Loading : MangaCategoryScreenState() @Immutable data class Success( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreenModel.kt index f736a9998..9b827b65c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreenModel.kt @@ -877,10 +877,10 @@ class AnimeInfoScreenModel( // Try to select the items in-between when possible val range: IntRange if (selectedIndex < selectedPositions[0]) { - range = selectedIndex + 1 until selectedPositions[0] + range = selectedIndex + 1 ..< selectedPositions[0] selectedPositions[0] = selectedIndex } else if (selectedIndex > selectedPositions[1]) { - range = (selectedPositions[1] + 1) until selectedIndex + range = (selectedPositions[1] + 1) ..< selectedIndex selectedPositions[1] = selectedIndex } else { // Just select itself @@ -976,10 +976,10 @@ class AnimeInfoScreenModel( data class DeleteEpisodes(val episodes: List) : Dialog() data class DuplicateAnime(val anime: Anime, val duplicate: Anime) : Dialog() data class ShowQualities(val episode: Episode, val anime: Anime, val source: AnimeSource) : Dialog() - object ChangeAnimeSkipIntro : Dialog() - object SettingsSheet : Dialog() - object TrackSheet : Dialog() - object FullCover : Dialog() + data object ChangeAnimeSkipIntro : Dialog() + data object SettingsSheet : Dialog() + data object TrackSheet : Dialog() + data object FullCover : Dialog() } fun dismissDialog() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreenModel.kt index 4dfb5fecd..370b6f430 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreenModel.kt @@ -867,10 +867,10 @@ class MangaInfoScreenModel( // Try to select the items in-between when possible val range: IntRange if (selectedIndex < selectedPositions[0]) { - range = selectedIndex + 1 until selectedPositions[0] + range = selectedIndex + 1 ..< selectedPositions[0] selectedPositions[0] = selectedIndex } else if (selectedIndex > selectedPositions[1]) { - range = (selectedPositions[1] + 1) until selectedIndex + range = (selectedPositions[1] + 1) ..< selectedIndex selectedPositions[1] = selectedIndex } else { // Just select itself @@ -959,9 +959,9 @@ class MangaInfoScreenModel( data class ChangeCategory(val manga: Manga, val initialSelection: List>) : Dialog() data class DeleteChapters(val chapters: List) : Dialog() data class DuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog() - object SettingsSheet : Dialog() - object TrackSheet : Dialog() - object FullCover : Dialog() + data object SettingsSheet : Dialog() + data object TrackSheet : Dialog() + data object FullCover : Dialog() } fun dismissDialog() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/anime/AnimeHistoryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/anime/AnimeHistoryScreenModel.kt index ef8f53ddf..1b1d0a6c4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/anime/AnimeHistoryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/anime/AnimeHistoryScreenModel.kt @@ -120,14 +120,14 @@ class AnimeHistoryScreenModel( } sealed class Dialog { - object DeleteAll : Dialog() + data object DeleteAll : Dialog() data class Delete(val history: AnimeHistoryWithRelations) : Dialog() } sealed class Event { data class OpenEpisode(val episode: Episode?) : Event() - object InternalError : Event() - object HistoryCleared : Event() + data object InternalError : Event() + data object HistoryCleared : Event() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/manga/MangaHistoryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/manga/MangaHistoryScreenModel.kt index 1d87a176b..cab14a60f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/manga/MangaHistoryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/manga/MangaHistoryScreenModel.kt @@ -120,14 +120,14 @@ class MangaHistoryScreenModel( } sealed class Dialog { - object DeleteAll : Dialog() + data object DeleteAll : Dialog() data class Delete(val history: MangaHistoryWithRelations) : Dialog() } sealed class Event { data class OpenChapter(val chapter: Chapter?) : Event() - object InternalError : Event() - object HistoryCleared : Event() + data object InternalError : Event() + data object HistoryCleared : Event() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt index 7205eb4e2..4986fa03b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt @@ -339,8 +339,8 @@ object HomeScreen : Screen() { sealed class Tab { data class Animelib(val animeIdToOpen: Long? = null) : Tab() data class Library(val mangaIdToOpen: Long? = null) : Tab() - object Updates : Tab() - object History : Tab() + data object Updates : Tab() + data object History : Tab() data class Browse(val toExtensions: Boolean = false) : Tab() data class More(val toDownloads: Boolean) : Tab() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryScreenModel.kt index 73d10f9b3..8ea7a66b8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryScreenModel.kt @@ -665,7 +665,7 @@ class AnimeLibraryScreenModel( } sealed class Dialog { - object SettingsSheet : Dialog() + data object SettingsSheet : Dialog() data class ChangeCategory(val anime: List, val initialSelection: List>) : Dialog() data class DeleteAnime(val anime: List) : Dialog() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryScreenModel.kt index aa8f733fa..8c9419bc3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryScreenModel.kt @@ -659,7 +659,7 @@ class MangaLibraryScreenModel( } sealed class Dialog { - object SettingsSheet : Dialog() + data object SettingsSheet : Dialog() data class ChangeCategory(val manga: List, val initialSelection: List>) : Dialog() data class DeleteManga(val manga: List) : Dialog() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt index 2f1e0ce3a..e04f76db8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt @@ -135,7 +135,7 @@ private class MoreScreenModel( } sealed class DownloadQueueState { - object Stopped : DownloadQueueState() + data object Stopped : DownloadQueueState() data class Paused(val pending: Int) : DownloadQueueState() data class Downloading(val pending: Int) : DownloadQueueState() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index 20f5ced69..cfa7a61c9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -488,7 +488,7 @@ class ReaderActivity : BaseActivity() { setOnClickListener { popupMenu( - items = ReadingModeType.values().map { it.flagValue to it.stringRes }, + items = ReadingModeType.entries.map { it.flagValue to it.stringRes }, selectedItemId = viewModel.getMangaReadingMode(resolveDefault = false), ) { val newReadingMode = ReadingModeType.fromPreference(itemId) @@ -541,7 +541,7 @@ class ReaderActivity : BaseActivity() { setOnClickListener { popupMenu( - items = OrientationType.values().map { it.flagValue to it.stringRes }, + items = OrientationType.entries.map { it.flagValue to it.stringRes }, selectedItemId = viewModel.manga?.orientationType?.toInt() ?: readerPreferences.defaultOrientationType().get(), ) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt index c0d3c9a9d..05867d379 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt @@ -908,13 +908,13 @@ class ReaderViewModel( } sealed class Dialog { - object Loading : Dialog() - object Settings : Dialog() + data object Loading : Dialog() + data object Settings : Dialog() data class PageActions(val page: ReaderPage) : Dialog() } sealed class Event { - object ReloadViewerChapters : Event() + data object ReloadViewerChapters : Event() data class SetOrientation(val orientation: Int) : Event() data class SetCoverResult(val result: SetAsCoverResult) : Event() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/model/ReaderChapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/model/ReaderChapter.kt index f3279a455..7db453bd1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/model/ReaderChapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/model/ReaderChapter.kt @@ -40,9 +40,9 @@ data class ReaderChapter(val chapter: Chapter) { } sealed class State { - object Wait : State() - object Loading : State() - class Error(val error: Throwable) : State() - class Loaded(val pages: List) : State() + data object Wait : State() + data object Loading : State() + data class Error(val error: Throwable) : State() + data class Loaded(val pages: List) : State() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/OrientationType.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/OrientationType.kt index 0125a78a4..bf5135153 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/OrientationType.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/OrientationType.kt @@ -18,6 +18,6 @@ enum class OrientationType(val prefValue: Int, val flag: Int, @StringRes val str companion object { const val MASK = 0x00000038 - fun fromPreference(preference: Int?): OrientationType = values().find { it.flagValue == preference } ?: DEFAULT + fun fromPreference(preference: Int?): OrientationType = entries.find { it.flagValue == preference } ?: DEFAULT } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt index 73975e28f..e3f3b38b7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt @@ -22,7 +22,7 @@ enum class ReadingModeType(val prefValue: Int, @StringRes val stringRes: Int, @D companion object { const val MASK = 0x00000007 - fun fromPreference(preference: Int?): ReadingModeType = values().find { it.flagValue == preference } ?: DEFAULT + fun fromPreference(preference: Int?): ReadingModeType = entries.find { it.flagValue == preference } ?: DEFAULT fun isPagerType(preference: Int): Boolean { val mode = fromPreference(preference) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerNavigation.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerNavigation.kt index d4acd01b1..aedfb2bbd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerNavigation.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerNavigation.kt @@ -10,11 +10,11 @@ import eu.kanade.tachiyomi.util.lang.invert abstract class ViewerNavigation { sealed class NavigationRegion(@StringRes val nameRes: Int, val colorRes: Int) { - object MENU : NavigationRegion(R.string.action_menu, R.color.navigation_menu) - object PREV : NavigationRegion(R.string.nav_zone_prev, R.color.navigation_prev) - object NEXT : NavigationRegion(R.string.nav_zone_next, R.color.navigation_next) - object LEFT : NavigationRegion(R.string.nav_zone_left, R.color.navigation_left) - object RIGHT : NavigationRegion(R.string.nav_zone_right, R.color.navigation_right) + data object MENU : NavigationRegion(R.string.action_menu, R.color.navigation_menu) + data object PREV : NavigationRegion(R.string.nav_zone_prev, R.color.navigation_prev) + data object NEXT : NavigationRegion(R.string.nav_zone_next, R.color.navigation_next) + data object LEFT : NavigationRegion(R.string.nav_zone_left, R.color.navigation_left) + data object RIGHT : NavigationRegion(R.string.nav_zone_right, R.color.navigation_right) } data class Region( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/anime/AnimeUpdatesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/anime/AnimeUpdatesScreenModel.kt index 0b00489b3..4e2b075c2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/anime/AnimeUpdatesScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/anime/AnimeUpdatesScreenModel.kt @@ -307,10 +307,10 @@ class AnimeUpdatesScreenModel( // Try to select the items in-between when possible val range: IntRange if (selectedIndex < selectedPositions[0]) { - range = selectedIndex + 1 until selectedPositions[0] + range = selectedIndex + 1 ..< selectedPositions[0] selectedPositions[0] = selectedIndex } else if (selectedIndex > selectedPositions[1]) { - range = (selectedPositions[1] + 1) until selectedIndex + range = (selectedPositions[1] + 1) ..< selectedIndex selectedPositions[1] = selectedIndex } else { // Just select itself @@ -384,7 +384,7 @@ class AnimeUpdatesScreenModel( } sealed class Event { - object InternalError : Event() + data object InternalError : Event() data class LibraryUpdateTriggered(val started: Boolean) : Event() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/manga/MangaUpdatesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/manga/MangaUpdatesScreenModel.kt index 391551dc3..3e56d5e74 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/manga/MangaUpdatesScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/manga/MangaUpdatesScreenModel.kt @@ -295,10 +295,10 @@ class MangaUpdatesScreenModel( // Try to select the items in-between when possible val range: IntRange if (selectedIndex < selectedPositions[0]) { - range = selectedIndex + 1 until selectedPositions[0] + range = selectedIndex + 1 ..< selectedPositions[0] selectedPositions[0] = selectedIndex } else if (selectedIndex > selectedPositions[1]) { - range = (selectedPositions[1] + 1) until selectedIndex + range = (selectedPositions[1] + 1) ..< selectedIndex selectedPositions[1] = selectedIndex } else { // Just select itself @@ -371,7 +371,7 @@ class MangaUpdatesScreenModel( } sealed class Event { - object InternalError : Event() + data object InternalError : Event() data class LibraryUpdateTriggered(val started: Boolean) : Event() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/GLUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/GLUtil.kt index 59876e002..886b522a3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/GLUtil.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/GLUtil.kt @@ -27,7 +27,7 @@ object GLUtil { var maximumTextureSize = 0 // Iterate through all the configurations to located the maximum texture size - for (i in 0 until totalConfigurations[0]) { + for (i in 0 ..< totalConfigurations[0]) { // Only need to check for width since opengl textures are always squared egl.eglGetConfigAttrib(display, configurationsList[i], EGL10.EGL_MAX_PBUFFER_WIDTH, textureSize) diff --git a/core-metadata/src/main/java/tachiyomi/core/metadata/comicinfo/ComicInfo.kt b/core-metadata/src/main/java/tachiyomi/core/metadata/comicinfo/ComicInfo.kt index 76186216d..06cce7ccc 100644 --- a/core-metadata/src/main/java/tachiyomi/core/metadata/comicinfo/ComicInfo.kt +++ b/core-metadata/src/main/java/tachiyomi/core/metadata/comicinfo/ComicInfo.kt @@ -150,12 +150,12 @@ enum class ComicInfoPublishingStatus( companion object { fun toComicInfoValue(value: Long): String { - return values().firstOrNull { it.sMangaModelValue == value.toInt() }?.comicInfoValue + return entries.firstOrNull { it.sMangaModelValue == value.toInt() }?.comicInfoValue ?: UNKNOWN.comicInfoValue } fun toSMangaValue(value: String?): Int { - return values().firstOrNull { it.comicInfoValue == value }?.sMangaModelValue + return entries.firstOrNull { it.comicInfoValue == value }?.sMangaModelValue ?: UNKNOWN.sMangaModelValue } } diff --git a/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt b/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt index 786d44423..0338e2f88 100644 --- a/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt +++ b/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt @@ -330,7 +330,7 @@ object ImageUtil { } return buildList { - val range = 0 until partCount + val range = 0 ..< partCount for (index in range) { // Only continue if the list is empty or there is image remaining if (isNotEmpty() && imageHeight <= last().bottomOffset) break @@ -440,7 +440,7 @@ object ImageUtil { var blackStreak = false var whiteStreak = false val notOffset = x == left || x == right - inner@ for ((index, y) in (0 until image.height step image.height / 25).withIndex()) { + inner@ for ((index, y) in (0 ..< image.height step image.height / 25).withIndex()) { val pixel = image[x, y] val pixelOff = image[x + (if (x < image.width / 2) -offsetX else offsetX), y] if (pixel.isWhite()) { diff --git a/data/src/main/java/tachiyomi/data/DatabaseAdapter.kt b/data/src/main/java/tachiyomi/data/DatabaseAdapter.kt index db6d90209..75ab0d874 100644 --- a/data/src/main/java/tachiyomi/data/DatabaseAdapter.kt +++ b/data/src/main/java/tachiyomi/data/DatabaseAdapter.kt @@ -21,10 +21,8 @@ val listOfStringsAdapter = object : ColumnAdapter, String> { } val updateStrategyAdapter = object : ColumnAdapter { - private val enumValues by lazy { UpdateStrategy.values() } - override fun decode(databaseValue: Long): UpdateStrategy = - enumValues.getOrElse(databaseValue.toInt()) { UpdateStrategy.ALWAYS_UPDATE } + UpdateStrategy.entries.getOrElse(databaseValue.toInt()) { UpdateStrategy.ALWAYS_UPDATE } override fun encode(value: UpdateStrategy): Long = value.ordinal.toLong() } diff --git a/domain/src/main/java/tachiyomi/domain/category/anime/interactor/CreateAnimeCategoryWithName.kt b/domain/src/main/java/tachiyomi/domain/category/anime/interactor/CreateAnimeCategoryWithName.kt index 090a2b24e..ef4de2513 100644 --- a/domain/src/main/java/tachiyomi/domain/category/anime/interactor/CreateAnimeCategoryWithName.kt +++ b/domain/src/main/java/tachiyomi/domain/category/anime/interactor/CreateAnimeCategoryWithName.kt @@ -39,7 +39,7 @@ class CreateAnimeCategoryWithName( } sealed class Result { - object Success : Result() + data object Success : Result() data class InternalError(val error: Throwable) : Result() } } diff --git a/domain/src/main/java/tachiyomi/domain/category/anime/interactor/DeleteAnimeCategory.kt b/domain/src/main/java/tachiyomi/domain/category/anime/interactor/DeleteAnimeCategory.kt index 0ed1c3071..8cdb13b75 100644 --- a/domain/src/main/java/tachiyomi/domain/category/anime/interactor/DeleteAnimeCategory.kt +++ b/domain/src/main/java/tachiyomi/domain/category/anime/interactor/DeleteAnimeCategory.kt @@ -36,7 +36,7 @@ class DeleteAnimeCategory( } sealed class Result { - object Success : Result() + data object Success : Result() data class InternalError(val error: Throwable) : Result() } } diff --git a/domain/src/main/java/tachiyomi/domain/category/anime/interactor/ReorderAnimeCategory.kt b/domain/src/main/java/tachiyomi/domain/category/anime/interactor/ReorderAnimeCategory.kt index 75ac769f2..213d023b6 100644 --- a/domain/src/main/java/tachiyomi/domain/category/anime/interactor/ReorderAnimeCategory.kt +++ b/domain/src/main/java/tachiyomi/domain/category/anime/interactor/ReorderAnimeCategory.kt @@ -58,8 +58,8 @@ class ReorderAnimeCategory( } sealed class Result { - object Success : Result() - object Unchanged : Result() + data object Success : Result() + data object Unchanged : Result() data class InternalError(val error: Throwable) : Result() } diff --git a/domain/src/main/java/tachiyomi/domain/category/anime/interactor/UpdateAnimeCategory.kt b/domain/src/main/java/tachiyomi/domain/category/anime/interactor/UpdateAnimeCategory.kt index d112f7622..5a4372066 100644 --- a/domain/src/main/java/tachiyomi/domain/category/anime/interactor/UpdateAnimeCategory.kt +++ b/domain/src/main/java/tachiyomi/domain/category/anime/interactor/UpdateAnimeCategory.kt @@ -18,7 +18,7 @@ class UpdateAnimeCategory( } sealed class Result { - object Success : Result() + data object Success : Result() data class Error(val error: Exception) : Result() } } diff --git a/domain/src/main/java/tachiyomi/domain/category/manga/interactor/CreateMangaCategoryWithName.kt b/domain/src/main/java/tachiyomi/domain/category/manga/interactor/CreateMangaCategoryWithName.kt index 6b99b49d3..e587c3986 100644 --- a/domain/src/main/java/tachiyomi/domain/category/manga/interactor/CreateMangaCategoryWithName.kt +++ b/domain/src/main/java/tachiyomi/domain/category/manga/interactor/CreateMangaCategoryWithName.kt @@ -39,7 +39,7 @@ class CreateMangaCategoryWithName( } sealed class Result { - object Success : Result() + data object Success : Result() data class InternalError(val error: Throwable) : Result() } } diff --git a/domain/src/main/java/tachiyomi/domain/category/manga/interactor/DeleteMangaCategory.kt b/domain/src/main/java/tachiyomi/domain/category/manga/interactor/DeleteMangaCategory.kt index df7e7541a..0043a3022 100644 --- a/domain/src/main/java/tachiyomi/domain/category/manga/interactor/DeleteMangaCategory.kt +++ b/domain/src/main/java/tachiyomi/domain/category/manga/interactor/DeleteMangaCategory.kt @@ -36,7 +36,7 @@ class DeleteMangaCategory( } sealed class Result { - object Success : Result() + data object Success : Result() data class InternalError(val error: Throwable) : Result() } } diff --git a/domain/src/main/java/tachiyomi/domain/category/manga/interactor/ReorderMangaCategory.kt b/domain/src/main/java/tachiyomi/domain/category/manga/interactor/ReorderMangaCategory.kt index 68e0cf090..993b45882 100644 --- a/domain/src/main/java/tachiyomi/domain/category/manga/interactor/ReorderMangaCategory.kt +++ b/domain/src/main/java/tachiyomi/domain/category/manga/interactor/ReorderMangaCategory.kt @@ -58,8 +58,8 @@ class ReorderMangaCategory( } sealed class Result { - object Success : Result() - object Unchanged : Result() + data object Success : Result() + data object Unchanged : Result() data class InternalError(val error: Throwable) : Result() } diff --git a/domain/src/main/java/tachiyomi/domain/category/manga/interactor/UpdateMangaCategory.kt b/domain/src/main/java/tachiyomi/domain/category/manga/interactor/UpdateMangaCategory.kt index ccfa6e468..f68a6140b 100644 --- a/domain/src/main/java/tachiyomi/domain/category/manga/interactor/UpdateMangaCategory.kt +++ b/domain/src/main/java/tachiyomi/domain/category/manga/interactor/UpdateMangaCategory.kt @@ -18,7 +18,7 @@ class UpdateMangaCategory( } sealed class Result { - object Success : Result() + data object Success : Result() data class Error(val error: Exception) : Result() } } diff --git a/domain/src/main/java/tachiyomi/domain/library/anime/model/AnimeLibrarySortMode.kt b/domain/src/main/java/tachiyomi/domain/library/anime/model/AnimeLibrarySortMode.kt index 2c530e47f..6c354e5f0 100644 --- a/domain/src/main/java/tachiyomi/domain/library/anime/model/AnimeLibrarySortMode.kt +++ b/domain/src/main/java/tachiyomi/domain/library/anime/model/AnimeLibrarySortMode.kt @@ -24,15 +24,15 @@ data class AnimeLibrarySort( override val mask: Long = 0b00111100L - object Alphabetical : Type(0b00000000) - object LastSeen : Type(0b00000100) - object LastUpdate : Type(0b00001000) - object UnseenCount : Type(0b00001100) - object TotalEpisodes : Type(0b00010000) - object LatestEpisode : Type(0b00010100) - object EpisodeFetchDate : Type(0b00011000) - object DateAdded : Type(0b00011100) - object AiringTime : Type(0b00100000) + data object Alphabetical : Type(0b00000000) + data object LastSeen : Type(0b00000100) + data object LastUpdate : Type(0b00001000) + data object UnseenCount : Type(0b00001100) + data object TotalEpisodes : Type(0b00010000) + data object LatestEpisode : Type(0b00010100) + data object EpisodeFetchDate : Type(0b00011000) + data object DateAdded : Type(0b00011100) + data object AiringTime : Type(0b00100000) companion object { fun valueOf(flag: Long): Type { @@ -47,8 +47,8 @@ data class AnimeLibrarySort( override val mask: Long = 0b01000000L - object Ascending : Direction(0b01000000) - object Descending : Direction(0b00000000) + data object Ascending : Direction(0b01000000) + data object Descending : Direction(0b00000000) companion object { fun valueOf(flag: Long): Direction { diff --git a/domain/src/main/java/tachiyomi/domain/library/manga/model/MangaLibrarySortMode.kt b/domain/src/main/java/tachiyomi/domain/library/manga/model/MangaLibrarySortMode.kt index 55d9b7f36..5d28423dc 100644 --- a/domain/src/main/java/tachiyomi/domain/library/manga/model/MangaLibrarySortMode.kt +++ b/domain/src/main/java/tachiyomi/domain/library/manga/model/MangaLibrarySortMode.kt @@ -24,14 +24,14 @@ data class MangaLibrarySort( override val mask: Long = 0b00111100L - object Alphabetical : Type(0b00000000) - object LastRead : Type(0b00000100) - object LastUpdate : Type(0b00001000) - object UnreadCount : Type(0b00001100) - object TotalChapters : Type(0b00010000) - object LatestChapter : Type(0b00010100) - object ChapterFetchDate : Type(0b00011000) - object DateAdded : Type(0b00011100) + data object Alphabetical : Type(0b00000000) + data object LastRead : Type(0b00000100) + data object LastUpdate : Type(0b00001000) + data object UnreadCount : Type(0b00001100) + data object TotalChapters : Type(0b00010000) + data object LatestChapter : Type(0b00010100) + data object ChapterFetchDate : Type(0b00011000) + data object DateAdded : Type(0b00011100) companion object { fun valueOf(flag: Long): Type { @@ -46,8 +46,8 @@ data class MangaLibrarySort( override val mask: Long = 0b01000000L - object Ascending : Direction(0b01000000) - object Descending : Direction(0b00000000) + data object Ascending : Direction(0b01000000) + data object Descending : Direction(0b00000000) companion object { fun valueOf(flag: Long): Direction { diff --git a/domain/src/main/java/tachiyomi/domain/library/model/LibraryDisplayMode.kt b/domain/src/main/java/tachiyomi/domain/library/model/LibraryDisplayMode.kt index b443ec6c4..ee05c22ff 100644 --- a/domain/src/main/java/tachiyomi/domain/library/model/LibraryDisplayMode.kt +++ b/domain/src/main/java/tachiyomi/domain/library/model/LibraryDisplayMode.kt @@ -2,10 +2,10 @@ package tachiyomi.domain.library.model sealed class LibraryDisplayMode { - object CompactGrid : LibraryDisplayMode() - object ComfortableGrid : LibraryDisplayMode() - object List : LibraryDisplayMode() - 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 { diff --git a/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt b/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt index 435d87a29..89950d30d 100644 --- a/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt +++ b/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt @@ -72,8 +72,8 @@ class GetApplicationRelease( ) sealed class Result { - class NewUpdate(val release: Release) : Result() - object NoNewUpdate : Result() - object ThirdPartyInstallation : Result() + data class NewUpdate(val release: Release) : Result() + data object NoNewUpdate : Result() + data object ThirdPartyInstallation : Result() } } diff --git a/domain/src/main/java/tachiyomi/domain/source/manga/model/Pin.kt b/domain/src/main/java/tachiyomi/domain/source/manga/model/Pin.kt index 483054e7d..6abd0f975 100644 --- a/domain/src/main/java/tachiyomi/domain/source/manga/model/Pin.kt +++ b/domain/src/main/java/tachiyomi/domain/source/manga/model/Pin.kt @@ -1,9 +1,9 @@ package tachiyomi.domain.source.manga.model sealed class Pin(val code: Int) { - object Unpinned : Pin(0b00) - object Pinned : Pin(0b01) - object Actual : Pin(0b10) + data object Unpinned : Pin(0b00) + data object Pinned : Pin(0b01) + data object Actual : Pin(0b10) } inline fun Pins(builder: Pins.PinsBuilder.() -> Unit = {}): Pins { diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 1c5c86a92..01ee818ab 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,5 +1,5 @@ [versions] -compiler = "1.4.8" +compiler = "1.5.0" compose-bom = "2023.07.00-alpha01" accompanist = "0.31.5-beta" diff --git a/gradle/kotlinx.versions.toml b/gradle/kotlinx.versions.toml index caf8f42a2..717ad74ee 100644 --- a/gradle/kotlinx.versions.toml +++ b/gradle/kotlinx.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin_version = "1.8.22" +kotlin_version = "1.9.0" serialization_version = "1.5.1" xml_serialization_version = "0.86.1" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d8f0be4e9..eea37b5f2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -aboutlib_version = "10.8.2" +aboutlib_version = "10.8.3" okhttp_version = "5.0.0-alpha.11" shizuku_version = "12.2.0" sqlite = "2.3.1" @@ -29,7 +29,7 @@ jsoup = "org.jsoup:jsoup:1.16.1" disklrucache = "com.jakewharton:disklrucache:2.0.2" unifile = "com.github.tachiyomiorg:unifile:17bec43" -junrar = "com.github.junrar:junrar:7.5.4" +junrar = "com.github.junrar:junrar:7.5.5" sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" } sqlite-ktx = { module = "androidx.sqlite:sqlite-ktx", version.ref = "sqlite" } @@ -45,7 +45,7 @@ coil-gif = { module = "io.coil-kt:coil-gif" } coil-compose = { module = "io.coil-kt:coil-compose" } subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:c8e2650" -image-decoder = "com.github.tachiyomiorg:image-decoder:7879b45" +image-decoder = "com.github.tachiyomiorg:image-decoder:16eda64574" natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1" @@ -64,7 +64,7 @@ swipe = "me.saket.swipe:swipe:1.2.0" logcat = "com.squareup.logcat:logcat:0.1" -acra-http = "ch.acra:acra-http:5.10.1" +acra-http = "ch.acra:acra-http:5.11.0" aboutLibraries-core = { module = "com.mikepenz:aboutlibraries-core", version.ref = "aboutlib_version" } aboutLibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlib_version" } diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/VerticalFastScroller.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/VerticalFastScroller.kt index 41ccbac00..bef047104 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/VerticalFastScroller.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/VerticalFastScroller.kt @@ -211,7 +211,7 @@ private fun rememberColumnWidthSums( gridWidth, horizontalArrangement.spacing.roundToPx(), ).toMutableList().apply { - for (i in 1 until size) { + for (i in 1 ..< size) { this[i] += this[i - 1] } } diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/manga/UpdatesMangaWidget.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/manga/UpdatesMangaWidget.kt index 275d29a04..82bcce1d9 100644 --- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/manga/UpdatesMangaWidget.kt +++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/manga/UpdatesMangaWidget.kt @@ -36,8 +36,8 @@ fun UpdatesMangaWidget(data: List>?) { } else if (data.isEmpty()) { Text(text = stringResource(R.string.information_no_recent)) } else { - (0 until rowCount).forEach { i -> - val coverRow = (0 until columnCount).mapNotNull { j -> + (0 ..< rowCount).forEach { i -> + val coverRow = (0 ..< columnCount).mapNotNull { j -> data.getOrNull(j + (i * columnCount)) } if (coverRow.isNotEmpty()) {