diff --git a/BankingPersistence/src/commonMain/kotlin/net/codinux/banking/persistence/BankingRepository.kt b/BankingPersistence/src/commonMain/kotlin/net/codinux/banking/persistence/BankingRepository.kt index 86fa639..413ae36 100644 --- a/BankingPersistence/src/commonMain/kotlin/net/codinux/banking/persistence/BankingRepository.kt +++ b/BankingPersistence/src/commonMain/kotlin/net/codinux/banking/persistence/BankingRepository.kt @@ -13,6 +13,7 @@ import net.codinux.banking.persistence.entities.BankAccessEntity import net.codinux.banking.persistence.entities.UiSettingsEntity import net.codinux.banking.ui.model.AccountTransactionViewModel import net.codinux.banking.ui.model.settings.AppSettings +import net.codinux.banking.ui.model.settings.ImageSettings interface BankingRepository { @@ -25,6 +26,11 @@ interface BankingRepository { suspend fun saveUiSettings(settings: UiSettingsEntity) + fun getImageSettings(id: String): ImageSettings? + + suspend fun saveImageSettings(settings: ImageSettings) + + fun getAllBanks(): List suspend fun persistBank(bank: BankAccess): BankAccessEntity diff --git a/BankingPersistence/src/commonMain/kotlin/net/codinux/banking/persistence/InMemoryBankingRepository.kt b/BankingPersistence/src/commonMain/kotlin/net/codinux/banking/persistence/InMemoryBankingRepository.kt index 123b12f..f8b9bc7 100644 --- a/BankingPersistence/src/commonMain/kotlin/net/codinux/banking/persistence/InMemoryBankingRepository.kt +++ b/BankingPersistence/src/commonMain/kotlin/net/codinux/banking/persistence/InMemoryBankingRepository.kt @@ -13,6 +13,7 @@ import net.codinux.banking.persistence.entities.BankAccessEntity import net.codinux.banking.persistence.entities.UiSettingsEntity import net.codinux.banking.ui.model.AccountTransactionViewModel import net.codinux.banking.ui.model.settings.AppSettings +import net.codinux.banking.ui.model.settings.ImageSettings import net.codinux.banking.ui.model.settings.TransactionsGrouping class InMemoryBankingRepository( @@ -29,6 +30,8 @@ class InMemoryBankingRepository( private var uiSettings: UiSettingsEntity = UiSettingsEntity(true, TransactionsGrouping.Month, true, true, true) + private var imageSettings = mutableMapOf() + override fun getAppSettings(): AppSettings? = appSettings @@ -43,6 +46,13 @@ class InMemoryBankingRepository( } + override fun getImageSettings(id: String) = imageSettings[id] + + override suspend fun saveImageSettings(settings: ImageSettings) { + imageSettings[settings.id] = settings + } + + override fun getAllBanks(): List = banks.toList() override suspend fun persistBank(bank: BankAccess): BankAccessEntity { diff --git a/BankingPersistence/src/commonMain/kotlin/net/codinux/banking/persistence/SqliteBankingRepository.kt b/BankingPersistence/src/commonMain/kotlin/net/codinux/banking/persistence/SqliteBankingRepository.kt index 37a631f..d3a6bc2 100644 --- a/BankingPersistence/src/commonMain/kotlin/net/codinux/banking/persistence/SqliteBankingRepository.kt +++ b/BankingPersistence/src/commonMain/kotlin/net/codinux/banking/persistence/SqliteBankingRepository.kt @@ -23,9 +23,9 @@ import net.codinux.banking.client.model.tan.TanMethod import net.codinux.banking.client.model.tan.TanMethodType import net.codinux.banking.persistence.entities.* import net.codinux.banking.ui.model.AccountTransactionViewModel -import net.codinux.banking.ui.model.settings.AppAuthenticationMethod +import net.codinux.banking.ui.model.settings.* import net.codinux.banking.ui.model.settings.AppSettings -import net.codinux.banking.ui.model.settings.TransactionsGrouping +import net.codinux.banking.ui.model.settings.ImageSettings import net.codinux.log.logger import kotlin.enums.EnumEntries import kotlin.js.JsName @@ -39,7 +39,7 @@ open class SqliteBankingRepository : BankingRepository { private val schema = BankmeisterDb.Schema - private val sqlDriver = createSqlDriverDriver("Bankmeister.db", schema, 1L) + private val sqlDriver = createSqlDriverDriver("Bankmeister.db", schema, 2L) private val database = BankmeisterDb(sqlDriver) @@ -89,6 +89,16 @@ open class SqliteBankingRepository : BankingRepository { } + override fun getImageSettings(id: String): ImageSettings? = + settingsQueries.getImageSettings(id) { height, frequency -> + ImageSettings(id, mapToInt(height), mapToInt(frequency)) + }.executeAsOneOrNull() + + override suspend fun saveImageSettings(settings: ImageSettings) { + settingsQueries.upsertImageSettings(settings.id, mapInt(settings.height), mapInt(settings.frequency)) + } + + override fun getAllBanks(): List { val bankAccounts = getAllBankAccounts().groupBy { it.bankId } val tanMethods = getAllTanMethods().groupBy { it.bankId }.mapValues { it.value.toMutableList() } diff --git a/BankingPersistence/src/commonMain/kotlin/net/codinux/banking/ui/model/settings/ImageSettings.kt b/BankingPersistence/src/commonMain/kotlin/net/codinux/banking/ui/model/settings/ImageSettings.kt new file mode 100644 index 0000000..9ae4737 --- /dev/null +++ b/BankingPersistence/src/commonMain/kotlin/net/codinux/banking/ui/model/settings/ImageSettings.kt @@ -0,0 +1,11 @@ +package net.codinux.banking.ui.model.settings + +class ImageSettings( + val id: String, + + var height: Int, + + var frequency: Int? = null // only needed for flicker code +) { + override fun toString() = "$id $height${if (frequency != null) " (frequency = $frequency)" else ""}" +} \ No newline at end of file diff --git a/BankingPersistence/src/commonMain/sqldelight/migrations/1.sqm b/BankingPersistence/src/commonMain/sqldelight/migrations/1.sqm new file mode 100644 index 0000000..409212e --- /dev/null +++ b/BankingPersistence/src/commonMain/sqldelight/migrations/1.sqm @@ -0,0 +1,9 @@ + +CREATE TABLE IF NOT EXISTS ImageSettings ( + id TEXT PRIMARY KEY, + + height INTEGER NOT NULL, + width INTEGER, -- not used right now, add it just in case + + frequency INTEGER +); \ No newline at end of file diff --git a/BankingPersistence/src/commonMain/sqldelight/net/codinux/banking/persistence/Settings.sq b/BankingPersistence/src/commonMain/sqldelight/net/codinux/banking/persistence/Settings.sq index a7f32b3..7923972 100644 --- a/BankingPersistence/src/commonMain/sqldelight/net/codinux/banking/persistence/Settings.sq +++ b/BankingPersistence/src/commonMain/sqldelight/net/codinux/banking/persistence/Settings.sq @@ -70,3 +70,22 @@ SELECT * FROM UiSettings WHERE id = 1; upsertUiSettings: INSERT OR REPLACE INTO UiSettings(id, transactionsGrouping, showBalance, showBankIcons, showColoredAmounts, showTransactionsInAlternatingColors) VALUES (1, ?, ?, ?, ?, ?); + + + +CREATE TABLE IF NOT EXISTS ImageSettings ( + id TEXT PRIMARY KEY, + + height INTEGER NOT NULL, + width INTEGER, -- not used right now, add it just in case + + frequency INTEGER +); + + +getImageSettings: +SELECT height, frequency FROM ImageSettings WHERE id = ?; + +upsertImageSettings: +INSERT OR REPLACE INTO ImageSettings(id, height, frequency) +VALUES (?, ?, ?); diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/tan/ChipTanFlickerCodeView.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/tan/ChipTanFlickerCodeView.kt index 165e87e..b05f4d2 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/tan/ChipTanFlickerCodeView.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/tan/ChipTanFlickerCodeView.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import net.codinux.banking.client.model.tan.FlickerCode +import net.codinux.banking.ui.config.DI import net.codinux.banking.ui.model.Config.NewLine import net.codinux.banking.ui.service.tan.Bit import net.codinux.banking.ui.service.tan.FlickerCodeAnimator @@ -22,23 +23,38 @@ import net.codinux.banking.ui.service.tan.Step private const val FrequencyStepSize = 2 +private val DefaultStripesHeight = 240.dp private val StripesHeightStepSize = 7.dp private val StripesWidthStepSize = 2.dp private val SpaceBetweenStripesStepSize = 1.dp +private val bankingService = DI.bankingService + @Composable fun ChipTanFlickerCodeView(flickerCode: FlickerCode, textColor: Color = Color.Black) { val animator = remember { FlickerCodeAnimator() } - var stripesHeight by remember { mutableStateOf(240.dp) } + val flickerCodeSettings by remember { mutableStateOf(bankingService.getImageSettingsOrCreateDefault("FlickerCode", 240)) } - var stripesWidth by remember { mutableStateOf(45.dp) } + val sizeFactor by remember { mutableStateOf( + if (flickerCodeSettings.height == 240) { + 1 + } else { + val diff = flickerCodeSettings.height.dp - DefaultStripesHeight - var spaceBetweenStripes by remember { mutableStateOf(15.dp) } + (diff / StripesHeightStepSize).toInt() + } + ) } - var frequency by remember { mutableStateOf(FlickerCodeAnimator.DefaultFrequency) } + var stripesHeight by remember { mutableStateOf(DefaultStripesHeight + StripesHeightStepSize.times(sizeFactor)) } + + var stripesWidth by remember { mutableStateOf(45.dp + StripesWidthStepSize.times(sizeFactor)) } + + var spaceBetweenStripes by remember { mutableStateOf(15.dp + SpaceBetweenStripesStepSize.times(sizeFactor)) } + + var frequency by remember { mutableStateOf(flickerCodeSettings.frequency ?: FlickerCodeAnimator.DefaultFrequency) } var isPaused by remember { mutableStateOf(false) } @@ -51,6 +67,9 @@ fun ChipTanFlickerCodeView(flickerCode: FlickerCode, textColor: Color = Color.Bl stripesWidth = width stripesHeight = height spaceBetweenStripes = spaceBetween + + flickerCodeSettings.height = height.value.toInt() + bankingService.saveImageSettingsDebounced(flickerCodeSettings, coroutineScope) } fun decreaseSize() { @@ -76,6 +95,9 @@ fun ChipTanFlickerCodeView(flickerCode: FlickerCode, textColor: Color = Color.Bl frequency = newFrequency animator.setFrequency(newFrequency) + + flickerCodeSettings.frequency = newFrequency + bankingService.saveImageSettingsDebounced(flickerCodeSettings, coroutineScope) } fun decreaseFrequency() { diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/tan/ImageView.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/tan/ImageView.kt index 2e2c157..7424e76 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/tan/ImageView.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/tan/ImageView.kt @@ -9,11 +9,16 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.dp import net.codinux.banking.ui.config.Colors +import net.codinux.banking.ui.config.DI import net.codinux.banking.ui.service.createImageBitmap + +private val bankingService = DI.bankingService + @Composable fun ImageView( imageBytes: ByteArray, + imageSettingsId: String, contentDescription: String, initialImageHeight: Int = 300, minImageHeight: Int = 0, @@ -22,11 +27,19 @@ fun ImageView( textColor: Color = Colors.MaterialThemeTextColor, ) { - var imageHeight by remember { mutableStateOf(initialImageHeight) } + val imageSettings = bankingService.getImageSettingsOrCreateDefault(imageSettingsId, initialImageHeight) + + var imageHeight by remember { mutableStateOf(imageSettings.height) } + + val coroutineScope = rememberCoroutineScope() fun changeImageSize(by: Int) { imageHeight += by + + imageSettings.height = imageHeight + + bankingService.saveImageSettingsDebounced(imageSettings, coroutineScope) } diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/config/DI.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/config/DI.kt index ac4ac62..53e6af7 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/config/DI.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/config/DI.kt @@ -36,7 +36,7 @@ object DI { var bankingRepository: BankingRepository = InMemoryBankingRepository(emptyList()) - val bankingService by lazy { BankingService(uiState, uiSettings, bankingRepository, bankFinder) } + val bankingService by lazy { BankingService(uiState, uiSettings, uiService, bankingRepository, bankFinder) } fun setRepository(repository: BankingRepository) { diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/EnterTanDialog.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/EnterTanDialog.kt index ec250b2..2c57050 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/EnterTanDialog.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/dialogs/EnterTanDialog.kt @@ -147,7 +147,8 @@ fun EnterTanDialog(tanChallengeReceived: TanChallengeReceived, onDismiss: () -> tanImage.imageBytesBase64?.let { imageBytesBase64 -> val imageBytes = Base64.decode(imageBytesBase64) - ImageView(imageBytes, "Bild mit enkodierter TAN", 250, 100, 500, textColor = textColor) + // if it becomes necessary may also add the bank to ImageSettings.id to make ImageSettings bank specific + ImageView(imageBytes, challenge.selectedTanMethod.type.toString(), "Bild mit enkodierter TAN", 250, 100, 500, textColor = textColor) } } } diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/CreateEpcQrCodeScreen.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/CreateEpcQrCodeScreen.kt index 57f476f..d4fbc9e 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/CreateEpcQrCodeScreen.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/screens/CreateEpcQrCodeScreen.kt @@ -86,7 +86,7 @@ fun CreateEpcQrCodeScreen(onClosed: () -> Unit) { Text("Mit EPC QR Codes, welche als GiroCode, scan2code, ... vermarktet werden, können Überweisungsdaten ganz einfach von Banking Apps eingelesen werden.") Text("Hier können Sie Ihren eigenen erstellen, so dass jemand Ihre Überweisungsdaten einlesen und Ihnen ganz schnell Geld überweisen kann.") } else { - ImageView(epcQrCodeBytes!!, "Erzeugter EPC QR Code", 350, 100, 700) + ImageView(epcQrCodeBytes!!, "EpcQrCode", "Erzeugter EPC QR Code", 350, 100, 700) Row(Modifier.fillMaxWidth().padding(top = 8.dp), horizontalArrangement = Arrangement.Center) { Text("Scannen Sie diesen Code auf einem anderen Gerät mit einer Banking App, z. B. Bankmeister", textAlign = TextAlign.Center, modifier = Modifier.padding(horizontal = 16.dp).padding(top = 8.dp)) 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 8a7f1a6..508683f 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 @@ -28,6 +28,7 @@ import net.codinux.banking.ui.model.BankInfo import net.codinux.banking.ui.model.error.* import net.codinux.banking.ui.model.events.AccountTransactionsRetrievedEvent import net.codinux.banking.ui.model.events.TransferredMoneyEvent +import net.codinux.banking.ui.model.settings.ImageSettings import net.codinux.banking.ui.model.settings.AppSettings import net.codinux.banking.ui.settings.UiSettings import net.codinux.banking.ui.state.UiState @@ -39,6 +40,7 @@ import org.jetbrains.compose.resources.ExperimentalResourceApi class BankingService( private val uiState: UiState, private val uiSettings: UiSettings, + private val uiService: UiService, private val bankingRepository: BankingRepository, private val bankFinder: BankFinder ) { @@ -93,6 +95,22 @@ class BankingService( )) + fun getImageSettingsOrCreateDefault(id: String, defaultHeight: Int): ImageSettings = + bankingRepository.getImageSettings(id) ?: ImageSettings(id, defaultHeight) + + fun saveImageSettingsDebounced(settings: ImageSettings, coroutineScope: CoroutineScope) { + uiService.debounce(coroutineScope, 1_000) { + saveImageSettings(settings) + } + } + + suspend fun saveImageSettings(settings: ImageSettings) = try { + bankingRepository.saveImageSettings(settings) + } catch (e: Throwable) { + log.error(e) { "Could not save ImageSettings ${settings.id}"} + } + + fun getAllBanks() = bankingRepository.getAllBanks() suspend fun updateBank(bank: BankAccessEntity, loginName: String, password: String, bankName: String?) { diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/service/UiService.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/service/UiService.kt index 29193f0..562a292 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/service/UiService.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/service/UiService.kt @@ -4,9 +4,9 @@ import kotlinx.coroutines.* class UiService { - fun debounce(coroutineScope: CoroutineScope, action: () -> Unit): Job { + fun debounce(coroutineScope: CoroutineScope, delayMillis: Long = 250, action: suspend () -> Unit): Job { return coroutineScope.launch { - delay(250) + delay(delayMillis) action() } }