diff --git a/app/src/main/java/eu/kanade/core/util/SourceUtil.kt b/app/src/main/java/eu/kanade/core/util/SourceUtil.kt new file mode 100644 index 000000000..c976e1e6a --- /dev/null +++ b/app/src/main/java/eu/kanade/core/util/SourceUtil.kt @@ -0,0 +1,13 @@ +package eu.kanade.core.util + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import tachiyomi.domain.source.service.SourceManager +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +@Composable +fun ifSourcesLoaded(): Boolean { + return remember { Injekt.get().isInitialized }.collectAsState().value +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt index fb1c8e176..0fdbabe03 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt @@ -18,6 +18,7 @@ import kotlinx.coroutines.ensureActive import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart @@ -311,18 +312,12 @@ class DownloadCache( } // Try to wait until extensions and sources have loaded - var sources = getSources() - if (sources.isEmpty()) { - withTimeoutOrNull(30.seconds) { - while (!extensionManager.isInitialized) { - delay(2.seconds) - } + var sources = emptyList() + withTimeoutOrNull(30.seconds) { + extensionManager.isInitialized.first { it } + sourceManager.isInitialized.first { it } - while (extensionManager.availableExtensionsFlow.value.isNotEmpty() && sources.isEmpty()) { - delay(2.seconds) - sources = getSources() - } - } + sources = getSources() } val sourceMap = sources.associate { provider.getSourceDirName(it).lowercase() to it.id } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index 4a5eff1ac..15f386d38 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -16,6 +16,7 @@ import eu.kanade.tachiyomi.util.system.toast import kotlinx.coroutines.async import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.emptyFlow import logcat.LogPriority @@ -41,8 +42,8 @@ class ExtensionManager( private val trustExtension: TrustExtension = Injekt.get(), ) { - var isInitialized = false - private set + private val _isInitialized = MutableStateFlow(false) + val isInitialized: StateFlow = _isInitialized.asStateFlow() /** * API where all the available extensions can be found. @@ -108,7 +109,7 @@ class ExtensionManager( .filterIsInstance() .map { it.extension } - isInitialized = true + _isInitialized.value = true } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt index a7618bd8c..0fa40ba9d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt @@ -9,6 +9,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -28,6 +30,9 @@ class AndroidSourceManager( private val sourceRepository: StubSourceRepository, ) : SourceManager { + private val _isInitialized = MutableStateFlow(false) + override val isInitialized: StateFlow = _isInitialized.asStateFlow() + private val downloadManager: DownloadManager by injectLazy() private val scope = CoroutineScope(Job() + Dispatchers.IO) @@ -60,6 +65,7 @@ class AndroidSourceManager( } } sourcesMapFlow.value = mutableMap + _isInitialized.value = true } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt index 3eac8accf..c144caa2a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt @@ -31,6 +31,7 @@ import androidx.preference.forEach import androidx.preference.getOnBindEditTextListener import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R @@ -40,6 +41,7 @@ import eu.kanade.tachiyomi.source.sourcePreferences import eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText.Companion.setIncognito import tachiyomi.domain.source.service.SourceManager import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.screens.LoadingScreen import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -47,6 +49,11 @@ class SourcePreferencesScreen(val sourceId: Long) : Screen() { @Composable override fun Content() { + if (!ifSourcesLoaded()) { + LoadingScreen() + return + } + val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt index acd8278a0..3a2adcc21 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt @@ -18,6 +18,7 @@ import androidx.paging.compose.collectAsLazyPagingItems import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.presentation.browse.BrowseSourceContent import eu.kanade.presentation.components.SearchToolbar import eu.kanade.presentation.util.Screen @@ -34,6 +35,7 @@ import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.i18n.stringResource +import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.source.local.LocalSource data class SourceSearchScreen( @@ -44,6 +46,11 @@ data class SourceSearchScreen( @Composable override fun Content() { + if (!ifSourcesLoaded()) { + LoadingScreen() + return + } + val uriHandler = LocalUriHandler.current val navigator = LocalNavigator.currentOrThrow val scope = rememberCoroutineScope() 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 4bbf6d7dc..61c30f604 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 @@ -35,6 +35,7 @@ import androidx.paging.compose.collectAsLazyPagingItems import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.presentation.browse.BrowseSourceContent import eu.kanade.presentation.browse.MissingSourceScreen import eu.kanade.presentation.browse.components.BrowseSourceToolbar @@ -60,6 +61,7 @@ import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource +import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.source.local.LocalSource data class BrowseSourceScreen( @@ -73,6 +75,11 @@ data class BrowseSourceScreen( @Composable override fun Content() { + if (!ifSourcesLoaded()) { + LoadingScreen() + return + } + val screenModel = rememberScreenModel { BrowseSourceScreenModel(sourceId, listingQuery) } val state by screenModel.state.collectAsState() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt index a09595c4d..44164f49b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.setValue import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.presentation.browse.GlobalSearchScreen import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen @@ -23,6 +24,11 @@ class GlobalSearchScreen( @Composable override fun Content() { + if (!ifSourcesLoaded()) { + LoadingScreen() + return + } + val navigator = LocalNavigator.currentOrThrow val screenModel = rememberScreenModel { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt index f7dad1d8c..d3578aff5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt @@ -22,6 +22,7 @@ import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.domain.manga.model.hasCustomCover import eu.kanade.domain.manga.model.toSManga import eu.kanade.presentation.category.components.ChangeCategoryDialog @@ -73,6 +74,11 @@ class MangaScreen( @Composable override fun Content() { + if (!ifSourcesLoaded()) { + LoadingScreen() + return + } + val navigator = LocalNavigator.currentOrThrow val context = LocalContext.current val haptic = LocalHapticFeedback.current 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 e21fa11c6..530ee87e2 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 @@ -40,6 +40,7 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.google.android.material.elevation.SurfaceColors import com.google.android.material.transition.platform.MaterialContainerTransform import dev.chrisbanes.insetter.applyInsetter +import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.domain.base.BasePreferences import eu.kanade.presentation.reader.DisplayRefreshHost import eu.kanade.presentation.reader.OrientationSelectDialog @@ -344,6 +345,10 @@ class ReaderActivity : BaseActivity() { ) } + if (!ifSourcesLoaded()) { + return@setComposeContent + } + val isHttpSource = viewModel.getSource() is HttpSource val isFullscreen by readerPreferences.fullscreen().collectAsState() val flashOnPageChange by readerPreferences.flashOnPageChange().collectAsState() 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 a59f0500b..432eff749 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 @@ -47,6 +47,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @@ -264,6 +265,7 @@ class ReaderViewModel @JvmOverloads constructor( try { val manga = getManga.await(mangaId) if (manga != null) { + sourceManager.isInitialized.first { it } mutableState.update { it.copy(manga = manga) } if (chapterId == -1L) chapterId = initialChapterId diff --git a/domain/src/main/java/tachiyomi/domain/source/service/SourceManager.kt b/domain/src/main/java/tachiyomi/domain/source/service/SourceManager.kt index 8b832f34c..da4fb3929 100644 --- a/domain/src/main/java/tachiyomi/domain/source/service/SourceManager.kt +++ b/domain/src/main/java/tachiyomi/domain/source/service/SourceManager.kt @@ -4,10 +4,13 @@ import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.online.HttpSource import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow import tachiyomi.domain.source.model.StubSource interface SourceManager { + val isInitialized: StateFlow + val catalogueSources: Flow> fun get(sourceKey: Long): Source?