Compare commits

...

4 Commits

10 changed files with 86 additions and 40 deletions

View File

@ -31,7 +31,7 @@ interface BankingRepository {
suspend fun updateBank(bank: BankAccessEntity, loginName: String, password: String, bankName: String?)
suspend fun updateBank(bank: BankAccessEntity, clientData: String?)
suspend fun updateBank(bank: BankAccessEntity, serializedClientData: String?)
suspend fun updateAccount(account: BankAccountEntity, userSetDisplayName: String?, hideAccount: Boolean, includeInAutomaticAccountsUpdate: Boolean)

View File

@ -55,7 +55,7 @@ class InMemoryBankingRepository(
// no-op
}
override suspend fun updateBank(bank: BankAccessEntity, clientData: String?) {
override suspend fun updateBank(bank: BankAccessEntity, serializedClientData: String?) {
// no-op
}

View File

@ -95,9 +95,9 @@ open class SqliteBankingRepository : BankingRepository {
val tanMedia = getAllTanMedia().groupBy { it.bankId }.mapValues { it.value.toMutableList() }
val holdings = getAllHoldings().groupBy { it.accountId }
return bankQueries.getAllBanks { id, domesticBankCode, loginName, password, bankName, bic, customerName, userId, selectedTanMethodIdentifier, selectedTanMediumIdentifier, bankingGroup, serverAddress, countryCode, clientData, userSetDisplayName, displayIndex, iconUrl, wrongCredentialsEntered ->
return bankQueries.getAllBanks { id, domesticBankCode, loginName, password, bankName, bic, customerName, userId, selectedTanMethodIdentifier, selectedTanMediumIdentifier, bankingGroup, serverAddress, countryCode, serializedClientData, userSetDisplayName, displayIndex, iconUrl, wrongCredentialsEntered ->
BankAccessEntity(id, domesticBankCode, loginName, password, bankName, bic, customerName, userId, getAccountsOfBank(id, bankAccounts, holdings), selectedTanMethodIdentifier, tanMethods[id] ?: mutableListOf(), selectedTanMediumIdentifier, tanMedia[id] ?: mutableListOf(),
bankingGroup?.let { BankingGroup.valueOf(it) }, serverAddress, countryCode, userSetDisplayName, displayIndex.toInt(), iconUrl, wrongCredentialsEntered, clientData)
bankingGroup?.let { BankingGroup.valueOf(it) }, serverAddress, countryCode, userSetDisplayName, displayIndex.toInt(), iconUrl, wrongCredentialsEntered, null, serializedClientData)
}.executeAsList()
}
@ -111,7 +111,7 @@ open class SqliteBankingRepository : BankingRepository {
return bankQueries.transactionWithResult {
bankQueries.insertBank(bank.domesticBankCode, bank.loginName, bank.password, bank.bankName, bank.bic,
bank.customerName, bank.userId, bank.selectedTanMethodIdentifier, bank.selectedTanMediumIdentifier,
bank.bankingGroup?.name, bank.serverAddress, bank.countryCode, bank.clientData, bank.userSetDisplayName, bank.displayIndex.toLong(), bank.iconUrl, bank.wrongCredentialsEntered
bank.bankingGroup?.name, bank.serverAddress, bank.countryCode, bank.serializedClientData, bank.userSetDisplayName, bank.displayIndex.toLong(), bank.iconUrl, bank.wrongCredentialsEntered
)
val bankId = getLastInsertedId() // getLastInsertedId() / last_insert_rowid() has to be called in a transaction with the insert operation, otherwise it will not work
@ -141,10 +141,10 @@ open class SqliteBankingRepository : BankingRepository {
}
}
override suspend fun updateBank(bank: BankAccessEntity, clientData: String?) {
override suspend fun updateBank(bank: BankAccessEntity, serializedClientData: String?) {
bankQueries.transaction {
if (clientData != null) {
bankQueries.updateBankClientData(clientData, bank.id)
if (serializedClientData != null) {
bankQueries.updateBankClientData(serializedClientData, bank.id)
}
}
}

View File

@ -34,7 +34,8 @@ class BankAccessEntity(
iconUrl: String? = null,
wrongCredentialsEntered: Boolean = false,
clientData: String? = null
clientData: Any? = null,
serializedClientData: String? = null
) : BankAccess(domesticBankCode, loginName, password, bankName, bic, customerName, userId, accounts, selectedTanMethodIdentifier, tanMethods, selectedTanMediumIdentifier, tanMedia, bankingGroup, serverAddress, countryCode) {
init {
@ -45,6 +46,7 @@ class BankAccessEntity(
this.wrongCredentialsEntered = wrongCredentialsEntered
this.clientData = clientData
this.serializedClientData = serializedClientData
}

View File

@ -25,7 +25,7 @@ import net.codinux.banking.ui.composables.settings.UiSettings
import net.codinux.banking.ui.composables.text.ItemDivider
import net.codinux.banking.ui.config.Colors
import net.codinux.banking.ui.config.DI
import net.codinux.banking.ui.extensions.verticalScroll
import net.codinux.banking.ui.extensions.rememberVerticalScroll
import org.jetbrains.compose.resources.imageResource
private val uiState = DI.uiState
@ -60,7 +60,7 @@ fun SideMenuContent() {
val coroutineScope = rememberCoroutineScope()
Column(Modifier.fillMaxSize().background(Colors.DrawerContentBackground).verticalScroll()) {
Column(Modifier.fillMaxSize().background(Colors.DrawerContentBackground).rememberVerticalScroll()) {
Column(Modifier.fillMaxWidth().height(HeaderHeight.dp).background(HeaderBackground).padding(16.dp)) {
Spacer(Modifier.weight(1f))

View File

@ -1,9 +1,7 @@
package net.codinux.banking.ui.extensions
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
@ -14,8 +12,14 @@ import net.codinux.log.Log
fun Modifier.verticalScroll() = this.verticalScroll(ScrollState(0), enabled = true)
@Composable
fun Modifier.rememberVerticalScroll() = this.verticalScroll(rememberScrollState())
fun Modifier.horizontalScroll() = this.horizontalScroll(ScrollState(0), enabled = true)
@Composable
fun Modifier.rememberHorizontalScroll() = this.horizontalScroll(rememberScrollState())
@Composable
// we need to support three different cases:

View File

@ -1,9 +1,7 @@
package net.codinux.banking.ui.screens
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
@ -12,7 +10,7 @@ import kotlinx.coroutines.launch
import net.codinux.banking.client.model.isNegative
import net.codinux.banking.persistence.entities.AccountTransactionEntity
import net.codinux.banking.ui.config.DI
import net.codinux.banking.ui.extensions.verticalScroll
import net.codinux.banking.ui.extensions.rememberVerticalScroll
import net.codinux.banking.ui.forms.LabelledValue
import net.codinux.banking.ui.forms.OutlinedTextField
import net.codinux.banking.ui.forms.SectionHeader
@ -72,7 +70,7 @@ fun AccountTransactionDetailsScreen(transaction: AccountTransactionEntity, onClo
onClosed = onClosed
) {
SelectionContainer {
Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
Column(Modifier.fillMaxSize().rememberVerticalScroll()) {
Column(Modifier.fillMaxWidth()) {
SectionHeader(if (isExpense) "Empfänger*in" else "Zahlende*r", false)

View File

@ -2,9 +2,7 @@ package net.codinux.banking.ui.screens
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.*
@ -21,6 +19,7 @@ import net.codinux.banking.ui.composables.tan.ImageSizeControls
import net.codinux.banking.ui.config.Colors
import net.codinux.banking.ui.config.DI
import net.codinux.banking.ui.extensions.ImeNext
import net.codinux.banking.ui.extensions.rememberVerticalScroll
import net.codinux.banking.ui.forms.CaptionText
import net.codinux.banking.ui.forms.OutlinedTextField
import net.codinux.banking.ui.forms.Select
@ -88,7 +87,7 @@ fun CreateEpcQrCodeScreen(onClosed: () -> Unit) {
FullscreenViewBase("EPC QR Code erstellen", "Schließen", onClosed = onClosed) {
Column(Modifier.fillMaxWidth().verticalScroll(rememberScrollState())) {
Column(Modifier.fillMaxWidth().rememberVerticalScroll()) {
if (epcQrCodeGeneratingError != null) {
Text("QR Code konnte nicht erstellt werden:${NewLine}$epcQrCodeGeneratingError", color = MaterialTheme.colors.error, modifier = Modifier.padding(vertical = 8.dp))
} else if (epcQrCodeBytes == null) {

View File

@ -5,26 +5,46 @@ import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontFamily
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import net.codinux.banking.ui.IOorDefault
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import net.codinux.banking.client.model.BankAccount
import net.codinux.banking.persistence.entities.BankAccountEntity
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.extensions.horizontalScroll
import net.codinux.banking.ui.extensions.verticalScroll
import net.codinux.banking.ui.forms.SectionHeader
import net.codinux.banking.ui.forms.Select
import net.codinux.banking.ui.model.Config.NewLine
@Composable
fun FeedbackScreen(onClosed: () -> Unit) {
val banks = DI.uiState.banks.collectAsState().value
val messageLog by remember { mutableStateOf(DI.bankingService.getMessageLog()) }
val displayedLogEntries by remember { derivedStateOf { messageLog.sortedBy { it.messageNumber } } }
val accountsWithMessageLog: List<BankAccount?> by remember(messageLog) { derivedStateOf {
listOf<BankAccount?>(null) + messageLog.mapNotNull { it.account }.toSet().toList()
} }
var selectedAccount by remember { mutableStateOf<BankAccount?>(null) }
val bankOfSelectedAccount by remember(selectedAccount) {
// TODO: MessageLogEntries of added accounts contain a BankAccount instead of a BankAccountEntity object
derivedStateOf { banks.firstOrNull { it.id == (selectedAccount as? BankAccountEntity)?.bankId } }
}
val displayedLogEntries by remember(messageLog, selectedAccount) { derivedStateOf {
messageLog.filter { selectedAccount == null || it.account == selectedAccount }.sortedBy { it.messageNumber }
} }
val displayedLogEntriesText by remember(displayedLogEntries) {
derivedStateOf { displayedLogEntries.map {
@ -34,27 +54,50 @@ fun FeedbackScreen(onClosed: () -> Unit) {
val clipboardManager = LocalClipboardManager.current
val coroutineScope = rememberCoroutineScope()
coroutineScope.launch(Dispatchers.IOorDefault) {
}
FullscreenViewBase("Feedback", onClosed = onClosed) {
Column(Modifier.fillMaxWidth()) {
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
Text("Man kann uns direkt aus der App heraus noch kein Feedback schicken (kommt noch), aber schon mal das Nachrichten-Protokoll, das ihr bei Fehlern an die (unfähgigen) Entwickler schicken könnt:", modifier = Modifier.padding(horizontal = 24.dp).padding(top = 8.dp), textAlign = TextAlign.Center)
SectionHeader("Nachrichten-Protokoll:") // TODO: add a Switch "Add message protocol"
if (messageLog.isEmpty()) {
Row(Modifier.fillMaxSize().padding(24.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center) {
Text("Sie haben noch keine Aktion wie das Abrufen der Kontoumsätze ausgeführt, deshalb gibt es noch kein Nachrichtenprotokoll zu diesen Aktionen", textAlign = TextAlign.Center)
}
} else {
Row(Modifier.fillMaxWidth().padding(top = 6.dp), verticalAlignment = Alignment.CenterVertically) {
Select(
"Konto",
accountsWithMessageLog,
selectedAccount,
{ account -> selectedAccount = account },
{ account -> account?.displayName ?: "Alle Konten" },
Modifier.weight(0.9f),
leadingIcon = bankOfSelectedAccount?.let { { BankIcon(bankOfSelectedAccount) } },
dropDownItemContent = { account ->
Row(verticalAlignment = Alignment.CenterVertically) {
BankIcon(banks.firstOrNull { it.id == (account as? BankAccountEntity)?.bankId }, Modifier.padding(end = 6.dp))
Text(account?.displayName ?: "")
}
},
)
Spacer(Modifier.weight(0.1f))
TextButton({ clipboardManager.setText(AnnotatedString(displayedLogEntriesText))}) {
Text("Kopieren", color = Colors.CodinuxSecondaryColor)
}
}
Column(Modifier.verticalScroll().horizontalScroll()) {
Column(Modifier.verticalScroll().horizontalScroll().padding(top = 8.dp)) {
SelectionContainer(modifier = Modifier.fillMaxSize()) {
Text(displayedLogEntriesText, fontFamily = FontFamily.Monospace)
}
}
}
}
}
}

View File

@ -291,7 +291,7 @@ class BankingService(
bankingRepository.updateAccount(account, response.balance ?: account.balance, response.transactionsRetrievalTime, response.retrievedTransactionsFrom ?: account.retrievedTransactionsFrom)
}
bankingRepository.updateBank(bank, bank.clientData)
bankingRepository.updateBank(bank, bank.serializedClientData)
notifyBanksListUpdated() // notify about updated values like account balance
} catch (e: Throwable) {