From ae644c9e835bb08047efa5802a2fe65837272356 Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Tue, 18 Aug 2026 11:44:34 +0200 Subject: [PATCH 1/5] fix: show passphrase toast on sheet --- .../hardware/HwPassphrasePromptSheet.kt | 37 ++++++++++++++----- changelog.d/next/1161.fixed.md | 1 + 2 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 changelog.d/next/1161.fixed.md diff --git a/app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt b/app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt index f175f6c62..333ffce86 100644 --- a/app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt +++ b/app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt @@ -2,6 +2,7 @@ package to.bitkit.ui.screens.transfer.hardware import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -21,6 +22,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clipToBounds @@ -38,6 +40,7 @@ import dev.chrisbanes.haze.hazeSource import dev.chrisbanes.haze.rememberHazeState import kotlinx.coroutines.launch import to.bitkit.R +import to.bitkit.ui.appViewModel import to.bitkit.ui.components.BodyM import to.bitkit.ui.components.BottomSheet import to.bitkit.ui.components.Display @@ -46,6 +49,7 @@ import to.bitkit.ui.components.PrimaryButton import to.bitkit.ui.components.SecondaryButton import to.bitkit.ui.components.SheetSize import to.bitkit.ui.components.TextInput +import to.bitkit.ui.components.ToastOverlay import to.bitkit.ui.components.VerticalSpacer import to.bitkit.ui.scaffold.SheetTopBar import to.bitkit.ui.shared.effects.BlockScreenshots @@ -90,18 +94,33 @@ fun HwPassphrasePromptSheet( sheetState = sheetState, modifier = Modifier.imePadding() ) { - Content( - isVerifying = isVerifying, - onSubmit = { - dismissKeyboard() - onSubmit(it) - }, - onCancel = { closeSheet() }, - modifier = Modifier.sheetHeight(SheetSize.LARGE, isModal = true) - ) + Box { + Content( + isVerifying = isVerifying, + onSubmit = { + dismissKeyboard() + onSubmit(it) + }, + onCancel = { closeSheet() }, + modifier = Modifier.sheetHeight(SheetSize.LARGE, isModal = true) + ) + SheetToastHost() + } } } +@Composable +private fun SheetToastHost() { + val app = appViewModel ?: return + val currentToast by app.currentToast.collectAsStateWithLifecycle() + ToastOverlay( + toast = currentToast, + onDismiss = { app.hideToast() }, + onDragStart = { app.pauseToast() }, + onDragEnd = { app.resumeToast() }, + ) +} + @Composable private fun Content( isVerifying: Boolean, diff --git a/changelog.d/next/1161.fixed.md b/changelog.d/next/1161.fixed.md new file mode 100644 index 000000000..4ba787629 --- /dev/null +++ b/changelog.d/next/1161.fixed.md @@ -0,0 +1 @@ +The wrong-passphrase error toast is now visible on top of the hardware transfer passphrase sheet. From 1e4804bb3cd352dca7421a5ddb66827d36a3dd1e Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Tue, 18 Aug 2026 12:24:01 +0200 Subject: [PATCH 2/5] fix: blur toast over passphrase sheet --- .../transfer/hardware/HwPassphrasePromptSheet.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt b/app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt index 333ffce86..90b84af24 100644 --- a/app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt +++ b/app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt @@ -36,6 +36,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import dev.chrisbanes.haze.HazeState import dev.chrisbanes.haze.hazeSource import dev.chrisbanes.haze.rememberHazeState import kotlinx.coroutines.launch @@ -72,6 +73,7 @@ fun HwPassphrasePromptSheet( onDismiss: () -> Unit, ) { val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + val toastHazeState = rememberHazeState(blurEnabled = true) val scope = rememberCoroutineScope() val focusManager = LocalFocusManager.current val keyboardController = LocalSoftwareKeyboardController.current @@ -102,19 +104,22 @@ fun HwPassphrasePromptSheet( onSubmit(it) }, onCancel = { closeSheet() }, - modifier = Modifier.sheetHeight(SheetSize.LARGE, isModal = true) + modifier = Modifier + .sheetHeight(SheetSize.LARGE, isModal = true) + .hazeSource(toastHazeState, zIndex = 0f) ) - SheetToastHost() + SheetToastHost(hazeState = toastHazeState) } } } @Composable -private fun SheetToastHost() { +private fun SheetToastHost(hazeState: HazeState) { val app = appViewModel ?: return val currentToast by app.currentToast.collectAsStateWithLifecycle() ToastOverlay( toast = currentToast, + hazeState = hazeState, onDismiss = { app.hideToast() }, onDragStart = { app.pauseToast() }, onDragEnd = { app.resumeToast() }, From bd2c98abb2c6fb71b51160e38422073d1d422166 Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Tue, 18 Aug 2026 12:31:29 +0200 Subject: [PATCH 3/5] fix: harden passphrase sheet toast host --- .../hardware/HwPassphrasePromptSheet.kt | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt b/app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt index 90b84af24..a9b4f89e9 100644 --- a/app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt +++ b/app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt @@ -22,7 +22,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue -import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clipToBounds @@ -36,6 +35,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import dev.chrisbanes.haze.HazeState import dev.chrisbanes.haze.hazeSource import dev.chrisbanes.haze.rememberHazeState @@ -59,6 +59,7 @@ import to.bitkit.ui.shared.util.gradientBackground import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors import to.bitkit.ui.utils.withAccent +import to.bitkit.viewmodels.AppViewModel /** * Asks for the passphrase of the hidden wallet a transfer signs from. Bitkit never stores it, so @@ -74,6 +75,7 @@ fun HwPassphrasePromptSheet( ) { val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val toastHazeState = rememberHazeState(blurEnabled = true) + val app = appViewModel val scope = rememberCoroutineScope() val focusManager = LocalFocusManager.current val keyboardController = LocalSoftwareKeyboardController.current @@ -96,7 +98,7 @@ fun HwPassphrasePromptSheet( sheetState = sheetState, modifier = Modifier.imePadding() ) { - Box { + Box(modifier = Modifier.fillMaxSize()) { Content( isVerifying = isVerifying, onSubmit = { @@ -108,19 +110,26 @@ fun HwPassphrasePromptSheet( .sheetHeight(SheetSize.LARGE, isModal = true) .hazeSource(toastHazeState, zIndex = 0f) ) - SheetToastHost(hazeState = toastHazeState) + if (app != null) { + SheetToastHost( + app = app, + hazeState = toastHazeState, + ) + } } } } @Composable -private fun SheetToastHost(hazeState: HazeState) { - val app = appViewModel ?: return +private fun SheetToastHost( + app: AppViewModel, + hazeState: HazeState, +) { val currentToast by app.currentToast.collectAsStateWithLifecycle() ToastOverlay( toast = currentToast, - hazeState = hazeState, onDismiss = { app.hideToast() }, + hazeState = hazeState, onDragStart = { app.pauseToast() }, onDragEnd = { app.resumeToast() }, ) From c9b9a85148ac6539ed08c8a2d8b27c7d2ca9d084 Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Tue, 18 Aug 2026 12:36:13 +0200 Subject: [PATCH 4/5] chore: rename changelog fragment Co-authored-by: Cursor --- changelog.d/next/{1161.fixed.md => 1165.fixed.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog.d/next/{1161.fixed.md => 1165.fixed.md} (100%) diff --git a/changelog.d/next/1161.fixed.md b/changelog.d/next/1165.fixed.md similarity index 100% rename from changelog.d/next/1161.fixed.md rename to changelog.d/next/1165.fixed.md From 7a1df87e7cebe5593771a6b1b4a20a52c639a0d6 Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Wed, 19 Aug 2026 10:34:48 +0200 Subject: [PATCH 5/5] fix: hoist toast host into bottom sheet --- .../to/bitkit/ui/components/BottomSheet.kt | 45 +++++++++++++++- .../hardware/HwPassphrasePromptSheet.kt | 51 ++++--------------- changelog.d/next/1165.fixed.md | 2 +- 3 files changed, 54 insertions(+), 44 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/components/BottomSheet.kt b/app/src/main/java/to/bitkit/ui/components/BottomSheet.kt index 0acc61634..906f56c9a 100644 --- a/app/src/main/java/to/bitkit/ui/components/BottomSheet.kt +++ b/app/src/main/java/to/bitkit/ui/components/BottomSheet.kt @@ -17,6 +17,7 @@ import androidx.compose.material3.SheetValue import androidx.compose.material3.contentColorFor import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -27,12 +28,18 @@ import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import dev.chrisbanes.haze.HazeState +import dev.chrisbanes.haze.hazeSource +import dev.chrisbanes.haze.rememberHazeState +import to.bitkit.ui.appViewModel import to.bitkit.ui.scaffold.SheetTopBar import to.bitkit.ui.shared.modifiers.sheetHeight import to.bitkit.ui.shared.util.gradientBackground import to.bitkit.ui.theme.AppShapes import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors +import to.bitkit.viewmodels.AppViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -60,6 +67,9 @@ fun BottomSheet( properties: ModalBottomSheetProperties = ModalBottomSheetDefaults.properties, content: @Composable ColumnScope.() -> Unit, ) { + val toastHazeState = rememberHazeState(blurEnabled = true) + val app = appViewModel + ModalBottomSheet( onDismissRequest = onDismissRequest, modifier = modifier.semantics { testTagsAsResourceId = true }, @@ -73,7 +83,40 @@ fun BottomSheet( dragHandle = dragHandle, contentWindowInsets = contentWindowInsets, properties = properties, - content = content, + ) { + Box(modifier = Modifier.fillMaxWidth()) { + Column( + modifier = Modifier + .fillMaxWidth() + .hazeSource(toastHazeState, zIndex = 0f) + ) { + content() + } + if (app != null) { + SheetToastHost( + app = app, + hazeState = toastHazeState, + modifier = Modifier.matchParentSize() + ) + } + } + } +} + +@Composable +private fun SheetToastHost( + app: AppViewModel, + hazeState: HazeState, + modifier: Modifier = Modifier, +) { + val currentToast by app.currentToast.collectAsStateWithLifecycle() + ToastOverlay( + toast = currentToast, + onDismiss = { app.hideToast() }, + hazeState = hazeState, + onDragStart = { app.pauseToast() }, + onDragEnd = { app.resumeToast() }, + modifier = modifier ) } diff --git a/app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt b/app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt index a9b4f89e9..f175f6c62 100644 --- a/app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt +++ b/app/src/main/java/to/bitkit/ui/screens/transfer/hardware/HwPassphrasePromptSheet.kt @@ -2,7 +2,6 @@ package to.bitkit.ui.screens.transfer.hardware import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -35,13 +34,10 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import dev.chrisbanes.haze.HazeState import dev.chrisbanes.haze.hazeSource import dev.chrisbanes.haze.rememberHazeState import kotlinx.coroutines.launch import to.bitkit.R -import to.bitkit.ui.appViewModel import to.bitkit.ui.components.BodyM import to.bitkit.ui.components.BottomSheet import to.bitkit.ui.components.Display @@ -50,7 +46,6 @@ import to.bitkit.ui.components.PrimaryButton import to.bitkit.ui.components.SecondaryButton import to.bitkit.ui.components.SheetSize import to.bitkit.ui.components.TextInput -import to.bitkit.ui.components.ToastOverlay import to.bitkit.ui.components.VerticalSpacer import to.bitkit.ui.scaffold.SheetTopBar import to.bitkit.ui.shared.effects.BlockScreenshots @@ -59,7 +54,6 @@ import to.bitkit.ui.shared.util.gradientBackground import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors import to.bitkit.ui.utils.withAccent -import to.bitkit.viewmodels.AppViewModel /** * Asks for the passphrase of the hidden wallet a transfer signs from. Bitkit never stores it, so @@ -74,8 +68,6 @@ fun HwPassphrasePromptSheet( onDismiss: () -> Unit, ) { val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) - val toastHazeState = rememberHazeState(blurEnabled = true) - val app = appViewModel val scope = rememberCoroutineScope() val focusManager = LocalFocusManager.current val keyboardController = LocalSoftwareKeyboardController.current @@ -98,43 +90,18 @@ fun HwPassphrasePromptSheet( sheetState = sheetState, modifier = Modifier.imePadding() ) { - Box(modifier = Modifier.fillMaxSize()) { - Content( - isVerifying = isVerifying, - onSubmit = { - dismissKeyboard() - onSubmit(it) - }, - onCancel = { closeSheet() }, - modifier = Modifier - .sheetHeight(SheetSize.LARGE, isModal = true) - .hazeSource(toastHazeState, zIndex = 0f) - ) - if (app != null) { - SheetToastHost( - app = app, - hazeState = toastHazeState, - ) - } - } + Content( + isVerifying = isVerifying, + onSubmit = { + dismissKeyboard() + onSubmit(it) + }, + onCancel = { closeSheet() }, + modifier = Modifier.sheetHeight(SheetSize.LARGE, isModal = true) + ) } } -@Composable -private fun SheetToastHost( - app: AppViewModel, - hazeState: HazeState, -) { - val currentToast by app.currentToast.collectAsStateWithLifecycle() - ToastOverlay( - toast = currentToast, - onDismiss = { app.hideToast() }, - hazeState = hazeState, - onDragStart = { app.pauseToast() }, - onDragEnd = { app.resumeToast() }, - ) -} - @Composable private fun Content( isVerifying: Boolean, diff --git a/changelog.d/next/1165.fixed.md b/changelog.d/next/1165.fixed.md index 4ba787629..d225d884d 100644 --- a/changelog.d/next/1165.fixed.md +++ b/changelog.d/next/1165.fixed.md @@ -1 +1 @@ -The wrong-passphrase error toast is now visible on top of the hardware transfer passphrase sheet. +Error toasts are now visible on top of modal bottom sheets, including the hardware transfer passphrase sheet.