Implemented doing the same transfer again or transfer money to the recipient when long pressing a TransactionListItem
This commit is contained in:
parent
ac308700c0
commit
418c188eb6
|
@ -23,6 +23,7 @@ import net.codinux.banking.ui.composables.NavigationMenuItem
|
||||||
import net.codinux.banking.ui.composables.settings.UiSettings
|
import net.codinux.banking.ui.composables.settings.UiSettings
|
||||||
import net.codinux.banking.ui.config.Colors
|
import net.codinux.banking.ui.config.Colors
|
||||||
import net.codinux.banking.ui.config.DI
|
import net.codinux.banking.ui.config.DI
|
||||||
|
import net.codinux.banking.ui.model.ShowTransferMoneyDialogData
|
||||||
import org.jetbrains.compose.resources.imageResource
|
import org.jetbrains.compose.resources.imageResource
|
||||||
|
|
||||||
private val uiState = DI.uiState
|
private val uiState = DI.uiState
|
||||||
|
@ -99,7 +100,7 @@ fun SideMenuContent() {
|
||||||
|
|
||||||
NavigationMenuItem(itemModifier, "Neue Überweisung", textColor, horizontalPadding = ItemHorizontalPadding,
|
NavigationMenuItem(itemModifier, "Neue Überweisung", textColor, horizontalPadding = ItemHorizontalPadding,
|
||||||
icon = { Icon(Icons.Filled.Add, "Konto hinzufügen", Modifier.size(iconSize), tint = textColor) }) {
|
icon = { Icon(Icons.Filled.Add, "Konto hinzufügen", Modifier.size(iconSize), tint = textColor) }) {
|
||||||
uiState.showTransferMoneyDialog.value = true
|
uiState.showTransferMoneyDialogData.value = ShowTransferMoneyDialogData()
|
||||||
|
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
drawerState.close()
|
drawerState.close()
|
||||||
|
|
|
@ -14,7 +14,7 @@ private val formatUtil = DI.formatUtil
|
||||||
@Composable
|
@Composable
|
||||||
fun StateHandler(uiState: UiState, snackbarHostState: SnackbarHostState) {
|
fun StateHandler(uiState: UiState, snackbarHostState: SnackbarHostState) {
|
||||||
val showAddAccountDialog by uiState.showAddAccountDialog.collectAsState()
|
val showAddAccountDialog by uiState.showAddAccountDialog.collectAsState()
|
||||||
val showTransferMoneyDialog by uiState.showTransferMoneyDialog.collectAsState()
|
val showTransferMoneyDialogData by uiState.showTransferMoneyDialogData.collectAsState()
|
||||||
val showExportScreen by uiState.showExportScreen.collectAsState()
|
val showExportScreen by uiState.showExportScreen.collectAsState()
|
||||||
|
|
||||||
val tanChallengeReceived by uiState.tanChallengeReceived.collectAsState()
|
val tanChallengeReceived by uiState.tanChallengeReceived.collectAsState()
|
||||||
|
@ -28,8 +28,8 @@ fun StateHandler(uiState: UiState, snackbarHostState: SnackbarHostState) {
|
||||||
AddAccountDialog { uiState.showAddAccountDialog.value = false }
|
AddAccountDialog { uiState.showAddAccountDialog.value = false }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showTransferMoneyDialog) {
|
showTransferMoneyDialogData?.let { data ->
|
||||||
TransferMoneyDialog { uiState.showTransferMoneyDialog.value = false }
|
TransferMoneyDialog(data) { uiState.showTransferMoneyDialogData.value = null }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showExportScreen) {
|
if (showExportScreen) {
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
package net.codinux.banking.ui.composables.transactions
|
package net.codinux.banking.ui.composables.transactions
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.Divider
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.DpOffset
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import net.codinux.banking.client.model.UserAccount
|
import net.codinux.banking.client.model.UserAccount
|
||||||
import net.codinux.banking.ui.composables.BankIcon
|
import net.codinux.banking.ui.composables.BankIcon
|
||||||
import net.codinux.banking.ui.config.Colors
|
import net.codinux.banking.ui.config.Colors
|
||||||
import net.codinux.banking.ui.config.DI
|
import net.codinux.banking.ui.config.DI
|
||||||
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
||||||
|
import net.codinux.banking.ui.model.ShowTransferMoneyDialogData
|
||||||
|
|
||||||
private val uiSettings = DI.uiSettings
|
private val uiSettings = DI.uiSettings
|
||||||
|
|
||||||
|
@ -32,9 +33,35 @@ fun TransactionListItem(userAccount: UserAccount?, transaction: AccountTransacti
|
||||||
|
|
||||||
val backgroundColor = if (zebraStripes && itemIndex % 2 == 1) Colors.Zinc100_50 else Color.White
|
val backgroundColor = if (zebraStripes && itemIndex % 2 == 1) Colors.Zinc100_50 else Color.White
|
||||||
|
|
||||||
|
val bottomPadding = 56.dp
|
||||||
|
|
||||||
|
var showMenuAt by remember { mutableStateOf<DpOffset?>(null) }
|
||||||
|
|
||||||
|
|
||||||
|
fun newMoneyTransferToOtherParty(withSameData: Boolean) {
|
||||||
|
showMenuAt = null
|
||||||
|
|
||||||
|
DI.uiState.showTransferMoneyDialogData.value = ShowTransferMoneyDialogData(
|
||||||
|
DI.uiState.userAccounts.value.firstNotNullOf { it.accounts.firstOrNull { it.id == transaction.bankAccountId } },
|
||||||
|
transaction.otherPartyName,
|
||||||
|
if (withSameData) transaction.amount else null,
|
||||||
|
if (withSameData) transaction.reference else null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
.background(color = backgroundColor)
|
.background(color = backgroundColor)
|
||||||
|
.pointerInput(Unit) {
|
||||||
|
detectTapGestures(
|
||||||
|
onLongPress = {
|
||||||
|
if (transaction.otherPartyName != null) { // TODO: also check if IBAN is set
|
||||||
|
showMenuAt = DpOffset(it.x.dp, it.y.dp - bottomPadding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
.padding(horizontal = 6.dp, vertical = 6.dp)
|
.padding(horizontal = 6.dp, vertical = 6.dp)
|
||||||
) {
|
) {
|
||||||
Column(Modifier.weight(1f)) {
|
Column(Modifier.weight(1f)) {
|
||||||
|
@ -61,7 +88,7 @@ fun TransactionListItem(userAccount: UserAccount?, transaction: AccountTransacti
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(Modifier.width(6.dp).background(Color.Yellow))
|
Spacer(Modifier.width(6.dp))
|
||||||
|
|
||||||
Column(Modifier.width(90.dp), horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.Center) {
|
Column(Modifier.width(90.dp), horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.Center) {
|
||||||
Text(
|
Text(
|
||||||
|
@ -75,6 +102,18 @@ fun TransactionListItem(userAccount: UserAccount?, transaction: AccountTransacti
|
||||||
text = formatUtil.formatDate(transaction.valueDate)
|
text = formatUtil.formatDate(transaction.valueDate)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DropdownMenu(showMenuAt != null, { showMenuAt = null }, Modifier.widthIn(min = 350.dp),
|
||||||
|
offset = showMenuAt ?: DpOffset.Zero,
|
||||||
|
) {
|
||||||
|
DropdownMenuItem({ newMoneyTransferToOtherParty(false) }) {
|
||||||
|
Text("Neue Überweisung an ${transaction.otherPartyName} ...")
|
||||||
|
}
|
||||||
|
|
||||||
|
DropdownMenuItem({ newMoneyTransferToOtherParty(true) }) {
|
||||||
|
Text("Neue Überweisung mit gleichen Daten ...")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemIndex < countItems - 1) {
|
if (itemIndex < countItems - 1) {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import net.codinux.banking.ui.config.DI
|
||||||
import net.codinux.banking.ui.forms.AutocompleteTextField
|
import net.codinux.banking.ui.forms.AutocompleteTextField
|
||||||
import net.codinux.banking.ui.forms.OutlinedTextField
|
import net.codinux.banking.ui.forms.OutlinedTextField
|
||||||
import net.codinux.banking.ui.forms.Select
|
import net.codinux.banking.ui.forms.Select
|
||||||
|
import net.codinux.banking.ui.model.ShowTransferMoneyDialogData
|
||||||
import net.codinux.banking.ui.model.error.ErroneousAction
|
import net.codinux.banking.ui.model.error.ErroneousAction
|
||||||
import net.codinux.banking.ui.service.RecipientFinder
|
import net.codinux.banking.ui.service.RecipientFinder
|
||||||
|
|
||||||
|
@ -35,6 +36,7 @@ private val formatUtil = DI.formatUtil
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TransferMoneyDialog(
|
fun TransferMoneyDialog(
|
||||||
|
data: ShowTransferMoneyDialogData,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val userAccounts = uiState.userAccounts.value
|
val userAccounts = uiState.userAccounts.value
|
||||||
|
@ -51,12 +53,12 @@ fun TransferMoneyDialog(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var senderAccount by remember { mutableStateOf(accountsSupportingTransferringMoney.first()) }
|
var senderAccount by remember { mutableStateOf(data.senderAccount ?: accountsSupportingTransferringMoney.first()) }
|
||||||
|
|
||||||
var recipientName by remember { mutableStateOf("") }
|
var recipientName by remember { mutableStateOf(data.recipientName ?: "") }
|
||||||
var recipientAccountIdentifier by remember { mutableStateOf("") }
|
var recipientAccountIdentifier by remember { mutableStateOf("") }
|
||||||
var amount by remember { mutableStateOf("") }
|
var amount by remember { mutableStateOf(data.amount?.amount ?: "") }
|
||||||
var paymentReference by remember { mutableStateOf("") }
|
var paymentReference by remember { mutableStateOf(data.reference ?: "") }
|
||||||
val accountSupportsInstantTransfer by remember(senderAccount) { derivedStateOf { senderAccount.supportsAnyFeature(BankAccountFeatures.InstantPayment) } }
|
val accountSupportsInstantTransfer by remember(senderAccount) { derivedStateOf { senderAccount.supportsAnyFeature(BankAccountFeatures.InstantPayment) } }
|
||||||
var instantTransfer by remember { mutableStateOf(false) }
|
var instantTransfer by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
@ -134,6 +136,7 @@ fun TransferMoneyDialog(
|
||||||
Column(Modifier.padding(top = verticalSpace)) {
|
Column(Modifier.padding(top = verticalSpace)) {
|
||||||
AutocompleteTextField(
|
AutocompleteTextField(
|
||||||
"Name des Empfängers / der Empfängerin",
|
"Name des Empfängers / der Empfängerin",
|
||||||
|
recipientName,
|
||||||
dropdownMaxHeight = 350.dp,
|
dropdownMaxHeight = 350.dp,
|
||||||
minTextLengthForSearch = 0,
|
minTextLengthForSearch = 0,
|
||||||
onEnteredTextChanged = { recipientName = it },
|
onEnteredTextChanged = { recipientName = it },
|
||||||
|
@ -194,6 +197,7 @@ fun TransferMoneyDialog(
|
||||||
|
|
||||||
AutocompleteTextField(
|
AutocompleteTextField(
|
||||||
"Verwendungszweck (optional)",
|
"Verwendungszweck (optional)",
|
||||||
|
paymentReference,
|
||||||
dropdownMaxHeight = 250.dp,
|
dropdownMaxHeight = 250.dp,
|
||||||
minTextLengthForSearch = 0,
|
minTextLengthForSearch = 0,
|
||||||
getItemTitle = { suggestion -> suggestion.reference },
|
getItemTitle = { suggestion -> suggestion.reference },
|
||||||
|
|
|
@ -19,6 +19,7 @@ import net.codinux.banking.ui.config.Colors
|
||||||
@Composable
|
@Composable
|
||||||
fun <T> AutocompleteTextField(
|
fun <T> AutocompleteTextField(
|
||||||
label: String,
|
label: String,
|
||||||
|
initialValue: String? = null,
|
||||||
onSelectedItemChanged: (T?) -> Unit,
|
onSelectedItemChanged: (T?) -> Unit,
|
||||||
onEnteredTextChanged: ((String) -> Unit)? = null,
|
onEnteredTextChanged: ((String) -> Unit)? = null,
|
||||||
minTextLengthForSearch: Int = 1,
|
minTextLengthForSearch: Int = 1,
|
||||||
|
@ -29,7 +30,7 @@ fun <T> AutocompleteTextField(
|
||||||
fetchSuggestions: suspend (query: String) -> Collection<T> = { emptyList() },
|
fetchSuggestions: suspend (query: String) -> Collection<T> = { emptyList() },
|
||||||
suggestionContent: @Composable (T) -> Unit
|
suggestionContent: @Composable (T) -> Unit
|
||||||
) {
|
) {
|
||||||
var searchQuery by remember { mutableStateOf("") }
|
var searchQuery by remember { mutableStateOf(initialValue ?: "") }
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
var isLoading by remember { mutableStateOf(false) }
|
var isLoading by remember { mutableStateOf(false) }
|
||||||
var suggestions by remember { mutableStateOf<Collection<T>>(emptyList()) }
|
var suggestions by remember { mutableStateOf<Collection<T>>(emptyList()) }
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
package net.codinux.banking.ui.model
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.Amount
|
||||||
|
import net.codinux.banking.dataaccess.entities.BankAccountEntity
|
||||||
|
|
||||||
|
data class ShowTransferMoneyDialogData(
|
||||||
|
val senderAccount: BankAccountEntity? = null,
|
||||||
|
val recipientName: String? = null,
|
||||||
|
// TODO: add recipient account identifier
|
||||||
|
val amount: Amount? = null,
|
||||||
|
val reference: String? = null
|
||||||
|
)
|
|
@ -3,14 +3,10 @@ package net.codinux.banking.ui.state
|
||||||
import androidx.compose.material.DrawerState
|
import androidx.compose.material.DrawerState
|
||||||
import androidx.compose.material.DrawerValue
|
import androidx.compose.material.DrawerValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import kotlinx.coroutines.coroutineScope
|
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import net.codinux.banking.dataaccess.entities.UserAccountEntity
|
import net.codinux.banking.dataaccess.entities.UserAccountEntity
|
||||||
import net.codinux.banking.ui.model.AccountTransactionViewModel
|
import net.codinux.banking.ui.model.*
|
||||||
import net.codinux.banking.ui.model.AccountTransactionsFilter
|
|
||||||
import net.codinux.banking.ui.model.TanChallengeReceived
|
|
||||||
import net.codinux.banking.ui.model.error.ApplicationError
|
import net.codinux.banking.ui.model.error.ApplicationError
|
||||||
import net.codinux.banking.ui.model.error.BankingClientError
|
import net.codinux.banking.ui.model.error.BankingClientError
|
||||||
import net.codinux.banking.ui.model.error.ErroneousAction
|
import net.codinux.banking.ui.model.error.ErroneousAction
|
||||||
|
@ -45,7 +41,7 @@ class UiState : ViewModel() {
|
||||||
|
|
||||||
val showAddAccountDialog = MutableStateFlow(false)
|
val showAddAccountDialog = MutableStateFlow(false)
|
||||||
|
|
||||||
val showTransferMoneyDialog = MutableStateFlow(false)
|
val showTransferMoneyDialogData = MutableStateFlow<ShowTransferMoneyDialogData?>(null)
|
||||||
|
|
||||||
val showExportScreen = MutableStateFlow(false)
|
val showExportScreen = MutableStateFlow(false)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue