From 242aeb6a68cd02a3e99ed29f0ac0c73b1a8334d6 Mon Sep 17 00:00:00 2001 From: arkon Date: Wed, 15 Feb 2023 22:47:47 -0500 Subject: [PATCH] Avoid crashing if opening browse with unavailable source (cherry picked from commit 0ef7650c1a0ae7c4c6e17e458695191ce78944cb) --- .../presentation/browse/BrowseSourceScreen.kt | 30 ++++++++++++++-- .../browse/components/BrowseSourceToolbar.kt | 4 +-- .../kanade/tachiyomi/source/SourceManager.kt | 2 +- .../source/browse/BrowseSourceScreen.kt | 29 ++++++++++------ .../source/browse/BrowseSourceScreenModel.kt | 34 ++++++++++++------- 5 files changed, 71 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt index 8faf022665..b1edd5cf1e 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt @@ -1,6 +1,7 @@ package eu.kanade.presentation.browse import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.HelpOutline @@ -11,6 +12,7 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarResult import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.paging.LoadState import androidx.paging.compose.LazyPagingItems @@ -18,19 +20,22 @@ import eu.kanade.data.source.NoResultsException import eu.kanade.presentation.browse.components.BrowseSourceComfortableGrid import eu.kanade.presentation.browse.components.BrowseSourceCompactGrid import eu.kanade.presentation.browse.components.BrowseSourceList +import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.EmptyScreenAction import eu.kanade.presentation.components.LoadingScreen +import eu.kanade.presentation.components.Scaffold import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.LocalSource +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.SourceManager import kotlinx.coroutines.flow.StateFlow import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.manga.model.Manga @Composable fun BrowseSourceContent( - source: CatalogueSource?, + source: Source?, mangaList: LazyPagingItems>, columns: GridCells, displayMode: LibraryDisplayMode, @@ -139,3 +144,24 @@ fun BrowseSourceContent( } } } + +@Composable +fun MissingSourceScreen( + source: SourceManager.StubSource, + navigateUp: () -> Unit, +) { + Scaffold( + topBar = { scrollBehavior -> + AppBar( + title = source.name, + navigateUp = navigateUp, + scrollBehavior = scrollBehavior, + ) + }, + ) { paddingValues -> + EmptyScreen( + message = source.getSourceNotInstalledException().message!!, + modifier = Modifier.padding(paddingValues), + ) + } +} diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt index 9d82dc4dd6..e0d4bc51a9 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt @@ -20,15 +20,15 @@ import eu.kanade.presentation.components.DropdownMenu import eu.kanade.presentation.components.RadioMenuItem import eu.kanade.presentation.components.SearchToolbar import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.LocalSource +import eu.kanade.tachiyomi.source.Source import tachiyomi.domain.library.model.LibraryDisplayMode @Composable fun BrowseSourceToolbar( searchQuery: String?, onSearchQueryChange: (String?) -> Unit, - source: CatalogueSource?, + source: Source?, displayMode: LibraryDisplayMode, onDisplayModeChange: (LibraryDisplayMode) -> Unit, navigateUp: () -> Unit, diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt index 2c0f947007..a93084a33b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt @@ -152,6 +152,6 @@ class SourceManager( } } - inner class SourceNotInstalledException(val sourceString: String) : + inner class SourceNotInstalledException(sourceString: String) : Exception(context.getString(R.string.source_not_installed, sourceString)) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt index aa5aa3bc99..f14182f063 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt @@ -39,6 +39,7 @@ import cafe.adriel.voyager.core.screen.uniqueScreenKey import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow import eu.kanade.presentation.browse.BrowseSourceContent +import eu.kanade.presentation.browse.MissingSourceScreen import eu.kanade.presentation.browse.components.BrowseSourceToolbar import eu.kanade.presentation.browse.components.RemoveMangaDialog import eu.kanade.presentation.components.ChangeCategoryDialog @@ -48,7 +49,9 @@ import eu.kanade.presentation.components.Scaffold import eu.kanade.presentation.util.AssistContentScreen import eu.kanade.presentation.util.padding import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.LocalSource +import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing import eu.kanade.tachiyomi.ui.category.CategoryScreen @@ -73,17 +76,10 @@ data class BrowseSourceScreen( @Composable override fun Content() { - val navigator = LocalNavigator.currentOrThrow - val scope = rememberCoroutineScope() - val context = LocalContext.current - val haptic = LocalHapticFeedback.current - val uriHandler = LocalUriHandler.current - val screenModel = rememberScreenModel { BrowseSourceScreenModel(sourceId, listingQuery) } val state by screenModel.state.collectAsState() - val snackbarHostState = remember { SnackbarHostState() } - + val navigator = LocalNavigator.currentOrThrow val navigateUp: () -> Unit = { when { !state.isUserQuery && state.toolbarQuery != null -> screenModel.setToolbarQuery(null) @@ -91,8 +87,21 @@ data class BrowseSourceScreen( } } - val onHelpClick = { uriHandler.openUri(LocalSource.HELP_URL) } + if (screenModel.source is SourceManager.StubSource) { + MissingSourceScreen( + source = screenModel.source, + navigateUp = navigateUp, + ) + return + } + val scope = rememberCoroutineScope() + val context = LocalContext.current + val haptic = LocalHapticFeedback.current + val uriHandler = LocalUriHandler.current + val snackbarHostState = remember { SnackbarHostState() } + + val onHelpClick = { uriHandler.openUri(LocalSource.HELP_URL) } val onWebViewClick = f@{ val source = screenModel.source as? HttpSource ?: return@f navigator.push( @@ -147,7 +156,7 @@ data class BrowseSourceScreen( Text(text = stringResource(R.string.popular)) }, ) - if (screenModel.source.supportsLatest) { + if ((screenModel.source as CatalogueSource).supportsLatest) { FilterChip( selected = state.listing == Listing.Latest, onClick = { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt index 6c2e5fc575..6b40f6b829 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt @@ -102,23 +102,25 @@ class BrowseSourceScreenModel( var displayMode by sourcePreferences.sourceDisplayMode().asState(coroutineScope) - val source = sourceManager.get(sourceId) as CatalogueSource + val source = sourceManager.getOrStub(sourceId) init { - mutableState.update { - var query: String? = null - var listing = it.listing + if (source is CatalogueSource) { + mutableState.update { + var query: String? = null + var listing = it.listing - if (listing is Listing.Search) { - query = listing.query - listing = Listing.Search(query, source.getFilterList()) + if (listing is Listing.Search) { + query = listing.query + listing = Listing.Search(query, source.getFilterList()) + } + + it.copy( + listing = listing, + filters = source.getFilterList(), + toolbarQuery = query, + ) } - - it.copy( - listing = listing, - filters = source.getFilterList(), - toolbarQuery = query, - ) } } @@ -162,6 +164,8 @@ class BrowseSourceScreenModel( } fun resetFilters() { + if (source !is CatalogueSource) return + mutableState.update { it.copy(filters = source.getFilterList()) } } @@ -170,6 +174,8 @@ class BrowseSourceScreenModel( } fun search(query: String? = null, filters: FilterList? = null) { + if (source !is CatalogueSource) return + val input = state.value.listing as? Listing.Search ?: Listing.Search(query = null, filters = source.getFilterList()) @@ -185,6 +191,8 @@ class BrowseSourceScreenModel( } fun searchGenre(genreName: String) { + if (source !is CatalogueSource) return + val defaultFilters = source.getFilterList() var genreExists = false