From 3a391c835f77728d528addbea2ca859f043ec1ce Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 06:57:24 +0000 Subject: [PATCH] refactor: cut more shared-screen blockers (step 6 wave 5 seams) - PlatformBackHandler (commonsUI): AndroidX BackHandler on Android, Compose Multiplatform's ui-backhandler on desktop and iOS. The app's 21 BackHandler users switch to it. commonsUI adds androidx.activity:activity-compose (androidMain) and org.jetbrains.compose.ui:ui-backhandler (skikoMain); both Apache-2.0, both already shipped. - rememberLongNotice joins rememberShortNotice, so Toast.LENGTH_LONG messages keep their duration; four screens drop their Toasts for the shared notices. - The wallet screens use formatGrouped instead of NumberFormat and the shared Clipboard getText/setText extensions instead of ClipboardManager. - formatMonthDayTime (commonsUI) builds "May 28 14:32" from DateSkeletonFormatter("MMMd") and the system-clock time-of-day formatter; the two transaction lists use it. - UsageKeys uses quartz's ConcurrentMap. LocalCacheHost gains a UsageCounter (no-op by default, the Android host feeds its resource-usage ledger), so reconnectPoolAfterJoin no longer reaches for Amethyst.instance. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01S7FuNBSKiyVecARSoE4B9P --- .../amethyst/AmethystLocalCacheHost.kt | 3 ++ .../service/resourceusage/UsageKeys.kt | 8 ++--- .../mediaServers/BlossomImportScreen.kt | 9 ++--- .../amethyst/ui/call/CallScreen.kt | 4 +-- .../nip22Comments/GenericCommentPostScreen.kt | 4 +-- .../AccountSwitcherAndLeftDrawerLayout.kt | 4 +-- .../loggedIn/badges/award/AwardBadgeScreen.kt | 4 +-- .../old/OldBookmarkListScreen.kt | 12 ++----- .../screen/loggedIn/browser/WebAppScreen.kt | 6 ++-- .../screen/loggedIn/buzz/BuzzCanvasScreen.kt | 4 +-- .../screen/loggedIn/buzz/BuzzJoinReconnect.kt | 7 ++-- .../chats/privateDM/send/NewGroupDMScreen.kt | 4 +-- .../send/PrivateMessageEditFieldRow.kt | 4 +-- .../chats/publicChannels/send/EditFieldRow.kt | 4 +-- .../nip23LongForm/LongFormPostScreen.kt | 4 +-- .../nip99Classifieds/NewProductScreen.kt | 4 +-- .../screen/loggedIn/embed/EmbeddedTabLayer.kt | 6 ++-- .../loggedIn/favorites/NostrAppScreen.kt | 4 +-- .../loggedIn/gitRepo/code/GitCodeTab.kt | 6 ++-- .../loggedIn/gitRepo/code/GitCommitLog.kt | 6 ++-- .../loggedIn/home/ShortNotePostScreen.kt | 4 +-- .../screen/loggedIn/home/VoiceReplyScreen.kt | 4 +-- .../loggedIn/home/nip75Goals/NewGoalScreen.kt | 4 +-- .../room/activity/NestActivityContent.kt | 4 +-- .../publicMessages/NewPublicMessageScreen.kt | 4 +-- .../loggedIn/settings/AllSettingsScreen.kt | 11 +++--- .../screen/loggedIn/wallet/OnchainSection.kt | 4 +-- .../wallet/OnchainTransactionsScreen.kt | 15 ++++---- .../loggedIn/wallet/WalletDetailScreen.kt | 5 ++- .../loggedIn/wallet/WalletReceiveScreen.kt | 19 +++++----- .../ui/screen/loggedIn/wallet/WalletScreen.kt | 14 ++++---- .../loggedIn/wallet/WalletSendScreen.kt | 16 ++++----- .../wallet/WalletTransactionsScreen.kt | 15 ++++---- .../wallet/wizard/CashuWalletWizardScreen.kt | 4 +-- .../loggedIn/workouts/NewWorkoutScreen.kt | 4 +-- .../commons/model/cache/LocalCacheHost.kt | 4 +++ .../amethyst/commons/service/UsageCounter.kt | 34 ++++++++++++++++++ commonsUI/build.gradle.kts | 7 ++++ .../components/PlatformBackHandler.android.kt | 30 ++++++++++++++++ .../ui/components/ShortNotice.android.kt | 6 ++++ .../ui/components/PlatformBackHandler.kt | 33 +++++++++++++++++ .../commons/ui/components/ShortNotice.kt | 4 +++ .../amethyst/commons/ui/note/MonthDayTime.kt | 35 +++++++++++++++++++ .../commons/ui/components/ShortNotice.ios.kt | 3 ++ .../commons/ui/components/ShortNotice.jvm.kt | 3 ++ .../components/PlatformBackHandler.skiko.kt | 32 +++++++++++++++++ gradle/libs.versions.toml | 1 + 47 files changed, 301 insertions(+), 125 deletions(-) create mode 100644 commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/UsageCounter.kt create mode 100644 commonsUI/src/androidMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/PlatformBackHandler.android.kt create mode 100644 commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/PlatformBackHandler.kt create mode 100644 commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/MonthDayTime.kt create mode 100644 commonsUI/src/skikoMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/PlatformBackHandler.skiko.kt diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/AmethystLocalCacheHost.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/AmethystLocalCacheHost.kt index 4b97f1a2f8..7273c65a6b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/AmethystLocalCacheHost.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/AmethystLocalCacheHost.kt @@ -24,6 +24,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.FileSystemNip95BlobStore import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.commons.model.cache.LocalCacheHost import com.vitorpamplona.amethyst.commons.model.cache.Nip95BlobStore +import com.vitorpamplona.amethyst.commons.service.UsageCounter import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.relay.client.stats.RelayStats import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl @@ -54,6 +55,8 @@ class AmethystLocalCacheHost( override val relayStats: RelayStats get() = modules.relayStats + override val usageCounter: UsageCounter = UsageCounter { key, amount -> modules.resourceUsage.add(key, amount) } + override fun relaySelfPubKey(relay: NormalizedRelayUrl): HexKey? = modules.nip11Cache.getFromCache(relay).self override fun relayInfo(relay: NormalizedRelayUrl): Nip11RelayInformation = modules.nip11Cache.getFromCache(relay) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/resourceusage/UsageKeys.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/resourceusage/UsageKeys.kt index fd222cf993..179dc5e929 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/resourceusage/UsageKeys.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/resourceusage/UsageKeys.kt @@ -30,7 +30,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.MachineReadabl import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.MachineReadablePrefix.RESTRICTED import com.vitorpamplona.quartz.nip01Core.relay.commands.toClient.MachineReadablePrefix.UNSUPPORTED import com.vitorpamplona.quartz.nip66RelayMonitor.reachability.RelayObserver -import java.util.concurrent.ConcurrentHashMap +import com.vitorpamplona.quartz.utils.concurrent.ConcurrentMap /** * Counter-key grammar for the resource-usage ledger. Keys are flat strings so @@ -489,8 +489,8 @@ object UsageKeys { private const val UP = "up" private const val DOWN = "down" - private val VERB_UP_KEYS = ConcurrentHashMap>() - private val VERB_DOWN_KEYS = ConcurrentHashMap>() + private val VERB_UP_KEYS = ConcurrentMap>() + private val VERB_DOWN_KEYS = ConcurrentMap>() /** * `relay.purpose.home_feed.sent` / `.bytes` — REQ frames and REQ bytes by the @@ -562,7 +562,7 @@ object UsageKeys { private val PURPOSE_SUFFIXES = arrayOf("sent", "bytes", "down", "downn", "dupbytes") - private val PURPOSE_KEYS = ConcurrentHashMap>() + private val PURPOSE_KEYS = ConcurrentMap>() /** The five `relay.purpose..*` keys, indexed by the `P_` constants above. */ private fun purposeKeys(purpose: String): Array = PURPOSE_KEYS.getOrPut(purpose) { Array(PURPOSE_SUFFIXES.size) { "relay.purpose.$purpose.${PURPOSE_SUFFIXES[it]}" } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/BlossomImportScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/BlossomImportScreen.kt index f6ff467acc..9da5c9ee73 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/BlossomImportScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/actions/mediaServers/BlossomImportScreen.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.actions.mediaServers -import android.widget.Toast import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -53,7 +52,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp @@ -83,6 +81,7 @@ import com.vitorpamplona.amethyst.commons.resources.blossom_import_sources_secti import com.vitorpamplona.amethyst.commons.resources.blossom_import_start_button import com.vitorpamplona.amethyst.commons.resources.blossom_import_title import com.vitorpamplona.amethyst.commons.resources.delete_media_server +import com.vitorpamplona.amethyst.commons.ui.components.rememberLongNotice import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarWithBackButton import com.vitorpamplona.amethyst.commons.ui.pluralStringRes @@ -123,7 +122,7 @@ fun BlossomImportScreen( // kind-10063 list arrives from a relay just after the screen opens (common on cold start). val targetServers by accountViewModel.account.blossomServers.flow .collectAsStateWithLifecycle() - val context = LocalContext.current + val longNotice = rememberLongNotice() Scaffold( topBar = { @@ -254,9 +253,7 @@ fun BlossomImportScreen( when (vm.importSelected()) { is ImportStart.Started -> nav.popBack() ImportStart.Busy -> - Toast - .makeText(context, blossomImportBusyStr, Toast.LENGTH_LONG) - .show() + longNotice.show(blossomImportBusyStr) ImportStart.Empty -> {} } }, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/call/CallScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/call/CallScreen.kt index 93d00acb4d..f91d87f7b2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/call/CallScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/call/CallScreen.kt @@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.ui.call import android.app.Activity import android.media.projection.MediaProjectionManager -import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.background @@ -71,6 +70,7 @@ import com.vitorpamplona.amethyst.commons.resources.call_incoming import com.vitorpamplona.amethyst.commons.resources.call_incoming_video import com.vitorpamplona.amethyst.commons.resources.call_incoming_voice import com.vitorpamplona.amethyst.commons.resources.call_reject +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel import com.vitorpamplona.amethyst.ui.call.session.CallSession @@ -98,7 +98,7 @@ fun CallScreen( val emptyStringFlow = remember { kotlinx.coroutines.flow.MutableStateFlow(null) } val errorMessage by (callSession?.errorMessage ?: emptyStringFlow).collectAsState() - BackHandler(enabled = callState !is CallState.Idle && callState !is CallState.Ended) { + PlatformBackHandler(enabled = callState !is CallState.Idle && callState !is CallState.Ended) { scope.launch { callManager.hangup() } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/GenericCommentPostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/GenericCommentPostScreen.kt index 492cf11b65..6c064c59c1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/GenericCommentPostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/nip22Comments/GenericCommentPostScreen.kt @@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.ui.note.nip22Comments import android.content.Intent import android.net.Uri -import androidx.activity.compose.BackHandler import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Box @@ -79,6 +78,7 @@ import com.vitorpamplona.amethyst.commons.resources.post_anonymously import com.vitorpamplona.amethyst.commons.resources.what_s_on_your_mind import com.vitorpamplona.amethyst.commons.resources.zapraiser import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar import com.vitorpamplona.amethyst.commons.ui.note.BaseUserPicture @@ -227,7 +227,7 @@ fun GenericCommentPostScreen( StrippingFailureDialog(postViewModel.strippingFailureConfirmation) - BackHandler { + PlatformBackHandler { accountViewModel.launchSigner { postViewModel.sendDraftSync() onUiThread { postViewModel.cancel() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountSwitcherAndLeftDrawerLayout.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountSwitcherAndLeftDrawerLayout.kt index d601b4f9a2..ba3b6dfea6 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountSwitcherAndLeftDrawerLayout.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountSwitcherAndLeftDrawerLayout.kt @@ -21,7 +21,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn import android.content.res.Configuration -import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight @@ -48,6 +47,7 @@ import androidx.compose.ui.platform.LocalConfiguration import androidx.navigation.NavDestination.Companion.hasRoute import androidx.navigation.compose.currentBackStackEntryAsState import com.vitorpamplona.amethyst.commons.model.navigation.Route +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.layouts.LocalScreenLayout import com.vitorpamplona.amethyst.commons.ui.layouts.NavigationStyle import com.vitorpamplona.amethyst.commons.ui.theme.DividerThickness @@ -182,7 +182,7 @@ private fun ModalDrawerShell( gesturesEnabled = drawerGesturesEnabled, drawerContent = { DrawerContent(nav, openSheet, accountViewModel) - BackHandler(enabled = nav.drawerState.isOpen, nav::closeDrawer) + PlatformBackHandler(enabled = nav.drawerState.isOpen, nav::closeDrawer) }, content = { if (showRail) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/award/AwardBadgeScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/award/AwardBadgeScreen.kt index bcd80f4559..8c399774c1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/award/AwardBadgeScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/badges/award/AwardBadgeScreen.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.badges.award -import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -56,6 +55,7 @@ import com.vitorpamplona.amethyst.commons.resources.award_badge_remove_recipient import com.vitorpamplona.amethyst.commons.resources.award_badge_search_label import com.vitorpamplona.amethyst.commons.resources.award_badge_search_placeholder import com.vitorpamplona.amethyst.commons.ui.components.Nip05OrPubkeyLine +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.SavingTopBar @@ -93,7 +93,7 @@ fun AwardBadgeScreen( onDispose { userSuggestions.reset() } } - BackHandler { + PlatformBackHandler { nav.popBack() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/old/OldBookmarkListScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/old/OldBookmarkListScreen.kt index 00080bf8d5..28acf3594d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/old/OldBookmarkListScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/bookmarkgroups/old/OldBookmarkListScreen.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.bookmarkgroups.old -import android.widget.Toast import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -44,7 +43,6 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.commons.icons.symbols.Icon @@ -58,6 +56,7 @@ import com.vitorpamplona.amethyst.commons.resources.old_bookmarks_title import com.vitorpamplona.amethyst.commons.resources.private_bookmarks import com.vitorpamplona.amethyst.commons.resources.public_bookmarks import com.vitorpamplona.amethyst.commons.ui.components.DeletedItemsBanner +import com.vitorpamplona.amethyst.commons.ui.components.rememberShortNotice import com.vitorpamplona.amethyst.commons.ui.layouts.DisappearingScaffold import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.commons.ui.navigation.bottombars.FabBottomBarPadded @@ -114,7 +113,7 @@ private fun RenderOldBookmarkScreen( ) { val pagerState = rememberPagerState { 2 } val coroutineScope = rememberCoroutineScope() - val context = LocalContext.current + val shortNotice = rememberShortNotice() val cache = accountViewModel.account.cache val deletedEventIds = remember(bookmarkState) { mutableSetOf() } @@ -177,12 +176,7 @@ private fun RenderOldBookmarkScreen( accountViewModel.launchSigner { accountViewModel.account.migrateOldBookmarksToNew() coroutineScope.launch { - Toast - .makeText( - context, - loadStringRes(Res.string.migrate_bookmarks_success), - Toast.LENGTH_SHORT, - ).show() + shortNotice.show(loadStringRes(Res.string.migrate_bookmarks_success)) } } }, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/browser/WebAppScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/browser/WebAppScreen.kt index 7a50ebb6f3..d2947e9dfc 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/browser/WebAppScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/browser/WebAppScreen.kt @@ -24,7 +24,6 @@ import android.Manifest import android.content.pm.PackageManager import android.os.Build import android.widget.Toast -import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.RequiresApi @@ -74,6 +73,7 @@ import com.vitorpamplona.amethyst.commons.model.navigation.Route import com.vitorpamplona.amethyst.commons.model.navigation.favoriteIds import com.vitorpamplona.amethyst.commons.resources.Res import com.vitorpamplona.amethyst.commons.resources.browser_unsupported +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.platform.AppBottomBar import com.vitorpamplona.amethyst.commons.ui.stringRes @@ -299,9 +299,9 @@ private fun EmbeddedWebAppTab( } val isFullscreen by controller.isFullscreen - BackHandler(enabled = canGoBack && !isFullscreen) { controller.back() } + PlatformBackHandler(enabled = canGoBack && !isFullscreen) { controller.back() } // A fullscreen video inside the tab: back leaves fullscreen first, as in Chrome. - BackHandler(enabled = isFullscreen) { controller.exitFullscreen() } + PlatformBackHandler(enabled = isFullscreen) { controller.exitFullscreen() } EmbeddedPageUi(controller, chrome.ui, showPageInfo, onPageInfoDismiss = { showPageInfo = false }) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/BuzzCanvasScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/BuzzCanvasScreen.kt index ccf9e302cc..88b62bc0ac 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/BuzzCanvasScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/BuzzCanvasScreen.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.buzz -import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -63,6 +62,7 @@ import com.vitorpamplona.amethyst.commons.resources.buzz_canvas_empty import com.vitorpamplona.amethyst.commons.resources.buzz_canvas_save import com.vitorpamplona.amethyst.commons.resources.buzz_canvas_title import com.vitorpamplona.amethyst.commons.resources.cancel +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.components.TranslatableRichTextViewer import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.TopBarExtensibleWithBackButton @@ -222,7 +222,7 @@ private fun CanvasEditor( val scope = rememberCoroutineScope() // Back cancels the edit and returns to the rendered canvas rather than leaving the screen. - BackHandler(enabled = !saving, onBack = onClose) + PlatformBackHandler(enabled = !saving, onBack = onClose) Scaffold( topBar = { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/BuzzJoinReconnect.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/BuzzJoinReconnect.kt index 9d419d535b..feadf3604a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/BuzzJoinReconnect.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/buzz/BuzzJoinReconnect.kt @@ -20,7 +20,7 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.buzz -import com.vitorpamplona.amethyst.Amethyst +import com.vitorpamplona.amethyst.commons.model.cache.LocalCache import com.vitorpamplona.amethyst.service.resourceusage.UsageKeys import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient @@ -45,9 +45,6 @@ import com.vitorpamplona.quartz.nip01Core.relay.client.INostrClient * subscription — so `relay.trigger.buzz` undercounts joins, not re-challenges. */ internal fun reconnectPoolAfterJoin(client: INostrClient) { - // Guarded like every other ledger write that reaches the application singleton - // (MediaPlayTimeTracker, the workers): a diagnostics counter must never break a - // user-visible join, and `Amethyst.instance` is lateinit. - runCatching { Amethyst.instance.resourceUsage.add(UsageKeys.relayTrigger(UsageKeys.TRIGGER_BUZZ), 1) } + LocalCache.appHost.usageCounter.add(UsageKeys.relayTrigger(UsageKeys.TRIGGER_BUZZ), 1) client.reconnect(onlyIfChanged = false, ignoreRetryDelays = true) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/NewGroupDMScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/NewGroupDMScreen.kt index f5a15641f7..15c46d0264 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/NewGroupDMScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/NewGroupDMScreen.kt @@ -21,7 +21,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send import android.net.Uri -import androidx.activity.compose.BackHandler import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy import androidx.compose.foundation.layout.Box @@ -88,6 +87,7 @@ import com.vitorpamplona.amethyst.commons.richtext.EncryptedMediaUrlVideo import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo import com.vitorpamplona.amethyst.commons.richtext.RichTextParser +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeToMessage @@ -181,7 +181,7 @@ fun NewGroupDMScreen( WatchAndLoadMyEmojiList(accountViewModel) - BackHandler { + PlatformBackHandler { accountViewModel.launchSigner { postViewModel.sendDraftSync() onUiThread { postViewModel.cancel() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt index ac480c057f..1df6ad0988 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/privateDM/send/PrivateMessageEditFieldRow.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.privateDM.send -import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -63,6 +62,7 @@ import com.vitorpamplona.amethyst.commons.resources.retry_without_encryption import com.vitorpamplona.amethyst.commons.resources.this_message_will_disappear_in import com.vitorpamplona.amethyst.commons.resources.upload_without_encryption_warning import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.routes.routeFor @@ -118,7 +118,7 @@ fun PrivateMessageEditFieldRow( onSendNewMessage: () -> Unit, nav: INav, ) { - BackHandler { + PlatformBackHandler { if (channelScreenModel.message.text.isNotBlank()) { accountViewModel.launchSigner { channelScreenModel.sendDraftSync() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/EditFieldRow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/EditFieldRow.kt index 6cb3e598d1..b4f2124ed2 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/EditFieldRow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/chats/publicChannels/send/EditFieldRow.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.publicChannels.send -import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -54,6 +53,7 @@ import com.vitorpamplona.amethyst.commons.resources.buzz_editing_banner import com.vitorpamplona.amethyst.commons.resources.cancel import com.vitorpamplona.amethyst.commons.resources.reply_here import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.note.creators.userSuggestions.ShowUserSuggestionList import com.vitorpamplona.amethyst.commons.ui.stringRes @@ -82,7 +82,7 @@ fun EditFieldRow( onSendNewMessage: suspend () -> Unit, nav: INav, ) { - BackHandler { + PlatformBackHandler { accountViewModel.launchSigner { channelScreenModel.sendDraftSync() onUiThread { channelScreenModel.cancel() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostScreen.kt index c0932f7e1b..129fc19262 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip23LongForm/LongFormPostScreen.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip23LongForm -import androidx.activity.compose.BackHandler import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.border import androidx.compose.foundation.clickable @@ -103,6 +102,7 @@ import com.vitorpamplona.amethyst.commons.resources.your_article_title import com.vitorpamplona.amethyst.commons.resources.zapraiser import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog import com.vitorpamplona.amethyst.commons.ui.components.MyAsyncImage +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar import com.vitorpamplona.amethyst.commons.ui.note.creators.contentWarning.ContentSensitivityExplainer @@ -169,7 +169,7 @@ fun LongFormPostScreen( StrippingFailureDialog(postViewModel.strippingFailureConfirmation) - BackHandler { + PlatformBackHandler { accountViewModel.launchSigner { postViewModel.sendDraftSync() onUiThread { postViewModel.cancel() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductScreen.kt index 0d283ddfb9..4536d7d26b 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/discover/nip99Classifieds/NewProductScreen.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip99Classifieds -import androidx.activity.compose.BackHandler import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -53,6 +52,7 @@ import com.vitorpamplona.amethyst.commons.resources.lightning_invoice import com.vitorpamplona.amethyst.commons.resources.new_product import com.vitorpamplona.amethyst.commons.resources.zapraiser import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar @@ -148,7 +148,7 @@ fun NewProductScreen( StrippingFailureDialog(postViewModel.strippingFailureConfirmation) - BackHandler { + PlatformBackHandler { accountViewModel.launchSigner { postViewModel.sendDraftSync() onUiThread { postViewModel.cancel() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/embed/EmbeddedTabLayer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/embed/EmbeddedTabLayer.kt index ba49a8480c..e63004a330 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/embed/EmbeddedTabLayer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/embed/EmbeddedTabLayer.kt @@ -29,7 +29,6 @@ import android.graphics.BitmapFactory import android.os.Build import android.os.SystemClock import android.view.ViewGroup -import androidx.activity.compose.BackHandler import androidx.annotation.RequiresApi import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Canvas @@ -90,6 +89,7 @@ import com.vitorpamplona.amethyst.commons.browser.ui.pill.BrowserPillEvent import com.vitorpamplona.amethyst.commons.browser.ui.pill.ConsoleLine import com.vitorpamplona.amethyst.commons.browser.ui.pill.ConsoleSheet import com.vitorpamplona.amethyst.commons.browser.ui.pill.FindInPagePill +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.napplethost.BrowserWebTools import kotlinx.coroutines.delay import kotlinx.serialization.json.buildJsonObject @@ -306,7 +306,7 @@ fun EmbeddedTabLayer(barFavoriteIds: List) { } if (pillExpanded) { - BackHandler { pillExpanded = false } + PlatformBackHandler { pillExpanded = false } Box( Modifier .fillMaxSize() @@ -361,7 +361,7 @@ fun EmbeddedTabLayer(barFavoriteIds: List) { // Find in page: opened from the pill's Find tile. if (findShowing && findBridge != null) { - BackHandler { closeFind() } + PlatformBackHandler { closeFind() } val result by findBridge.findResult FindInPagePill( query = findQuery, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/favorites/NostrAppScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/favorites/NostrAppScreen.kt index 5dccbbf0dc..41306f42d5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/favorites/NostrAppScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/favorites/NostrAppScreen.kt @@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.favorites import android.os.Build import android.widget.Toast -import androidx.activity.compose.BackHandler import androidx.annotation.RequiresApi import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize @@ -73,6 +72,7 @@ import com.vitorpamplona.amethyst.commons.resources.favorite_apps import com.vitorpamplona.amethyst.commons.resources.favorite_notice_paid import com.vitorpamplona.amethyst.commons.resources.favorite_notice_published import com.vitorpamplona.amethyst.commons.resources.favorite_notice_uploaded +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.platform.AppBottomBar @@ -262,7 +262,7 @@ private fun EmbeddedNostrAppTab( onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } } - BackHandler(enabled = canGoBack) { controller.back() } + PlatformBackHandler(enabled = canGoBack) { controller.back() } if (showAccess) { Dialog(onDismissRequest = { showAccess = false }, properties = DialogProperties(usePlatformDefaultWidth = false)) { diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepo/code/GitCodeTab.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepo/code/GitCodeTab.kt index 7926d67b00..04a9464841 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepo/code/GitCodeTab.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepo/code/GitCodeTab.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepo.code -import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll @@ -73,6 +72,7 @@ import com.vitorpamplona.amethyst.commons.resources.git_repo_no_clone_url import com.vitorpamplona.amethyst.commons.resources.git_repo_no_search_results import com.vitorpamplona.amethyst.commons.resources.git_repo_root import com.vitorpamplona.amethyst.commons.resources.git_repo_search_files +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.layouts.LocalDisappearingBarState import com.vitorpamplona.amethyst.commons.ui.layouts.LocalDisappearingScaffoldPadding import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav @@ -141,7 +141,7 @@ private fun CodeBrowser( if (openPath != null) { val entry = remember(openFilePath) { snapshot.entryAt(openPath) } - BackHandler { openFilePath = null } + PlatformBackHandler { openFilePath = null } Column(Modifier.fillMaxSize().padding(scaffoldPaddingTop)) { FileHeader(name = openPath.lastOrNull() ?: "", onBack = { openFilePath = null }) HorizontalDivider(thickness = 0.5.dp) @@ -164,7 +164,7 @@ private fun CodeBrowser( var query by rememberSaveable(snapshot.headCommit) { mutableStateOf("") } val searching = query.isNotBlank() - BackHandler(enabled = searching || path.isNotEmpty()) { + PlatformBackHandler(enabled = searching || path.isNotEmpty()) { if (searching) query = "" else pathString = path.dropLast(1).joinToString("/") } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepo/code/GitCommitLog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepo/code/GitCommitLog.kt index 22b7e59ed2..295232a901 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepo/code/GitCommitLog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/gitRepo/code/GitCommitLog.kt @@ -21,7 +21,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.gitRepo.code import android.text.format.DateUtils -import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -66,6 +65,7 @@ import com.vitorpamplona.amethyst.commons.resources.git_repo_code_loading import com.vitorpamplona.amethyst.commons.resources.git_repo_commits import com.vitorpamplona.amethyst.commons.resources.git_repo_file_load_error import com.vitorpamplona.amethyst.commons.resources.git_repo_no_commits +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.note.ArrowBackIcon import com.vitorpamplona.amethyst.commons.ui.note.GitDiffView import com.vitorpamplona.amethyst.commons.ui.stringRes @@ -103,7 +103,7 @@ fun GitCommitLog( if (openCommit != null) { val commit = remember(openCommit, commits) { commits?.firstOrNull { it.oid == openCommit } } - BackHandler { openCommit = null } + PlatformBackHandler { openCommit = null } Column(Modifier.fillMaxSize()) { LogHeader(title = commit?.shortOid ?: "", onBack = { openCommit = null }) HorizontalDivider(thickness = 0.5.dp) @@ -116,7 +116,7 @@ fun GitCommitLog( return } - BackHandler { onBack() } + PlatformBackHandler { onBack() } Column(Modifier.fillMaxSize()) { LogHeader(title = stringRes(Res.string.git_repo_commits), onBack = onBack) HorizontalDivider(thickness = 0.5.dp) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt index 0bf885e812..f1469af6f3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/ShortNotePostScreen.kt @@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.home import android.content.Intent import android.net.Uri -import androidx.activity.compose.BackHandler import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.tween import androidx.compose.foundation.clickable @@ -110,6 +109,7 @@ import com.vitorpamplona.amethyst.commons.resources.schedule_post_always_on_prom import com.vitorpamplona.amethyst.commons.resources.what_s_on_your_mind import com.vitorpamplona.amethyst.commons.resources.zapraiser import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar import com.vitorpamplona.amethyst.commons.ui.note.BaseUserPicture @@ -275,7 +275,7 @@ internal fun NewPostScreenInner( StrippingFailureDialog(postViewModel.strippingFailureConfirmation) - BackHandler { + PlatformBackHandler { accountViewModel.launchSigner { postViewModel.sendDraftSync() onUiThread { postViewModel.cancel() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/VoiceReplyScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/VoiceReplyScreen.kt index 6eb9f05f9d..f9b5232f26 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/VoiceReplyScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/VoiceReplyScreen.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.home -import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets @@ -40,6 +39,7 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar import com.vitorpamplona.amethyst.commons.ui.note.NoteCompose import com.vitorpamplona.amethyst.commons.ui.note.types.ReplyRenderType @@ -71,7 +71,7 @@ fun VoiceReplyScreen( viewModel.load(replyToNoteId, recordingFilePath, mimeType, duration, amplitudesJson) } - BackHandler { + PlatformBackHandler { viewModel.cancel() nav.popBack() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/nip75Goals/NewGoalScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/nip75Goals/NewGoalScreen.kt index a8fc08fab7..fe49ecb469 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/nip75Goals/NewGoalScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/home/nip75Goals/NewGoalScreen.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.home.nip75Goals -import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -59,6 +58,7 @@ import com.vitorpamplona.amethyst.commons.resources.goal_summary_placeholder import com.vitorpamplona.amethyst.commons.resources.goal_website_label import com.vitorpamplona.amethyst.commons.resources.goal_website_placeholder import com.vitorpamplona.amethyst.commons.resources.new_goal +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar @@ -93,7 +93,7 @@ fun NewGoalScreen( accountViewModel: AccountViewModel, nav: INav, ) { - BackHandler { + PlatformBackHandler { goalViewModel.cancel() nav.popBack() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/activity/NestActivityContent.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/activity/NestActivityContent.kt index 959c747bce..b0e5bed777 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/activity/NestActivityContent.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/nests/room/activity/NestActivityContent.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.nests.room.activity -import androidx.activity.compose.BackHandler import androidx.activity.compose.LocalActivity import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -52,6 +51,7 @@ import com.vitorpamplona.amethyst.commons.resources.nest_loading_room import com.vitorpamplona.amethyst.commons.resources.nest_unjoinable_back import com.vitorpamplona.amethyst.commons.resources.nest_unjoinable_body import com.vitorpamplona.amethyst.commons.resources.nest_unjoinable_title +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.note.LoadAddressableNote import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel @@ -362,7 +362,7 @@ private fun NestActivityBody( // Failed) or PIP isn't supported, we let back fall through to the // default Activity.finish() so the user can cancel out cleanly. val canMinimize = isPipSupported && !isInPipMode && ui.connection is ConnectionUiState.Connected - BackHandler(enabled = canMinimize) { onMinimize() } + PlatformBackHandler(enabled = canMinimize) { onMinimize() } if (isInPipMode) { NestPipScreen( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageScreen.kt index 724e63e4c6..c8fbb8787c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/publicMessages/NewPublicMessageScreen.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.publicMessages -import androidx.activity.compose.BackHandler import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy import androidx.compose.foundation.layout.Column @@ -61,6 +60,7 @@ import com.vitorpamplona.amethyst.commons.resources.messages_new_message_to_capt import com.vitorpamplona.amethyst.commons.resources.public_message import com.vitorpamplona.amethyst.commons.resources.zapraiser import com.vitorpamplona.amethyst.commons.service.upload.ui.StrippingFailureDialog +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar @@ -142,7 +142,7 @@ fun NewPublicMessageScreen( StrippingFailureDialog(postViewModel.strippingFailureConfirmation) - BackHandler { + PlatformBackHandler { accountViewModel.launchSigner { postViewModel.sendDraftSync() onUiThread { postViewModel.cancel() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/AllSettingsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/AllSettingsScreen.kt index 8de85d3e54..e969cc41ea 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/AllSettingsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/AllSettingsScreen.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings -import android.widget.Toast import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -46,7 +45,6 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -62,6 +60,8 @@ import com.vitorpamplona.amethyst.commons.resources.reset_marmot_confirm_title import com.vitorpamplona.amethyst.commons.resources.reset_marmot_failure import com.vitorpamplona.amethyst.commons.resources.reset_marmot_success import com.vitorpamplona.amethyst.commons.resources.settings_search_no_results +import com.vitorpamplona.amethyst.commons.ui.components.rememberLongNotice +import com.vitorpamplona.amethyst.commons.ui.components.rememberShortNotice import com.vitorpamplona.amethyst.commons.ui.loadStringRes import com.vitorpamplona.amethyst.commons.ui.navigation.navs.EmptyNav import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav @@ -98,7 +98,8 @@ fun AllSettingsScreen( accountViewModel: AccountViewModel, nav: INav, ) { - val context = LocalContext.current + val shortNotice = rememberShortNotice() + val longNotice = rememberLongNotice() val uriHandler = LocalUriHandler.current val scope = rememberCoroutineScope() var showResetMarmotDialog by remember { mutableStateOf(false) } @@ -186,13 +187,13 @@ fun AllSettingsScreen( try { accountViewModel.resetMarmotState() launch(Dispatchers.Main) { - Toast.makeText(context, successMessage, Toast.LENGTH_SHORT).show() + shortNotice.show(successMessage) } } catch (e: Exception) { val failureMessage = loadStringRes(Res.string.reset_marmot_failure, e.message ?: "") launch(Dispatchers.Main) { - Toast.makeText(context, failureMessage, Toast.LENGTH_LONG).show() + longNotice.show(failureMessage) } } finally { isResettingMarmot = false diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt index e331aa82c6..da4dd5f1d8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt @@ -80,9 +80,9 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.sats import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.bitcoinColor +import com.vitorpamplona.amethyst.commons.util.formatGrouped import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel import kotlinx.coroutines.launch -import java.text.NumberFormat /** * "Bitcoin" card on the wallet screen, shown above the lightning NWC wallet @@ -277,7 +277,7 @@ private fun BalanceBlock( OnchainBalanceStatus.READY -> { val formatted = remember(sats) { - NumberFormat.getIntegerInstance().format(sats ?: 0L) + formatGrouped(sats ?: 0L) } Text( text = formatted, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainTransactionsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainTransactionsScreen.kt index f926d7fdc5..fb35e33663 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainTransactionsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainTransactionsScreen.kt @@ -51,7 +51,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.UriHandler import androidx.compose.ui.text.font.FontFamily @@ -81,13 +80,14 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.note.LoadUser import com.vitorpamplona.amethyst.commons.ui.note.UserPicture import com.vitorpamplona.amethyst.commons.ui.note.UsernameDisplay +import com.vitorpamplona.amethyst.commons.ui.note.formatMonthDayTime +import com.vitorpamplona.amethyst.commons.ui.note.rememberTimeOfDayFormatter import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.TransactionFilter import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.ui.theme.bitcoinColor +import com.vitorpamplona.amethyst.commons.util.formatGrouped import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.note.formatMonthDayTime import com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet.datasource.OnchainZapsFilterAssemblerSubscription -import java.text.NumberFormat import kotlin.math.absoluteValue @OptIn(ExperimentalMaterial3Api::class) @@ -309,15 +309,14 @@ private fun OnchainTransactionItem( val amountSats = view.tx.netValueSats.absoluteValue val formattedAmount = remember(view.tx.netValueSats) { - val fmt = NumberFormat.getIntegerInstance() - (if (isIncoming) "+" else "-") + fmt.format(amountSats) + (if (isIncoming) "+" else "-") + formatGrouped(amountSats) } - val context = LocalContext.current + val timeOfDay = rememberTimeOfDayFormatter() val dateText = - remember(view.tx.blockTime, view.tx.confirmations, context) { + remember(view.tx.blockTime, view.tx.confirmations, timeOfDay) { val ts = view.tx.blockTime - if (ts != null) formatMonthDayTime(ts, context) else "" + if (ts != null) formatMonthDayTime(ts, timeOfDay) else "" } val counterpartyPubkeyHex = view.counterpartyPubkeyHex() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletDetailScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletDetailScreen.kt index 7ca59ebe05..ad82a41d08 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletDetailScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletDetailScreen.kt @@ -65,8 +65,8 @@ import com.vitorpamplona.amethyst.commons.resources.wallet_transactions import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.WalletViewModel import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.util.formatGrouped import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import java.text.NumberFormat @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -122,8 +122,7 @@ fun WalletDetailScreen( } else { val formattedBalance = remember(balance) { - val fmt = NumberFormat.getIntegerInstance() - fmt.format(balance ?: 0L) + formatGrouped(balance ?: 0L) } Text( text = formattedBalance, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletReceiveScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletReceiveScreen.kt index cfee01025f..6289767a30 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletReceiveScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletReceiveScreen.kt @@ -20,9 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -52,10 +49,11 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalClipboard import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign @@ -73,13 +71,15 @@ import com.vitorpamplona.amethyst.commons.resources.wallet_creating_invoice import com.vitorpamplona.amethyst.commons.resources.wallet_description import com.vitorpamplona.amethyst.commons.resources.wallet_receive import com.vitorpamplona.amethyst.commons.resources.wallet_sats +import com.vitorpamplona.amethyst.commons.ui.components.util.setText import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.qrcode.QrCodeDrawer import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.ReceiveState import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.WalletViewModel import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.util.formatGrouped import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import java.text.NumberFormat +import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -102,7 +102,8 @@ fun WalletReceiveScreen( val receiveState by walletViewModel.receiveState.collectAsState() var amountText by remember { mutableStateOf("") } var descriptionText by remember { mutableStateOf("") } - val context = LocalContext.current + val clipboard = LocalClipboard.current + val clipboardScope = rememberCoroutineScope() Scaffold( topBar = { @@ -194,8 +195,7 @@ fun WalletReceiveScreen( val formattedAmount = remember(state.amount) { - val fmt = NumberFormat.getIntegerInstance() - fmt.format(state.amount) + formatGrouped(state.amount) } Text( @@ -233,8 +233,7 @@ fun WalletReceiveScreen( ) { OutlinedButton( onClick = { - val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - clipboard.setPrimaryClip(ClipData.newPlainText("invoice", state.invoice)) + clipboardScope.launch { clipboard.setText(state.invoice) } }, modifier = Modifier diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletScreen.kt index 6fe151f908..603addad9d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletScreen.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet -import android.widget.Toast import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -67,7 +66,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -103,6 +101,7 @@ import com.vitorpamplona.amethyst.commons.resources.wallet_rename_title import com.vitorpamplona.amethyst.commons.resources.wallet_sats import com.vitorpamplona.amethyst.commons.resources.wallet_save import com.vitorpamplona.amethyst.commons.resources.wallet_set_default +import com.vitorpamplona.amethyst.commons.ui.components.rememberLongNotice import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.platform.AppBottomBar import com.vitorpamplona.amethyst.commons.ui.pluralStringRes @@ -110,10 +109,10 @@ import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.CashuWalletV import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.WalletInfo import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.WalletViewModel import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.util.formatGrouped import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel import com.vitorpamplona.quartz.experimental.clink.debits.DebitFrequency import kotlinx.coroutines.launch -import java.text.NumberFormat import androidx.compose.material3.Icon as Material3Icon @OptIn(ExperimentalMaterial3Api::class) @@ -248,7 +247,7 @@ private fun MultiWalletHomeContent( cashuMintCount: Int, ) { val walletInfoList by walletViewModel.walletInfoList.collectAsState() - val context = LocalContext.current + val longNotice = rememberLongNotice() val budgetApprovedMsg = stringRes(Res.string.clink_budget_approved) val debitNoResponseMsg = stringRes(Res.string.clink_debit_no_response) @@ -298,7 +297,7 @@ private fun MultiWalletHomeContent( !error.isNullOrBlank() -> error else -> debitNoResponseMsg } - Toast.makeText(context, msg, Toast.LENGTH_LONG).show() + longNotice.show(msg) } } } else { @@ -460,8 +459,7 @@ private fun WalletCard( Column(horizontalAlignment = Alignment.End) { val formattedBalance = remember(walletInfo.balanceSats) { - val fmt = NumberFormat.getIntegerInstance() - fmt.format(walletInfo.balanceSats ?: 0L) + formatGrouped(walletInfo.balanceSats ?: 0L) } Text( text = formattedBalance, @@ -574,7 +572,7 @@ private fun CashuWalletRow( onClick: () -> Unit, ) { val formattedBalance = - remember(balanceSats) { NumberFormat.getIntegerInstance().format(balanceSats) } + remember(balanceSats) { formatGrouped(balanceSats) } Card( modifier = Modifier diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletSendScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletSendScreen.kt index 0c35d86bdc..735b458de5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletSendScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletSendScreen.kt @@ -20,8 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.wallet -import android.content.ClipboardManager -import android.content.Context import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -47,10 +45,11 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalClipboard import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel @@ -63,11 +62,13 @@ import com.vitorpamplona.amethyst.commons.resources.wallet_pay import com.vitorpamplona.amethyst.commons.resources.wallet_payment_sending import com.vitorpamplona.amethyst.commons.resources.wallet_payment_success import com.vitorpamplona.amethyst.commons.resources.wallet_send +import com.vitorpamplona.amethyst.commons.ui.components.util.getText import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.SendState import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.WalletViewModel import com.vitorpamplona.amethyst.commons.ui.stringRes import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel +import kotlinx.coroutines.launch @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -89,7 +90,8 @@ fun WalletSendScreen( val sendState by walletViewModel.sendState.collectAsState() var invoiceText by remember { mutableStateOf("") } - val context = LocalContext.current + val clipboard = LocalClipboard.current + val clipboardScope = rememberCoroutineScope() Scaffold( topBar = { @@ -128,11 +130,7 @@ fun WalletSendScreen( maxLines = 5, trailingIcon = { IconButton(onClick = { - val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - val clip = clipboard.primaryClip - if (clip != null && clip.itemCount > 0) { - invoiceText = clip.getItemAt(0).text?.toString() ?: "" - } + clipboardScope.launch { invoiceText = clipboard.getText() ?: "" } }) { Icon( symbol = MaterialSymbols.ContentPaste, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletTransactionsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletTransactionsScreen.kt index 4cd03b8a5b..95fb71e0c4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletTransactionsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletTransactionsScreen.kt @@ -51,7 +51,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -78,14 +77,15 @@ import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.note.LoadUser import com.vitorpamplona.amethyst.commons.ui.note.UserPicture import com.vitorpamplona.amethyst.commons.ui.note.UsernameDisplay +import com.vitorpamplona.amethyst.commons.ui.note.formatMonthDayTime +import com.vitorpamplona.amethyst.commons.ui.note.rememberTimeOfDayFormatter import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.TransactionFilter import com.vitorpamplona.amethyst.commons.ui.screen.loggedIn.wallet.WalletViewModel import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.util.formatGrouped import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import com.vitorpamplona.amethyst.ui.note.formatMonthDayTime import com.vitorpamplona.quartz.nip47WalletConnect.rpc.NwcTransaction import com.vitorpamplona.quartz.nip47WalletConnect.rpc.NwcTransactionType -import java.text.NumberFormat @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -278,14 +278,13 @@ private fun TransactionItem( val amountSats = (tx.amount ?: 0L) / 1000L val formattedAmount = remember(amountSats) { - val fmt = NumberFormat.getIntegerInstance() - (if (isIncoming) "+" else "-") + fmt.format(amountSats) + (if (isIncoming) "+" else "-") + formatGrouped(amountSats) } - val context = LocalContext.current + val timeOfDay = rememberTimeOfDayFormatter() val dateText = - remember(tx.created_at, context) { - tx.created_at?.let { formatMonthDayTime(it, context) } ?: "" + remember(tx.created_at, timeOfDay) { + tx.created_at?.let { formatMonthDayTime(it, timeOfDay) } ?: "" } val directionLabel = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/wizard/CashuWalletWizardScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/wizard/CashuWalletWizardScreen.kt index 8f3bc76fa8..bbf929d1b3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/wizard/CashuWalletWizardScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/wizard/CashuWalletWizardScreen.kt @@ -101,8 +101,8 @@ import com.vitorpamplona.amethyst.commons.resources.cashu_wizard_title import com.vitorpamplona.amethyst.commons.resources.cashu_wizard_use_wallet import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.stringRes +import com.vitorpamplona.amethyst.commons.util.formatGrouped import com.vitorpamplona.amethyst.commons.viewmodels.AccountViewModel -import java.text.NumberFormat /** * Find-or-create wizard for the Cashu wallet. Reached from the wallet screen's @@ -227,7 +227,7 @@ fun CashuWalletWizardScreen( } } -private fun formatSats(sats: Long): String = NumberFormat.getInstance().format(sats) +private fun formatSats(sats: Long): String = formatGrouped(sats) /** * Animate a sats figure counting up from zero the first time it's shown, then diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/NewWorkoutScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/NewWorkoutScreen.kt index 8a168763c0..211417c48e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/NewWorkoutScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/NewWorkoutScreen.kt @@ -20,7 +20,6 @@ */ package com.vitorpamplona.amethyst.ui.screen.loggedIn.workouts -import androidx.activity.compose.BackHandler import androidx.compose.animation.Crossfade import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -70,6 +69,7 @@ import com.vitorpamplona.amethyst.commons.resources.workout_minutes import com.vitorpamplona.amethyst.commons.resources.workout_notes import com.vitorpamplona.amethyst.commons.resources.workout_seconds import com.vitorpamplona.amethyst.commons.resources.workout_title +import com.vitorpamplona.amethyst.commons.ui.components.PlatformBackHandler import com.vitorpamplona.amethyst.commons.ui.insets.imePaddingSafe import com.vitorpamplona.amethyst.commons.ui.navigation.navs.INav import com.vitorpamplona.amethyst.commons.ui.navigation.topbars.PostingTopBar @@ -91,7 +91,7 @@ fun NewWorkoutScreen( postViewModel.init(accountViewModel) postViewModel.prefill(prefill) - BackHandler { + PlatformBackHandler { postViewModel.cancel() nav.popBack() } diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/cache/LocalCacheHost.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/cache/LocalCacheHost.kt index be7628aff7..f0885e8207 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/cache/LocalCacheHost.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/model/cache/LocalCacheHost.kt @@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.commons.model.cache import com.vitorpamplona.amethyst.commons.relays.nip11RelayInfo.Nip11CachedRetriever import com.vitorpamplona.amethyst.commons.relays.nip11RelayInfo.Nip11Fetcher +import com.vitorpamplona.amethyst.commons.service.UsageCounter import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.relay.client.stats.RelayStats import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl @@ -81,6 +82,9 @@ interface LocalCacheHost { */ fun relaySelfPubKey(relay: NormalizedRelayUrl): HexKey? = null + /** The resource-usage ledger's counters; counts nothing by default. */ + val usageCounter: UsageCounter get() = UsageCounter.None + /** * The relay's NIP-11 document as far as the shell has it in memory. Never fetches: `null` * (or a document with nothing in it) means "not loaded yet", which callers must treat as diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/UsageCounter.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/UsageCounter.kt new file mode 100644 index 0000000000..5e6118e8d1 --- /dev/null +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/service/UsageCounter.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.commons.service + +/** Adds to a diagnostics counter in the app's resource-usage ledger. Never throws. */ +fun interface UsageCounter { + fun add( + key: String, + amount: Long, + ) + + companion object { + /** Counts nothing: hosts without a ledger. */ + val None = UsageCounter { _, _ -> } + } +} diff --git a/commonsUI/build.gradle.kts b/commonsUI/build.gradle.kts index 1ff1db16a9..53b1c20463 100644 --- a/commonsUI/build.gradle.kts +++ b/commonsUI/build.gradle.kts @@ -212,6 +212,8 @@ kotlin { implementation(libs.androidx.ui.tooling.preview) // WindowInsetsControllerCompat, for DisappearingScaffold's immersive status bar. implementation(libs.androidx.core.ktx) + // BackHandler, behind the shared PlatformBackHandler. Apache-2.0, already in the app. + implementation(libs.androidx.activity.compose) } } @@ -229,6 +231,11 @@ kotlin { val skikoMain = create("skikoMain") { dependsOn(commonMain.get()) + dependencies { + // BackHandler for desktop (Esc) and iOS (swipe), behind PlatformBackHandler. + // JetBrains Compose, Apache-2.0; Android uses AndroidX's instead. + implementation(libs.jetbrains.compose.ui.backhandler) + } } getByName("jvmMain").dependsOn(skikoMain) iosMain.dependsOn(skikoMain) diff --git a/commonsUI/src/androidMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/PlatformBackHandler.android.kt b/commonsUI/src/androidMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/PlatformBackHandler.android.kt new file mode 100644 index 0000000000..fbd0460a54 --- /dev/null +++ b/commonsUI/src/androidMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/PlatformBackHandler.android.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.commons.ui.components + +import androidx.activity.compose.BackHandler +import androidx.compose.runtime.Composable + +@Composable +actual fun PlatformBackHandler( + enabled: Boolean, + onBack: () -> Unit, +) = BackHandler(enabled, onBack) diff --git a/commonsUI/src/androidMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ShortNotice.android.kt b/commonsUI/src/androidMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ShortNotice.android.kt index 24d16cd38a..994d6332c0 100644 --- a/commonsUI/src/androidMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ShortNotice.android.kt +++ b/commonsUI/src/androidMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ShortNotice.android.kt @@ -30,3 +30,9 @@ actual fun rememberShortNotice(): ShortNotice { val context = LocalContext.current return remember(context) { ShortNotice { message -> Toast.makeText(context, message, Toast.LENGTH_SHORT).show() } } } + +@Composable +actual fun rememberLongNotice(): ShortNotice { + val context = LocalContext.current + return remember(context) { ShortNotice { message -> Toast.makeText(context, message, Toast.LENGTH_LONG).show() } } +} diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/PlatformBackHandler.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/PlatformBackHandler.kt new file mode 100644 index 0000000000..60225f29ed --- /dev/null +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/PlatformBackHandler.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.commons.ui.components + +import androidx.compose.runtime.Composable + +/** + * Intercepts the system back gesture/button while [enabled]: AndroidX's `BackHandler` on + * Android, Compose Multiplatform's on desktop (Esc) and iOS (swipe back). + */ +@Composable +expect fun PlatformBackHandler( + enabled: Boolean = true, + onBack: () -> Unit, +) diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ShortNotice.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ShortNotice.kt index afcf42777d..8a5766f8bd 100644 --- a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ShortNotice.kt +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ShortNotice.kt @@ -33,3 +33,7 @@ fun interface ShortNotice { */ @Composable expect fun rememberShortNotice(): ShortNotice + +/** Like [rememberShortNotice], shown longer: for errors the reader needs time to take in. */ +@Composable +expect fun rememberLongNotice(): ShortNotice diff --git a/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/MonthDayTime.kt b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/MonthDayTime.kt new file mode 100644 index 0000000000..313eb57144 --- /dev/null +++ b/commonsUI/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/note/MonthDayTime.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.commons.ui.note + +private val monthDay = DateSkeletonFormatter("MMMd") + +/** + * Locale-ordered month and day followed by [timeOfDay] ("May 28 14:32" / "28 May 2:32 PM"). Pass + * [rememberTimeOfDayFormatter] so the clock follows the system 12/24-hour setting. + */ +fun formatMonthDayTime( + epochSeconds: Long, + timeOfDay: (epochMillis: Long) -> String, +): String { + val millis = epochSeconds * 1000L + return "${monthDay.format(millis)} ${timeOfDay(millis)}" +} diff --git a/commonsUI/src/iosMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ShortNotice.ios.kt b/commonsUI/src/iosMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ShortNotice.ios.kt index a991b09cfb..b0d73e3abf 100644 --- a/commonsUI/src/iosMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ShortNotice.ios.kt +++ b/commonsUI/src/iosMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ShortNotice.ios.kt @@ -27,3 +27,6 @@ private val loggedNotice = ShortNotice { message -> Log.d("ShortNotice") { messa @Composable actual fun rememberShortNotice(): ShortNotice = loggedNotice + +@Composable +actual fun rememberLongNotice(): ShortNotice = loggedNotice diff --git a/commonsUI/src/jvmMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ShortNotice.jvm.kt b/commonsUI/src/jvmMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ShortNotice.jvm.kt index a991b09cfb..b0d73e3abf 100644 --- a/commonsUI/src/jvmMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ShortNotice.jvm.kt +++ b/commonsUI/src/jvmMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/ShortNotice.jvm.kt @@ -27,3 +27,6 @@ private val loggedNotice = ShortNotice { message -> Log.d("ShortNotice") { messa @Composable actual fun rememberShortNotice(): ShortNotice = loggedNotice + +@Composable +actual fun rememberLongNotice(): ShortNotice = loggedNotice diff --git a/commonsUI/src/skikoMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/PlatformBackHandler.skiko.kt b/commonsUI/src/skikoMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/PlatformBackHandler.skiko.kt new file mode 100644 index 0000000000..70ba995c8b --- /dev/null +++ b/commonsUI/src/skikoMain/kotlin/com/vitorpamplona/amethyst/commons/ui/components/PlatformBackHandler.skiko.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.commons.ui.components + +import androidx.compose.runtime.Composable +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.backhandler.BackHandler + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +actual fun PlatformBackHandler( + enabled: Boolean, + onBack: () -> Unit, +) = BackHandler(enabled, onBack) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 13297f858e..45b096ea6b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -197,6 +197,7 @@ jetbrains-compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", jetbrains-compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "jetbrainsCompose" } jetbrains-compose-ui-tooling = { module = "org.jetbrains.compose.ui:ui-tooling", version.ref = "jetbrainsCompose" } jetbrains-compose-ui-tooling-preview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "jetbrainsCompose" } +jetbrains-compose-ui-backhandler = { module = "org.jetbrains.compose.ui:ui-backhandler", version.ref = "jetbrainsCompose" } jetbrains-compose-ui-test-junit4 = { module = "org.jetbrains.compose.ui:ui-test-junit4", version.ref = "jetbrainsCompose" } google-mlkit-genai-proofreading = { group = "com.google.mlkit", name = "genai-proofreading", version.ref = "genaiProofreading" } google-mlkit-genai-prompt = { group = "com.google.mlkit", name = "genai-prompt", version.ref = "genaiPrompt" }