Skip to content

Commit c45312f

Browse files
committed
refactor: apply separation of concerns in SendAmountScreen
1 parent b3226b3 commit c45312f

File tree

1 file changed

+176
-109
lines changed

1 file changed

+176
-109
lines changed

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

Lines changed: 176 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import to.bitkit.ui.shared.util.gradientBackground
4242
import to.bitkit.ui.theme.AppThemeSurface
4343
import to.bitkit.ui.theme.Colors
4444
import to.bitkit.viewmodels.CurrencyUiState
45+
import to.bitkit.viewmodels.CurrencyViewModel
4546
import to.bitkit.viewmodels.MainUiState
4647
import to.bitkit.viewmodels.SendEvent
4748
import to.bitkit.viewmodels.SendMethod
@@ -57,143 +58,209 @@ fun SendAmountScreen(
5758
onEvent: (SendEvent) -> Unit,
5859
) {
5960
val currencyVM = currencyViewModel ?: return
60-
6161
var input: String by remember { mutableStateOf("") }
6262

63-
LaunchedEffect(currencyUiState.primaryDisplay) {
64-
input = when(currencyUiState.primaryDisplay) {
65-
PrimaryDisplay.BITCOIN -> {
66-
val amountLong = currencyVM.convertFiatToSats(input.toDoubleOrNull() ?: 0.0) ?: 0
67-
if (amountLong > 0.0) amountLong.toString() else ""
68-
}
63+
AmountInputHandler(
64+
input = input,
65+
primaryDisplay = currencyUiState.primaryDisplay,
66+
displayUnit = currencyUiState.displayUnit,
67+
onInputChanged = { newInput -> input = newInput },
68+
onAmountCalculated = { sats -> onEvent(SendEvent.AmountChange(value = sats)) },
69+
currencyVM = currencyVM
70+
)
6971

70-
PrimaryDisplay.FIAT -> {
71-
val convertedAmount = currencyVM.convert(input.toLongOrDefault(0L))
72-
if ((convertedAmount?.value ?: BigDecimal(0)) > BigDecimal(0)) convertedAmount?.formatted.toString() else ""
73-
}
72+
Column(
73+
modifier = Modifier
74+
.fillMaxSize()
75+
.gradientBackground()
76+
) {
77+
SheetTopBar(stringResource(R.string.title_send_amount)) {
78+
onEvent(SendEvent.AmountReset)
79+
onBack()
7480
}
75-
}
7681

77-
LaunchedEffect(input) {
78-
val sats: String = when(currencyUiState.primaryDisplay) {
79-
PrimaryDisplay.BITCOIN -> {
80-
if (currencyUiState.displayUnit == BitcoinDisplayUnit.MODERN) input else (input.toLongOrDefault(0L) * 100_000_000).toString()
82+
when (walletUiState.nodeLifecycleState) {
83+
is NodeLifecycleState.Running -> {
84+
SendAmountContent(
85+
input = input,
86+
uiState = uiState,
87+
currencyUiState = currencyUiState,
88+
onInputChanged = { input = it },
89+
onEvent = onEvent
90+
)
8191
}
82-
83-
PrimaryDisplay.FIAT -> {
84-
val convertedAmount = currencyVM.convertFiatToSats(input.toDoubleOrNull() ?: 0.0) ?: 0L
85-
convertedAmount.toString()
92+
else -> {
93+
SyncNodeView(
94+
modifier = Modifier
95+
.fillMaxWidth()
96+
.weight(1f)
97+
)
8698
}
8799
}
88-
onEvent(SendEvent.AmountChange(value = sats))
100+
}
101+
}
102+
103+
@Composable
104+
private fun SendAmountContent(
105+
input: String,
106+
uiState: SendUiState,
107+
currencyUiState: CurrencyUiState,
108+
onInputChanged: (String) -> Unit,
109+
onEvent: (SendEvent) -> Unit,
110+
) {
111+
val balances = LocalBalances.current
112+
val availableAmount = when (uiState.payMethod) {
113+
SendMethod.ONCHAIN -> balances.totalOnchainSats.toLong()
114+
SendMethod.LIGHTNING -> balances.totalLightningSats.toLong()
89115
}
90116

91117
Column(
92-
modifier = Modifier
93-
.fillMaxSize()
94-
.gradientBackground()
118+
modifier = Modifier.padding(horizontal = 16.dp)
95119
) {
96-
SheetTopBar(stringResource(R.string.title_send_amount)) {
97-
onEvent(SendEvent.AmountReset)
98-
onBack()
120+
Spacer(Modifier.height(16.dp))
121+
122+
NumberPadTextField(input = input, modifier = Modifier.fillMaxWidth())
123+
124+
Spacer(modifier = Modifier.height(24.dp))
125+
Spacer(modifier = Modifier.weight(1f))
126+
127+
Text13Up(
128+
text = stringResource(R.string.wallet__send_available),
129+
color = Colors.White64,
130+
)
131+
Spacer(modifier = Modifier.height(4.dp))
132+
133+
Row(
134+
verticalAlignment = Alignment.CenterVertically,
135+
) {
136+
MoneySSB(sats = availableAmount.toLong())
137+
138+
Spacer(modifier = Modifier.weight(1f))
139+
140+
PaymentMethodButton(uiState = uiState, onEvent = onEvent)
141+
Spacer(modifier = Modifier.width(8.dp))
142+
UnitButton(modifier = Modifier.height(28.dp))
99143
}
100144

101-
if (walletUiState.nodeLifecycleState is NodeLifecycleState.Running) {
102-
Spacer(Modifier.height(16.dp))
145+
HorizontalDivider(modifier = Modifier.padding(vertical = 24.dp))
103146

104-
Column(
105-
modifier = Modifier.padding(horizontal = 16.dp)
106-
) {
107-
NumberPadTextField(input = input, modifier = Modifier.fillMaxWidth())
147+
Keyboard(
148+
onClick = { number ->
149+
onInputChanged(if (input == "0") number else input + number)
150+
},
151+
onClickBackspace = {
152+
onInputChanged(if (input.length > 1) input.dropLast(1) else "0")
153+
},
154+
isDecimal = currencyUiState.primaryDisplay == PrimaryDisplay.FIAT,
155+
modifier = Modifier.fillMaxWidth(),
156+
)
108157

109-
Spacer(modifier = Modifier.height(24.dp))
110-
Spacer(modifier = Modifier.weight(1f))
158+
Spacer(modifier = Modifier.height(41.dp))
111159

112-
Text13Up(
113-
text = stringResource(R.string.wallet__send_available),
114-
color = Colors.White64,
115-
)
116-
Spacer(modifier = Modifier.height(4.dp))
117-
118-
Row(
119-
verticalAlignment = Alignment.CenterVertically,
120-
) {
121-
val balances = LocalBalances.current
122-
val availableAmount = when (uiState.payMethod) {
123-
SendMethod.ONCHAIN -> balances.totalOnchainSats.toLong()
124-
SendMethod.LIGHTNING -> balances.totalLightningSats.toLong()
125-
}
126-
MoneySSB(sats = availableAmount.toLong())
127-
128-
Spacer(modifier = Modifier.weight(1f))
129-
130-
OutlinedColorButton(
131-
onClick = { onEvent(SendEvent.PaymentMethodSwitch) },
132-
enabled = uiState.isUnified,
133-
color = when (uiState.payMethod) {
134-
SendMethod.ONCHAIN -> Colors.Brand
135-
SendMethod.LIGHTNING -> Colors.Purple
136-
},
137-
modifier = Modifier.height(28.dp)
138-
) {
139-
Text13Up(
140-
text = when (uiState.payMethod) {
141-
SendMethod.ONCHAIN -> stringResource(R.string.savings)
142-
SendMethod.LIGHTNING -> stringResource(R.string.spending)
143-
},
144-
color = when (uiState.payMethod) {
145-
SendMethod.ONCHAIN -> Colors.Brand
146-
SendMethod.LIGHTNING -> Colors.Purple
147-
}
148-
)
149-
}
150-
Spacer(modifier = Modifier.width(8.dp))
151-
UnitButton(
152-
modifier = Modifier.height(28.dp)
153-
)
154-
}
155-
156-
HorizontalDivider(modifier = Modifier.padding(vertical = 24.dp))
157-
158-
Keyboard(
159-
onClick = { number ->
160-
if (input == "0") input = number else input+=number
161-
},
162-
onClickBackspace = {
163-
input = if (input.length > 1) input.dropLast(1) else "0"
164-
},
165-
isDecimal = currencyUiState.primaryDisplay == PrimaryDisplay.FIAT,
166-
modifier = Modifier.fillMaxWidth(),
167-
)
160+
PrimaryButton(
161+
text = stringResource(R.string.continue_button),
162+
enabled = uiState.isAmountInputValid,
163+
onClick = { onEvent(SendEvent.AmountContinue(uiState.amountInput)) },
164+
)
168165

169-
Spacer(modifier = Modifier.height(41.dp))
166+
Spacer(modifier = Modifier.height(16.dp))
167+
}
168+
}
170169

171-
PrimaryButton(
172-
text = stringResource(R.string.continue_button),
173-
enabled = uiState.isAmountInputValid,
174-
onClick = { onEvent(SendEvent.AmountContinue(uiState.amountInput)) },
175-
)
170+
@Composable
171+
private fun PaymentMethodButton(
172+
uiState: SendUiState,
173+
onEvent: (SendEvent) -> Unit,
174+
) {
175+
OutlinedColorButton(
176+
onClick = { onEvent(SendEvent.PaymentMethodSwitch) },
177+
enabled = uiState.isUnified,
178+
color = when (uiState.payMethod) {
179+
SendMethod.ONCHAIN -> Colors.Brand
180+
SendMethod.LIGHTNING -> Colors.Purple
181+
},
182+
modifier = Modifier.height(28.dp)
183+
) {
184+
Text13Up(
185+
text = when (uiState.payMethod) {
186+
SendMethod.ONCHAIN -> stringResource(R.string.savings)
187+
SendMethod.LIGHTNING -> stringResource(R.string.spending)
188+
},
189+
color = when (uiState.payMethod) {
190+
SendMethod.ONCHAIN -> Colors.Brand
191+
SendMethod.LIGHTNING -> Colors.Purple
192+
}
193+
)
194+
}
195+
}
196+
197+
@Composable
198+
private fun AmountInputHandler(
199+
input: String,
200+
primaryDisplay: PrimaryDisplay,
201+
displayUnit: BitcoinDisplayUnit,
202+
onInputChanged: (String) -> Unit,
203+
onAmountCalculated: (String) -> Unit,
204+
currencyVM: CurrencyViewModel
205+
) {
206+
LaunchedEffect(primaryDisplay) {
207+
val newInput = when(primaryDisplay) {
208+
PrimaryDisplay.BITCOIN -> {
209+
val amountLong = currencyVM.convertFiatToSats(input.toDoubleOrNull() ?: 0.0) ?: 0
210+
if (amountLong > 0.0) amountLong.toString() else ""
211+
}
212+
PrimaryDisplay.FIAT -> {
213+
val convertedAmount = currencyVM.convert(input.toLongOrDefault(0L))
214+
if ((convertedAmount?.value ?: BigDecimal(0)) > BigDecimal(0)) convertedAmount?.formatted.toString() else ""
215+
}
216+
}
217+
onInputChanged(newInput)
218+
}
176219

177-
Spacer(modifier = Modifier.height(16.dp))
220+
LaunchedEffect(input) {
221+
val sats = when(primaryDisplay) {
222+
PrimaryDisplay.BITCOIN -> {
223+
if (displayUnit == BitcoinDisplayUnit.MODERN) input else (input.toLongOrDefault(0L) * 100_000_000).toString()
224+
}
225+
PrimaryDisplay.FIAT -> {
226+
val convertedAmount = currencyVM.convertFiatToSats(input.toDoubleOrNull() ?: 0.0) ?: 0L
227+
convertedAmount.toString()
178228
}
179-
} else {
180-
SyncNodeView(
181-
modifier = Modifier
182-
.fillMaxWidth()
183-
.weight(1f)
184-
)
185229
}
230+
onAmountCalculated(sats)
186231
}
187232
}
188233

189-
@Preview(showBackground = true)
234+
@Preview(showBackground = true, name = "Running - Lightning")
190235
@Composable
191-
private fun Preview1() {
236+
private fun PreviewRunningLightning() {
192237
AppThemeSurface {
193238
SendAmountScreen(
194239
uiState = SendUiState(
195240
payMethod = SendMethod.LIGHTNING,
196-
amountInput = "100"
241+
amountInput = "100",
242+
isAmountInputValid = true,
243+
isUnified = true
244+
),
245+
walletUiState = MainUiState(
246+
nodeLifecycleState = NodeLifecycleState.Running
247+
),
248+
onBack = {},
249+
onEvent = {},
250+
)
251+
}
252+
}
253+
254+
@Preview(showBackground = true, name = "Running - Onchain")
255+
@Composable
256+
private fun PreviewRunningOnchain() {
257+
AppThemeSurface {
258+
SendAmountScreen(
259+
uiState = SendUiState(
260+
payMethod = SendMethod.ONCHAIN,
261+
amountInput = "5000",
262+
isAmountInputValid = true,
263+
isUnified = true
197264
),
198265
walletUiState = MainUiState(
199266
nodeLifecycleState = NodeLifecycleState.Running
@@ -204,9 +271,9 @@ private fun Preview1() {
204271
}
205272
}
206273

207-
@Preview(showBackground = true)
274+
@Preview(showBackground = true, name = "Initializing")
208275
@Composable
209-
private fun Preview2() {
276+
private fun PreviewInitializing() {
210277
AppThemeSurface {
211278
SendAmountScreen(
212279
uiState = SendUiState(

0 commit comments

Comments
 (0)