From 34cb8617f8ca0fa26d401a0b5b618b0ea75789d9 Mon Sep 17 00:00:00 2001 From: dankito Date: Sun, 30 Aug 2020 23:14:44 +0200 Subject: [PATCH] Implemented setting banks' display order --- .../net/dankito/banking/ui/model/Customer.kt | 6 ++++-- .../dankito/banking/ui/model/Displayable.kt | 8 +++++++ .../banking/ui/model/OrderedDisplayable.kt | 8 +++++++ .../dankito/banking/ui/model/tan/TanMedium.kt | 6 ++++-- .../banking/ui/model/tan/TanProcedure.kt | 6 ++++-- .../banking/ui/presenter/BankingPresenter.kt | 6 ++++++ .../net/dankito/banking/util/Extensions.kt | 7 +++++++ .../BankingiOSApp.xcdatamodel/contents | 2 ++ .../BankingiOSApp/persistence/AppData.swift | 7 +++++++ .../persistence/Extensions.swift | 21 +++++++++++++++++++ .../BankingiOSApp/persistence/Mapper.swift | 14 ++++++++++--- .../BankingiOSApp/ui/views/AccountsTab.swift | 2 +- .../ui/views/SettingsDialog.swift | 18 +++++++++------- .../banking/mapper/fints4kModelMapper.kt | 8 ++++++- 14 files changed, 101 insertions(+), 18 deletions(-) create mode 100644 ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Displayable.kt create mode 100644 ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/OrderedDisplayable.kt diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Customer.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Customer.kt index 4bcfb667..64494531 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Customer.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Customer.kt @@ -19,7 +19,7 @@ open class Customer( open var userId: String = customerId, open var iconUrl: String? = null, open var accounts: List = listOf() -) { +) : OrderedDisplayable { internal constructor() : this("", "", "", "", "", "", "") // for object deserializers @@ -45,9 +45,11 @@ open class Customer( open var userSetDisplayName: String? = null - open val displayName: String + override val displayName: String get() = userSetDisplayName ?: bankName + override var displayIndex: Int = 0 + open val balance: BigDecimal get() = accounts.map { it.balance }.sum() diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Displayable.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Displayable.kt new file mode 100644 index 00000000..d90138d2 --- /dev/null +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/Displayable.kt @@ -0,0 +1,8 @@ +package net.dankito.banking.ui.model + + +interface Displayable { + + val displayName: String + +} \ No newline at end of file diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/OrderedDisplayable.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/OrderedDisplayable.kt new file mode 100644 index 00000000..b833df92 --- /dev/null +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/OrderedDisplayable.kt @@ -0,0 +1,8 @@ +package net.dankito.banking.ui.model + + +interface OrderedDisplayable : Displayable { + + var displayIndex: Int + +} \ No newline at end of file diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/tan/TanMedium.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/tan/TanMedium.kt index 16f94467..29feb19a 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/tan/TanMedium.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/tan/TanMedium.kt @@ -1,10 +1,12 @@ package net.dankito.banking.ui.model.tan +import net.dankito.banking.ui.model.Displayable + open class TanMedium( - val displayName: String, + override val displayName: String, val status: TanMediumStatus -) { +) : Displayable { internal constructor() : this("", TanMediumStatus.Available) // for object deserializers diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/tan/TanProcedure.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/tan/TanProcedure.kt index ac383a71..88bc2048 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/tan/TanProcedure.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/model/tan/TanProcedure.kt @@ -1,13 +1,15 @@ package net.dankito.banking.ui.model.tan +import net.dankito.banking.ui.model.Displayable + open class TanProcedure( - val displayName: String, + override val displayName: String, val type: TanProcedureType, val bankInternalProcedureCode: String, val maxTanInputLength: Int? = null, val allowedTanFormat: AllowedTanFormat = AllowedTanFormat.Alphanumeric -) { +) : Displayable { internal constructor() : this("", TanProcedureType.EnterTan, "") // for object deserializers diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/presenter/BankingPresenter.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/presenter/BankingPresenter.kt index 8fb1fecf..af235c6a 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/presenter/BankingPresenter.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/ui/presenter/BankingPresenter.kt @@ -381,6 +381,12 @@ open class BankingPresenter( } + open fun allAccountsUpdated() { + customers.forEach { account -> + accountUpdated(account) + } + } + open fun accountUpdated(account: Customer) { persistAccount(account) } diff --git a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/util/Extensions.kt b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/util/Extensions.kt index 1010a826..637ee011 100644 --- a/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/util/Extensions.kt +++ b/ui/BankingUiCommon/src/commonMain/kotlin/net/dankito/banking/util/Extensions.kt @@ -1,5 +1,7 @@ package net.dankito.banking.util +import net.dankito.banking.ui.model.OrderedDisplayable + fun String.ofMaxLength(maxLength: Int): String { if(this.length > maxLength && maxLength > 0) { @@ -26,4 +28,9 @@ fun Collection.containsExactly(otherCollection: Collection): Boolean { } return true +} + + +fun Collection.sortedByDisplayIndex(): Collection { + return this.sortedBy { it.displayIndex } } \ No newline at end of file diff --git a/ui/BankingiOSApp/BankingiOSApp/BankingiOSApp.xcdatamodeld/BankingiOSApp.xcdatamodel/contents b/ui/BankingiOSApp/BankingiOSApp/BankingiOSApp.xcdatamodeld/BankingiOSApp.xcdatamodel/contents index 00a16290..f1a43629 100644 --- a/ui/BankingiOSApp/BankingiOSApp/BankingiOSApp.xcdatamodeld/BankingiOSApp.xcdatamodel/contents +++ b/ui/BankingiOSApp/BankingiOSApp/BankingiOSApp.xcdatamodeld/BankingiOSApp.xcdatamodel/contents @@ -42,6 +42,7 @@ + @@ -63,6 +64,7 @@ + diff --git a/ui/BankingiOSApp/BankingiOSApp/persistence/AppData.swift b/ui/BankingiOSApp/BankingiOSApp/persistence/AppData.swift index 8da1da5d..f4d568f5 100644 --- a/ui/BankingiOSApp/BankingiOSApp/persistence/AppData.swift +++ b/ui/BankingiOSApp/BankingiOSApp/persistence/AppData.swift @@ -7,6 +7,7 @@ class AppData : ObservableObject { @Inject private var presenter: BankingPresenterSwift @Published var banks: [Customer] = [] + @Published var banksSorted: [Customer] = [] @Published var hasAtLeastOneAccountBeenAdded: Bool = false @@ -24,9 +25,15 @@ class AppData : ObservableObject { private func setFieldsForBanks(_ banks: [Customer]) { self.banks = presenter.customers + self.banksSorted = banks.sortedByDisplayIndex() hasAtLeastOneAccountBeenAdded = banks.isNotEmpty hasAccountsThatSupportTransferringMoney = banks.flatMap { $0.accounts }.first(where: { $0.supportsTransferringMoney }) != nil } + + func banksDisplayIndexChanged() { + self.banksSorted = banks.sortedByDisplayIndex() + } + } diff --git a/ui/BankingiOSApp/BankingiOSApp/persistence/Extensions.swift b/ui/BankingiOSApp/BankingiOSApp/persistence/Extensions.swift index 882fc8ea..8d3bb84b 100644 --- a/ui/BankingiOSApp/BankingiOSApp/persistence/Extensions.swift +++ b/ui/BankingiOSApp/BankingiOSApp/persistence/Extensions.swift @@ -71,6 +71,27 @@ extension Array where Element == AccountTransaction { } +extension Array where Element: OrderedDisplayable { + + func sortedByDisplayIndex() -> [Element] { + return self.sorted { $0.displayIndex <= $1.displayIndex } + } + + + func reorder(from sourceIndices: IndexSet, to destinationIndex: Int) -> [Element] { + var elements = self + + elements.move(fromOffsets: sourceIndices, toOffset: destinationIndex) + + for (index, element) in elements.enumerated() { + element.displayIndex = Int32(index) + } + + return elements.sortedByDisplayIndex() + } + +} + extension BankInfo : Identifiable { diff --git a/ui/BankingiOSApp/BankingiOSApp/persistence/Mapper.swift b/ui/BankingiOSApp/BankingiOSApp/persistence/Mapper.swift index f2773577..3d6ab8c9 100644 --- a/ui/BankingiOSApp/BankingiOSApp/persistence/Mapper.swift +++ b/ui/BankingiOSApp/BankingiOSApp/persistence/Mapper.swift @@ -4,7 +4,7 @@ import BankingUiSwift class Mapper { - + /* Cache mapped object to not save them twice */ private var mappedBanks = [Customer:PersistedCustomer]() @@ -19,14 +19,17 @@ class Mapper { let mapped = Customer(bankCode: map(customer.bankCode), customerId: map(customer.customerId), password: map(customer.password), finTsServerAddress: map(customer.finTsServerAddress), bankName: map(customer.bankName), bic: map(customer.bic), customerName: map(customer.customerName), userId: map(customer.userId), iconUrl: customer.iconUrl, accounts: []) mapped.userSetDisplayName = customer.userSetDisplayName + mapped.displayIndex = customer.displayIndex mapped.accounts = map(mapped, customer.accounts?.array as? [PersistedBankAccount]) - mappedBanks[mapped] = customer - mapped.supportedTanProcedures = map(customer.supportedTanProcedures?.array as? [PersistedTanProcedure]) mapped.selectedTanProcedure = mapped.supportedTanProcedures.first(where: { $0.bankInternalProcedureCode == customer.selectedTanProcedureCode }) + mapped.technicalId = customer.objectIDAsString + + mappedBanks[mapped] = customer + return mapped } @@ -44,6 +47,7 @@ class Mapper { mapped.iconUrl = customer.iconUrl mapped.userSetDisplayName = customer.userSetDisplayName + mapped.displayIndex = customer.displayIndex mapped.accounts = NSOrderedSet(array: map(mapped, customer.accounts, context)) @@ -66,9 +70,12 @@ class Mapper { mapped.haveAllTransactionsBeenFetched = account.haveAllTransactionsBeenFetched mapped.userSetDisplayName = account.userSetDisplayName + mapped.displayIndex = account.displayIndex mapped.bookedTransactions = map(mapped, account.transactions as? Set) + mapped.technicalId = account.objectIDAsString + mappedAccounts[mapped] = account return mapped @@ -101,6 +108,7 @@ class Mapper { mapped.haveAllTransactionsBeenFetched = account.haveAllTransactionsBeenFetched mapped.userSetDisplayName = account.userSetDisplayName + mapped.displayIndex = account.displayIndex mapped.transactions = NSSet(array: map(mapped, account.bookedTransactions, context)) diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/AccountsTab.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/AccountsTab.swift index 07aefea2..a94bcf45 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/views/AccountsTab.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/AccountsTab.swift @@ -22,7 +22,7 @@ struct AccountsTab: View { Form { AllBanksListItem(banks: data.banks) - ForEach(data.banks) { bank in + ForEach(data.banks.sortedByDisplayIndex()) { bank in BankListItem(bank: bank) } diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/SettingsDialog.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/SettingsDialog.swift index 8bbd564b..3187cfc4 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/views/SettingsDialog.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/SettingsDialog.swift @@ -9,11 +9,6 @@ struct SettingsDialog: View { @Inject var presenter: BankingPresenterSwift - private var banksSorted: [Customer] { - return data.banks.sorted { $0.displayIndex <= $1.displayIndex } - } - - @State private var askToDeleteAccountMessage: Message? = nil @@ -21,11 +16,12 @@ struct SettingsDialog: View { Form { Section(header: EditButton().frame(maxWidth: .infinity, alignment: .trailing) .overlay(Text("Bank Credentials"), alignment: .leading)) { - ForEach(banksSorted) { bank in + ForEach(data.banksSorted) { bank in NavigationLink(destination: LazyView(BankSettingsDialog(bank))) { IconedTitleView(bank) } } + .onMove(perform: reorderBanks) .onDelete(perform: deleteBanks) } } @@ -36,9 +32,17 @@ struct SettingsDialog: View { } + func reorderBanks(from sourceIndices: IndexSet, to destinationIndex: Int) { + let _ = data.banksSorted.reorder(from: sourceIndices, to: destinationIndex) + + data.banksDisplayIndexChanged() + + presenter.allAccountsUpdated() + } + func deleteBanks(at offsets: IndexSet) { for offset in offsets { - let bankToDelete = banksSorted[offset] + let bankToDelete = data.banksSorted[offset] askUserToDeleteAccount(bankToDelete) } } diff --git a/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/mapper/fints4kModelMapper.kt b/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/mapper/fints4kModelMapper.kt index 7729deb9..e2f3f373 100644 --- a/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/mapper/fints4kModelMapper.kt +++ b/ui/fints4kBankingClient/src/commonMain/kotlin/net/dankito/banking/mapper/fints4kModelMapper.kt @@ -91,7 +91,13 @@ open class fints4kModelMapper { open fun mapBankAccounts(customer: Customer, accountData: List): List { - return accountData.map { mapBankAccount(customer, it) } + return accountData.mapIndexed { index, account -> + val mappedAccount = mapBankAccount(customer, account) + + mappedAccount.displayIndex = index + + mappedAccount + } } open fun mapBankAccount(customer: Customer, accountData: AccountData): BankAccount {