244 lines
10 KiB
Swift
244 lines
10 KiB
Swift
import SwiftUI
|
|
import BankingUiSwift
|
|
|
|
|
|
struct AccountTransactionsDialog: View {
|
|
|
|
static private let DoNotShowFetchAllTransactionsOverlayForUserDefaultsKeyPrefix = "DoNotShowFetchAllTransactionsOverlayFor_"
|
|
|
|
|
|
private let title: String
|
|
|
|
private let allTransactions: [IAccountTransaction]
|
|
|
|
@State private var balanceOfAllTransactions: CommonBigDecimal
|
|
|
|
private let areMoreThanOneBanksTransactionsDisplayed: Bool
|
|
|
|
|
|
@State private var haveAllTransactionsBeenFetched: Bool
|
|
|
|
@State private var showFetchAllTransactionsOverlay: Bool
|
|
|
|
@State private var accountsForWhichNotAllTransactionsHaveBeenFetched: [IBankAccount]
|
|
|
|
|
|
@State private var filteredTransactions: [IAccountTransaction]
|
|
|
|
@State private var balanceOfFilteredTransactions: CommonBigDecimal
|
|
|
|
@State private var searchText = ""
|
|
|
|
private var searchTextBinding: Binding<String> {
|
|
Binding<String>(
|
|
get: { self.searchText },
|
|
set: {
|
|
self.searchText = $0
|
|
self.filterTransactions($0)
|
|
})
|
|
}
|
|
|
|
|
|
@State private var errorMessage: Message? = nil
|
|
|
|
|
|
@Inject private var presenter: BankingPresenterSwift
|
|
|
|
|
|
init(allBanks: [ICustomer]) {
|
|
let allAccounts = allBanks.flatMap { $0.accounts }
|
|
|
|
self.init("All accounts", allAccounts.flatMap { $0.bookedTransactions }, allBanks.sumBalances(), allAccounts.filter { $0.haveAllTransactionsBeenFetched == false })
|
|
|
|
presenter.selectedAllBankAccounts()
|
|
}
|
|
|
|
init(bank: ICustomer) {
|
|
self.init(bank.displayName, bank.accounts.flatMap { $0.bookedTransactions }, bank.balance, bank.accounts.filter { $0.haveAllTransactionsBeenFetched == false })
|
|
|
|
presenter.selectedAccount(customer: bank)
|
|
}
|
|
|
|
init(account: IBankAccount) {
|
|
self.init(account.displayName, account.bookedTransactions, account.balance, account.haveAllTransactionsBeenFetched ? [] : [account])
|
|
|
|
presenter.selectedBankAccount(bankAccount: account)
|
|
}
|
|
|
|
fileprivate init(_ title: String, _ transactions: [IAccountTransaction], _ balance: CommonBigDecimal, _ accountsForWhichNotAllTransactionsHaveBeenFetched: [IBankAccount] = []) {
|
|
self.title = title
|
|
|
|
self.allTransactions = transactions
|
|
self._filteredTransactions = State(initialValue: transactions.sorted { $0.valueDate.date > $1.valueDate.date })
|
|
|
|
self._balanceOfAllTransactions = State(initialValue: balance)
|
|
self._balanceOfFilteredTransactions = State(initialValue: balance)
|
|
|
|
self.areMoreThanOneBanksTransactionsDisplayed = Set(allTransactions.compactMap { $0.bankAccount }.compactMap { $0.customer as! Customer }).count > 1
|
|
|
|
_accountsForWhichNotAllTransactionsHaveBeenFetched = State(initialValue: accountsForWhichNotAllTransactionsHaveBeenFetched)
|
|
_haveAllTransactionsBeenFetched = State(initialValue: accountsForWhichNotAllTransactionsHaveBeenFetched.isEmpty)
|
|
_showFetchAllTransactionsOverlay = State(initialValue: accountsForWhichNotAllTransactionsHaveBeenFetched.isNotEmpty)
|
|
}
|
|
|
|
|
|
var body: some View {
|
|
VStack {
|
|
Form {
|
|
Section {
|
|
SearchBarWithLabel(searchTextBinding, returnKeyType: .done) {
|
|
HStack {
|
|
Text("\(String(self.filteredTransactions.count)) transactions")
|
|
.styleAsDetail()
|
|
|
|
Spacer()
|
|
|
|
AmountLabel(amount: self.balanceOfFilteredTransactions)
|
|
}
|
|
}
|
|
}
|
|
|
|
Section {
|
|
ForEach(filteredTransactions, id: \.technicalId) { transaction in
|
|
AccountTransactionListItem(transaction, self.areMoreThanOneBanksTransactionsDisplayed)
|
|
}
|
|
}
|
|
|
|
if haveAllTransactionsBeenFetched == false && showFetchAllTransactionsOverlay == false {
|
|
Section {
|
|
HStack {
|
|
Spacer()
|
|
|
|
fetchAllTransactionsButton
|
|
|
|
Spacer()
|
|
}
|
|
}
|
|
.frame(maxWidth: .infinity, minHeight: 40)
|
|
.systemGroupedBackground()
|
|
.listRowInsets(EdgeInsets())
|
|
}
|
|
}
|
|
.systemGroupedBackground()
|
|
|
|
if showFetchAllTransactionsOverlay {
|
|
VStack {
|
|
Spacer()
|
|
|
|
HStack(alignment: .center) {
|
|
Button(action: { self.doNotShowFetchAllTransactionsOverlayAnymore() }) {
|
|
Text("x")
|
|
.bold()
|
|
}
|
|
|
|
Spacer()
|
|
|
|
fetchAllTransactionsButton
|
|
|
|
Spacer()
|
|
}
|
|
.padding(.horizontal, 6)
|
|
|
|
Spacer()
|
|
}
|
|
.frame(height: 40)
|
|
.padding(0)
|
|
.systemGroupedBackground()
|
|
.overlay(Divider(color: Color.gray), alignment: .top)
|
|
}
|
|
}
|
|
.executeMutatingMethod {
|
|
self.showFetchAllTransactionsOverlay = self.shouldShowFetchAllTransactionsOverlay
|
|
}
|
|
.alert(message: $errorMessage)
|
|
.showNavigationBarTitle(LocalizedStringKey(title))
|
|
.navigationBarItems(trailing: UpdateButton { _, executingDone in self.updateTransactions(executingDone) })
|
|
}
|
|
|
|
|
|
private var fetchAllTransactionsButton: some View {
|
|
Button("Fetch all account transactions") {
|
|
self.fetchAllTransactions(self.accountsForWhichNotAllTransactionsHaveBeenFetched)
|
|
}
|
|
}
|
|
|
|
|
|
private func updateTransactions(_ executingDone: @escaping () -> Void) {
|
|
presenter.updateSelectedBankAccountTransactionsAsync { response in
|
|
executingDone()
|
|
|
|
self.balanceOfAllTransactions = self.presenter.balanceOfSelectedBankAccounts
|
|
|
|
if response.successful {
|
|
self.filterTransactions(self.searchText)
|
|
}
|
|
else if response.userCancelledAction == false {
|
|
if let failedAccount = getAccountThatFailed(response) {
|
|
self.errorMessage = Message(title: Text("Could not fetch latest transactions"), message: Text("Could not fetch latest transactions for \(failedAccount.displayName). Error message from your bank: \(response.errorToShowToUser ?? "")."))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func fetchAllTransactions(_ accounts: [IBankAccount]) {
|
|
accounts.forEach { account in
|
|
presenter.fetchAllAccountTransactionsAsync(bankAccount: account, callback: self.handleGetAllTransactionsResult)
|
|
}
|
|
}
|
|
|
|
private func handleGetAllTransactionsResult(_ response: GetTransactionsResponse) {
|
|
self.accountsForWhichNotAllTransactionsHaveBeenFetched = self.accountsForWhichNotAllTransactionsHaveBeenFetched.filter { $0.haveAllTransactionsBeenFetched == false }
|
|
self.haveAllTransactionsBeenFetched = self.accountsForWhichNotAllTransactionsHaveBeenFetched.isEmpty
|
|
self.showFetchAllTransactionsOverlay = shouldShowFetchAllTransactionsOverlay
|
|
|
|
if response.successful {
|
|
self.filterTransactions(self.searchText)
|
|
}
|
|
else if response.userCancelledAction == false {
|
|
if let failedAccount = getAccountThatFailed(response) {
|
|
self.errorMessage = Message(title: Text("Could not fetch all transactions"), message: Text("Could not fetch all transactions for \(failedAccount.displayName). Error message from your bank: \(response.errorToShowToUser ?? "")."))
|
|
}
|
|
}
|
|
}
|
|
|
|
private func filterTransactions(_ query: String) {
|
|
self.filteredTransactions = presenter.searchSelectedAccountTransactions(query: query).sorted { $0.valueDate.date > $1.valueDate.date }
|
|
|
|
self.balanceOfFilteredTransactions = query.isBlank ? balanceOfAllTransactions : filteredTransactions.sumAmounts()
|
|
}
|
|
|
|
private func getAccountThatFailed(_ response: GetTransactionsResponse) -> IBankAccount? {
|
|
return response.retrievedData.first { $0.successfullyRetrievedData == false }?.account
|
|
}
|
|
|
|
|
|
private func doNotShowFetchAllTransactionsOverlayAnymore() {
|
|
for account in accountsForWhichNotAllTransactionsHaveBeenFetched {
|
|
UserDefaults.standard.set(true, forKey: Self.DoNotShowFetchAllTransactionsOverlayForUserDefaultsKeyPrefix + account.technicalId)
|
|
}
|
|
|
|
showFetchAllTransactionsOverlay = false
|
|
}
|
|
|
|
private var shouldShowFetchAllTransactionsOverlay: Bool {
|
|
if accountsForWhichNotAllTransactionsHaveBeenFetched.isNotEmpty {
|
|
var copy = accountsForWhichNotAllTransactionsHaveBeenFetched
|
|
|
|
copy.removeAll { UserDefaults.standard.bool(forKey: Self.DoNotShowFetchAllTransactionsOverlayForUserDefaultsKeyPrefix + $0.technicalId, defaultValue: false) }
|
|
|
|
return copy.isNotEmpty
|
|
}
|
|
|
|
return false
|
|
}
|
|
}
|
|
|
|
|
|
struct AccountTransactionsDialog_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
AccountTransactionsDialog(previewBanks[0].displayName, [
|
|
AccountTransaction(bankAccount: previewBanks[0].accounts[0] as! BankAccount, amount: CommonBigDecimal(double: 1234.56), currency: "€", unparsedUsage: "Usage", bookingDate: CommonDate(year: 2020, month: 5, day: 7), otherPartyName: "Marieke Musterfrau", otherPartyBankCode: nil, otherPartyAccountId: nil, bookingText: "SEPA Ueberweisung", valueDate: CommonDate(year: 2020, month: 5, day: 7))
|
|
],
|
|
CommonBigDecimal(double: 84.12))
|
|
}
|
|
}
|