Skip to content

Commit 601363c

Browse files
authored
Merge pull request #284 from synonymdev/feat/send-fee-custom
Send Set Custom Fee
2 parents 13bda54 + 51cf701 commit 601363c

File tree

15 files changed

+523
-130
lines changed

15 files changed

+523
-130
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
.cxx
1010
local.properties
1111
.cursor
12+
*.local.*
1213

1314
# Secrets
1415
google-services.json

app/detekt-baseline.xml

Lines changed: 3 additions & 31 deletions
Large diffs are not rendered by default.

app/src/main/java/to/bitkit/repositories/LightningRepo.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ class LightningRepo @Inject constructor(
541541
val coinSelectionAlgorithm = coinSelectionPreference.toCoinSelectAlgorithm().getOrThrow()
542542

543543
Logger.debug("Selecting UTXOs with algorithm: $coinSelectionAlgorithm for sats: $sats", context = TAG)
544-
Logger.verbose("All spendable UTXOs: $allSpendableUtxos", context = TAG)
544+
Logger.verbose("All spendable UTXOs(${allSpendableUtxos.size}): $allSpendableUtxos", context = TAG)
545545

546546
lightningService.selectUtxosWithAlgorithm(
547547
targetAmountSats = sats,

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
package to.bitkit.ui.components
22

3+
import androidx.compose.foundation.background
34
import androidx.compose.foundation.layout.Box
5+
import androidx.compose.foundation.layout.fillMaxWidth
46
import androidx.compose.foundation.layout.padding
57
import androidx.compose.foundation.layout.size
68
import androidx.compose.material3.MaterialTheme
79
import androidx.compose.material3.Surface
810
import androidx.compose.runtime.Composable
11+
import androidx.compose.ui.Alignment
912
import androidx.compose.ui.Modifier
1013
import androidx.compose.ui.semantics.contentDescription
1114
import androidx.compose.ui.semantics.semantics
15+
import androidx.compose.ui.tooling.preview.Preview
1216
import androidx.compose.ui.unit.dp
17+
import to.bitkit.ui.theme.AppThemeSurface
1318
import to.bitkit.ui.theme.Colors
1419

1520
@Composable
@@ -20,9 +25,24 @@ fun SheetDragHandle(
2025
color = Colors.White32,
2126
shape = MaterialTheme.shapes.extraLarge,
2227
modifier = modifier
23-
.padding(top = 12.dp, bottom = 4.dp)
28+
.padding(top = 12.dp)
2429
.semantics { contentDescription = "Drag handle" }
2530
) {
2631
Box(Modifier.size(width = 32.dp, height = 4.dp))
2732
}
2833
}
34+
35+
@Composable
36+
@Preview(showBackground = true)
37+
private fun Preview() {
38+
AppThemeSurface {
39+
Box(
40+
contentAlignment = Alignment.Center,
41+
modifier = Modifier
42+
.fillMaxWidth()
43+
.background(color = Colors.Gray6)
44+
) {
45+
SheetDragHandle()
46+
}
47+
}
48+
}

app/src/main/java/to/bitkit/ui/scaffold/SheetTopBar.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.statusBars
1212
import androidx.compose.foundation.layout.windowInsetsPadding
1313
import androidx.compose.material.icons.Icons
1414
import androidx.compose.material.icons.automirrored.filled.ArrowBack
15-
import androidx.compose.material3.ExperimentalMaterial3Api
1615
import androidx.compose.material3.Icon
1716
import androidx.compose.material3.IconButton
1817
import androidx.compose.material3.LocalMinimumInteractiveComponentSize
@@ -27,8 +26,8 @@ import to.bitkit.R
2726
import to.bitkit.ui.components.Subtitle
2827
import to.bitkit.ui.theme.AppThemeSurface
2928

29+
/** For pixel perfection from FIGMA, use the back arrow + 14 padding as starting point for added space under this. */
3030
@Composable
31-
@OptIn(ExperimentalMaterial3Api::class)
3231
fun SheetTopBar(
3332
titleText: String?,
3433
modifier: Modifier = Modifier,
@@ -37,7 +36,7 @@ fun SheetTopBar(
3736
Box(
3837
modifier = modifier
3938
.fillMaxWidth()
40-
.height(42.dp)
39+
.height(54.dp)
4140
) {
4241
titleText?.let {
4342
Subtitle(
@@ -107,7 +106,7 @@ private fun PreviewNoText() {
107106
private fun PreviewOverflow() {
108107
AppThemeSurface {
109108
SheetTopBar(
110-
titleText = "Overflowing Text In This Sheet Top Bar Preview",
109+
titleText = "Overflowing Title Text In This Preview",
111110
onBack = {},
112111
)
113112
}

app/src/main/java/to/bitkit/ui/screens/transfer/external/ExternalFeeCustomScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ private fun Preview() {
178178
AppThemeSurface {
179179
Content(
180180
input = "5",
181-
totalFeeText = "₿ 256 for average transaction ($0.25)"
181+
totalFeeText = "₿ 256 for this transaction ($0.25)"
182182
)
183183
}
184184
}

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

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.padding
1616
import androidx.compose.foundation.layout.size
1717
import androidx.compose.foundation.rememberScrollState
1818
import androidx.compose.foundation.verticalScroll
19+
import androidx.compose.material3.CircularProgressIndicator
1920
import androidx.compose.material3.HorizontalDivider
2021
import androidx.compose.material3.Icon
2122
import androidx.compose.runtime.Composable
@@ -31,6 +32,7 @@ import androidx.compose.ui.Modifier
3132
import androidx.compose.ui.res.painterResource
3233
import androidx.compose.ui.res.stringResource
3334
import androidx.compose.ui.text.style.TextOverflow
35+
import androidx.compose.ui.tooling.preview.Devices
3436
import androidx.compose.ui.tooling.preview.Preview
3537
import androidx.compose.ui.unit.dp
3638
import androidx.lifecycle.SavedStateHandle
@@ -53,12 +55,12 @@ import to.bitkit.ui.components.BottomSheetPreview
5355
import to.bitkit.ui.components.ButtonSize
5456
import to.bitkit.ui.components.Caption13Up
5557
import to.bitkit.ui.components.FillHeight
56-
import to.bitkit.ui.components.MoneySSB
5758
import to.bitkit.ui.components.PrimaryButton
5859
import to.bitkit.ui.components.SwipeToConfirm
5960
import to.bitkit.ui.components.TagButton
6061
import to.bitkit.ui.components.TextInput
6162
import to.bitkit.ui.components.VerticalSpacer
63+
import to.bitkit.ui.components.rememberMoneyText
6264
import to.bitkit.ui.scaffold.AppAlertDialog
6365
import to.bitkit.ui.scaffold.SheetTopBar
6466
import to.bitkit.ui.settingsViewModel
@@ -68,6 +70,7 @@ import to.bitkit.ui.shared.util.gradientBackground
6870
import to.bitkit.ui.theme.AppThemeSurface
6971
import to.bitkit.ui.theme.Colors
7072
import to.bitkit.ui.utils.rememberBiometricAuthSupported
73+
import to.bitkit.ui.utils.withAccent
7174
import to.bitkit.viewmodels.AmountWarning
7275
import to.bitkit.viewmodels.LnurlParams
7376
import to.bitkit.viewmodels.SendEvent
@@ -339,11 +342,16 @@ private fun OnChainDescription(
339342
tint = fee.color,
340343
modifier = Modifier.size(16.dp)
341344
)
342-
Row {
343-
BodySSB(stringResource(fee.title) + " (")
344-
MoneySSB(sats = uiState.fee, accent = Colors.White)
345-
BodySSB(")")
346-
}
345+
uiState.fee.takeIf { it > 0 }
346+
?.let { rememberMoneyText(it) }
347+
?.let {
348+
BodySSB(
349+
text = "${stringResource(fee.title)} ($it)".withAccent(accentColor = Colors.White),
350+
maxLines = 1,
351+
overflow = TextOverflow.MiddleEllipsis,
352+
)
353+
}
354+
?: CircularProgressIndicator(Modifier.size(14.dp), Colors.White64, 2.dp)
347355
Icon(
348356
painterResource(R.drawable.ic_pencil_simple),
349357
contentDescription = null,
@@ -497,9 +505,10 @@ private fun sendUiState() = SendUiState(
497505
payeeNodeId = null,
498506
description = "Some invoice description",
499507
),
508+
fee = 45554,
500509
)
501510

502-
@Preview(showSystemUi = true)
511+
@Preview(showSystemUi = true, group = "onchain")
503512
@Composable
504513
private fun PreviewOnChain() {
505514
AppThemeSurface {
@@ -516,6 +525,42 @@ private fun PreviewOnChain() {
516525
}
517526
}
518527

528+
@Preview(showSystemUi = true, group = "onchain", device = Devices.NEXUS_5)
529+
@Composable
530+
private fun PreviewOnChainLongFeeSmallScreen() {
531+
AppThemeSurface {
532+
BottomSheetPreview {
533+
Content(
534+
uiState = sendUiState().copy(
535+
selectedTags = listOf("car", "house", "uber"),
536+
fee = 654321,
537+
),
538+
isLoading = false,
539+
showBiometrics = false,
540+
modifier = Modifier.sheetHeight(),
541+
)
542+
}
543+
}
544+
}
545+
546+
@Preview(showSystemUi = true, group = "onchain")
547+
@Composable
548+
private fun PreviewOnChainFeeLoading() {
549+
AppThemeSurface {
550+
BottomSheetPreview {
551+
Content(
552+
uiState = sendUiState().copy(
553+
selectedTags = listOf("car", "house", "uber"),
554+
fee = 0,
555+
),
556+
isLoading = false,
557+
showBiometrics = false,
558+
modifier = Modifier.sheetHeight(),
559+
)
560+
}
561+
}
562+
}
563+
519564
@Preview(showSystemUi = true)
520565
@Composable
521566
private fun PreviewLightning() {

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

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,71 @@ package to.bitkit.ui.screens.wallets.send
22

33
import androidx.compose.foundation.layout.Column
44
import androidx.compose.foundation.layout.fillMaxSize
5+
import androidx.compose.foundation.layout.height
56
import androidx.compose.foundation.layout.navigationBarsPadding
67
import androidx.compose.foundation.layout.padding
78
import androidx.compose.runtime.Composable
9+
import androidx.compose.runtime.LaunchedEffect
10+
import androidx.compose.runtime.getValue
11+
import androidx.compose.runtime.rememberUpdatedState
812
import androidx.compose.ui.Modifier
913
import androidx.compose.ui.platform.testTag
1014
import androidx.compose.ui.res.stringResource
1115
import androidx.compose.ui.tooling.preview.Preview
1216
import androidx.compose.ui.unit.dp
17+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
1318
import to.bitkit.R
19+
import to.bitkit.ext.toLongOrDefault
20+
import to.bitkit.models.BITCOIN_SYMBOL
21+
import to.bitkit.models.TransactionSpeed
1422
import to.bitkit.ui.components.BodyM
1523
import to.bitkit.ui.components.BottomSheetPreview
16-
import to.bitkit.ui.components.Display
1724
import to.bitkit.ui.components.FillHeight
25+
import to.bitkit.ui.components.LargeRow
26+
import to.bitkit.ui.components.NumberPadSimple
1827
import to.bitkit.ui.components.PrimaryButton
1928
import to.bitkit.ui.components.VerticalSpacer
2029
import to.bitkit.ui.components.settings.SectionHeader
2130
import to.bitkit.ui.scaffold.SheetTopBar
2231
import to.bitkit.ui.shared.modifiers.sheetHeight
2332
import to.bitkit.ui.shared.util.gradientBackground
2433
import to.bitkit.ui.theme.AppThemeSurface
25-
import to.bitkit.viewmodels.SendUiState
34+
import to.bitkit.ui.theme.Colors
2635

2736
@Composable
2837
fun SendFeeCustomScreen(
29-
uiState: SendUiState,
3038
onBack: () -> Unit,
31-
onContinue: () -> Unit,
39+
onContinue: (TransactionSpeed) -> Unit,
40+
viewModel: SendFeeViewModel,
3241
) {
42+
val currentOnContinue by rememberUpdatedState(onContinue)
43+
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
44+
45+
LaunchedEffect(uiState.shouldContinue, uiState.custom) {
46+
if (uiState.shouldContinue == true) {
47+
uiState.custom?.let { currentOnContinue(it) }
48+
}
49+
}
50+
3351
Content(
34-
uiState = uiState,
52+
input = uiState.input,
53+
totalFeeText = uiState.totalFeeText,
54+
onKeyPress = viewModel::onKeyPress,
3555
onBack = onBack,
36-
onContinue = onContinue,
56+
onContinue = { viewModel.validateCustomFee() },
3757
)
3858
}
3959

4060
@Composable
4161
private fun Content(
42-
uiState: SendUiState,
62+
input: String,
63+
totalFeeText: String,
4364
modifier: Modifier = Modifier,
65+
onKeyPress: (String) -> Unit = {},
4466
onBack: () -> Unit = {},
4567
onContinue: () -> Unit = {},
4668
) {
69+
val isValid = input.toLongOrDefault(0) != 0L
4770
Column(
4871
modifier = modifier
4972
.fillMaxSize()
@@ -53,17 +76,33 @@ private fun Content(
5376
) {
5477
SheetTopBar(stringResource(R.string.wallet__send_fee_custom), onBack = onBack)
5578
Column(
56-
modifier = Modifier.padding(horizontal = 16.dp)
79+
modifier = Modifier
80+
.padding(horizontal = 16.dp)
81+
.fillMaxSize()
5782
) {
58-
SectionHeader(stringResource(R.string.wallet__send_fee_and_speed))
59-
Display("TODO")
60-
BodyM("Lint hack " + uiState.speed.toString())
83+
SectionHeader(title = stringResource(R.string.common__sat_vbyte))
84+
LargeRow(
85+
prefix = null,
86+
text = input.ifEmpty { "0" },
87+
symbol = BITCOIN_SYMBOL,
88+
showSymbol = true,
89+
)
90+
91+
if (isValid) {
92+
VerticalSpacer(28.dp)
93+
BodyM(totalFeeText, color = Colors.White64)
94+
}
6195

62-
FillHeight(min = 16.dp)
96+
FillHeight()
6397

98+
NumberPadSimple(
99+
onPress = onKeyPress,
100+
modifier = Modifier.height(350.dp)
101+
)
64102
PrimaryButton(
65103
text = stringResource(R.string.common__continue),
66104
onClick = onContinue,
105+
enabled = isValid,
67106
modifier = Modifier.testTag("continue_btn")
68107
)
69108
VerticalSpacer(16.dp)
@@ -77,7 +116,8 @@ private fun Preview() {
77116
AppThemeSurface {
78117
BottomSheetPreview {
79118
Content(
80-
uiState = SendUiState(),
119+
input = "5",
120+
totalFeeText = "₿ 256 for this transaction ($0.25)",
81121
modifier = Modifier.sheetHeight(),
82122
)
83123
}

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import androidx.compose.ui.res.painterResource
2323
import androidx.compose.ui.res.stringResource
2424
import androidx.compose.ui.tooling.preview.Preview
2525
import androidx.compose.ui.unit.dp
26-
import androidx.hilt.navigation.compose.hiltViewModel
2726
import androidx.lifecycle.compose.collectAsStateWithLifecycle
2827
import to.bitkit.R
2928
import to.bitkit.models.FeeRate
@@ -55,7 +54,7 @@ fun SendFeeRateScreen(
5554
onBack: () -> Unit,
5655
onContinue: () -> Unit,
5756
onSelect: (TransactionSpeed) -> Unit,
58-
viewModel: SendFeeViewModel = hiltViewModel(),
57+
viewModel: SendFeeViewModel,
5958
) {
6059
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
6160

@@ -108,8 +107,8 @@ private fun Content(
108107
feeRate = feeRate,
109108
sats = sats,
110109
isSelected = uiState.selected == feeRate,
111-
isDisabled = false, // TODO
112-
onClick = { onSelect(feeRate) },
110+
isDisabled = feeRate in uiState.disabledRates,
111+
onClick = { if (feeRate !in uiState.disabledRates) onSelect(feeRate) },
113112
modifier = Modifier.testTag("fee_${feeRate.name}_button"),
114113
)
115114
}

0 commit comments

Comments
 (0)