@@ -16,23 +16,21 @@ import androidx.compose.runtime.Composable
1616import androidx.compose.runtime.LaunchedEffect
1717import androidx.compose.runtime.collectAsState
1818import androidx.compose.runtime.getValue
19- import androidx.compose.runtime.mutableLongStateOf
2019import androidx.compose.runtime.mutableStateOf
2120import androidx.compose.runtime.remember
22- import androidx.compose.runtime.rememberCoroutineScope
23- import androidx.compose.runtime.saveable.rememberSaveable
2421import androidx.compose.runtime.setValue
2522import androidx.compose.ui.Alignment
2623import androidx.compose.ui.Modifier
2724import androidx.compose.ui.platform.testTag
2825import androidx.compose.ui.res.stringResource
26+ import androidx.compose.ui.tooling.preview.Devices.NEXUS_5
27+ import androidx.compose.ui.tooling.preview.Preview
2928import androidx.compose.ui.unit.dp
3029import androidx.lifecycle.compose.collectAsStateWithLifecycle
31- import kotlinx.coroutines.launch
3230import to.bitkit.R
31+ import to.bitkit.ext.mockOrder
3332import to.bitkit.ui.LocalCurrencies
3433import to.bitkit.ui.appViewModel
35- import to.bitkit.ui.blocktankViewModel
3634import to.bitkit.ui.components.AmountInput
3735import to.bitkit.ui.components.Caption13Up
3836import to.bitkit.ui.components.Display
@@ -42,8 +40,12 @@ import to.bitkit.ui.components.PrimaryButton
4240import to.bitkit.ui.scaffold.AppTopBar
4341import to.bitkit.ui.scaffold.CloseNavIcon
4442import to.bitkit.ui.scaffold.ScreenColumn
43+ import to.bitkit.ui.theme.AppThemeSurface
4544import to.bitkit.ui.theme.Colors
4645import to.bitkit.ui.utils.withAccent
46+ import to.bitkit.viewmodels.TransferEffect
47+ import to.bitkit.viewmodels.TransferToSpendingUiState
48+ import to.bitkit.viewmodels.TransferValues
4749import to.bitkit.viewmodels.TransferViewModel
4850
4951@Composable
@@ -53,18 +55,64 @@ fun SpendingAdvancedScreen(
5355 onCloseClick : () -> Unit = {},
5456 onOrderCreated : () -> Unit = {},
5557) {
56- val scope = rememberCoroutineScope()
5758 val app = appViewModel ? : return
58- val blocktank = blocktankViewModel ? : return
59- val currencies = LocalCurrencies .current
6059 val state by viewModel.spendingUiState.collectAsStateWithLifecycle()
6160 val order = state.order ? : return
61+ val transferValues by viewModel.transferValues.collectAsState()
62+
63+ LaunchedEffect (order.clientBalanceSat) {
64+ viewModel.updateTransferValues(order.clientBalanceSat)
65+ }
66+
67+ LaunchedEffect (Unit ) {
68+ viewModel.transferEffects.collect { effect ->
69+ when (effect) {
70+ 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+ )
77+ }
78+ }
79+ }
80+
81+ 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
85+ }
86+
87+ Content (
88+ uiState = state,
89+ transferValues = transferValues,
90+ isValid = isValid,
91+ onBack = onBackClick,
92+ onClose = onCloseClick,
93+ onAmountChange = viewModel::onReceivingAmountChange,
94+ onContinue = viewModel::onSpendingAdvancedContinue,
95+ )
96+ }
97+
98+ @Composable
99+ private fun Content (
100+ uiState : TransferToSpendingUiState ,
101+ transferValues : TransferValues ,
102+ isValid : Boolean ,
103+ onBack : () -> Unit ,
104+ onClose : () -> Unit ,
105+ onAmountChange : (Long ) -> Unit ,
106+ onContinue : () -> Unit ,
107+ ) {
108+ val currencies = LocalCurrencies .current
109+ uiState.order ? : return
62110
63111 ScreenColumn {
64112 AppTopBar (
65113 titleText = stringResource(R .string.lightning__transfer__nav_title),
66- onBackClick = onBackClick ,
67- actions = { CloseNavIcon (onCloseClick ) },
114+ onBackClick = onBack ,
115+ actions = { CloseNavIcon (onClose ) },
68116 )
69117 Column (
70118 modifier = Modifier
@@ -73,38 +121,9 @@ fun SpendingAdvancedScreen(
73121 .imePadding()
74122 .testTag(" SpendingAdvanced" )
75123 ) {
76- var receivingSatsAmount by rememberSaveable { mutableLongStateOf(0 ) }
77124 var overrideSats: Long? by remember { mutableStateOf(null ) }
78-
79- val clientBalance = order.clientBalanceSat
80- var feeEstimate: Long? by remember { mutableStateOf(null ) }
81125 var isLoading by remember { mutableStateOf(false ) }
82126
83- val transferValues by viewModel.transferValues.collectAsState()
84-
85- LaunchedEffect (clientBalance) {
86- viewModel.updateTransferValues(clientBalance)
87- }
88-
89- val isValid = transferValues.let {
90- val isAboveMin = receivingSatsAmount.toULong() >= it.minLspBalance
91- val isBelowMax = receivingSatsAmount.toULong() <= it.maxLspBalance
92- isAboveMin && isBelowMax
93- }
94-
95- // Update feeEstimate
96- LaunchedEffect (receivingSatsAmount, transferValues) {
97- feeEstimate = null
98- if (! isValid) return @LaunchedEffect
99- runCatching {
100- val estimate = blocktank.estimateOrderFee(
101- spendingBalanceSats = clientBalance,
102- receivingBalanceSats = receivingSatsAmount.toULong(),
103- )
104- feeEstimate = estimate.feeSat.toLong()
105- }
106- }
107-
108127 Spacer (modifier = Modifier .height(32 .dp))
109128 Display (
110129 text = stringResource(R .string.lightning__spending_advanced__title)
@@ -113,10 +132,11 @@ fun SpendingAdvancedScreen(
113132 Spacer (modifier = Modifier .height(32 .dp))
114133
115134 AmountInput (
135+ defaultValue = uiState.receivingAmount,
116136 primaryDisplay = currencies.primaryDisplay,
117137 overrideSats = overrideSats,
118138 onSatsChange = { sats ->
119- receivingSatsAmount = sats
139+ onAmountChange( sats)
120140 overrideSats = null
121141 },
122142 modifier = Modifier .testTag(" SpendingAdvancedNumberField" )
@@ -132,7 +152,7 @@ fun SpendingAdvancedScreen(
132152 color = Colors .White64 ,
133153 )
134154 Spacer (modifier = Modifier .width(4 .dp))
135- feeEstimate?.let {
155+ uiState. feeEstimate?.let {
136156 MoneySSB (it)
137157 } ? : run {
138158 Caption13Up (text = " —" , color = Colors .White64 )
@@ -184,20 +204,7 @@ fun SpendingAdvancedScreen(
184204 text = stringResource(R .string.common__continue),
185205 onClick = {
186206 isLoading = true
187- scope.launch {
188- try {
189- val newOrder = blocktank.createOrder(
190- spendingBalanceSats = clientBalance,
191- receivingBalanceSats = receivingSatsAmount.toULong(),
192- )
193- viewModel.onAdvancedOrderCreated(newOrder)
194- onOrderCreated()
195- } catch (e: Throwable ) {
196- app.toast(e)
197- } finally {
198- isLoading = false
199- }
200- }
207+ onContinue()
201208 },
202209 enabled = ! isLoading && isValid,
203210 isLoading = isLoading,
@@ -208,3 +215,76 @@ fun SpendingAdvancedScreen(
208215 }
209216 }
210217}
218+
219+ @Preview(showSystemUi = true )
220+ @Composable
221+ private fun Preview () {
222+ AppThemeSurface {
223+ Content (
224+ uiState = TransferToSpendingUiState (
225+ order = mockOrder().copy(clientBalanceSat = 100_000u ),
226+ receivingAmount = 55_000L ,
227+ feeEstimate = 2_500L ,
228+ ),
229+ transferValues = TransferValues (
230+ defaultLspBalance = 50_000u ,
231+ minLspBalance = 10_000u ,
232+ maxLspBalance = 90_000u ,
233+ ),
234+ isValid = true ,
235+ onBack = {},
236+ 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 = {},
262+ onContinue = {},
263+ )
264+ }
265+ }
266+
267+ @Preview(showSystemUi = true )
268+ @Composable
269+ private fun PreviewLoading () {
270+ AppThemeSurface {
271+ Content (
272+ uiState = TransferToSpendingUiState (
273+ order = mockOrder().copy(clientBalanceSat = 50_000u ),
274+ receivingAmount = 20_000L ,
275+ feeEstimate = null ,
276+ isLoading = true ,
277+ ),
278+ transferValues = TransferValues (
279+ defaultLspBalance = 25_000u ,
280+ minLspBalance = 10_000u ,
281+ maxLspBalance = 40_000u ,
282+ ),
283+ isValid = true ,
284+ onBack = {},
285+ onClose = {},
286+ onAmountChange = {},
287+ onContinue = {},
288+ )
289+ }
290+ }
0 commit comments