@@ -3,14 +3,10 @@ package to.bitkit.ui.screens.transfer
33import androidx.compose.foundation.layout.Arrangement
44import androidx.compose.foundation.layout.Column
55import androidx.compose.foundation.layout.Row
6- import androidx.compose.foundation.layout.Spacer
76import androidx.compose.foundation.layout.fillMaxSize
87import androidx.compose.foundation.layout.fillMaxWidth
9- import androidx.compose.foundation.layout.height
10- import androidx.compose.foundation.layout.imePadding
118import androidx.compose.foundation.layout.padding
129import androidx.compose.foundation.layout.requiredHeight
13- import androidx.compose.foundation.layout.width
1410import androidx.compose.material3.HorizontalDivider
1511import androidx.compose.runtime.Composable
1612import androidx.compose.runtime.LaunchedEffect
@@ -26,88 +22,117 @@ import androidx.compose.ui.res.stringResource
2622import androidx.compose.ui.tooling.preview.Devices.NEXUS_5
2723import androidx.compose.ui.tooling.preview.Preview
2824import androidx.compose.ui.unit.dp
25+ import androidx.hilt.navigation.compose.hiltViewModel
2926import androidx.lifecycle.compose.collectAsStateWithLifecycle
3027import to.bitkit.R
3128import to.bitkit.ext.mockOrder
29+ import to.bitkit.models.Toast
30+ import to.bitkit.repositories.CurrencyState
3231import to.bitkit.ui.LocalCurrencies
3332import to.bitkit.ui.appViewModel
34- import to.bitkit.ui.components.AmountInput
3533import to.bitkit.ui.components.Caption13Up
3634import to.bitkit.ui.components.Display
35+ import to.bitkit.ui.components.FillHeight
36+ import to.bitkit.ui.components.HorizontalSpacer
3737import to.bitkit.ui.components.MoneySSB
38+ import to.bitkit.ui.components.NumberPad
3839import to.bitkit.ui.components.NumberPadActionButton
40+ import to.bitkit.ui.components.NumberPadTextField
3941import to.bitkit.ui.components.PrimaryButton
42+ import to.bitkit.ui.components.VerticalSpacer
4043import to.bitkit.ui.scaffold.AppTopBar
4144import to.bitkit.ui.scaffold.CloseNavIcon
4245import to.bitkit.ui.scaffold.ScreenColumn
4346import to.bitkit.ui.theme.AppThemeSurface
4447import to.bitkit.ui.theme.Colors
4548import to.bitkit.ui.utils.withAccent
49+ import to.bitkit.viewmodels.AmountInputViewModel
4650import to.bitkit.viewmodels.TransferEffect
4751import to.bitkit.viewmodels.TransferToSpendingUiState
4852import to.bitkit.viewmodels.TransferValues
4953import to.bitkit.viewmodels.TransferViewModel
54+ import to.bitkit.viewmodels.previewAmountInputViewModel
5055
56+ @Suppress(" ViewModelForwarding" )
5157@Composable
5258fun 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
99125private 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
221238private 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 }
0 commit comments