commit 37e9730c4e985d4ed05d6e4f5e62d1ccc870f35c
parent 28cdacb6e6a3a131abb0dc71e188eb62d57dc3bf
Author: Iván Ávalos <avalos@disroot.org>
Date: Tue, 10 Sep 2024 12:18:19 +0200
[wallet] deposit x-taler-bank host selection
Diffstat:
5 files changed, 272 insertions(+), 163 deletions(-)
diff --git a/wallet/src/main/java/net/taler/wallet/deposit/DepositFragment.kt b/wallet/src/main/java/net/taler/wallet/deposit/DepositFragment.kt
@@ -67,6 +67,7 @@ class DepositFragment : Fragment() {
TalerSurface {
val state = depositManager.depositState.collectAsStateLifecycleAware()
val wireTypes = remember { mutableStateListOf<WireType>() }
+ val talerBankHostnames = remember { mutableStateListOf<String>() }
val coroutine = rememberCoroutineScope()
if (amount.currency == CURRENCY_BTC) MakeBitcoinDepositComposable(
@@ -79,6 +80,7 @@ class DepositFragment : Fragment() {
) else MakeDepositComposable(
state = state.value,
supportedWireTypes = wireTypes,
+ talerBankHostnames = talerBankHostnames,
amount = amount.withSpec(spec),
presetName = receiverName,
presetIban = iban,
@@ -89,10 +91,15 @@ class DepositFragment : Fragment() {
LaunchedEffect(Unit) {
coroutine.launch {
- scopeInfo?.let {
+ scopeInfo?.let { scopeInfo ->
depositManager
- .getDepositWireTypesForCurrency(it)
- ?.let { types -> wireTypes.addAll(types) }
+ .getDepositWireTypesForCurrency(scopeInfo)
+ ?.let { result ->
+ wireTypes.addAll(result.wireTypes)
+ talerBankHostnames.addAll(result.wireTypeDetails.flatMap {
+ it.talerBankHostnames
+ }.distinct())
+ }
}
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/deposit/DepositManager.kt b/wallet/src/main/java/net/taler/wallet/deposit/DepositManager.kt
@@ -154,15 +154,15 @@ class DepositManager(
return response
}
- suspend fun getDepositWireTypesForCurrency(scopeInfo: ScopeInfo): List<WireType>? {
- var result: List<WireType>? = null
+ suspend fun getDepositWireTypesForCurrency(scopeInfo: ScopeInfo): GetDepositWireTypesForCurrencyResponse? {
+ var result: GetDepositWireTypesForCurrencyResponse? = null
api.request("getDepositWireTypesForCurrency", GetDepositWireTypesForCurrencyResponse.serializer()) {
put("currency", scopeInfo.currency)
put("scopeInfo", JSONObject(BackendManager.json.encodeToString(scopeInfo)))
}.onError {
Log.e(TAG, "Error getDepositWireTypesForCurrency $it")
}.onSuccess {
- result = it.wireTypes
+ result = it
}
return result
}
@@ -188,6 +188,7 @@ data class CreateDepositGroupResponse(
@Serializable
data class GetDepositWireTypesForCurrencyResponse(
val wireTypes: List<WireType>,
+ val wireTypeDetails: List<WireTypeDetails>,
)
@Serializable
@@ -199,4 +200,10 @@ enum class WireType {
@SerialName("x-taler-bank")
TalerBank,
-}
-\ No newline at end of file
+}
+
+@Serializable
+data class WireTypeDetails(
+ val paymentTargetType: WireType,
+ val talerBankHostnames: List<String>,
+)
+\ No newline at end of file
diff --git a/wallet/src/main/java/net/taler/wallet/deposit/MakeDepositComposable.kt b/wallet/src/main/java/net/taler/wallet/deposit/MakeDepositComposable.kt
@@ -24,13 +24,11 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.ScrollableTabRow
import androidx.compose.material3.Surface
import androidx.compose.material3.Tab
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -39,9 +37,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier
-import androidx.compose.ui.focus.FocusRequester
-import androidx.compose.ui.focus.focusRequester
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@@ -58,6 +53,7 @@ import net.taler.wallet.transactions.TransactionAmountComposable
fun MakeDepositComposable(
state: DepositState,
supportedWireTypes: List<WireType>,
+ talerBankHostnames: List<String>,
amount: Amount,
presetName: String? = null,
presetIban: String? = null,
@@ -93,7 +89,7 @@ fun MakeDepositComposable(
var ibanName by rememberSaveable { mutableStateOf(presetName ?: "") }
var ibanIban by rememberSaveable { mutableStateOf(presetIban ?: "") }
var talerName by rememberSaveable { mutableStateOf(presetName ?: "") }
- var talerHost by rememberSaveable { mutableStateOf("") }
+ var talerHost by rememberSaveable { mutableStateOf(talerBankHostnames.firstOrNull() ?: "") }
var talerAccount by rememberSaveable { mutableStateOf("") }
when(selectedWireType) {
@@ -101,7 +97,7 @@ fun MakeDepositComposable(
var ibanError by rememberSaveable { mutableStateOf(false) }
val coroutineScope = rememberCoroutineScope()
- MakeDepositIbanForm(
+ MakeDepositIBAN(
name = ibanName,
iban = ibanIban,
state = state,
@@ -118,10 +114,11 @@ fun MakeDepositComposable(
)
}
- WireType.TalerBank -> MakeDepositTalerBankForm(
+ WireType.TalerBank -> MakeDepositTaler(
name = talerName,
host = talerHost,
account = talerAccount,
+ supportedHosts = talerBankHostnames,
state = state,
onFormEdited = { name, host, account ->
talerName = name
@@ -232,153 +229,6 @@ fun MakeDepositWireTypeChooser(
}
}
-@Composable
-fun MakeDepositIbanForm(
- state: DepositState,
- name: String,
- iban: String,
- ibanError: Boolean,
- onFormEdited: (name: String, iban: String) -> Unit
-) {
- val focusRequester = remember { FocusRequester() }
-
- OutlinedTextField(
- modifier = Modifier
- .padding(16.dp)
- .focusRequester(focusRequester)
- .fillMaxWidth(),
- value = name,
- enabled = !state.showFees,
- onValueChange = { input ->
- onFormEdited(input, iban)
- },
- singleLine = true,
- isError = name.isBlank(),
- label = {
- Text(
- stringResource(R.string.send_deposit_name),
- color = if (name.isBlank()) {
- MaterialTheme.colorScheme.error
- } else Color.Unspecified,
- )
- }
- )
-
- LaunchedEffect(Unit) {
- focusRequester.requestFocus()
- }
-
- OutlinedTextField(
- modifier = Modifier
- .padding(horizontal = 16.dp)
- .fillMaxWidth(),
- value = iban,
- singleLine = true,
- enabled = !state.showFees,
- onValueChange = { input ->
- onFormEdited(name, input.uppercase())
-
- },
- isError = ibanError,
- supportingText = {
- if (ibanError) {
- Text(
- modifier = Modifier.fillMaxWidth(),
- text = stringResource(R.string.send_deposit_iban_error),
- color = MaterialTheme.colorScheme.error
- )
- }
- },
- label = {
- Text(
- text = stringResource(R.string.send_deposit_iban),
- color = if (ibanError) {
- MaterialTheme.colorScheme.error
- } else Color.Unspecified,
- )
- }
- )
-}
-
-@Composable
-fun MakeDepositTalerBankForm(
- state: DepositState,
- name: String,
- host: String,
- account: String,
- onFormEdited: (name: String, host: String, account: String) -> Unit
-) {
- val focusRequester = remember { FocusRequester() }
-
- OutlinedTextField(
- modifier = Modifier
- .padding(16.dp)
- .focusRequester(focusRequester)
- .fillMaxWidth(),
- value = name,
- enabled = !state.showFees,
- onValueChange = { input ->
- onFormEdited(input, host, account)
- },
- singleLine = true,
- isError = name.isBlank(),
- label = {
- Text(
- stringResource(R.string.send_deposit_name),
- color = if (name.isBlank()) {
- MaterialTheme.colorScheme.error
- } else Color.Unspecified,
- )
- }
- )
-
- LaunchedEffect(Unit) {
- focusRequester.requestFocus()
- }
-
- OutlinedTextField(
- modifier = Modifier
- .padding(horizontal = 16.dp)
- .fillMaxWidth(),
- value = host,
- enabled = !state.showFees,
- onValueChange = { input ->
- onFormEdited(name, input, account)
- },
- singleLine = true,
- isError = host.isBlank(),
- label = {
- Text(
- stringResource(R.string.send_deposit_host),
- color = if (host.isBlank()) {
- MaterialTheme.colorScheme.error
- } else Color.Unspecified,
- )
- }
- )
-
- OutlinedTextField(
- modifier = Modifier
- .padding(16.dp)
- .fillMaxWidth(),
- value = account,
- singleLine = true,
- enabled = !state.showFees,
- onValueChange = { input ->
- onFormEdited(name, host, input)
- },
- isError = account.isBlank(),
- label = {
- Text(
- text = stringResource(R.string.send_deposit_account),
- color = if (account.isBlank()) {
- MaterialTheme.colorScheme.error
- } else Color.Unspecified,
- )
- }
- )
-}
-
@Preview
@Composable
fun PreviewMakeDepositComposable() {
@@ -390,6 +240,7 @@ fun PreviewMakeDepositComposable() {
MakeDepositComposable(
state = state,
supportedWireTypes = listOf(WireType.TalerBank, WireType.IBAN),
+ talerBankHostnames = listOf("bank.demo.taler.net", "bank.test.taler.net"),
amount = Amount.fromString("TESTKUDOS", "42.23"),
validateIban = { true },
onMakeIbanDeposit = { _, _, _ -> },
diff --git a/wallet/src/main/java/net/taler/wallet/deposit/MakeDepositIBAN.kt b/wallet/src/main/java/net/taler/wallet/deposit/MakeDepositIBAN.kt
@@ -0,0 +1,101 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2024 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.wallet.deposit
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import net.taler.wallet.R
+
+@Composable
+fun MakeDepositIBAN(
+ state: DepositState,
+ name: String,
+ iban: String,
+ ibanError: Boolean,
+ onFormEdited: (name: String, iban: String) -> Unit
+) {
+ val focusRequester = remember { FocusRequester() }
+
+ OutlinedTextField(
+ modifier = Modifier
+ .padding(16.dp)
+ .focusRequester(focusRequester)
+ .fillMaxWidth(),
+ value = name,
+ enabled = !state.showFees,
+ onValueChange = { input ->
+ onFormEdited(input, iban)
+ },
+ singleLine = true,
+ isError = name.isBlank(),
+ label = {
+ Text(
+ stringResource(R.string.send_deposit_name),
+ color = if (name.isBlank()) {
+ MaterialTheme.colorScheme.error
+ } else Color.Unspecified,
+ )
+ }
+ )
+
+ LaunchedEffect(Unit) {
+ focusRequester.requestFocus()
+ }
+
+ OutlinedTextField(
+ modifier = Modifier
+ .padding(horizontal = 16.dp)
+ .fillMaxWidth(),
+ value = iban,
+ singleLine = true,
+ enabled = !state.showFees,
+ onValueChange = { input ->
+ onFormEdited(name, input.uppercase())
+
+ },
+ isError = ibanError,
+ supportingText = {
+ if (ibanError) {
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ text = stringResource(R.string.send_deposit_iban_error),
+ color = MaterialTheme.colorScheme.error
+ )
+ }
+ },
+ label = {
+ Text(
+ text = stringResource(R.string.send_deposit_iban),
+ color = if (ibanError) {
+ MaterialTheme.colorScheme.error
+ } else Color.Unspecified,
+ )
+ }
+ )
+}
+\ No newline at end of file
diff --git a/wallet/src/main/java/net/taler/wallet/deposit/MakeDepositTaler.kt b/wallet/src/main/java/net/taler/wallet/deposit/MakeDepositTaler.kt
@@ -0,0 +1,141 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2024 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.wallet.deposit
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExposedDropdownMenuBox
+import androidx.compose.material3.ExposedDropdownMenuDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import net.taler.wallet.R
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun MakeDepositTaler(
+ state: DepositState,
+ supportedHosts: List<String>,
+ name: String,
+ host: String,
+ account: String,
+ onFormEdited: (name: String, host: String, account: String) -> Unit
+) {
+ val focusRequester = remember { FocusRequester() }
+
+ OutlinedTextField(
+ modifier = Modifier
+ .padding(16.dp)
+ .focusRequester(focusRequester)
+ .fillMaxWidth(),
+ value = name,
+ enabled = !state.showFees,
+ onValueChange = { input ->
+ onFormEdited(input, host, account)
+ },
+ singleLine = true,
+ isError = name.isBlank(),
+ label = {
+ Text(
+ stringResource(R.string.send_deposit_name),
+ color = if (name.isBlank()) {
+ MaterialTheme.colorScheme.error
+ } else Color.Unspecified,
+ )
+ }
+ )
+
+ LaunchedEffect(Unit) {
+ focusRequester.requestFocus()
+ }
+
+ var expanded by remember { mutableStateOf(false) }
+ ExposedDropdownMenuBox(
+ expanded = expanded,
+ onExpandedChange = { expanded = it },
+ ) {
+ OutlinedTextField(
+ modifier = Modifier
+ .padding(horizontal = 16.dp)
+ .fillMaxWidth()
+ .menuAnchor(),
+ readOnly = true,
+ enabled = !state.showFees,
+ value = host,
+ trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded) },
+ onValueChange = {},
+ label = {
+ Text(
+ stringResource(R.string.send_deposit_host),
+ color = if (host.isBlank()) {
+ MaterialTheme.colorScheme.error
+ } else Color.Unspecified,
+ )
+ },
+ )
+
+ ExposedDropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false },
+ ) {
+ supportedHosts.forEach {
+ DropdownMenuItem(
+ text = { Text(it) },
+ onClick = {
+ onFormEdited(name, it, account)
+ expanded = false
+ },
+ )
+ }
+ }
+ }
+
+ OutlinedTextField(
+ modifier = Modifier
+ .padding(16.dp)
+ .fillMaxWidth(),
+ value = account,
+ singleLine = true,
+ enabled = !state.showFees,
+ onValueChange = { input ->
+ onFormEdited(name, host, input)
+ },
+ isError = account.isBlank(),
+ label = {
+ Text(
+ text = stringResource(R.string.send_deposit_account),
+ color = if (account.isBlank()) {
+ MaterialTheme.colorScheme.error
+ } else Color.Unspecified,
+ )
+ }
+ )
+}
+\ No newline at end of file