Skip to content

Commit 5c62554

Browse files
authored
feat(bills): render bills at the app root; drop camera frosting (#1220)
* feat(bills): render bills at the app root; drop camera frosting Phase B of moving bills out of the scanner. A presented bill now renders at the app root instead of inside the camera screen, so it appears over ANY screen and is fully decoupled from the camera. - New BillOverlay (:shared:bills), mounted at the app root in App.kt next to ScrimOverlay. It reads the app-scoped billState via LocalSessionController and owns the swipe-to-dismiss gesture, enter/exit animation, tip-modal positioning, and the below-bill decorators. Touch-transparent when no bill is present. - ScannableContainer slims down to camera + HUD: it no longer draws the bill, and hides its DecorView HUD while a bill is up (billState.bill != null). - Remove the frosted tip-card camera backdrop entirely: delete TipCardCameraBackdrop, drop the previewView plumbing from Scanner, and render TipCard as a solid opaque card (LocalTipCard{Color,BaseAlpha} now default to the opaque fallback; the backdrop composition local is gone). The FrostedTipCard feature flag is now unused (left in place; harmless). * feat(bills): hide the tab bar while a bill/tip card is presented Bills render at the app root above everything, so the v2 tab bar would show beneath a presented bill. Gate AppNavigationBar visibility on the app-scoped billState (bill == null) so the bar slides out while a bill is up and returns on dismiss. * feat(bills): scrim behind the bill (over app content) via the root ScrimController When a bill/tip card is presented over app content (not the camera), dim and block the content beneath it so it reads as a focused modal. Drive the shared root ScrimController (its ScrimOverlay already sits just below BillOverlay), so it uses the theme scrim colour and gets tap-to-dismiss for free. Skipped on the scanner tab, where the live camera stays visible behind the bill. - ScrimController.hide() made public so BillOverlay can conceal the scrim when the bill clears itself (e.g. swipe dismiss) without re-invoking onDismiss.
1 parent 6ae70d7 commit 5c62554

9 files changed

Lines changed: 232 additions & 351 deletions

File tree

‎apps/flipcash/app/build.gradle.kts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ dependencies {
183183

184184
implementation(project(":apps:flipcash:core"))
185185
implementation(project(":apps:flipcash:shared:accesskey"))
186+
implementation(project(":apps:flipcash:shared:bills"))
186187
implementation(project(":apps:flipcash:shared:analytics"))
187188
implementation(project(":apps:flipcash:shared:appsettings"))
188189
implementation(project(":apps:flipcash:shared:appupdates"))

‎apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/App.kt‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import com.getcode.navigation.results.rememberNavResultStateRegistry
6969
import com.getcode.navigation.scenes.ModalBottomSheetSceneStrategy
7070
import com.getcode.navigation.scrim.LocalScrimController
7171
import com.getcode.navigation.scrim.ScrimController
72+
import com.flipcash.app.bills.BillOverlay
7273
import com.getcode.navigation.scrim.ScrimOverlay
7374
import com.getcode.theme.CodeTheme
7475
import com.getcode.ui.biometrics.LocalBiometricsState
@@ -223,6 +224,11 @@ internal fun App(
223224
}
224225

225226
ScrimOverlay(scrimController)
227+
228+
// Bills render at the app root (not inside the scanner) so a
229+
// presented bill appears over any screen. Reads the app-scoped
230+
// billState via LocalSessionController.
231+
BillOverlay()
226232
}
227233

228234
val emailCodeChannel = LocalEmailCodeChannel.current

‎apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/AppNavigationBar.kt‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.navigationBarsPadding
1111
import androidx.compose.foundation.layout.padding
1212
import androidx.compose.runtime.Composable
1313
import androidx.compose.runtime.getValue
14+
import androidx.compose.runtime.remember
1415
import androidx.compose.ui.Alignment
1516
import androidx.compose.ui.Modifier
1617
import androidx.compose.ui.unit.dp
@@ -25,9 +26,12 @@ import com.flipcash.app.core.ui.NavigationBar
2526
import com.flipcash.app.core.ui.rememberNavigationBarState
2627
import com.flipcash.app.featureflags.FeatureFlag
2728
import com.flipcash.app.featureflags.LocalFeatureFlags
29+
import com.flipcash.app.session.LocalSessionController
2830
import com.getcode.manager.BottomBarManager
2931
import com.getcode.navigation.core.CodeNavigator
3032
import com.getcode.theme.CodeTheme
33+
import kotlinx.coroutines.flow.flowOf
34+
import kotlinx.coroutines.flow.map
3135

3236
/**
3337
* The hoisted v2 navigation bar — root chrome, not owned by any screen. It renders over whichever
@@ -55,13 +59,20 @@ internal fun AppNavigationBar(
5559
// while one is showing so it sits below the modal instead of floating over it.
5660
val bottomBarMessages by BottomBarManager.messages.collectAsStateWithLifecycle()
5761

62+
// A bill/tip card renders at the app root above everything; hide the bar so it doesn't show
63+
// beneath the presented bill.
64+
val session = LocalSessionController.current
65+
val billUp by remember(session) {
66+
session?.billState?.map { it.bill != null } ?: flowOf(false)
67+
}.collectAsStateWithLifecycle(initialValue = false)
68+
5869
Box(
5970
modifier = Modifier
6071
.then(modifier),
6172
contentAlignment = Alignment.BottomCenter,
6273
) {
6374
AnimatedVisibility(
64-
visible = topTab != null && bottomBarMessages.isEmpty(),
75+
visible = topTab != null && bottomBarMessages.isEmpty() && !billUp,
6576
enter = slideInVertically { it } + fadeIn(),
6677
exit = slideOutVertically { it } + fadeOut(),
6778
) {

‎apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/Scanner.kt‎

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.flipcash.app.scanner.internal
22

33
import android.annotation.SuppressLint
4-
import androidx.camera.view.PreviewView
54
import androidx.compose.runtime.Composable
65
import androidx.compose.runtime.DisposableEffect
76
import androidx.compose.runtime.LaunchedEffect
@@ -64,9 +63,6 @@ internal fun Scanner() {
6463
var isPinching by remember { mutableStateOf(false) }
6564
var zoomRatio by remember { mutableFloatStateOf(1f) }
6665

67-
// Exposed by CodeScanner so the tip card can snapshot the live feed for its blurred backdrop.
68-
var previewView by remember { mutableStateOf<PreviewView?>(null) }
69-
7066
LaunchedEffect(biometricsState, previewing) {
7167
if (previewing == true) {
7268
focusManager.clearFocus()
@@ -84,7 +80,6 @@ internal fun Scanner() {
8480
isPaused = isPaused,
8581
isPinching = isPinching,
8682
zoomRatio = zoomRatio,
87-
previewView = previewView,
8883
onAction = {
8984
when (it) {
9085
ScannerDecorItem.Give -> {
@@ -116,7 +111,6 @@ internal fun Scanner() {
116111
cameraAvailable = true
117112
previewing = it
118113
},
119-
onPreviewViewChanged = { previewView = it },
120114
onCodeScanned = { result ->
121115
when (result) {
122116
is CodeScanResult.QrCode -> {

0 commit comments

Comments
 (0)