From 02e36e702140fbb82a6394dc4cd35f1dffff7846 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Sat, 27 Jul 2024 15:07:11 +0800 Subject: [PATCH] [New Reader] Implement open archive intent --- app/src/main/AndroidManifest.xml | 39 +-- .../ehviewer/gallery/ArchivePageLoader.kt | 17 +- .../com/hippo/ehviewer/gallery/PageLoader2.kt | 10 +- .../com/hippo/ehviewer/spider/SpiderQueen.kt | 5 +- .../com/hippo/ehviewer/ui/CommonOperations.kt | 15 +- .../com/hippo/ehviewer/ui/MainActivity.kt | 19 +- .../hippo/ehviewer/ui/reader/ReaderScreen.kt | 331 +++++++++++------- .../ehviewer/ui/screen/DownloadsScreen.kt | 4 +- .../ehviewer/ui/screen/GalleryListScreen.kt | 4 +- .../hippo/ehviewer/ui/screen/HistoryScreen.kt | 4 +- .../ehviewer/ui/settings/FilterScreen.kt | 4 +- .../com/hippo/ehviewer/ui/tools/Await.kt | 29 ++ .../ehviewer/ui/tools/DeferedComposable.kt | 26 -- .../com/hippo/ehviewer/ui/tools/Dialog.kt | 6 +- .../tachiyomi/ui/reader/loader/PageLoader.kt | 13 +- 15 files changed, 311 insertions(+), 215 deletions(-) create mode 100644 app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Await.kt delete mode 100644 app/src/main/kotlin/com/hippo/ehviewer/ui/tools/DeferedComposable.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dd1dbcb34..e9f00a844 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -70,26 +70,6 @@ - - - - - - - - - - - - - - - - @@ -123,6 +103,25 @@ + + + + + + + + + + + + + + + + when (intent.action) { Intent.ACTION_VIEW -> { - val url = intent.data?.toString() - if (url != null && !navigator.navWithUrl(url)) { - val new = dialogState.awaitInputText(initial = url, title = cannotParse) - addTextToClipboard(new) + val uri = intent.data ?: return@collect + when (uri.scheme) { + SCHEME_CONTENT, SCHEME_FILE -> { + navigator.navToReader(applicationContext, uri) + } + + else -> { + val url = uri.toString() + if (!navigator.navWithUrl(url)) { + val new = dialogState.awaitInputText(initial = url, title = cannotParse) + addTextToClipboard(new) + } + } } } Intent.ACTION_SEND -> { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/ReaderScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/ReaderScreen.kt index cd7beed6c..17a86017e 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/ReaderScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/ReaderScreen.kt @@ -1,5 +1,8 @@ package com.hippo.ehviewer.ui.reader +import android.content.Context +import android.net.Uri +import android.os.Parcelable import androidx.compose.animation.animateColorAsState import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme @@ -21,6 +24,7 @@ import androidx.compose.material3.BottomSheetDefaults import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -37,8 +41,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.core.view.WindowInsetsControllerCompat +import arrow.core.Either +import arrow.core.raise.ensure import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.hippo.ehviewer.R import com.hippo.ehviewer.Settings @@ -48,8 +55,12 @@ import com.hippo.ehviewer.download.DownloadManager import com.hippo.ehviewer.download.archiveFile import com.hippo.ehviewer.gallery.ArchivePageLoader import com.hippo.ehviewer.gallery.EhPageLoader +import com.hippo.ehviewer.gallery.PageLoader2 import com.hippo.ehviewer.ui.composing -import com.hippo.ehviewer.ui.tools.Deferred +import com.hippo.ehviewer.ui.tools.Await +import com.hippo.ehviewer.ui.tools.DialogState +import com.hippo.ehviewer.util.displayString +import com.hippo.files.toOkioPath import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.navigation.DestinationsNavigator @@ -63,11 +74,51 @@ import kotlin.coroutines.resume import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.sample import kotlinx.coroutines.launch +import kotlinx.parcelize.Parcelize import moe.tarsin.kt.unreachable +sealed interface ReaderScreenArgs : Parcelable { + @Parcelize + data class Gallery(val info: BaseGalleryInfo, val page: Int) : ReaderScreenArgs + + @Parcelize + data class Archive(val uri: Uri) : ReaderScreenArgs +} + @Destination @Composable -fun ReaderScreen(info: BaseGalleryInfo, page: Int = -1, navigator: DestinationsNavigator) = composing(navigator) { +fun ReaderScreen(args: ReaderScreenArgs, navigator: DestinationsNavigator) = composing(navigator) { + Await({ preparePageLoader(args) }) { pageLoader -> + DisposableEffect(pageLoader) { + pageLoader.start() + onDispose { + pageLoader.stop() + } + } + val readingFailed = stringResource(R.string.error_reading_failed) + Await({ + Either.catch { + readingFailed.takeUnless { pageLoader.awaitReady() } + }.fold({ it.displayString() }, { it }) + }) { error -> + if (error == null) { + val info = (args as? ReaderScreenArgs.Gallery)?.info + ReaderScreen(pageLoader, info, navigator) + } else { + Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Text( + text = error, + color = MaterialTheme.colorScheme.error, + style = MaterialTheme.typography.titleLarge, + ) + } + } + } + } +} + +@Composable +fun ReaderScreen(pageLoader: PageLoader2, info: BaseGalleryInfo?, navigator: DestinationsNavigator) = composing(navigator) { ConfigureKeepScreenOn() LaunchedEffect(Unit) { val orientation = requestedOrientation @@ -75,21 +126,11 @@ fun ReaderScreen(info: BaseGalleryInfo, page: Int = -1, navigator: DestinationsN .onCompletion { requestedOrientation = orientation } .collect { setOrientation(it) } } - val pageLoader = remember { - val archive = DownloadManager.getDownloadInfo(info.gid)?.archiveFile - archive?.let { ArchivePageLoader(it, info.gid, page) } ?: EhPageLoader(info, page) - } - LaunchedEffect(Unit) { + LaunchedEffect(pageLoader) { Settings.cropBorder.changesFlow().collect { pageLoader.restart() } } - DisposableEffect(pageLoader) { - pageLoader.start() - onDispose { - pageLoader.stop() - } - } val showSeekbar by Settings.showReaderSeekbar.collectAsState() val readingMode by Settings.readingMode.collectAsState { ReadingModeType.fromPreference(it) } val volumeKeysEnabled by Settings.readWithVolumeKeys.collectAsState() @@ -103,139 +144,163 @@ fun ReaderScreen(info: BaseGalleryInfo, page: Int = -1, navigator: DestinationsN uiController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_DEFAULT } } - Deferred({ pageLoader.awaitReady() }) { - val lazyListState = rememberLazyListState() - val pagerState = rememberPagerState { pageLoader.size } - val syncState = rememberSliderPagerDoubleSyncState(lazyListState, pagerState, pageLoader) - Box { - var appbarVisible by remember { mutableStateOf(false) } - val bgColor by collectBackgroundColorAsState() - syncState.Sync(ReadingModeType.isWebtoon(readingMode)) { appbarVisible = false } - if (fullscreen) { - LaunchedEffect(Unit) { - snapshotFlow { appbarVisible }.collect { - uiController.isSystemBarsVisible = it - } + val lazyListState = rememberLazyListState() + val pagerState = rememberPagerState { pageLoader.size } + val syncState = rememberSliderPagerDoubleSyncState(lazyListState, pagerState, pageLoader) + Box { + var appbarVisible by remember { mutableStateOf(false) } + val bgColor by collectBackgroundColorAsState() + syncState.Sync(ReadingModeType.isWebtoon(readingMode)) { appbarVisible = false } + if (fullscreen) { + LaunchedEffect(Unit) { + snapshotFlow { appbarVisible }.collect { + uiController.isSystemBarsVisible = it } } - VolumeKeysHandler( - enabled = { volumeKeysEnabled && !appbarVisible }, - movePrevious = { syncState.sliderScrollTo(syncState.sliderValue - 1) }, - moveNext = { syncState.sliderScrollTo(syncState.sliderValue + 1) }, - ) - GalleryPager( - type = readingMode, - pagerState = pagerState, - lazyListState = lazyListState, - pageLoader = pageLoader, - onSelectPage = { page -> - if (Settings.readerLongTapAction.value) { - launch { - dialog { cont -> - fun dispose() = cont.resume(Unit) - val state = rememberModalBottomSheetState() - ModalBottomSheet( - onDismissRequest = { dispose() }, - modifier = Modifier.windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Top)), - sheetState = state, - contentWindowInsets = { WindowInsets(0) }, - ) { - ReaderPageSheetMeta( - retry = { pageLoader.retryPage(page.index) }, - retryOrigin = { pageLoader.retryPage(page.index, true) }, - share = { launchIO { with(pageLoader) { shareImage(page, info) } } }, - copy = { launchIO { with(pageLoader) { copy(page) } } }, - save = { launchIO { with(pageLoader) { save(page) } } }, - saveTo = { launchIO { with(pageLoader) { saveTo(page) } } }, - dismiss = { launch { state.hide().also { dispose() } } }, - ) - } - } - } - } - }, - onMenuRegionClick = { appbarVisible = !appbarVisible }, - modifier = Modifier.background(bgColor), - contentPadding = if (fullscreen) { - if (cutoutShort) { - PaddingValues(0.dp) - } else { - WindowInsets.displayCutout.asPaddingValues() - } - } else { - WindowInsets.systemBars.asPaddingValues() - }, - ) - val brightness by Settings.customBrightness.collectAsState() - val brightnessValue by Settings.customBrightnessValue.collectAsState() - val colorOverlayEnabled by Settings.colorFilter.collectAsState() - val colorOverlay by Settings.colorFilterValue.collectAsState() - val colorOverlayMode by Settings.colorFilterMode.collectAsState { - when (it) { - 0 -> BlendMode.SrcOver - 1 -> BlendMode.Multiply - 2 -> BlendMode.Screen - 3 -> BlendMode.Overlay - 4 -> BlendMode.Lighten - 5 -> BlendMode.Darken - else -> unreachable() - } - } - ReaderContentOverlay( - brightness = { brightnessValue }.takeIf { brightness && brightnessValue < 0 }, - color = { colorOverlay }.takeIf { colorOverlayEnabled }, - colorBlendMode = colorOverlayMode, - ) - if (brightness) { - LaunchedEffect(Unit) { - Settings.customBrightnessValue.valueFlow().sample(100) - .onCompletion { setCustomBrightnessValue(0) } - .collect { setCustomBrightnessValue(it) } - } - } - val showPageNumber by Settings.showPageNumber.collectAsState() - if (showPageNumber && !appbarVisible) { - CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.bodySmall) { - PageIndicatorText( - currentPage = syncState.sliderValue, - totalPages = pageLoader.size, - modifier = Modifier.align(Alignment.BottomCenter).navigationBarsPadding(), - ) - } - } - ReaderAppBars( - visible = appbarVisible, - isRtl = readingMode == ReadingModeType.RIGHT_TO_LEFT, - showSeekBar = showSeekbar, - currentPage = syncState.sliderValue, - totalPages = pageLoader.size, - onSliderValueChange = { syncState.sliderScrollTo(it + 1) }, - onClickSettings = { + } + VolumeKeysHandler( + enabled = { volumeKeysEnabled && !appbarVisible }, + movePrevious = { syncState.sliderScrollTo(syncState.sliderValue - 1) }, + moveNext = { syncState.sliderScrollTo(syncState.sliderValue + 1) }, + ) + GalleryPager( + type = readingMode, + pagerState = pagerState, + lazyListState = lazyListState, + pageLoader = pageLoader, + onSelectPage = { page -> + if (Settings.readerLongTapAction.value) { launch { dialog { cont -> fun dispose() = cont.resume(Unit) - var isColorFilter by remember { mutableStateOf(false) } - val scrim by animateColorAsState( - targetValue = if (isColorFilter) Color.Transparent else BottomSheetDefaults.ScrimColor, - label = "ScrimColor", - ) + val state = rememberModalBottomSheetState() ModalBottomSheet( onDismissRequest = { dispose() }, modifier = Modifier.windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Top)), - // Yeah, I know color state should not be read here, but we have to do it... - scrimColor = scrim, - dragHandle = null, + sheetState = state, contentWindowInsets = { WindowInsets(0) }, ) { - SettingsPager(modifier = Modifier.fillMaxSize()) { page -> - isColorFilter = page == 2 - appbarVisible = !isColorFilter - } + ReaderPageSheetMeta( + retry = { pageLoader.retryPage(page.index) }, + retryOrigin = { pageLoader.retryPage(page.index, true) }, + share = { launchIO { with(pageLoader) { shareImage(page, info) } } }, + copy = { launchIO { with(pageLoader) { copy(page) } } }, + save = { launchIO { with(pageLoader) { save(page) } } }, + saveTo = { launchIO { with(pageLoader) { saveTo(page) } } }, + dismiss = { launch { state.hide().also { dispose() } } }, + ) } } } - }, - ) + } + }, + onMenuRegionClick = { appbarVisible = !appbarVisible }, + modifier = Modifier.background(bgColor), + contentPadding = if (fullscreen) { + if (cutoutShort) { + PaddingValues(0.dp) + } else { + WindowInsets.displayCutout.asPaddingValues() + } + } else { + WindowInsets.systemBars.asPaddingValues() + }, + ) + val brightness by Settings.customBrightness.collectAsState() + val brightnessValue by Settings.customBrightnessValue.collectAsState() + val colorOverlayEnabled by Settings.colorFilter.collectAsState() + val colorOverlay by Settings.colorFilterValue.collectAsState() + val colorOverlayMode by Settings.colorFilterMode.collectAsState { + when (it) { + 0 -> BlendMode.SrcOver + 1 -> BlendMode.Multiply + 2 -> BlendMode.Screen + 3 -> BlendMode.Overlay + 4 -> BlendMode.Lighten + 5 -> BlendMode.Darken + else -> unreachable() + } + } + ReaderContentOverlay( + brightness = { brightnessValue }.takeIf { brightness && brightnessValue < 0 }, + color = { colorOverlay }.takeIf { colorOverlayEnabled }, + colorBlendMode = colorOverlayMode, + ) + if (brightness) { + LaunchedEffect(Unit) { + Settings.customBrightnessValue.valueFlow().sample(100) + .onCompletion { setCustomBrightnessValue(0) } + .collect { setCustomBrightnessValue(it) } + } + } + val showPageNumber by Settings.showPageNumber.collectAsState() + if (showPageNumber && !appbarVisible) { + CompositionLocalProvider(LocalTextStyle provides MaterialTheme.typography.bodySmall) { + PageIndicatorText( + currentPage = syncState.sliderValue, + totalPages = pageLoader.size, + modifier = Modifier.align(Alignment.BottomCenter).navigationBarsPadding(), + ) + } + } + ReaderAppBars( + visible = appbarVisible, + isRtl = readingMode == ReadingModeType.RIGHT_TO_LEFT, + showSeekBar = showSeekbar, + currentPage = syncState.sliderValue, + totalPages = pageLoader.size, + onSliderValueChange = { syncState.sliderScrollTo(it + 1) }, + onClickSettings = { + launch { + dialog { cont -> + fun dispose() = cont.resume(Unit) + var isColorFilter by remember { mutableStateOf(false) } + val scrim by animateColorAsState( + targetValue = if (isColorFilter) Color.Transparent else BottomSheetDefaults.ScrimColor, + label = "ScrimColor", + ) + ModalBottomSheet( + onDismissRequest = { dispose() }, + modifier = Modifier.windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Top)), + // Yeah, I know color state should not be read here, but we have to do it... + scrimColor = scrim, + dragHandle = null, + contentWindowInsets = { WindowInsets(0) }, + ) { + SettingsPager(modifier = Modifier.fillMaxSize()) { page -> + isColorFilter = page == 2 + appbarVisible = !isColorFilter + } + } + } + } + }, + ) + } +} + +context(Context, DialogState, DestinationsNavigator) +private suspend fun preparePageLoader(args: ReaderScreenArgs) = when (args) { + is ReaderScreenArgs.Gallery -> { + val info = args.info + val page = args.page + val archive = DownloadManager.getDownloadInfo(info.gid)?.archiveFile + if (archive != null) { + ArchivePageLoader(archive, info.gid, page) + } else { + EhPageLoader(info, page) + } + } + is ReaderScreenArgs.Archive -> { + ArchivePageLoader(args.uri.toOkioPath()) { invalidator -> + awaitInputText( + title = getString(R.string.archive_need_passwd), + hint = getString(R.string.archive_passwd), + onUserDismiss = { popBackStack() }, + ) { text -> + ensure(text.isNotBlank()) { getString(R.string.passwd_cannot_be_empty) } + ensure(invalidator(text)) { getString(R.string.passwd_wrong) } + } } } } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt index 7ed5dbb4f..5ba65ed5e 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt @@ -105,7 +105,7 @@ import com.hippo.ehviewer.ui.main.GalleryInfoGridItem import com.hippo.ehviewer.ui.main.plus import com.hippo.ehviewer.ui.navToReader import com.hippo.ehviewer.ui.showMoveDownloadLabelList -import com.hippo.ehviewer.ui.tools.Deferred +import com.hippo.ehviewer.ui.tools.Await import com.hippo.ehviewer.ui.tools.FastScrollLazyColumn import com.hippo.ehviewer.ui.tools.FastScrollLazyVerticalStaggeredGrid import com.hippo.ehviewer.ui.tools.HapticFeedbackType @@ -599,7 +599,7 @@ fun DownloadsScreen(navigator: DestinationsNavigator) = composing(navigator) { } } - Deferred({ delay(200) }) { + Await({ delay(200) }) { if (list.isEmpty()) { Column( modifier = Modifier.padding(realPadding).fillMaxSize(), diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryListScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryListScreen.kt index 6a1d5f77f..0b031e0fe 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryListScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryListScreen.kt @@ -124,7 +124,7 @@ import com.hippo.ehviewer.ui.main.GalleryInfoListItem import com.hippo.ehviewer.ui.main.GalleryList import com.hippo.ehviewer.ui.main.ImageSearch import com.hippo.ehviewer.ui.main.SearchFilter -import com.hippo.ehviewer.ui.tools.Deferred +import com.hippo.ehviewer.ui.tools.Await import com.hippo.ehviewer.ui.tools.HapticFeedbackType import com.hippo.ehviewer.ui.tools.animateFloatMergePredictiveBackAsState import com.hippo.ehviewer.ui.tools.delegateSnapshotUpdate @@ -467,7 +467,7 @@ fun GalleryListScreen(lub: ListUrlBuilder, navigator: DestinationsNavigator) = c } } } - Deferred({ delay(200) }) { + Await({ delay(200) }) { if (quickSearchList.isEmpty()) { Text( text = stringResource(id = R.string.quick_search_tip), diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/HistoryScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/HistoryScreen.kt index 49f997938..59b9962b7 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/HistoryScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/HistoryScreen.kt @@ -52,7 +52,7 @@ import com.hippo.ehviewer.ui.composing import com.hippo.ehviewer.ui.doGalleryInfoAction import com.hippo.ehviewer.ui.main.GalleryInfoListItem import com.hippo.ehviewer.ui.main.plus -import com.hippo.ehviewer.ui.tools.Deferred +import com.hippo.ehviewer.ui.tools.Await import com.hippo.ehviewer.ui.tools.FastScrollLazyColumn import com.hippo.ehviewer.ui.tools.rememberInVM import com.hippo.ehviewer.ui.tools.thenIf @@ -164,7 +164,7 @@ fun HistoryScreen(navigator: DestinationsNavigator) = composing(navigator) { } } } - Deferred(keyword, { delay(200) }) { + Await(keyword, { delay(200) }) { if (historyData.itemCount == 0) { Column( modifier = Modifier.padding(paddingValues).padding(horizontal = marginH).fillMaxSize(), diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/FilterScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/FilterScreen.kt index a0544cef4..e42252bdb 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/FilterScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/FilterScreen.kt @@ -58,7 +58,7 @@ import com.hippo.ehviewer.client.EhFilter.trigger import com.hippo.ehviewer.collectAsState import com.hippo.ehviewer.dao.Filter import com.hippo.ehviewer.dao.FilterMode -import com.hippo.ehviewer.ui.tools.Deferred +import com.hippo.ehviewer.ui.tools.Await import com.hippo.ehviewer.ui.tools.LocalDialogState import com.hippo.ehviewer.ui.tools.thenIf import com.ramcosta.composedestinations.annotation.Destination @@ -213,7 +213,7 @@ fun FilterScreen(navigator: DestinationsNavigator) { } }, ) { paddingValues -> - Deferred({ allFilterMap.await() }) { filters -> + Await({ allFilterMap.await() }) { filters -> LazyColumn( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), contentPadding = paddingValues, diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Await.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Await.kt new file mode 100644 index 000000000..d595ccb19 --- /dev/null +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Await.kt @@ -0,0 +1,29 @@ +package com.hippo.ehviewer.ui.tools + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import arrow.core.None +import arrow.core.Option +import arrow.core.some + +@Composable +fun Await(block: suspend () -> T, content: @Composable (T) -> Unit) { + var completed by remember(block) { mutableStateOf>(None) } + LaunchedEffect(block) { + completed = block().some() + } + completed.onSome { content(it) } +} + +@Composable +fun Await(key: Any?, block: suspend () -> T, content: @Composable (T) -> Unit) { + var completed by remember(key, block) { mutableStateOf>(None) } + LaunchedEffect(key, block) { + completed = block().some() + } + completed.onSome { content(it) } +} diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/DeferedComposable.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/DeferedComposable.kt deleted file mode 100644 index 65d529801..000000000 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/DeferedComposable.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.hippo.ehviewer.ui.tools - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue - -@Composable -fun Deferred(block: suspend () -> T, content: @Composable (T) -> Unit) { - var completed by remember(block) { mutableStateOf(null) } - LaunchedEffect(block) { - completed = block() - } - completed?.let { content(it) } -} - -@Composable -fun Deferred(key: Any?, block: suspend () -> T, content: @Composable (T) -> Unit) { - var completed by remember(key, block) { mutableStateOf(null) } - LaunchedEffect(key, block) { - completed = block() - } - completed?.let { content(it) } -} diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Dialog.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Dialog.kt index 4752c098a..da33d7444 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Dialog.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/Dialog.kt @@ -156,13 +156,17 @@ class DialogState { hint: String? = null, isNumber: Boolean = false, @StringRes confirmText: Int = android.R.string.ok, + onUserDismiss: (() -> Unit)? = null, invalidator: (suspend Raise.(String) -> Unit)? = null, ) = dialog { cont -> val coroutineScope = rememberCoroutineScope() var state by remember(cont) { mutableStateOf(initial) } var error by remember(cont) { mutableStateOf(null) } AlertDialog( - onDismissRequest = { cont.cancel() }, + onDismissRequest = { + cont.cancel() + onUserDismiss?.invoke() + }, confirmButton = { TextButton(onClick = { if (invalidator == null) { diff --git a/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/loader/PageLoader.kt b/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/loader/PageLoader.kt index 099cb7a7d..c4100e983 100644 --- a/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/loader/PageLoader.kt +++ b/app/src/main/kotlin/eu/kanade/tachiyomi/ui/reader/loader/PageLoader.kt @@ -12,7 +12,6 @@ private const val MAX_CACHE_SIZE = 512 * 1024 * 1024 private const val MIN_CACHE_SIZE = 128 * 1024 * 1024 abstract class PageLoader { - private val cache by lazy { lruCache( maxSize = if (isAtLeastO) { @@ -27,6 +26,7 @@ abstract class PageLoader { }, ) } + val pages by lazy { check(size > 0) (0 until size).map { ReaderPage(it) } @@ -34,13 +34,16 @@ abstract class PageLoader { private val preloads = com.hippo.ehviewer.Settings.preloadImage.coerceIn(0, 100) - abstract suspend fun awaitReady(): Boolean + @CallSuper + open suspend fun awaitReady(): Boolean { + cache + return true + } + abstract val isReady: Boolean @CallSuper - open fun start() { - cache - } + abstract fun start() @CallSuper open fun stop() {