From f9d60e272d693fd645c42af33034f3e8ef2e5a13 Mon Sep 17 00:00:00 2001 From: Dude so hot Date: Fri, 17 Jan 2025 18:16:28 +0800 Subject: [PATCH 1/5] Introduce SnackbarContext as lambda env --- .../main/kotlin/com/hippo/ehviewer/ui/ComposeDefault.kt | 4 +++- .../main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt | 4 ++++ .../com/hippo/ehviewer/ui/screen/SnackbarContext.kt | 9 +++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SnackbarContext.kt diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/ComposeDefault.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/ComposeDefault.kt index ea2e624694..776c5d70f6 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/ComposeDefault.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/ComposeDefault.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import com.hippo.ehviewer.ui.i18n.LocalStrings import com.hippo.ehviewer.ui.i18n.Strings +import com.hippo.ehviewer.ui.screen.SnackbarContext import com.hippo.ehviewer.ui.screen.implicit import com.hippo.ehviewer.ui.tools.DialogState import com.hippo.ehviewer.ui.tools.LocalGlobalDialogState @@ -25,7 +26,7 @@ import kotlinx.coroutines.CoroutineScope @Composable inline fun AnimatedVisibilityScope.Screen( navigator: DestinationsNavigator, - block: @Composable context(Strings, MainActivity, SnackbarHostState, DialogState, SharedTransitionScope, TransitionsVisibilityScope, DestinationsNavigator, CoroutineScope) + block: @Composable context(Strings, MainActivity, SnackbarHostState, SnackbarContext, DialogState, SharedTransitionScope, TransitionsVisibilityScope, DestinationsNavigator, CoroutineScope) () -> R, ) = Box(modifier = Modifier.fillMaxSize()) { val dialogState = with(LocalGlobalDialogState.current) { rememberLocal() } @@ -35,6 +36,7 @@ inline fun AnimatedVisibilityScope.Screen( LocalStrings.current, with(LocalContext.current) { remember { findActivity() } }, LocalSnackBarHostState.current, + LocalSnackbarContext.current, dialogState, LocalSharedTransitionScope.current, this, diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt index f824ec0c73..47101b3b43 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt @@ -30,6 +30,7 @@ import androidx.compose.animation.SharedTransitionLayout import androidx.compose.animation.SharedTransitionScope import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.Image +import androidx.compose.foundation.MutatorMutex import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Spacer @@ -130,6 +131,7 @@ import com.hippo.ehviewer.ui.destinations.SignInScreenDestination import com.hippo.ehviewer.ui.destinations.SubscriptionScreenDestination import com.hippo.ehviewer.ui.destinations.ToplistScreenDestination import com.hippo.ehviewer.ui.destinations.WhatshotScreenDestination +import com.hippo.ehviewer.ui.screen.SnackbarContext import com.hippo.ehviewer.ui.screen.asDst import com.hippo.ehviewer.ui.screen.asDstWith import com.hippo.ehviewer.ui.screen.navWithUrl @@ -380,6 +382,7 @@ class MainActivity : EhActivity() { LocalSideSheetState provides sideSheetState, LocalDrawerHandle provides drawerHandle, LocalSnackBarHostState provides snackbarState, + LocalSnackbarContext provides remember { SnackbarContext(snackbarState, MutatorMutex()) }, LocalSnackBarFabPadding provides animateDpAsState(snackbarFabPadding, label = "SnackbarFabPadding"), LocalWindowSizeClass provides adaptiveInfo.windowSizeClass, ) { @@ -560,6 +563,7 @@ class MainActivity : EhActivity() { } } +val LocalSnackbarContext = compositionLocalOf { error("CompositionLocal LocalSnackbarContext not present!") } val LocalNavDrawerState = compositionLocalOf { error("CompositionLocal LocalNavDrawerState not present!") } val LocalSideSheetState = compositionLocalOf { error("CompositionLocal LocalSideSheetState not present!") } val LocalDrawerHandle = compositionLocalOf> { error("CompositionLocal LocalDrawerHandle not present!") } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SnackbarContext.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SnackbarContext.kt new file mode 100644 index 0000000000..ff72f9008d --- /dev/null +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SnackbarContext.kt @@ -0,0 +1,9 @@ +package com.hippo.ehviewer.ui.screen + +import androidx.compose.foundation.MutatorMutex +import androidx.compose.material3.SnackbarHostState + +class SnackbarContext( + val state: SnackbarHostState, + val mutex: MutatorMutex, +) From 94712e5e21371eba1355f129ac9cf55fc6c72c12 Mon Sep 17 00:00:00 2001 From: Dude so hot Date: Fri, 17 Jan 2025 18:24:01 +0800 Subject: [PATCH 2/5] Migrate to SnackbarContext, Part 1 --- .../com/hippo/ehviewer/ui/MainActivity.kt | 3 +- .../com/hippo/ehviewer/ui/main/AvatarIcon.kt | 4 +- .../ui/screen/GalleryDetailContent.kt | 37 +++++++++---------- .../ehviewer/ui/screen/SnackbarContext.kt | 13 +++++-- .../kotlin/moe/tarsin/coroutines/Result.kt | 7 ++-- 5 files changed, 33 insertions(+), 31 deletions(-) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt index 47101b3b43..999f38e328 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt @@ -30,7 +30,6 @@ import androidx.compose.animation.SharedTransitionLayout import androidx.compose.animation.SharedTransitionScope import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.Image -import androidx.compose.foundation.MutatorMutex import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Spacer @@ -382,7 +381,7 @@ class MainActivity : EhActivity() { LocalSideSheetState provides sideSheetState, LocalDrawerHandle provides drawerHandle, LocalSnackBarHostState provides snackbarState, - LocalSnackbarContext provides remember { SnackbarContext(snackbarState, MutatorMutex()) }, + LocalSnackbarContext provides remember { SnackbarContext(snackbarState) }, LocalSnackBarFabPadding provides animateDpAsState(snackbarFabPadding, label = "SnackbarFabPadding"), LocalWindowSizeClass provides adaptiveInfo.windowSizeClass, ) { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/AvatarIcon.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/AvatarIcon.kt index cb95af258e..53d9730dcf 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/AvatarIcon.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/AvatarIcon.kt @@ -20,7 +20,6 @@ import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LinearProgressIndicator -import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -48,6 +47,7 @@ import com.hippo.ehviewer.client.parser.HomeParser import com.hippo.ehviewer.collectAsState import com.hippo.ehviewer.ui.i18n.Strings import com.hippo.ehviewer.ui.login.refreshAccountInfo +import com.hippo.ehviewer.ui.screen.SnackbarContext import com.hippo.ehviewer.ui.tools.DialogState import com.hippo.ehviewer.util.displayString import com.ramcosta.composedestinations.navigation.DestinationsNavigator @@ -81,7 +81,7 @@ private val limitFlow: StateFlow = refreshEvent.conflate() .let { src -> merge(src, invalidateEvent.map { none() }) } .stateIn(limitScope, SharingStarted.Eagerly, none()) -context(CoroutineScope, DialogState, SnackbarHostState, DestinationsNavigator, Strings) +context(CoroutineScope, DialogState, SnackbarContext, DestinationsNavigator, Strings) @Composable fun AvatarIcon() { val hasSignedIn by Settings.hasSignedIn.collectAsState() diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryDetailContent.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryDetailContent.kt index 01ed111c25..ad64a049aa 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryDetailContent.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryDetailContent.kt @@ -34,7 +34,6 @@ import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet -import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -154,7 +153,7 @@ import kotlinx.coroutines.launch import moe.tarsin.coroutines.runSuspendCatching import moe.tarsin.coroutines.runSwallowingWithUI -context(CoroutineScope, DestinationsNavigator, DialogState, MainActivity, SnackbarHostState, SharedTransitionScope, TransitionsVisibilityScope, Strings) +context(CoroutineScope, DestinationsNavigator, DialogState, MainActivity, SnackbarContext, SharedTransitionScope, TransitionsVisibilityScope, Strings) @Composable fun GalleryDetailContent( galleryInfo: GalleryInfo, @@ -234,7 +233,7 @@ fun GalleryDetailContent( Text(text = stringResource(R.string.filter_the_uploader, uploader)) } Filter(FilterMode.UPLOADER, uploader).remember() - showSnackbar(filterAdded) + launchSnackbar(filterAdded) } } fun onDownloadButtonClick() { @@ -379,7 +378,7 @@ fun GalleryDetailContent( } } -context(Context, CoroutineScope, DestinationsNavigator, DialogState, SnackbarHostState, Strings) +context(Context, CoroutineScope, DestinationsNavigator, DialogState, SnackbarContext, Strings) @Composable fun BelowHeader(galleryDetail: GalleryDetail, voteTag: VoteTag) { @Composable @@ -483,13 +482,13 @@ fun BelowHeader(galleryDetail: GalleryDetail, voteTag: VoteTag) { modifyFavorites(galleryDetail.galleryInfo) }.onSuccess { add -> if (add) { - showSnackbar(addToFavoriteSuccess) + launchSnackbar(addToFavoriteSuccess) } else { - showSnackbar(removeFromFavoriteSuccess) + launchSnackbar(removeFromFavoriteSuccess) } }.onFailure { // TODO: We don't know if it's add or remove - showSnackbar(addToFavoriteFailure) + launchSnackbar(addToFavoriteFailure) } } } @@ -542,12 +541,12 @@ fun BelowHeader(galleryDetail: GalleryDetail, voteTag: VoteTag) { fun showArchiveDialog() { launchIO { if (galleryDetail.apiUid < 0) { - showSnackbar(signInFirst) + launchSnackbar(signInFirst) } else { runSuspendCatching { val (archiveList, funds) = bgWork { archiveResult.await() } if (archiveList.isEmpty()) { - showSnackbar(noArchives) + launchSnackbar(noArchives) } else { val selected = showNoButton { ArchiveList( @@ -557,15 +556,15 @@ fun BelowHeader(galleryDetail: GalleryDetail, voteTag: VoteTag) { ) } EhUtils.downloadArchive(galleryDetail, selected) - showSnackbar(downloadArchiveStarted) + launchSnackbar(downloadArchiveStarted) } }.onFailure { when (it) { - is NoHAtHClientException -> showSnackbar(downloadArchiveFailureNoHath) - is EhException -> showSnackbar(it.displayString()) + is NoHAtHClientException -> launchSnackbar(downloadArchiveFailureNoHath) + is EhException -> launchSnackbar(it.displayString()) else -> { logcat(it) - showSnackbar(downloadArchiveFailure) + launchSnackbar(downloadArchiveFailure) } } } @@ -589,7 +588,7 @@ fun BelowHeader(galleryDetail: GalleryDetail, voteTag: VoteTag) { suspend fun showTorrentDialog() { val (torrentList, key) = bgWork { torrentResult.await() } if (torrentList.isEmpty()) { - showSnackbar(noTorrents) + launchSnackbar(noTorrents) } else { val selected = showNoButton(false) { TorrentList( @@ -615,7 +614,7 @@ fun BelowHeader(galleryDetail: GalleryDetail, voteTag: VoteTag) { onClick = { launchIO { when { - galleryDetail.torrentCount <= 0 -> showSnackbar(noTorrents) + galleryDetail.torrentCount <= 0 -> launchSnackbar(noTorrents) else -> runSwallowingWithUI { showTorrentDialog() } } } @@ -638,7 +637,7 @@ fun BelowHeader(galleryDetail: GalleryDetail, voteTag: VoteTag) { fun showRateDialog() { launchIO { if (galleryDetail.apiUid < 0) { - showSnackbar(signInFirst) + launchSnackbar(signInFirst) return@launchIO } val pendingRating = awaitResult(galleryDetail.rating.coerceAtLeast(.5f), title = R.string.rate) { @@ -663,10 +662,10 @@ fun BelowHeader(galleryDetail: GalleryDetail, voteTag: VoteTag) { ratingCount = result.ratingCount } ratingText = getAllRatingText(result.rating, result.ratingCount) - showSnackbar(rateSucceed) + launchSnackbar(rateSucceed) }.onFailure { logcat(it) - showSnackbar(rateFailed) + launchSnackbar(rateFailed) } } } @@ -721,7 +720,7 @@ fun BelowHeader(galleryDetail: GalleryDetail, voteTag: VoteTag) { onSelect(addFilter) { awaitConfirmationOrCancel { Text(text = stringResource(R.string.filter_the_tag, tag)) } Filter(FilterMode.TAG, tag).remember() - showSnackbar(filterAdded) + launchSnackbar(filterAdded) } if (galleryDetail.apiUid >= 0) { when (vote) { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SnackbarContext.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SnackbarContext.kt index ff72f9008d..45a5d976e5 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SnackbarContext.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SnackbarContext.kt @@ -2,8 +2,13 @@ package com.hippo.ehviewer.ui.screen import androidx.compose.foundation.MutatorMutex import androidx.compose.material3.SnackbarHostState +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch -class SnackbarContext( - val state: SnackbarHostState, - val mutex: MutatorMutex, -) +class SnackbarContext(val state: SnackbarHostState) { + private val mutex = MutatorMutex() + + fun CoroutineScope.launchSnackbar(msg: String) = launch { + mutex.mutate { state.showSnackbar(msg) } + } +} diff --git a/app/src/main/kotlin/moe/tarsin/coroutines/Result.kt b/app/src/main/kotlin/moe/tarsin/coroutines/Result.kt index 45b2a599f9..0a65de9587 100644 --- a/app/src/main/kotlin/moe/tarsin/coroutines/Result.kt +++ b/app/src/main/kotlin/moe/tarsin/coroutines/Result.kt @@ -1,10 +1,9 @@ package moe.tarsin.coroutines -import androidx.compose.material3.SnackbarHostState +import com.hippo.ehviewer.ui.screen.SnackbarContext import com.hippo.ehviewer.util.displayString import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch inline fun Result.except() = onFailure { if (it is E) throw it } @@ -12,5 +11,5 @@ inline fun runSuspendCatching(block: () -> R) = runCatching(block).except T.runSuspendCatching(block: T.() -> R) = runCatching(block).except() -context(SnackbarHostState, CoroutineScope) -inline fun runSwallowingWithUI(block: () -> R) = runSuspendCatching(block).onFailure { e -> launch { showSnackbar(e.displayString()) } } +context(SnackbarContext, CoroutineScope) +inline fun runSwallowingWithUI(block: () -> R) = runSuspendCatching(block).onFailure { e -> launchSnackbar(e.displayString()) } From a89198b1c2cc71907baf5d74135898a5a23296a7 Mon Sep 17 00:00:00 2001 From: Dude so hot Date: Fri, 17 Jan 2025 18:33:09 +0800 Subject: [PATCH 3/5] Migrate Settings to SnackbarContext --- .../com/hippo/ehviewer/ui/ComposeDefault.kt | 4 +-- .../hippo/ehviewer/ui/settings/AboutScreen.kt | 14 ++------- .../ehviewer/ui/settings/AdvancedScreen.kt | 29 +++++++++---------- .../ehviewer/ui/settings/DownloadScreen.kt | 17 +++++------ .../hippo/ehviewer/ui/settings/EhScreen.kt | 8 +---- .../ehviewer/ui/settings/PrivacyScreen.kt | 8 +---- .../ehviewer/ui/settings/UConfigScreen.kt | 6 +--- 7 files changed, 28 insertions(+), 58 deletions(-) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/ComposeDefault.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/ComposeDefault.kt index 776c5d70f6..113ee09282 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/ComposeDefault.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/ComposeDefault.kt @@ -24,10 +24,10 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator import kotlinx.coroutines.CoroutineScope @Composable -inline fun AnimatedVisibilityScope.Screen( +inline fun AnimatedVisibilityScope.Screen( navigator: DestinationsNavigator, block: @Composable context(Strings, MainActivity, SnackbarHostState, SnackbarContext, DialogState, SharedTransitionScope, TransitionsVisibilityScope, DestinationsNavigator, CoroutineScope) - () -> R, + () -> Unit, ) = Box(modifier = Modifier.fillMaxSize()) { val dialogState = with(LocalGlobalDialogState.current) { rememberLocal() } with(NoopTransitionsVisibilityScope) { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AboutScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AboutScreen.kt index d50e6fb754..e43d7cd483 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AboutScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AboutScreen.kt @@ -14,14 +14,10 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarHost -import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext @@ -46,8 +42,6 @@ import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.navigation.DestinationsNavigator import eu.kanade.tachiyomi.util.lang.withUIContext -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import moe.tarsin.coroutines.runSuspendCatching private const val REPO_URL = "https://github.com/${BuildConfig.REPO_NAME}" @@ -58,9 +52,6 @@ private const val RELEASE_URL = "$REPO_URL/releases" fun AnimatedVisibilityScope.AboutScreen(navigator: DestinationsNavigator) = Screen(navigator) { val context = LocalContext.current val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() - val snackbarHostState = remember { SnackbarHostState() } - val coroutineScope = rememberCoroutineScope { Dispatchers.IO } - fun launchSnackBar(content: String) = coroutineScope.launch { snackbarHostState.showSnackbar(content) } Scaffold( topBar = { TopAppBar( @@ -73,7 +64,6 @@ fun AnimatedVisibilityScope.AboutScreen(navigator: DestinationsNavigator) = Scre scrollBehavior = scrollBehavior, ) }, - snackbarHost = { SnackbarHost(snackbarHostState) }, ) { paddingValues -> Column(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection).verticalScroll(rememberScrollState()).padding(paddingValues)) { Preference( @@ -117,9 +107,9 @@ fun AnimatedVisibilityScope.AboutScreen(navigator: DestinationsNavigator) = Scre runSuspendCatching { AppUpdater.checkForUpdate(true)?.let { showNewVersion(context, it) - } ?: launchSnackBar(alreadyLatestVersion) + } ?: launchSnackbar(alreadyLatestVersion) }.onFailure { - launchSnackBar(updateFailed(it.displayString())) + launchSnackbar(updateFailed(it.displayString())) } } } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt index fb975d91ff..4969b376ba 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt @@ -9,6 +9,7 @@ import androidx.activity.result.PickVisualMediaRequest import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia.ImageOnly import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -18,8 +19,6 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarHost -import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults @@ -41,6 +40,7 @@ import com.hippo.ehviewer.asMutableState import com.hippo.ehviewer.client.EhEngine import com.hippo.ehviewer.client.data.FavListUrlBuilder import com.hippo.ehviewer.collectAsState +import com.hippo.ehviewer.ui.Screen import com.hippo.ehviewer.ui.tools.observed import com.hippo.ehviewer.ui.tools.rememberedAccessor import com.hippo.ehviewer.util.AdsPlaceholderFile @@ -72,12 +72,10 @@ import moe.tarsin.coroutines.runSuspendCatching @Destination @Composable -fun AdvancedScreen(navigator: DestinationsNavigator) { +fun AnimatedVisibilityScope.AdvancedScreen(navigator: DestinationsNavigator) = Screen(navigator) { val context = LocalContext.current val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() - val snackbarHostState = remember { SnackbarHostState() } val coroutineScope = rememberCoroutineScope { Dispatchers.IO } - fun launchSnackBar(content: String) = coroutineScope.launch { snackbarHostState.showSnackbar(content) } Scaffold( topBar = { TopAppBar( @@ -90,7 +88,6 @@ fun AdvancedScreen(navigator: DestinationsNavigator) { scrollBehavior = scrollBehavior, ) }, - snackbarHost = { SnackbarHost(snackbarHostState) }, ) { paddingValues -> Column(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection).verticalScroll(rememberScrollState()).padding(paddingValues)) { SwitchPreference( @@ -149,10 +146,10 @@ fun AdvancedScreen(navigator: DestinationsNavigator) { CrashHandler.collectInfo(zipOs.writer()) Runtime.getRuntime().exec("logcat -d").inputStream.use { it.copyTo(zipOs) } } - launchSnackBar(getString(R.string.settings_advanced_dump_logcat_to, uri.displayPath)) + launchSnackbar(getString(R.string.settings_advanced_dump_logcat_to, uri.displayPath)) } }.onFailure { - launchSnackBar(dumpLogError) + launchSnackbar(dumpLogError) logcat(it) } } @@ -214,10 +211,10 @@ fun AdvancedScreen(navigator: DestinationsNavigator) { context.runCatching { grantUriPermission(BuildConfig.APPLICATION_ID, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) EhDB.exportDB(context, uri.toOkioPath()) - launchSnackBar(getString(R.string.settings_advanced_export_data_to, uri.displayPath)) + launchSnackbar(getString(R.string.settings_advanced_export_data_to, uri.displayPath)) }.onFailure { logcat(it) - launchSnackBar(exportFailed) + launchSnackbar(exportFailed) } } } @@ -233,10 +230,10 @@ fun AdvancedScreen(navigator: DestinationsNavigator) { context.runCatching { grantUriPermission(BuildConfig.APPLICATION_ID, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) EhDB.importDB(context, uri) - launchSnackBar(importSucceed) + launchSnackbar(importSucceed) }.onFailure { logcat(it) - launchSnackBar(importFailed) + launchSnackbar(importFailed) } } } @@ -255,13 +252,13 @@ fun AdvancedScreen(navigator: DestinationsNavigator) { tailrec suspend fun doBackup() { val result = EhEngine.getFavorites(favListUrlBuilder.build()) if (result.galleryInfoList.isEmpty()) { - launchSnackBar(backupNothing) + launchSnackbar(backupNothing) } else { if (favTotal == 0) favTotal = result.countArray.sum() favIndex += result.galleryInfoList.size val status = "($favIndex/$favTotal)" EhDB.putLocalFavorites(result.galleryInfoList) - launchSnackBar(context.getString(R.string.settings_advanced_backup_favorite_start, status)) + launchSnackbar(context.getString(R.string.settings_advanced_backup_favorite_start, status)) if (result.next != null) { delay(Settings.downloadDelay.toLong()) favListUrlBuilder.setIndex(result.next, true) @@ -273,10 +270,10 @@ fun AdvancedScreen(navigator: DestinationsNavigator) { runSuspendCatching { doBackup() }.onSuccess { - launchSnackBar(backupSucceed) + launchSnackbar(backupSucceed) }.onFailure { logcat(it) - launchSnackBar(backupFailed) + launchSnackbar(backupFailed) } } } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/DownloadScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/DownloadScreen.kt index 1e77f4cb6a..cfd51f3575 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/DownloadScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/DownloadScreen.kt @@ -83,7 +83,6 @@ import splitties.init.appCtx @Composable fun AnimatedVisibilityScope.DownloadScreen(navigator: DestinationsNavigator) = Screen(navigator) { val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() - fun launchSnackBar(content: String) = launch { showSnackbar(content) } Scaffold( topBar = { TopAppBar( @@ -110,7 +109,7 @@ fun AnimatedVisibilityScope.DownloadScreen(navigator: DestinationsNavigator) = S downloadLocationState = path }.onFailure { logcat(it) - launchSnackBar(settingsDownloadCantGetDownloadLocation) + launchSnackbar(settingsDownloadCantGetDownloadLocation) } } } @@ -154,7 +153,7 @@ fun AnimatedVisibilityScope.DownloadScreen(navigator: DestinationsNavigator) = S logcat(it) } } - launchSnackBar(settingsDownloadCantGetDownloadLocation) + launchSnackbar(settingsDownloadCantGetDownloadLocation) } } } @@ -238,10 +237,10 @@ fun AnimatedVisibilityScope.DownloadScreen(navigator: DestinationsNavigator) = S di.galleryInfo.also { SpiderDen(it, di.dirname!!).writeComicInfo(false) } } EhDB.updateGalleryInfo(toUpdate) - launchSnackBar(getString(R.string.settings_download_reload_metadata_successfully, toUpdate.size)) + launchSnackbar(getString(R.string.settings_download_reload_metadata_successfully, toUpdate.size)) } }.onFailure { - launchSnackBar(getString(R.string.settings_download_reload_metadata_failed, it.displayString())) + launchSnackbar(getString(R.string.settings_download_reload_metadata_failed, it.displayString())) } } val restoreFailed = stringResource(id = R.string.settings_download_restore_failed) @@ -282,7 +281,7 @@ fun AnimatedVisibilityScope.DownloadScreen(navigator: DestinationsNavigator) = S fillGalleryListByApi(it, EhUrl.referer) } if (result.isEmpty()) { - launchSnackBar(RESTORE_COUNT_MSG(restoreDirCount)) + launchSnackbar(RESTORE_COUNT_MSG(restoreDirCount)) } else { val count = result.parMap { if (it.pages != 0) { @@ -291,11 +290,11 @@ fun AnimatedVisibilityScope.DownloadScreen(navigator: DestinationsNavigator) = S SpiderDen(it.galleryInfo, it.dirname).writeComicInfo(false) } }.size - launchSnackBar(RESTORE_COUNT_MSG(count + restoreDirCount)) + launchSnackbar(RESTORE_COUNT_MSG(count + restoreDirCount)) } }.onFailure { logcat(it) - launchSnackBar(restoreFailed) + launchSnackbar(restoreFailed) } } WorkPreference( @@ -322,7 +321,7 @@ fun AnimatedVisibilityScope.DownloadScreen(navigator: DestinationsNavigator) = S } } val cnt = list.count { runCatching { it.delete() }.getOrNull() != null } - launchSnackBar(FINAL_CLEAR_REDUNDANCY_MSG(cnt)) + launchSnackbar(FINAL_CLEAR_REDUNDANCY_MSG(cnt)) } } } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/EhScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/EhScreen.kt index 3878adf9fd..371dd6869c 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/EhScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/EhScreen.kt @@ -17,14 +17,11 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarHost -import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier @@ -62,9 +59,7 @@ import kotlinx.datetime.LocalTime @Composable fun AnimatedVisibilityScope.EhScreen(navigator: DestinationsNavigator) = Screen(navigator) { val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() - val snackbarHostState = remember { SnackbarHostState() } val coroutineScope = rememberCoroutineScope { Dispatchers.IO } - fun launchSnackBar(content: String) = coroutineScope.launch { snackbarHostState.showSnackbar(content) } val hasSignedIn by Settings.hasSignedIn.collectAsState() Scaffold( topBar = { @@ -78,7 +73,6 @@ fun AnimatedVisibilityScope.EhScreen(navigator: DestinationsNavigator) = Screen( scrollBehavior = scrollBehavior, ) }, - snackbarHost = { SnackbarHost(snackbarHostState) }, ) { paddingValues -> Column( modifier = Modifier @@ -113,7 +107,7 @@ fun AnimatedVisibilityScope.EhScreen(navigator: DestinationsNavigator) = Screen( IconButton(onClick = { copyTextToClipboard(state.text, true) // Avoid double notify user since system have done that on Tiramisu above - if (!isAtLeastT) launchSnackBar(copiedToClipboard) + if (!isAtLeastT) launchSnackbar(copiedToClipboard) }) { Icon(imageVector = Icons.Default.ContentCopy, contentDescription = null) } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/PrivacyScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/PrivacyScreen.kt index 7cef31a652..b9af222ebc 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/PrivacyScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/PrivacyScreen.kt @@ -9,13 +9,10 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarHost -import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext @@ -36,8 +33,6 @@ import kotlinx.coroutines.launch @Composable fun AnimatedVisibilityScope.PrivacyScreen(navigator: DestinationsNavigator) = Screen(navigator) { val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() - val snackbarHostState = remember { SnackbarHostState() } - fun launchSnackBar(content: String) = launch { snackbarHostState.showSnackbar(content) } Scaffold( topBar = { TopAppBar( @@ -50,7 +45,6 @@ fun AnimatedVisibilityScope.PrivacyScreen(navigator: DestinationsNavigator) = Sc scrollBehavior = scrollBehavior, ) }, - snackbarHost = { SnackbarHost(snackbarHostState) }, ) { Column(modifier = Modifier.padding(it).nestedScroll(scrollBehavior.nestedScrollConnection)) { val security = Settings.security.asMutableState() @@ -89,7 +83,7 @@ fun AnimatedVisibilityScope.PrivacyScreen(navigator: DestinationsNavigator) = Sc title = R.string.clear_search_history_confirm, ) searchDatabase.searchDao().clear() - launchSnackBar(searchHistoryCleared) + launchSnackbar(searchHistoryCleared) } } } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/UConfigScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/UConfigScreen.kt index 48645d3d63..d84b07196f 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/UConfigScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/UConfigScreen.kt @@ -10,8 +10,6 @@ import androidx.compose.material.icons.filled.Check import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarHost -import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable @@ -38,7 +36,6 @@ private const val APPLY_JS = "javascript:(function(){var apply = document.getEle fun AnimatedVisibilityScope.UConfigScreen(navigator: DestinationsNavigator) = Screen(navigator) { val url = EhUrl.uConfigUrl var webview by remember { Atomic(null)::value } - val snackbarHostState = remember { SnackbarHostState() } Scaffold( topBar = { TopAppBar( @@ -60,7 +57,6 @@ fun AnimatedVisibilityScope.UConfigScreen(navigator: DestinationsNavigator) = Sc }, ) }, - snackbarHost = { SnackbarHost(snackbarHostState) }, ) { paddingValues -> val state = rememberWebViewState(url = url) WebView( @@ -69,7 +65,7 @@ fun AnimatedVisibilityScope.UConfigScreen(navigator: DestinationsNavigator) = Sc onCreated = { it.setDefaultSettings() }, factory = { WebView(it).apply { webview = this } }, ) - LaunchedEffect(Unit) { snackbarHostState.showSnackbar(applyTip) } + LaunchedEffect(Unit) { launchSnackbar(applyTip) } DisposableEffect(Unit) { onDispose { EhCookieStore.flush() From 741276f2191b482a06b966db2b885d2f57d49f12 Mon Sep 17 00:00:00 2001 From: Dude so hot Date: Fri, 17 Jan 2025 18:39:23 +0800 Subject: [PATCH 4/5] Migrate remaining to SnackbarContext --- .../hippo/ehviewer/ui/reader/PageActions.kt | 46 +++++++++---------- .../ui/screen/GalleryCommentsScreen.kt | 10 ++-- .../ehviewer/ui/screen/GalleryDetailScreen.kt | 6 +-- .../ehviewer/ui/screen/GalleryListScreen.kt | 4 +- .../ehviewer/ui/screen/ImageSearchScreen.kt | 2 +- 5 files changed, 33 insertions(+), 35 deletions(-) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PageActions.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PageActions.kt index cb55927a43..f33b89a0f6 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PageActions.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/reader/PageActions.kt @@ -10,7 +10,6 @@ import android.os.Environment import android.provider.MediaStore import android.webkit.MimeTypeMap import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.material3.SnackbarHostState import androidx.core.content.FileProvider import com.hippo.ehviewer.BuildConfig.APPLICATION_ID import com.hippo.ehviewer.R @@ -18,6 +17,7 @@ import com.hippo.ehviewer.client.EhUrl import com.hippo.ehviewer.client.data.GalleryInfo import com.hippo.ehviewer.gallery.Page import com.hippo.ehviewer.gallery.PageLoader +import com.hippo.ehviewer.ui.screen.SnackbarContext import com.hippo.ehviewer.util.AppConfig import com.hippo.ehviewer.util.FileUtils import com.hippo.ehviewer.util.awaitActivityResult @@ -28,9 +28,9 @@ import com.hippo.ehviewer.util.requestPermission import com.hippo.files.toOkioPath import eu.kanade.tachiyomi.util.system.logcat import java.io.File +import kotlinx.coroutines.CoroutineScope import kotlinx.datetime.Clock import moe.tarsin.coroutines.runSuspendCatching -import okio.Path.Companion.toOkioPath import splitties.systemservices.clipboardManager context(PageLoader) @@ -41,14 +41,14 @@ private fun Context.provideImage(index: Int): Uri? { return FileProvider.getUriForFile(this, "$APPLICATION_ID.fileprovider", file.toFile()) } -context(SnackbarHostState, Context, PageLoader) -suspend fun shareImage(page: Page, info: GalleryInfo? = null) { +context(SnackbarContext, CoroutineScope, Context, PageLoader) +fun shareImage(page: Page, info: GalleryInfo? = null) { val error = getString(R.string.error_cant_save_image) val share = getString(R.string.share_image) val noActivity = getString(R.string.error_cant_find_activity) val uri = provideImage(page.index) if (uri == null) { - showSnackbar(error) + launchSnackbar(error) return } val intent = Intent(Intent.ACTION_SEND).apply { @@ -61,35 +61,33 @@ suspend fun shareImage(page: Page, info: GalleryInfo? = null) { } try { startActivity(Intent.createChooser(intent, share)) - } catch (e: Throwable) { - showSnackbar(noActivity) + } catch (_: Throwable) { + launchSnackbar(noActivity) } } -context(SnackbarHostState, Context, PageLoader) -suspend fun copy(page: Page) { +context(SnackbarContext, CoroutineScope, Context, PageLoader) +fun copy(page: Page) { val error = getString(R.string.error_cant_save_image) val copied = getString(R.string.copied_to_clipboard) val uri = provideImage(page.index) if (uri == null) { - showSnackbar(error) + launchSnackbar(error) return } val clipData = ClipData.newUri(contentResolver, "ehviewer", uri) clipboardManager.setPrimaryClip(clipData) - if (!isAtLeastT) { - showSnackbar(copied) - } + if (!isAtLeastT) launchSnackbar(copied) } -context(SnackbarHostState, Context, PageLoader) +context(SnackbarContext, CoroutineScope, Context, PageLoader) suspend fun save(page: Page) { val granted = isAtLeastQ || requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) val cannotSave = getString(R.string.error_cant_save_image) if (granted) { val filename = getImageFilename(page.index) if (filename == null) { - showSnackbar(cannotSave) + launchSnackbar(cannotSave) return } val extension = FileUtils.getExtensionFromFilename(filename) @@ -108,7 +106,7 @@ suspend fun save(page: Page) { val path = File(dir, AppConfig.APP_DIRNAME) realPath = path.toString() if (!FileUtils.ensureDirectory(path)) { - showSnackbar(cannotSave) + launchSnackbar(cannotSave) return } values.put(MediaStore.MediaColumns.DATA, realPath + File.separator + filename) @@ -121,26 +119,26 @@ suspend fun save(page: Page) { } catch (e: Exception) { e.logcat(e) } - showSnackbar(cannotSave) + launchSnackbar(cannotSave) } else if (isAtLeastQ) { val contentValues = ContentValues() contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0) contentResolver.update(imageUri, contentValues, null, null) } - showSnackbar(getString(R.string.image_saved, realPath + File.separator + filename)) + launchSnackbar(getString(R.string.image_saved, realPath + File.separator + filename)) } else { - showSnackbar(cannotSave) + launchSnackbar(cannotSave) } } else { - showSnackbar(getString(R.string.permission_denied)) + launchSnackbar(getString(R.string.permission_denied)) } } -context(SnackbarHostState, Context, PageLoader) +context(SnackbarContext, CoroutineScope, Context, PageLoader) suspend fun saveTo(page: Page) { val filename = getImageFilename(page.index) if (filename == null) { - showSnackbar(getString(R.string.error_cant_save_image)) + launchSnackbar(getString(R.string.error_cant_save_image)) return } val extension = FileUtils.getExtensionFromFilename(filename) @@ -149,10 +147,10 @@ suspend fun saveTo(page: Page) { val uri = awaitActivityResult(ActivityResultContracts.CreateDocument(mimeType), filename) if (uri != null) { save(index, uri.toOkioPath()) - showSnackbar(getString(R.string.image_saved, uri.displayPath)) + launchSnackbar(getString(R.string.image_saved, uri.displayPath)) } }.onFailure { it.logcat(it) - showSnackbar(getString(R.string.error_cant_find_activity)) + launchSnackbar(getString(R.string.error_cant_find_activity)) } } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryCommentsScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryCommentsScreen.kt index c7d0f0c78e..336651d556 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryCommentsScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryCommentsScreen.kt @@ -220,10 +220,10 @@ fun AnimatedVisibilityScope.GalleryCommentsScreen(gid: Long, navigator: Destinat userComment = TextFieldValue() commentId = -1L comments = it - showSnackbar(msg) + launchSnackbar(msg) }.onFailure { val text = if (commentId != -1L) editCommentFail else commentFail - showSnackbar(text + "\n" + it.displayString()) + launchSnackbar(text + "\n" + it.displayString()) } } @@ -233,7 +233,7 @@ fun AnimatedVisibilityScope.GalleryCommentsScreen(gid: Long, navigator: Destinat awaitConfirmationOrCancel { Text(text = stringResource(R.string.filter_the_commenter, commenter)) } Filter(FilterMode.COMMENTER, commenter).remember() comments = comments.copy(comments = comments.comments.filterNot { it.user == commenter }) - showSnackbar(filterAdded) + launchSnackbar(filterAdded) } suspend fun showCommentVoteStatus(comment: GalleryComment) { @@ -346,7 +346,7 @@ fun AnimatedVisibilityScope.GalleryCommentsScreen(gid: Long, navigator: Destinat refreshComment(true) } }.onSuccess { result -> - showSnackbar( + launchSnackbar( if (isUp) { if (0 != result.vote) voteUpSucceed else cancelVoteUpSucceed } else { @@ -354,7 +354,7 @@ fun AnimatedVisibilityScope.GalleryCommentsScreen(gid: Long, navigator: Destinat }, ) }.onFailure { - showSnackbar(voteFailed) + launchSnackbar(voteFailed) } } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryDetailScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryDetailScreen.kt index 80afb1d800..2ae9f50c1e 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryDetailScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/GalleryDetailScreen.kt @@ -205,7 +205,7 @@ fun AnimatedVisibilityScope.GalleryDetailScreen(args: GalleryDetailScreenArgs, n val detail = galleryInfo as? GalleryDetail ?: return@DropdownMenuItem launchIO { if (detail.apiUid < 0) { - showSnackbar(signInFirst) + launchSnackbar(signInFirst) } else { val tags = awaitSelectTags() if (tags.isNotEmpty()) { @@ -244,7 +244,7 @@ fun AnimatedVisibilityScope.GalleryDetailScreen(args: GalleryDetailScreenArgs, n val key = getImageKey(gd.gid, it) imageCache.remove(key) } - showSnackbar(imageCacheCleared) + launchSnackbar(imageCacheCleared) } }, ) @@ -288,7 +288,7 @@ fun AnimatedVisibilityScope.GalleryDetailScreen(args: GalleryDetailScreenArgs, n file.delete() exportAsArchiveFailed } - showSnackbar(message = msg) + launchSnackbar(msg) } } } 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 a35c179dbc..2f80281dee 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 @@ -297,7 +297,7 @@ fun AnimatedVisibilityScope.GalleryListScreen(lub: ListUrlBuilder, navigator: De if (data.itemCount == 0) return@IconButton launch { if (urlBuilder.mode == MODE_IMAGE_SEARCH) { - showSnackbar(invalidImageQuickSearch) + launchSnackbar(invalidImageQuickSearch) } else { val firstItem = data.itemSnapshotList.items[getFirstVisibleItemIndex()] val next = firstItem.gid + 1 @@ -305,7 +305,7 @@ fun AnimatedVisibilityScope.GalleryListScreen(lub: ListUrlBuilder, navigator: De if (urlBuilder.equalsQuickSearch(q)) { val nextStr = q.name.substringAfterLast('@', "") if (nextStr.toLongOrNull() == next) { - showSnackbar(getString(R.string.duplicate_quick_search, q.name)) + launchSnackbar(getString(R.string.duplicate_quick_search, q.name)) return@launch } } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/ImageSearchScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/ImageSearchScreen.kt index d0b6195590..643cfd863d 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/ImageSearchScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/ImageSearchScreen.kt @@ -70,7 +70,7 @@ fun AnimatedVisibilityScope.ImageSearchScreen(navigator: DestinationsNavigator) ) } } else { - launch { showSnackbar(selectImageFirst) } + launchSnackbar(selectImageFirst) } }, modifier = Modifier.snackBarPadding(), From 8524eb742325ee5aa6b3f5024d2445a10783a654 Mon Sep 17 00:00:00 2001 From: Dude so hot Date: Fri, 17 Jan 2025 18:46:40 +0800 Subject: [PATCH 5/5] Migrate to SnackbarContext --- .../com/hippo/ehviewer/ui/ComposeDefault.kt | 4 +--- .../com/hippo/ehviewer/ui/MainActivity.kt | 21 ++++++++----------- .../hippo/ehviewer/ui/reader/ReaderScreen.kt | 4 ++-- .../ehviewer/ui/screen/SnackbarContext.kt | 13 +++++++++++- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/ComposeDefault.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/ComposeDefault.kt index 113ee09282..cff2b91a67 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/ComposeDefault.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/ComposeDefault.kt @@ -4,7 +4,6 @@ import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.animation.SharedTransitionScope import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -26,7 +25,7 @@ import kotlinx.coroutines.CoroutineScope @Composable inline fun AnimatedVisibilityScope.Screen( navigator: DestinationsNavigator, - block: @Composable context(Strings, MainActivity, SnackbarHostState, SnackbarContext, DialogState, SharedTransitionScope, TransitionsVisibilityScope, DestinationsNavigator, CoroutineScope) + block: @Composable context(Strings, MainActivity, SnackbarContext, DialogState, SharedTransitionScope, TransitionsVisibilityScope, DestinationsNavigator, CoroutineScope) () -> Unit, ) = Box(modifier = Modifier.fillMaxSize()) { val dialogState = with(LocalGlobalDialogState.current) { rememberLocal() } @@ -35,7 +34,6 @@ inline fun AnimatedVisibilityScope.Screen( block( LocalStrings.current, with(LocalContext.current) { remember { findActivity() } }, - LocalSnackBarHostState.current, LocalSnackbarContext.current, dialogState, LocalSharedTransitionScope.current, diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt index 999f38e328..3ac94961b7 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/MainActivity.kt @@ -66,7 +66,6 @@ import androidx.compose.material3.NavigationDrawerItem import androidx.compose.material3.Scaffold import androidx.compose.material3.ShapeDefaults import androidx.compose.material3.SnackbarHost -import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarResult import androidx.compose.material3.Text import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo @@ -221,7 +220,7 @@ class MainActivity : EhActivity() { val configuration = LocalConfiguration.current val navDrawerState = rememberDrawerState(DrawerValue.Closed) val sideSheetState = rememberDrawerState2(DrawerValue.Closed) - val snackbarState = remember { SnackbarHostState() } + val snackbar = remember { SnackbarContext() } val scope = rememberCoroutineScope() val navController = rememberNavController() val navigator = navController.rememberDestinationsNavigator() @@ -261,10 +260,10 @@ class MainActivity : EhActivity() { } } }.onFailure { - snackbarState.showSnackbar(getString(R.string.update_failed, it.displayString())) + snackbar.showSnackbar(getString(R.string.update_failed, it.displayString())) } } else { - snackbarState.showSnackbar(noNetwork) + snackbar.showSnackbar(noNetwork) } } } @@ -322,7 +321,7 @@ class MainActivity : EhActivity() { LaunchedEffect(Unit) { tipFlow.collectLatest { - snackbarState.showSnackbar(it) + snackbar.showSnackbar(it) } } val warning = stringResource(R.string.metered_network_warning) @@ -332,13 +331,13 @@ class MainActivity : EhActivity() { LaunchedEffect(Unit) { if (connectivityManager.isActiveNetworkMetered) { if (isAtLeastQ) { - val ret = snackbarState.showSnackbar(warning, settings, true) + val ret = snackbar.showSnackbar(warning, settings, true) if (ret == SnackbarResult.ActionPerformed) { val panelIntent = Intent(android.provider.Settings.Panel.ACTION_INTERNET_CONNECTIVITY) startActivity(panelIntent) } } else { - snackbarState.showSnackbar(warning) + snackbar.showSnackbar(warning) } } } @@ -361,7 +360,7 @@ class MainActivity : EhActivity() { launch = { navigator.navigate(ProgressScreenDestination(result2.gid, result2.pToken, result2.page)) } } launch?.let { - val ret = snackbarState.showSnackbar(snackMessage, snackAction, true) + val ret = snackbar.showSnackbar(snackMessage, snackAction, true) if (ret == SnackbarResult.ActionPerformed) it() } } @@ -380,15 +379,14 @@ class MainActivity : EhActivity() { LocalNavDrawerState provides navDrawerState, LocalSideSheetState provides sideSheetState, LocalDrawerHandle provides drawerHandle, - LocalSnackBarHostState provides snackbarState, - LocalSnackbarContext provides remember { SnackbarContext(snackbarState) }, + LocalSnackbarContext provides snackbar, LocalSnackBarFabPadding provides animateDpAsState(snackbarFabPadding, label = "SnackbarFabPadding"), LocalWindowSizeClass provides adaptiveInfo.windowSizeClass, ) { Scaffold( snackbarHost = { SnackbarHost( - hostState = snackbarState, + hostState = snackbar.state, modifier = Modifier.onGloballyPositioned { with(density) { snackbarFabPadding = it.size.height.toDp() @@ -566,7 +564,6 @@ val LocalSnackbarContext = compositionLocalOf { error("Composit val LocalNavDrawerState = compositionLocalOf { error("CompositionLocal LocalNavDrawerState not present!") } val LocalSideSheetState = compositionLocalOf { error("CompositionLocal LocalSideSheetState not present!") } val LocalDrawerHandle = compositionLocalOf> { error("CompositionLocal LocalDrawerHandle not present!") } -val LocalSnackBarHostState = compositionLocalOf { error("CompositionLocal LocalSnackBarHostState not present!") } val LocalSnackBarFabPadding = compositionLocalOf> { error("CompositionLocal LocalSnackBarFabPadding not present!") } val LocalSharedTransitionScope = compositionLocalOf { error("CompositionLocal LocalSharedTransitionScope not present!") } 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 2441bac7dd..6158a60b5e 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 @@ -27,7 +27,6 @@ import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet -import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable @@ -72,6 +71,7 @@ import com.hippo.ehviewer.gallery.useArchivePageLoader import com.hippo.ehviewer.gallery.useEhPageLoader import com.hippo.ehviewer.ui.MainActivity import com.hippo.ehviewer.ui.Screen +import com.hippo.ehviewer.ui.screen.SnackbarContext import com.hippo.ehviewer.ui.theme.EhTheme import com.hippo.ehviewer.ui.tools.Await import com.hippo.ehviewer.ui.tools.DialogState @@ -173,7 +173,7 @@ fun AnimatedVisibilityScope.ReaderScreen(args: ReaderScreenArgs, navigator: Dest } } -context(MainActivity, SnackbarHostState, DialogState, CoroutineScope) +context(MainActivity, SnackbarContext, DialogState, CoroutineScope) @Composable fun AnimatedVisibilityScope.ReaderScreen(pageLoader: PageLoader, info: BaseGalleryInfo?) { ConfigureKeepScreenOn() diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SnackbarContext.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SnackbarContext.kt index 45a5d976e5..9d9cdbaf16 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SnackbarContext.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/SnackbarContext.kt @@ -2,13 +2,24 @@ package com.hippo.ehviewer.ui.screen import androidx.compose.foundation.MutatorMutex import androidx.compose.material3.SnackbarHostState +import androidx.compose.runtime.Stable import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch -class SnackbarContext(val state: SnackbarHostState) { +@Stable +class SnackbarContext { private val mutex = MutatorMutex() + val state = SnackbarHostState() fun CoroutineScope.launchSnackbar(msg: String) = launch { mutex.mutate { state.showSnackbar(msg) } } + + suspend fun showSnackbar( + message: String, + actionLabel: String? = null, + withDismissAction: Boolean = false, + ) = mutex.mutate { + state.showSnackbar(message, actionLabel, withDismissAction) + } }