@@ -12,57 +12,84 @@ import androidx.compose.material3.HorizontalDivider
1212import androidx.compose.runtime.Composable
1313import androidx.compose.runtime.LaunchedEffect
1414import androidx.compose.runtime.getValue
15- import androidx.compose.runtime.mutableLongStateOf
16- import androidx.compose.runtime.mutableStateOf
17- import androidx.compose.runtime.remember
18- import androidx.compose.runtime.rememberCoroutineScope
19- import androidx.compose.runtime.saveable.rememberSaveable
20- import androidx.compose.runtime.setValue
2115import androidx.compose.ui.Alignment
2216import androidx.compose.ui.Modifier
23- import androidx.compose.ui.platform.LocalContext
2417import androidx.compose.ui.res.stringResource
18+ import androidx.compose.ui.tooling.preview.Preview
2519import androidx.compose.ui.unit.dp
2620import androidx.lifecycle.compose.collectAsStateWithLifecycle
27- import kotlinx.coroutines.launch
2821import to.bitkit.R
29- import to.bitkit.models.Toast
30- import to.bitkit.ui.LocalBalances
3122import to.bitkit.ui.LocalCurrencies
32- import to.bitkit.ui.appViewModel
33- import to.bitkit.ui.blocktankViewModel
3423import to.bitkit.ui.components.AmountInput
3524import to.bitkit.ui.components.Display
25+ import to.bitkit.ui.components.FillHeight
26+ import to.bitkit.ui.components.FillWidth
3627import to.bitkit.ui.components.MoneySSB
3728import to.bitkit.ui.components.NumberPadActionButton
3829import to.bitkit.ui.components.PrimaryButton
3930import to.bitkit.ui.components.Text13Up
4031import to.bitkit.ui.components.UnitButton
32+ import to.bitkit.ui.components.VerticalSpacer
4133import to.bitkit.ui.scaffold.AppTopBar
4234import to.bitkit.ui.scaffold.CloseNavIcon
4335import to.bitkit.ui.scaffold.ScreenColumn
36+ import to.bitkit.ui.theme.AppThemeSurface
4437import to.bitkit.ui.theme.Colors
4538import to.bitkit.ui.utils.withAccent
46- import to.bitkit.utils.Logger
39+ import to.bitkit.viewmodels.CurrencyUiState
40+ import to.bitkit.viewmodels.TransferEffect
41+ import to.bitkit.viewmodels.TransferToSpendingUiState
4742import to.bitkit.viewmodels.TransferViewModel
48- import kotlin.math.max
49- import kotlin.math.min
50- import kotlin.math.roundToLong
5143
5244@Composable
5345fun SpendingAmountScreen (
5446 viewModel : TransferViewModel ,
5547 onBackClick : () -> Unit = {},
5648 onCloseClick : () -> Unit = {},
5749 onOrderCreated : () -> Unit = {},
50+ toastException : (Throwable ) -> Unit ,
51+ toast : (title: String , description: String ) -> Unit ,
5852) {
59- val scope = rememberCoroutineScope()
60- val app = appViewModel ? : return
61- val blocktank = blocktankViewModel ? : return
6253 val currencies = LocalCurrencies .current
63- val resources = LocalContext .current.resources
64- val transferValues by viewModel.transferValues.collectAsStateWithLifecycle()
54+ val uiState by viewModel.spendingUiState.collectAsStateWithLifecycle()
6555
56+ LaunchedEffect (Unit ) {
57+ viewModel.updateLimits(retry = true )
58+ }
59+
60+ LaunchedEffect (Unit ) {
61+ viewModel.transferEffects.collect { effect ->
62+ when (effect) {
63+ TransferEffect .OnOrderCreated -> onOrderCreated()
64+ is TransferEffect .ToastError -> toast(effect.title, effect.description)
65+ is TransferEffect .ToastException -> toastException(effect.e)
66+ }
67+ }
68+ }
69+
70+ Content (
71+ uiState = uiState,
72+ currencies = currencies,
73+ onBackClick = onBackClick,
74+ onCloseClick = onCloseClick,
75+ onClickQuarter = viewModel::onClickQuarter,
76+ onClickMaxAmount = viewModel::onClickMaxAmount,
77+ onConfirmAmount = viewModel::onConfirmAmount,
78+ onAmountChanged = viewModel::onAmountChanged
79+ )
80+ }
81+
82+ @Composable
83+ private fun Content (
84+ uiState : TransferToSpendingUiState ,
85+ currencies : CurrencyUiState ,
86+ onBackClick : () -> Unit ,
87+ onCloseClick : () -> Unit ,
88+ onClickQuarter : () -> Unit ,
89+ onClickMaxAmount : () -> Unit ,
90+ onConfirmAmount : () -> Unit ,
91+ onAmountChanged : (Long ) -> Unit ,
92+ ) {
6693 ScreenColumn {
6794 AppTopBar (
6895 titleText = stringResource(R .string.lightning__transfer__nav_title),
@@ -75,56 +102,20 @@ fun SpendingAmountScreen(
75102 .fillMaxSize()
76103 .imePadding()
77104 ) {
78- var satsAmount by rememberSaveable { mutableLongStateOf(0 ) }
79- var overrideSats: Long? by remember { mutableStateOf(null ) }
80- var isLoading by remember { mutableStateOf(false ) }
81-
82- val availableAmount = LocalBalances .current.totalOnchainSats - 512u // default tx fee
83- var maxLspFee by remember { mutableStateOf(0uL ) }
84-
85- val feeMaximum = max(0 , availableAmount.toLong() - maxLspFee.toLong())
86- val maximum = min(
87- transferValues.maxClientBalance.toLong(),
88- feeMaximum
89- ) // TODO USE MAX AVAILABLE TO TRANSFER INSTEAD OF MAX ONCHAIN BALANCE
90-
91- // Update maxClientBalance Effect
92- LaunchedEffect (satsAmount) {
93- viewModel.updateTransferValues(satsAmount.toULong())
94- Logger .debug(
95- " satsAmount changed - maxClientBalance: ${transferValues.maxClientBalance} " ,
96- context = " SpendingAmountScreen"
97- )
98- }
99-
100- // Update maxLspFee Effect
101- LaunchedEffect (availableAmount, transferValues.maxLspBalance) { // TODO MOVE TO VIEWMODEL
102- runCatching {
103- val estimate = blocktank.estimateOrderFee(
104- spendingBalanceSats = availableAmount,
105- receivingBalanceSats = transferValues.maxLspBalance,
106- )
107- maxLspFee = estimate.feeSat
108- }
109- }
110-
111- Spacer (modifier = Modifier .height(32 .dp))
105+ VerticalSpacer (32 .dp)
112106 Display (
113107 text = stringResource(R .string.lightning__spending_amount__title)
114108 .withAccent(accentColor = Colors .Purple )
115109 )
116- Spacer (modifier = Modifier .height( 32 .dp) )
110+ VerticalSpacer ( 32 .dp)
117111
118112 AmountInput (
119113 primaryDisplay = currencies.primaryDisplay,
120- overrideSats = overrideSats,
121- onSatsChange = { sats ->
122- satsAmount = sats
123- overrideSats = null
124- },
114+ overrideSats = uiState.overrideSats,
115+ onSatsChange = onAmountChanged,
125116 )
126117
127- Spacer (modifier = Modifier .weight( 1f ) )
118+ FillHeight ( )
128119
129120 // Actions
130121 Row (
@@ -138,64 +129,51 @@ fun SpendingAmountScreen(
138129 color = Colors .White64 ,
139130 )
140131 Spacer (modifier = Modifier .height(8 .dp))
141- MoneySSB (sats = availableAmount.toLong() )
132+ MoneySSB (sats = uiState.balanceAfterFee )
142133 }
143- Spacer (modifier = Modifier .weight( 1f ) )
134+ FillWidth ( )
144135 UnitButton (color = Colors .Purple )
145136 // 25% Button
146137 NumberPadActionButton (
147138 text = stringResource(R .string.lightning__spending_amount__quarter),
148139 color = Colors .Purple ,
149- onClick = {
150- val quarter = (availableAmount.toDouble() / 4.0 ).roundToLong()
151- val amount = min(quarter, maximum)
152- overrideSats = amount
153- },
140+ onClick = onClickQuarter,
154141 )
155142 // Max Button
156143 NumberPadActionButton (
157144 text = stringResource(R .string.common__max),
158145 color = Colors .Purple ,
159- onClick = {
160- overrideSats = maximum
161- },
146+ onClick = onClickMaxAmount,
162147 )
163148 }
164149 HorizontalDivider ()
165- Spacer (modifier = Modifier .height( 16 .dp) )
150+ VerticalSpacer ( 16 .dp)
166151
167152 PrimaryButton (
168153 text = stringResource(R .string.common__continue),
169- onClick = {
170- if (transferValues.maxLspBalance == 0uL ) {
171- app.toast(
172- type = Toast .ToastType .ERROR ,
173- title = resources.getString(R .string.lightning__spending_amount__error_max__title),
174- description = resources.getString(
175- R .string.lightning__spending_amount__error_max__description_zero
176- ),
177- )
178- return @PrimaryButton
179- }
180-
181- isLoading = true
182- scope.launch {
183- try {
184- val order = blocktank.createOrder(satsAmount.toULong())
185- viewModel.onOrderCreated(order)
186- onOrderCreated()
187- } catch (e: Throwable ) {
188- app.toast(e)
189- } finally {
190- isLoading = false
191- }
192- }
193- },
194- enabled = ! isLoading && satsAmount != 0L ,
195- isLoading = isLoading,
154+ onClick = onConfirmAmount,
155+ enabled = uiState.satsAmount != 0L && uiState.satsAmount <= uiState.maxAllowedToSend,
156+ isLoading = uiState.isLoading,
196157 )
197158
198- Spacer (modifier = Modifier .height( 16 .dp) )
159+ VerticalSpacer ( 16 .dp)
199160 }
200161 }
201162}
163+
164+ @Preview(showBackground = true )
165+ @Composable
166+ private fun Preview () {
167+ AppThemeSurface {
168+ Content (
169+ uiState = TransferToSpendingUiState (),
170+ currencies = CurrencyUiState (),
171+ onBackClick = {},
172+ onCloseClick = {},
173+ onClickQuarter = {},
174+ onClickMaxAmount = {},
175+ onConfirmAmount = {},
176+ onAmountChanged = {},
177+ )
178+ }
179+ }
0 commit comments