Skip to content

Commit b6c6490

Browse files
committed
feat(transfer): use numpad for receiving capacity amount
1 parent b5effb5 commit b6c6490

File tree

4 files changed

+89
-105
lines changed

4 files changed

+89
-105
lines changed

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

Lines changed: 78 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,10 @@ package to.bitkit.ui.screens.transfer
33
import androidx.compose.foundation.layout.Arrangement
44
import androidx.compose.foundation.layout.Column
55
import androidx.compose.foundation.layout.Row
6-
import androidx.compose.foundation.layout.Spacer
76
import androidx.compose.foundation.layout.fillMaxSize
87
import androidx.compose.foundation.layout.fillMaxWidth
9-
import androidx.compose.foundation.layout.height
10-
import androidx.compose.foundation.layout.imePadding
118
import androidx.compose.foundation.layout.padding
129
import androidx.compose.foundation.layout.requiredHeight
13-
import androidx.compose.foundation.layout.width
1410
import androidx.compose.material3.HorizontalDivider
1511
import androidx.compose.runtime.Composable
1612
import androidx.compose.runtime.LaunchedEffect
@@ -26,88 +22,117 @@ import androidx.compose.ui.res.stringResource
2622
import androidx.compose.ui.tooling.preview.Devices.NEXUS_5
2723
import androidx.compose.ui.tooling.preview.Preview
2824
import androidx.compose.ui.unit.dp
25+
import androidx.hilt.navigation.compose.hiltViewModel
2926
import androidx.lifecycle.compose.collectAsStateWithLifecycle
3027
import to.bitkit.R
3128
import to.bitkit.ext.mockOrder
29+
import to.bitkit.models.Toast
30+
import to.bitkit.repositories.CurrencyState
3231
import to.bitkit.ui.LocalCurrencies
3332
import to.bitkit.ui.appViewModel
34-
import to.bitkit.ui.components.AmountInput
3533
import to.bitkit.ui.components.Caption13Up
3634
import to.bitkit.ui.components.Display
35+
import to.bitkit.ui.components.FillHeight
36+
import to.bitkit.ui.components.HorizontalSpacer
3737
import to.bitkit.ui.components.MoneySSB
38+
import to.bitkit.ui.components.NumberPad
3839
import to.bitkit.ui.components.NumberPadActionButton
40+
import to.bitkit.ui.components.NumberPadTextField
3941
import to.bitkit.ui.components.PrimaryButton
42+
import to.bitkit.ui.components.VerticalSpacer
4043
import to.bitkit.ui.scaffold.AppTopBar
4144
import to.bitkit.ui.scaffold.CloseNavIcon
4245
import to.bitkit.ui.scaffold.ScreenColumn
4346
import to.bitkit.ui.theme.AppThemeSurface
4447
import to.bitkit.ui.theme.Colors
4548
import to.bitkit.ui.utils.withAccent
49+
import to.bitkit.viewmodels.AmountInputViewModel
4650
import to.bitkit.viewmodels.TransferEffect
4751
import to.bitkit.viewmodels.TransferToSpendingUiState
4852
import to.bitkit.viewmodels.TransferValues
4953
import to.bitkit.viewmodels.TransferViewModel
54+
import to.bitkit.viewmodels.previewAmountInputViewModel
5055

56+
@Suppress("ViewModelForwarding")
5157
@Composable
5258
fun SpendingAdvancedScreen(
5359
viewModel: TransferViewModel,
5460
onBackClick: () -> Unit = {},
5561
onCloseClick: () -> Unit = {},
5662
onOrderCreated: () -> Unit = {},
63+
currencies: CurrencyState = LocalCurrencies.current,
64+
amountInputViewModel: AmountInputViewModel = hiltViewModel(),
5765
) {
5866
val app = appViewModel ?: return
5967
val state by viewModel.spendingUiState.collectAsStateWithLifecycle()
6068
val order = state.order ?: return
6169
val transferValues by viewModel.transferValues.collectAsState()
70+
val amountUiState by amountInputViewModel.uiState.collectAsStateWithLifecycle()
71+
var isLoading by remember { mutableStateOf(false) }
6272

6373
LaunchedEffect(order.clientBalanceSat) {
6474
viewModel.updateTransferValues(order.clientBalanceSat)
6575
}
6676

77+
LaunchedEffect(amountUiState.sats) {
78+
viewModel.onReceivingAmountChange(amountUiState.sats)
79+
}
80+
6781
LaunchedEffect(Unit) {
6882
viewModel.transferEffects.collect { effect ->
6983
when (effect) {
7084
TransferEffect.OnOrderCreated -> onOrderCreated()
71-
is TransferEffect.ToastException -> app.toast(effect.e)
72-
is TransferEffect.ToastError -> app.toast(
73-
type = to.bitkit.models.Toast.ToastType.ERROR,
74-
title = effect.title,
75-
description = effect.description,
76-
)
85+
is TransferEffect.ToastException -> {
86+
isLoading = false
87+
app.toast(effect.e)
88+
}
89+
90+
is TransferEffect.ToastError -> {
91+
isLoading = false
92+
app.toast(
93+
type = Toast.ToastType.ERROR,
94+
title = effect.title,
95+
description = effect.description,
96+
)
97+
}
7798
}
7899
}
79100
}
80101

81102
val isValid = transferValues.let {
82-
val isAboveMin = state.receivingAmount.toULong() >= it.minLspBalance
83-
val isBelowMax = state.receivingAmount.toULong() <= it.maxLspBalance
84-
state.receivingAmount > 0 && isAboveMin && isBelowMax
103+
val amount = amountUiState.sats.toULong()
104+
amount > 0u && amount in it.minLspBalance..it.maxLspBalance
85105
}
86106

87107
Content(
88108
uiState = state,
89109
transferValues = transferValues,
90110
isValid = isValid,
111+
isLoading = isLoading,
112+
amountInputViewModel = amountInputViewModel,
113+
currencies = currencies,
91114
onBack = onBackClick,
92115
onClose = onCloseClick,
93-
onAmountChange = viewModel::onReceivingAmountChange,
94-
onContinue = viewModel::onSpendingAdvancedContinue,
116+
onContinue = {
117+
isLoading = true
118+
viewModel.onSpendingAdvancedContinue(amountUiState.sats)
119+
},
95120
)
96121
}
97122

123+
@Suppress("ViewModelForwarding")
98124
@Composable
99125
private fun Content(
100126
uiState: TransferToSpendingUiState,
101127
transferValues: TransferValues,
102128
isValid: Boolean,
129+
isLoading: Boolean,
130+
amountInputViewModel: AmountInputViewModel,
103131
onBack: () -> Unit,
104132
onClose: () -> Unit,
105-
onAmountChange: (Long) -> Unit,
106133
onContinue: () -> Unit,
134+
currencies: CurrencyState = LocalCurrencies.current,
107135
) {
108-
val currencies = LocalCurrencies.current
109-
uiState.order ?: return
110-
111136
ScreenColumn {
112137
AppTopBar(
113138
titleText = stringResource(R.string.lightning__transfer__nav_title),
@@ -118,31 +143,28 @@ private fun Content(
118143
modifier = Modifier
119144
.padding(horizontal = 16.dp)
120145
.fillMaxSize()
121-
.imePadding()
122146
.testTag("SpendingAdvanced")
123147
) {
124-
var overrideSats: Long? by remember { mutableStateOf(null) }
125-
var isLoading by remember { mutableStateOf(false) }
148+
VerticalSpacer(minHeight = 16.dp, maxHeight = 32.dp)
126149

127-
Spacer(modifier = Modifier.height(32.dp))
128150
Display(
129151
text = stringResource(R.string.lightning__spending_advanced__title)
130152
.withAccent(accentColor = Colors.Purple)
131153
)
132-
Spacer(modifier = Modifier.height(32.dp))
133154

134-
AmountInput(
135-
defaultValue = uiState.receivingAmount,
136-
primaryDisplay = currencies.primaryDisplay,
137-
overrideSats = overrideSats,
138-
onSatsChange = { sats ->
139-
onAmountChange(sats)
140-
overrideSats = null
141-
},
142-
modifier = Modifier.testTag("SpendingAdvancedNumberField")
155+
FillHeight()
156+
157+
NumberPadTextField(
158+
viewModel = amountInputViewModel,
159+
currencies = currencies,
160+
showSecondaryField = false,
161+
modifier = Modifier
162+
.fillMaxWidth()
163+
.testTag("SpendingAdvancedNumberField")
143164
)
144165

145-
Spacer(modifier = Modifier.height(10.dp))
166+
VerticalSpacer(height = 16.dp)
167+
146168
Row(
147169
verticalAlignment = Alignment.CenterVertically,
148170
modifier = Modifier.requiredHeight(20.dp),
@@ -151,72 +173,67 @@ private fun Content(
151173
text = stringResource(R.string.lightning__spending_advanced__fee),
152174
color = Colors.White64,
153175
)
154-
Spacer(modifier = Modifier.width(4.dp))
176+
HorizontalSpacer(8.dp)
155177
uiState.feeEstimate?.let {
156-
MoneySSB(it)
178+
MoneySSB(it, showSymbol = true)
157179
} ?: run {
158180
Caption13Up(text = "", color = Colors.White64)
159181
}
160182
}
161183

162-
Spacer(modifier = Modifier.weight(1f))
184+
FillHeight()
163185

164-
// Actions Row
165186
Row(
166187
verticalAlignment = Alignment.Bottom,
167188
horizontalArrangement = Arrangement.SpaceBetween,
168189
modifier = Modifier
169190
.fillMaxWidth()
170191
.padding(vertical = 8.dp)
171192
) {
172-
// Min Button
173193
NumberPadActionButton(
174194
text = stringResource(R.string.common__min),
175195
color = Colors.Purple,
176-
onClick = {
177-
overrideSats = transferValues.minLspBalance.toLong()
178-
},
196+
onClick = { amountInputViewModel.setSats(transferValues.minLspBalance.toLong(), currencies) },
179197
modifier = Modifier.testTag("SpendingAdvancedMin")
180198
)
181-
// Default Button
182199
NumberPadActionButton(
183200
text = stringResource(R.string.common__default),
184201
color = Colors.Purple,
185-
onClick = {
186-
overrideSats = transferValues.defaultLspBalance.toLong()
187-
},
202+
onClick = { amountInputViewModel.setSats(transferValues.defaultLspBalance.toLong(), currencies) },
188203
modifier = Modifier.testTag("SpendingAdvancedDefault")
189204
)
190-
// Max Button
191205
NumberPadActionButton(
192206
text = stringResource(R.string.common__max),
193207
color = Colors.Purple,
194-
onClick = {
195-
overrideSats = transferValues.maxLspBalance.toLong()
196-
},
208+
onClick = { amountInputViewModel.setSats(transferValues.maxLspBalance.toLong(), currencies) },
197209
modifier = Modifier.testTag("SpendingAdvancedMax")
198210
)
199211
}
212+
200213
HorizontalDivider()
201-
Spacer(modifier = Modifier.height(16.dp))
214+
VerticalSpacer(16.dp)
215+
216+
NumberPad(
217+
viewModel = amountInputViewModel,
218+
currencies = currencies,
219+
)
202220

203221
PrimaryButton(
204222
text = stringResource(R.string.common__continue),
205-
onClick = {
206-
isLoading = true
207-
onContinue()
208-
},
223+
onClick = onContinue,
209224
enabled = !isLoading && isValid,
210225
isLoading = isLoading,
211226
modifier = Modifier.testTag("SpendingAdvancedContinue")
212227
)
213228

214-
Spacer(modifier = Modifier.height(16.dp))
229+
VerticalSpacer(16.dp)
215230
}
216231
}
217232
}
218233

219234
@Preview(showSystemUi = true)
235+
@Preview(showSystemUi = true, device = "id:pixel_9_pro_xl", name = "Large")
236+
@Preview(showSystemUi = true, device = NEXUS_5, name = "Small")
220237
@Composable
221238
private fun Preview() {
222239
AppThemeSurface {
@@ -232,33 +249,10 @@ private fun Preview() {
232249
maxLspBalance = 90_000u,
233250
),
234251
isValid = true,
252+
amountInputViewModel = previewAmountInputViewModel(),
253+
isLoading = false,
235254
onBack = {},
236255
onClose = {},
237-
onAmountChange = {},
238-
onContinue = {},
239-
)
240-
}
241-
}
242-
243-
@Preview(showSystemUi = true, device = NEXUS_5)
244-
@Composable
245-
private fun PreviewSmall() {
246-
AppThemeSurface {
247-
Content(
248-
uiState = TransferToSpendingUiState(
249-
order = mockOrder().copy(clientBalanceSat = 50_000u),
250-
receivingAmount = 120_521L,
251-
feeEstimate = 12_461L,
252-
),
253-
transferValues = TransferValues(
254-
defaultLspBalance = 50_000u,
255-
minLspBalance = 10_000u,
256-
maxLspBalance = 90_000u,
257-
),
258-
isValid = true,
259-
onBack = {},
260-
onClose = {},
261-
onAmountChange = {},
262256
onContinue = {},
263257
)
264258
}
@@ -281,9 +275,10 @@ private fun PreviewLoading() {
281275
maxLspBalance = 40_000u,
282276
),
283277
isValid = true,
278+
amountInputViewModel = previewAmountInputViewModel(),
279+
isLoading = true,
284280
onBack = {},
285281
onClose = {},
286-
onAmountChange = {},
287282
onContinue = {},
288283
)
289284
}

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

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,10 @@ private fun SpendingAmountNodeRunning(
225225
HorizontalDivider()
226226
VerticalSpacer(16.dp)
227227

228-
NumberPad(viewModel = amountInputViewModel)
228+
NumberPad(
229+
viewModel = amountInputViewModel,
230+
currencies = currencies,
231+
)
229232

230233
PrimaryButton(
231234
text = stringResource(R.string.common__continue),
@@ -240,6 +243,8 @@ private fun SpendingAmountNodeRunning(
240243
}
241244

242245
@Preview(showSystemUi = true)
246+
@Preview(showSystemUi = true, device = "id:pixel_9_pro_xl", name = "Large")
247+
@Preview(showSystemUi = true, device = NEXUS_5, name = "Small")
243248
@Composable
244249
private fun Preview() {
245250
AppThemeSurface {
@@ -257,25 +262,7 @@ private fun Preview() {
257262
}
258263
}
259264

260-
@Preview(showSystemUi = true, device = NEXUS_5)
261-
@Composable
262-
private fun PreviewSmall() {
263-
AppThemeSurface {
264-
Content(
265-
isNodeRunning = true,
266-
uiState = TransferToSpendingUiState(maxAllowedToSend = 158_234, balanceAfterFee = 158_234),
267-
amountInputViewModel = previewAmountInputViewModel(),
268-
currencies = CurrencyState(),
269-
onBackClick = {},
270-
onCloseClick = {},
271-
onClickQuarter = {},
272-
onClickMaxAmount = {},
273-
onConfirmAmount = {},
274-
)
275-
}
276-
}
277-
278-
@Preview(showSystemUi = true, device = NEXUS_5)
265+
@Preview(showSystemUi = true)
279266
@Composable
280267
private fun PreviewInitializing() {
281268
AppThemeSurface {

app/src/main/java/to/bitkit/viewmodels/AmountInputViewModel.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package to.bitkit.viewmodels
22

3+
import android.annotation.SuppressLint
34
import androidx.compose.runtime.Composable
45
import androidx.lifecycle.ViewModel
56
import androidx.lifecycle.viewModelScope
@@ -411,6 +412,7 @@ data class AmountInputUiState(
411412
val errorKey: String? = null,
412413
)
413414

415+
@SuppressLint("ViewModelConstructorInComposable")
414416
@Composable
415417
fun previewAmountInputViewModel(
416418
sats: Long = 4_567,

0 commit comments

Comments
 (0)