Skip to content

Commit 9a75488

Browse files
authored
Merge pull request #176 from synonymdev/feat/send-amount-warn
feat: Warn when sending over $100
2 parents 0778de0 + de2241f commit 9a75488

File tree

9 files changed

+298
-183
lines changed

9 files changed

+298
-183
lines changed

app/src/main/java/to/bitkit/services/CurrencyService.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ class CurrencyService @Inject constructor(
6969
)
7070
}
7171

72+
suspend fun convertSatsToFiat(satsAmount: Long, currency: String): Double {
73+
val rates = cachedRates ?: fetchLatestRates()
74+
val rate = getCurrentRate(currency, rates) ?: return 0.0
75+
76+
return convert(satsAmount.toLong(), rate)?.value?.toDouble() ?: 0.0
77+
}
78+
7279
fun convertFiatToSats(fiatValue: BigDecimal, rate: FxRate): ULong {
7380
val btcAmount = fiatValue.divide(BigDecimal.valueOf(rate.rate), 8, RoundingMode.HALF_UP)
7481
val satsDecimal = btcAmount.multiply(BigDecimal(SATS_IN_BTC))

app/src/main/java/to/bitkit/ui/components/Button.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,15 @@ fun TertiaryButton(
142142
isLoading: Boolean = false,
143143
size: ButtonSize = ButtonSize.Large,
144144
enabled: Boolean = true,
145+
fullWidth: Boolean = true,
145146
) {
146147
TextButton(
147148
onClick = onClick,
148149
enabled = enabled && !isLoading,
149150
colors = AppButtonDefaults.tertiaryColors,
150151
contentPadding = PaddingValues(horizontal = size.horizontalPadding),
151152
modifier = Modifier
152-
.fillMaxWidth()
153+
.then(if (fullWidth) Modifier.fillMaxWidth() else Modifier)
153154
.height(size.height)
154155
.then(modifier)
155156
) {

app/src/main/java/to/bitkit/ui/components/ClipboardDataDialog.kt

Lines changed: 0 additions & 89 deletions
This file was deleted.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package to.bitkit.ui.scaffold
2+
3+
import androidx.compose.foundation.layout.Box
4+
import androidx.compose.foundation.layout.fillMaxSize
5+
import androidx.compose.material3.AlertDialog
6+
import androidx.compose.material3.MaterialTheme
7+
import androidx.compose.material3.TextButton
8+
import androidx.compose.runtime.Composable
9+
import androidx.compose.ui.Modifier
10+
import androidx.compose.ui.tooling.preview.Preview
11+
import androidx.compose.ui.window.DialogProperties
12+
import to.bitkit.ui.components.BodyM
13+
import to.bitkit.ui.components.BodyMSB
14+
import to.bitkit.ui.components.Title
15+
import to.bitkit.ui.theme.AppThemeSurface
16+
import to.bitkit.ui.theme.Colors
17+
18+
@Composable
19+
fun AppAlertDialog(
20+
onDismissRequest: () -> Unit,
21+
title: String,
22+
text: String,
23+
confirmButtonText: String,
24+
dismissButtonText: String,
25+
onConfirm: () -> Unit,
26+
onDismiss: () -> Unit,
27+
properties: DialogProperties = DialogProperties(dismissOnClickOutside = false),
28+
) {
29+
AlertDialog(
30+
onDismissRequest = onDismissRequest,
31+
confirmButton = {
32+
TextButton(onClick = onConfirm) {
33+
BodyMSB(text = confirmButtonText)
34+
}
35+
},
36+
dismissButton = {
37+
TextButton(onClick = onDismiss) {
38+
BodyMSB(text = dismissButtonText, color = Colors.White64)
39+
}
40+
},
41+
title = { Title(text = title) },
42+
text = { BodyM(text = text, color = Colors.White64) },
43+
shape = MaterialTheme.shapes.medium,
44+
properties = properties,
45+
containerColor = Colors.Gray5,
46+
)
47+
}
48+
49+
@Preview(showSystemUi = true)
50+
@Composable
51+
private fun Preview() {
52+
AppThemeSurface {
53+
Box(Modifier.fillMaxSize()) {
54+
AppAlertDialog(
55+
onDismissRequest = {},
56+
title = "Are you sure?",
57+
text = "You're about do something critically cool. This action cannot be undone.",
58+
confirmButtonText = "Yes",
59+
dismissButtonText = "Cancel",
60+
onConfirm = {},
61+
onDismiss = {},
62+
)
63+
}
64+
}
65+
}

app/src/main/java/to/bitkit/ui/screens/wallets/HomeScreen.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ import to.bitkit.ui.utils.withAccent
9999
import to.bitkit.viewmodels.ActivityListViewModel
100100
import to.bitkit.viewmodels.AppViewModel
101101
import to.bitkit.viewmodels.MainUiState
102+
import to.bitkit.viewmodels.SendEvent
102103
import to.bitkit.viewmodels.SettingsViewModel
103104
import to.bitkit.viewmodels.WalletViewModel
104105

@@ -125,6 +126,7 @@ fun HomeScreen(
125126
walletViewModel = walletViewModel,
126127
startDestination = sheet.route,
127128
onComplete = { txSheet ->
129+
appViewModel.setSendEvent(SendEvent.Reset)
128130
appViewModel.hideSheet()
129131
txSheet?.let { appViewModel.showNewTransactionSheet(it) }
130132
}

app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAndReviewScreen.kt

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import to.bitkit.ui.components.Caption13Up
4848
import to.bitkit.ui.components.PrimaryButton
4949
import to.bitkit.ui.components.SwipeToConfirm
5050
import to.bitkit.ui.components.TagButton
51+
import to.bitkit.ui.scaffold.AppAlertDialog
5152
import to.bitkit.ui.scaffold.SheetTopBar
5253
import to.bitkit.ui.settingsViewModel
5354
import to.bitkit.ui.theme.AppThemeSurface
@@ -82,6 +83,7 @@ fun SendAndReviewScreen(
8283
val isBiometricEnabled by settings.isBiometricEnabled.collectAsStateWithLifecycle()
8384
val isBiometrySupported = rememberBiometricAuthSupported()
8485

86+
// Handle result from PinCheckScreen
8587
LaunchedEffect(savedStateHandle) {
8688
savedStateHandle.getStateFlow<Boolean?>(PIN_CHECK_RESULT_KEY, null)
8789
.filterNotNull()
@@ -91,6 +93,21 @@ fun SendAndReviewScreen(
9193
}
9294
}
9395

96+
// Handle pay confirm with auth check if needed
97+
LaunchedEffect(uiState.shouldConfirmPay) {
98+
if (uiState.shouldConfirmPay) {
99+
if (isPinEnabled && pinForPayments) {
100+
if (isBiometricEnabled && isBiometrySupported) {
101+
showBiometrics = true
102+
} else {
103+
onNavigateToPin()
104+
}
105+
} else {
106+
onEvent(SendEvent.PayConfirmed)
107+
}
108+
}
109+
}
110+
94111
SendAndReviewContent(
95112
uiState = uiState,
96113
isLoading = isLoading,
@@ -103,21 +120,13 @@ fun SendAndReviewScreen(
103120
scope.launch {
104121
isLoading = true
105122
delay(300)
106-
if (isPinEnabled && pinForPayments) {
107-
if (isBiometricEnabled && isBiometrySupported) {
108-
showBiometrics = true
109-
} else {
110-
onNavigateToPin()
111-
}
112-
} else {
113-
onEvent(SendEvent.SwipeToPay)
114-
}
123+
onEvent(SendEvent.SwipeToPay)
115124
}
116125
},
117126
onBiometricsSuccess = {
118127
isLoading = true
119128
showBiometrics = false
120-
onEvent(SendEvent.SwipeToPay)
129+
onEvent(SendEvent.PayConfirmed)
121130
},
122131
onBiometricsFailure = {
123132
isLoading = false
@@ -213,6 +222,24 @@ private fun SendAndReviewContent(
213222
onFailure = onBiometricsFailure,
214223
)
215224
}
225+
226+
if (uiState.showAmountWarningDialog) {
227+
AppAlertDialog(
228+
onDismissRequest = {
229+
onEvent(SendEvent.DismissAmountWarning)
230+
onBack()
231+
},
232+
title = stringResource(R.string.common__are_you_sure),
233+
text = stringResource(R.string.wallet__send_dialog1),
234+
confirmButtonText = stringResource(R.string.wallet__send_yes),
235+
dismissButtonText = stringResource(R.string.common__cancel),
236+
onConfirm = { onEvent(SendEvent.ConfirmAmountWarning) },
237+
onDismiss = {
238+
onEvent(SendEvent.DismissAmountWarning)
239+
onBack()
240+
},
241+
)
242+
}
216243
}
217244
}
218245

@@ -254,7 +281,7 @@ private fun OnChainDescription(
254281
tint = Colors.Brand,
255282
modifier = Modifier.size(16.dp)
256283
)
257-
BodySSB(text = "Normal (₿ 210)") //TODO GET FROM STATE
284+
BodySSB(text = "Normal (₿ 210)") // TODO GET FROM STATE
258285
Icon(
259286
painterResource(R.drawable.ic_pencil_simple),
260287
contentDescription = null,
@@ -284,7 +311,7 @@ private fun OnChainDescription(
284311
tint = Colors.Brand,
285312
modifier = Modifier.size(16.dp)
286313
)
287-
BodySSB(text = "± 20-60 minutes") //TODO GET FROM STATE
314+
BodySSB(text = "± 20-60 minutes") // TODO GET FROM STATE
288315
}
289316
Spacer(modifier = Modifier.weight(1f))
290317
HorizontalDivider(modifier = Modifier.padding(top = 16.dp))
@@ -330,7 +357,7 @@ private fun LightningDescription(
330357
tint = Colors.Purple,
331358
modifier = Modifier.size(16.dp)
332359
)
333-
BodySSB(text = "Instant (±$0.01)") //TODO GET FROM STATE
360+
BodySSB(text = "Instant (±$0.01)") // TODO GET FROM STATE
334361
}
335362
Spacer(modifier = Modifier.weight(1f))
336363
HorizontalDivider(modifier = Modifier.padding(vertical = 16.dp))
@@ -427,17 +454,7 @@ private fun PreviewOnChain() {
427454
bolt11 = "lnbcrt1…",
428455
payMethod = SendMethod.ONCHAIN,
429456
selectedTags = listOf("car", "house", "uber"),
430-
decodedInvoice = LightningInvoice(
431-
bolt11 = "bcrt123",
432-
paymentHash = ByteArray(0),
433-
amountSatoshis = 10000uL,
434-
timestampSeconds = 0uL,
435-
expirySeconds = 3600uL,
436-
isExpired = false,
437-
networkType = NetworkType.REGTEST,
438-
payeeNodeId = null,
439-
description = "Some invoice description",
440-
),
457+
decodedInvoice = null,
441458
),
442459
isLoading = false,
443460
showBiometrics = false,
@@ -464,17 +481,35 @@ private fun PreviewBio() {
464481
bolt11 = "lnbcrt1…",
465482
payMethod = SendMethod.ONCHAIN,
466483
selectedTags = listOf("car", "house", "uber"),
467-
decodedInvoice = LightningInvoice(
468-
bolt11 = "bcrt123",
469-
paymentHash = ByteArray(0),
470-
amountSatoshis = 10000uL,
471-
timestampSeconds = 0uL,
472-
expirySeconds = 3600uL,
473-
isExpired = false,
474-
networkType = NetworkType.REGTEST,
475-
payeeNodeId = null,
476-
description = "Some invoice description",
477-
),
484+
decodedInvoice = null,
485+
),
486+
isLoading = false,
487+
showBiometrics = true,
488+
onBack = {},
489+
onEvent = {},
490+
onClickAddTag = {},
491+
onClickTag = {},
492+
onSwipeToConfirm = {},
493+
onBiometricsSuccess = {},
494+
onBiometricsFailure = {},
495+
)
496+
}
497+
}
498+
499+
@Suppress("SpellCheckingInspection")
500+
@Preview
501+
@Composable
502+
private fun PreviewDialog() {
503+
AppThemeSurface {
504+
SendAndReviewContent(
505+
uiState = SendUiState(
506+
amount = 1234uL,
507+
address = "bcrt1qkgfgyxyqhvkdqh04sklnzxphmcds6vft6y7h0r",
508+
bolt11 = "lnbcrt1…",
509+
payMethod = SendMethod.ONCHAIN,
510+
selectedTags = listOf("car", "house", "uber"),
511+
decodedInvoice = null,
512+
showAmountWarningDialog = true,
478513
),
479514
isLoading = false,
480515
showBiometrics = true,

0 commit comments

Comments
 (0)