From dde54b75d34733ccbd384bb52344c8fdd99d10b1 Mon Sep 17 00:00:00 2001 From: dankito Date: Wed, 4 Sep 2024 17:12:36 +0200 Subject: [PATCH] Implemented TransferMoneyDialog --- .../banking/ui/composables/StateHandler.kt | 24 ++- .../banking/ui/config/Internationalization.kt | 2 + .../ui/dialogs/ApplicationErrorDialog.kt | 1 + .../ui/dialogs/BankingClientErrorDialog.kt | 1 + .../banking/ui/dialogs/TransferMoneyDialog.kt | 165 ++++++++++++++++++ .../net/codinux/banking/ui/forms/Select.kt | 59 +++++++ .../ui/model/error/BankingClientAction.kt | 5 +- .../banking/ui/model/error/ErroneousAction.kt | 5 +- ...t => AccountTransactionsRetrievedEvent.kt} | 2 +- .../ui/model/events/TransferredMoneyEvent.kt | 9 + .../banking/ui/service/BankingService.kt | 31 +++- .../net/codinux/banking/ui/state/UiState.kt | 28 +-- 12 files changed, 311 insertions(+), 21 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/TransferMoneyDialog.kt create mode 100644 composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/Select.kt rename composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/events/{AccountTransactionsRetrieved.kt => AccountTransactionsRetrievedEvent.kt} (87%) create mode 100644 composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/events/TransferredMoneyEvent.kt 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 214b651..852cd02 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 @@ -5,16 +5,16 @@ import androidx.compose.material.SnackbarHostState import androidx.compose.runtime.* import kotlinx.coroutines.launch import net.codinux.banking.ui.config.DI -import net.codinux.banking.ui.dialogs.AddAccountDialog -import net.codinux.banking.ui.dialogs.ApplicationErrorDialog -import net.codinux.banking.ui.dialogs.BankingClientErrorDialog -import net.codinux.banking.ui.dialogs.EnterTanDialog +import net.codinux.banking.ui.dialogs.* import net.codinux.banking.ui.screens.ExportScreen import net.codinux.banking.ui.state.UiState +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 showExportScreen by uiState.showExportScreen.collectAsState() val tanChallengeReceived by uiState.tanChallengeReceived.collectAsState() @@ -28,6 +28,10 @@ fun StateHandler(uiState: UiState, snackbarHostState: SnackbarHostState) { AddAccountDialog { uiState.showAddAccountDialog.value = false } } + if (showTransferMoneyDialog) { + TransferMoneyDialog { uiState.showTransferMoneyDialog.value = false } + } + if (showExportScreen) { ExportScreen { uiState.showExportScreen.value = false } } @@ -54,7 +58,7 @@ fun StateHandler(uiState: UiState, snackbarHostState: SnackbarHostState) { LaunchedEffect(Unit) { coroutineScope.launch { - DI.uiState.transactionsRetrievedEvents.collect { event -> + uiState.transactionsRetrievedEvents.collect { event -> val messagePrefix = if (event.newTransactions.isEmpty()) { "Keine neuen Umsätze" } else if (event.newTransactions.size == 1) { @@ -70,6 +74,16 @@ fun StateHandler(uiState: UiState, snackbarHostState: SnackbarHostState) { ) } } + + coroutineScope.launch { + uiState.transferredMoneyEvents.collect { event -> + snackbarHostState.showSnackbar( + message = "${formatUtil.formatAmount(event.amount, event.currency)} wurden erfolgreich an ${event.recipientName} überwiesen", + actionLabel = "Das freut mich", + duration = SnackbarDuration.Long + ) + } + } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/config/Internationalization.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/config/Internationalization.kt index 3a2ee9d..ec2cfd5 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/config/Internationalization.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/config/Internationalization.kt @@ -8,6 +8,8 @@ object Internationalization { const val ErrorUpdateAccountTransactions = "Umsätze konnten nicht aktualisiert werden" + const val ErrorTransferMoney = "Überweisung konnte nicht ausgeführt werden" + fun getTextForActionRequiringTan(action: ActionRequiringTan): String = when (action) { ActionRequiringTan.GetAnonymousBankInfo, diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/ApplicationErrorDialog.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/ApplicationErrorDialog.kt index 5130b65..42d5380 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/ApplicationErrorDialog.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/ApplicationErrorDialog.kt @@ -10,6 +10,7 @@ fun ApplicationErrorDialog(error: ApplicationError, onDismiss: (() -> Unit)? = n val title = when (error.erroneousAction) { ErroneousAction.AddAccount -> Internationalization.ErrorAddAccount ErroneousAction.UpdateAccountTransactions -> Internationalization.ErrorUpdateAccountTransactions + ErroneousAction.TransferMoney -> Internationalization.ErrorTransferMoney } // add exception stacktrace? diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/BankingClientErrorDialog.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/BankingClientErrorDialog.kt index 841eef9..f902519 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/BankingClientErrorDialog.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/BankingClientErrorDialog.kt @@ -11,6 +11,7 @@ fun BankingClientErrorDialog(error: BankingClientError, onDismiss: (() -> Unit)? val title = when (error.erroneousAction) { BankingClientAction.AddAccount -> Internationalization.ErrorAddAccount BankingClientAction.UpdateAccountTransactions -> Internationalization.ErrorUpdateAccountTransactions + BankingClientAction.TransferMoney -> Internationalization.ErrorTransferMoney } val text = if (error.error.internalError != null) { 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 new file mode 100644 index 0000000..f9ae3b0 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/TransferMoneyDialog.kt @@ -0,0 +1,165 @@ +package net.codinux.banking.ui.dialogs + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import net.codinux.banking.client.model.Amount +import net.codinux.banking.client.model.BankAccountFeatures +import net.codinux.banking.ui.IOorDefault +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.forms.OutlinedTextField +import net.codinux.banking.ui.forms.Select +import net.codinux.banking.ui.model.error.ErroneousAction + +private val uiState = DI.uiState + +private val bankingService = DI.bankingService + +@Composable +fun TransferMoneyDialog( + onDismiss: () -> Unit, +) { + val userAccounts = uiState.userAccounts.value + val accountsToUserAccount = userAccounts.sortedBy { it.displayIndex } + .flatMap { user -> user.accounts.sortedBy { it.displayIndex }.map { it to user } }.toMap() + + val accountsSupportingTransferringMoney = userAccounts.flatMap { it.accounts } + .filter { it.supportsAnyFeature(BankAccountFeatures.TransferMoney, BankAccountFeatures.InstantPayment) } + + if (accountsSupportingTransferringMoney.isEmpty()) { + uiState.applicationErrorOccurred(ErroneousAction.TransferMoney, "Keines Ihrer Konten unterstützt das Überweisen von Geld") + onDismiss() + return + } + + var senderAccount by remember { mutableStateOf(accountsSupportingTransferringMoney.first()) } + + var recipientName by remember { mutableStateOf("") } + var recipientAccountIdentifier by remember { mutableStateOf("") } + var amount by remember { mutableStateOf("") } + var paymentReference by remember { mutableStateOf("") } + val accountSupportsInstantTransfer by remember(senderAccount) { derivedStateOf { senderAccount.supportsAnyFeature(BankAccountFeatures.InstantPayment) } } + var instantTransfer by remember { mutableStateOf(false) } + + val isRequiredDataEntered by remember(recipientName, recipientAccountIdentifier, amount) { + // TODO: add check if it's a valid IBAN + derivedStateOf { recipientName.length > 2 && recipientAccountIdentifier.length >= 18 && amount.isNotBlank() } + } + + var isTransferringMoney by remember { mutableStateOf(false) } + + val verticalSpace = 8.dp + + val coroutineScope = rememberCoroutineScope() + + + fun confirmCalled() { + isTransferringMoney = true + + coroutineScope.launch(Dispatchers.IOorDefault) { + val successful = bankingService.transferMoney( + accountsToUserAccount[senderAccount]!!, senderAccount, + recipientName, recipientAccountIdentifier, + Amount(amount), "EUR", // TODO: add input field for currency + paymentReference, instantTransfer && accountSupportsInstantTransfer + // TODO: determine BIC to IBAN + ) + + withContext(Dispatchers.Main) { + isTransferringMoney = false + + if (successful) { + onDismiss() + } + } + } + } + + + BaseDialog( + title = "Neue Überweisung ...", + confirmButtonTitle = "Überweisen", + confirmButtonEnabled = isRequiredDataEntered && isTransferringMoney == false, + showProgressIndicatorOnConfirmButton = isTransferringMoney, + useMoreThanPlatformDefaultWidthOnMobile = true, + onDismiss = onDismiss, + onConfirm = { confirmCalled() } + ) { + Select( + "Konto", "Alle Konten anzeigen", + accountsSupportingTransferringMoney, senderAccount, { senderAccount = it }, + { account -> "${accountsToUserAccount[account]?.displayName} ${account.displayName}" }, + { BankIcon(accountsToUserAccount[senderAccount]) } + ) { account -> + Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { + BankIcon(accountsToUserAccount[account], Modifier.padding(end = 6.dp)) + + Text("${accountsToUserAccount[account]?.displayName} ${account.displayName}") + } + } + + Column(Modifier.padding(top = verticalSpace)) { + OutlinedTextField( + value = recipientName, + onValueChange = { recipientName = it }, + label = { Text("Name des Empfängers / der Empfängerin") }, + modifier = Modifier.fillMaxWidth(), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next) + ) + + Spacer(modifier = Modifier.height(verticalSpace)) + + OutlinedTextField( + value = recipientAccountIdentifier, + onValueChange = { recipientAccountIdentifier = it }, + label = { Text("IBAN") }, + modifier = Modifier.fillMaxWidth(), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next) + ) + + Spacer(modifier = Modifier.height(verticalSpace)) + + OutlinedTextField( + value = amount, + onValueChange = { amount = it }, + label = { Text("Betrag") }, + modifier = Modifier.fillMaxWidth(), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal, imeAction = ImeAction.Next) + ) + + Spacer(modifier = Modifier.height(verticalSpace)) + + OutlinedTextField( + value = paymentReference, + onValueChange = { paymentReference = it }, + label = { Text("Verwendungszweck") }, + modifier = Modifier.fillMaxWidth(), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next) + ) + + Row(Modifier.padding(top = verticalSpace), verticalAlignment = Alignment.CenterVertically) { + Switch( + checked = instantTransfer, + onCheckedChange = { instantTransfer = it }, + enabled = accountSupportsInstantTransfer, + modifier = Modifier.padding(end = 4.dp), + colors = SwitchDefaults.colors(checkedThumbColor = Colors.CodinuxSecondaryColor) + ) + + Text("Echtzeitüberweisung (evtl. kostenpflichtig)", Modifier.clickable { instantTransfer = !instantTransfer }) + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/Select.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/Select.kt new file mode 100644 index 0000000..5c983f6 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/forms/Select.kt @@ -0,0 +1,59 @@ +package net.codinux.banking.ui.forms + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.material.icons.filled.ArrowDropUp +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier + +@OptIn(ExperimentalMaterialApi::class) +@Composable +fun Select( + label: String, + contentDescription: String, + items: Collection, + selectedItem: T, + onSelectedItemChanged: (T) -> Unit, + getItemDisplayText: (T) -> String, + leadingIcon: @Composable (() -> Unit)? = null, + dropDownItemContent: @Composable ((T) -> Unit)? = null +) { + var showDropDownMenu by remember { mutableStateOf(false) } + + ExposedDropdownMenuBox(showDropDownMenu, { isExpanded -> showDropDownMenu = isExpanded }, Modifier.fillMaxWidth()) { + OutlinedTextField( + value = getItemDisplayText(selectedItem), + onValueChange = { }, + modifier = Modifier.fillMaxWidth(), + label = { Text(label) }, + readOnly = true, + maxLines = 1, + trailingIcon = { + if (showDropDownMenu) { + Icon(Icons.Filled.ArrowDropUp, contentDescription) + } else { + Icon(Icons.Filled.ArrowDropDown, contentDescription) + } + }, + leadingIcon = leadingIcon + ) + + // due to a bug (still not fixed since 2021) in ExposedDropdownMenu its popup has a maximum width of 800 pixel / 320dp which is too less to fit + // TextField's width, see https://issuetracker.google.com/issues/205589613 + DropdownMenu(showDropDownMenu, { showDropDownMenu = false }, Modifier.exposedDropdownSize(true)) { + items.forEach { item -> + DropdownMenuItem( + onClick = { + showDropDownMenu = false + onSelectedItemChanged(item) + } + ) { + dropDownItemContent?.invoke(item) ?: Text(getItemDisplayText(item)) + } + } + } + } + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/error/BankingClientAction.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/error/BankingClientAction.kt index bf62809..4e489e2 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/error/BankingClientAction.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/error/BankingClientAction.kt @@ -2,5 +2,8 @@ package net.codinux.banking.ui.model.error enum class BankingClientAction { AddAccount, - UpdateAccountTransactions + + UpdateAccountTransactions, + + TransferMoney } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/error/ErroneousAction.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/error/ErroneousAction.kt index 9ca90ee..6f1fef5 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/error/ErroneousAction.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/error/ErroneousAction.kt @@ -2,5 +2,8 @@ package net.codinux.banking.ui.model.error enum class ErroneousAction { AddAccount, - UpdateAccountTransactions + + UpdateAccountTransactions, + + TransferMoney } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/events/AccountTransactionsRetrieved.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/events/AccountTransactionsRetrievedEvent.kt similarity index 87% rename from composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/events/AccountTransactionsRetrieved.kt rename to composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/events/AccountTransactionsRetrievedEvent.kt index 3dd2d42..31e3ace 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/events/AccountTransactionsRetrieved.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/events/AccountTransactionsRetrievedEvent.kt @@ -4,7 +4,7 @@ import net.codinux.banking.client.model.BankAccount import net.codinux.banking.client.model.UserAccount import net.codinux.banking.ui.model.AccountTransactionViewModel -data class AccountTransactionsRetrieved( +data class AccountTransactionsRetrievedEvent( val user: UserAccount, val account: BankAccount, val newTransactions: List diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/events/TransferredMoneyEvent.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/events/TransferredMoneyEvent.kt new file mode 100644 index 0000000..956d343 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/events/TransferredMoneyEvent.kt @@ -0,0 +1,9 @@ +package net.codinux.banking.ui.model.events + +import net.codinux.banking.client.model.Amount + +data class TransferredMoneyEvent( + val recipientName: String, + val amount: Amount, + val currency: String +) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/service/BankingService.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/service/BankingService.kt index ce57395..c8adf17 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/service/BankingService.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/service/BankingService.kt @@ -9,6 +9,7 @@ import net.codinux.banking.client.model.* import net.codinux.banking.client.model.options.GetAccountDataOptions import net.codinux.banking.client.model.options.RetrieveTransactions import net.codinux.banking.client.model.request.GetAccountDataRequest +import net.codinux.banking.client.model.request.TransferMoneyRequestForUser import net.codinux.banking.client.model.response.* import net.codinux.banking.client.service.BankingModelService import net.codinux.banking.dataaccess.BankingRepository @@ -22,7 +23,8 @@ import net.codinux.banking.ui.model.AccountTransactionViewModel import net.codinux.banking.ui.model.BankInfo import net.codinux.banking.ui.model.TanChallengeReceived import net.codinux.banking.ui.model.error.* -import net.codinux.banking.ui.model.events.AccountTransactionsRetrieved +import net.codinux.banking.ui.model.events.AccountTransactionsRetrievedEvent +import net.codinux.banking.ui.model.events.TransferredMoneyEvent import net.codinux.banking.ui.state.UiState import net.codinux.csv.reader.CsvReader import net.codinux.log.logger @@ -81,7 +83,7 @@ class BankingService( } catch (e: Throwable) { log.error(e) { "Could not add account for ${bank.name} $loginName" } - uiState.applicationErrorOccurred(ErroneousAction.AddAccount, e) + uiState.applicationErrorOccurred(ErroneousAction.AddAccount, null, e) return false } @@ -157,7 +159,7 @@ class BankingService( } val transactionsViewModel = updateTransactionsInUi(newTransactionsEntities) - uiState.fireNewTransactionsRetrieved(AccountTransactionsRetrieved(user, account, transactionsViewModel)) + uiState.dispatchNewTransactionsRetrieved(AccountTransactionsRetrievedEvent(user, account, transactionsViewModel)) } } catch (e: Throwable) { log.error(e) { "Could not save updated account transactions for user $user" } @@ -175,6 +177,29 @@ class BankingService( } + suspend fun transferMoney(user: UserAccountEntity, account: BankAccountEntity, + recipientName: String, recipientAccountIdentifier: String, amount: Amount, currency: String, + paymentReference: String? = null, instantTransfer: Boolean = false, recipientBankIdentifier: String? = null): Boolean { + val response = client.transferMoneyAsync(TransferMoneyRequestForUser( + user.bankCode, user.loginName, user.password!!, + BankAccountIdentifier(account.identifier, account.subAccountNumber, account.iban), // TODO: use BankingClient's one + recipientName, recipientAccountIdentifier, recipientBankIdentifier, + amount, "EUR", + paymentReference, instantTransfer + )) + + if (response.error != null) { + handleUnsuccessfulBankingClientResponse(BankingClientAction.TransferMoney, response) + } else if (response.type == ResponseType.Success) { + uiState.dispatchTransferredMoney(TransferredMoneyEvent(recipientName, amount, currency)) + + updateAccountTransactions(user, account) + } + + return response.type == ResponseType.Success + } + + private fun handleUnsuccessfulBankingClientResponse(action: BankingClientAction, response: Response<*>) { log.error { "$action was not successful: $response" } 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 73f9a33..a9b362a 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 @@ -14,7 +14,8 @@ import net.codinux.banking.ui.model.TanChallengeReceived import net.codinux.banking.ui.model.error.ApplicationError import net.codinux.banking.ui.model.error.BankingClientError import net.codinux.banking.ui.model.error.ErroneousAction -import net.codinux.banking.ui.model.events.AccountTransactionsRetrieved +import net.codinux.banking.ui.model.events.AccountTransactionsRetrievedEvent +import net.codinux.banking.ui.model.events.TransferredMoneyEvent class UiState : ViewModel() { @@ -22,14 +23,17 @@ class UiState : ViewModel() { val transactions = MutableStateFlow>(emptyList()) - val transactionsRetrievedEvents = MutableSharedFlow() + val transactionsRetrievedEvents = MutableSharedFlow() - suspend fun fireNewTransactionsRetrieved(event: AccountTransactionsRetrieved) { - coroutineScope { - launch { - transactionsRetrievedEvents.emit(event) - } - } + suspend fun dispatchNewTransactionsRetrieved(event: AccountTransactionsRetrievedEvent) { + transactionsRetrievedEvents.emit(event) + } + + + val transferredMoneyEvents = MutableSharedFlow() + + suspend fun dispatchTransferredMoney(event: TransferredMoneyEvent) { + transferredMoneyEvents.emit(event) } @@ -41,8 +45,11 @@ class UiState : ViewModel() { val showAddAccountDialog = MutableStateFlow(false) + val showTransferMoneyDialog = MutableStateFlow(false) + val showExportScreen = MutableStateFlow(false) + val tanChallengeReceived = MutableStateFlow(null) val bankingClientErrorOccurred = MutableStateFlow(null) @@ -50,9 +57,10 @@ class UiState : ViewModel() { val applicationErrorOccurred = MutableStateFlow(null) - fun applicationErrorOccurred(erroneousAction: ErroneousAction, exception: Throwable, errorMessage: String? = null) { + fun applicationErrorOccurred(erroneousAction: ErroneousAction, errorMessage: String? = null, exception: Throwable? = null) { val message = errorMessage - ?: exception.message ?: exception::class.simpleName // TODO: find a better way to get error message from exception + ?: exception?.message // TODO: find a better way to get error message from exception + ?: exception?.let { it::class.simpleName } if (message != null) { applicationErrorOccurred.value = ApplicationError(erroneousAction, message, exception)