diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/appskeleton/SideMenuContent.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/appskeleton/SideMenuContent.kt index 572d3de..78db36e 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/appskeleton/SideMenuContent.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/appskeleton/SideMenuContent.kt @@ -23,6 +23,7 @@ import net.codinux.banking.ui.composables.NavigationMenuItem import net.codinux.banking.ui.composables.settings.UiSettings import net.codinux.banking.ui.config.Colors import net.codinux.banking.ui.config.DI +import net.codinux.banking.ui.model.ShowTransferMoneyDialogData import org.jetbrains.compose.resources.imageResource private val uiState = DI.uiState @@ -99,7 +100,7 @@ fun SideMenuContent() { NavigationMenuItem(itemModifier, "Neue Überweisung", textColor, horizontalPadding = ItemHorizontalPadding, icon = { Icon(Icons.Filled.Add, "Konto hinzufügen", Modifier.size(iconSize), tint = textColor) }) { - uiState.showTransferMoneyDialog.value = true + uiState.showTransferMoneyDialogData.value = ShowTransferMoneyDialogData() coroutineScope.launch { drawerState.close() diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/StateHandler.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/StateHandler.kt index 852cd02..c94d83d 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/StateHandler.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/StateHandler.kt @@ -14,7 +14,7 @@ private val formatUtil = DI.formatUtil @Composable fun StateHandler(uiState: UiState, snackbarHostState: SnackbarHostState) { 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 tanChallengeReceived by uiState.tanChallengeReceived.collectAsState() @@ -28,8 +28,8 @@ fun StateHandler(uiState: UiState, snackbarHostState: SnackbarHostState) { AddAccountDialog { uiState.showAddAccountDialog.value = false } } - if (showTransferMoneyDialog) { - TransferMoneyDialog { uiState.showTransferMoneyDialog.value = false } + showTransferMoneyDialogData?.let { data -> + TransferMoneyDialog(data) { uiState.showTransferMoneyDialogData.value = null } } if (showExportScreen) { diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/transactions/TransactionListItem.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/transactions/TransactionListItem.kt index 026aa41..42ed632 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/transactions/TransactionListItem.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/transactions/TransactionListItem.kt @@ -1,22 +1,23 @@ package net.codinux.banking.ui.composables.transactions import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.* -import androidx.compose.material.Divider -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue +import androidx.compose.material.* +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import net.codinux.banking.client.model.UserAccount import net.codinux.banking.ui.composables.BankIcon import net.codinux.banking.ui.config.Colors import net.codinux.banking.ui.config.DI import net.codinux.banking.ui.model.AccountTransactionViewModel +import net.codinux.banking.ui.model.ShowTransferMoneyDialogData 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 bottomPadding = 56.dp + + var showMenuAt by remember { mutableStateOf(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( modifier = Modifier.fillMaxWidth() .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) ) { 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) { Text( @@ -75,6 +102,18 @@ fun TransactionListItem(userAccount: UserAccount?, transaction: AccountTransacti 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) { diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/TransferMoneyDialog.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/TransferMoneyDialog.kt index 9fce794..edcfe01 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/TransferMoneyDialog.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/TransferMoneyDialog.kt @@ -24,6 +24,7 @@ import net.codinux.banking.ui.config.DI import net.codinux.banking.ui.forms.AutocompleteTextField import net.codinux.banking.ui.forms.OutlinedTextField 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.service.RecipientFinder @@ -35,6 +36,7 @@ private val formatUtil = DI.formatUtil @Composable fun TransferMoneyDialog( + data: ShowTransferMoneyDialogData, onDismiss: () -> Unit, ) { 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 amount by remember { mutableStateOf("") } - var paymentReference by remember { mutableStateOf("") } + var amount by remember { mutableStateOf(data.amount?.amount ?: "") } + var paymentReference by remember { mutableStateOf(data.reference ?: "") } val accountSupportsInstantTransfer by remember(senderAccount) { derivedStateOf { senderAccount.supportsAnyFeature(BankAccountFeatures.InstantPayment) } } var instantTransfer by remember { mutableStateOf(false) } @@ -134,6 +136,7 @@ fun TransferMoneyDialog( Column(Modifier.padding(top = verticalSpace)) { AutocompleteTextField( "Name des Empfängers / der Empfängerin", + recipientName, dropdownMaxHeight = 350.dp, minTextLengthForSearch = 0, onEnteredTextChanged = { recipientName = it }, @@ -194,6 +197,7 @@ fun TransferMoneyDialog( AutocompleteTextField( "Verwendungszweck (optional)", + paymentReference, dropdownMaxHeight = 250.dp, minTextLengthForSearch = 0, getItemTitle = { suggestion -> suggestion.reference }, diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/AutocompleteTextField.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/AutocompleteTextField.kt index 5f857f4..3e634a0 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/AutocompleteTextField.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/AutocompleteTextField.kt @@ -19,6 +19,7 @@ import net.codinux.banking.ui.config.Colors @Composable fun AutocompleteTextField( label: String, + initialValue: String? = null, onSelectedItemChanged: (T?) -> Unit, onEnteredTextChanged: ((String) -> Unit)? = null, minTextLengthForSearch: Int = 1, @@ -29,7 +30,7 @@ fun AutocompleteTextField( fetchSuggestions: suspend (query: String) -> Collection = { emptyList() }, suggestionContent: @Composable (T) -> Unit ) { - var searchQuery by remember { mutableStateOf("") } + var searchQuery by remember { mutableStateOf(initialValue ?: "") } var expanded by remember { mutableStateOf(false) } var isLoading by remember { mutableStateOf(false) } var suggestions by remember { mutableStateOf>(emptyList()) } diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/ShowTransferMoneyDialogData.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/ShowTransferMoneyDialogData.kt new file mode 100644 index 0000000..6cf74ed --- /dev/null +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/ShowTransferMoneyDialogData.kt @@ -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 +) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/state/UiState.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/state/UiState.kt index a9b362a..c9a170e 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/state/UiState.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/state/UiState.kt @@ -3,14 +3,10 @@ package net.codinux.banking.ui.state import androidx.compose.material.DrawerState import androidx.compose.material.DrawerValue import androidx.lifecycle.ViewModel -import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch import net.codinux.banking.dataaccess.entities.UserAccountEntity -import net.codinux.banking.ui.model.AccountTransactionViewModel -import net.codinux.banking.ui.model.AccountTransactionsFilter -import net.codinux.banking.ui.model.TanChallengeReceived +import net.codinux.banking.ui.model.* import net.codinux.banking.ui.model.error.ApplicationError import net.codinux.banking.ui.model.error.BankingClientError import net.codinux.banking.ui.model.error.ErroneousAction @@ -45,7 +41,7 @@ class UiState : ViewModel() { val showAddAccountDialog = MutableStateFlow(false) - val showTransferMoneyDialog = MutableStateFlow(false) + val showTransferMoneyDialogData = MutableStateFlow(null) val showExportScreen = MutableStateFlow(false)