diff --git a/ui/BankingiOSApp/BankingiOSApp.xcodeproj/project.pbxproj b/ui/BankingiOSApp/BankingiOSApp.xcodeproj/project.pbxproj index 9d7fe55f..8eb3e0ae 100644 --- a/ui/BankingiOSApp/BankingiOSApp.xcodeproj/project.pbxproj +++ b/ui/BankingiOSApp/BankingiOSApp.xcodeproj/project.pbxproj @@ -61,6 +61,14 @@ 36C4009D24D3236B005227AD /* UrlUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C4009C24D3236B005227AD /* UrlUtil.swift */; }; 36E21ECB24D88DF000649DC8 /* UIKitExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E21ECA24D88DF000649DC8 /* UIKitExtensions.swift */; }; 36E21ECF24DA0EEE00649DC8 /* IconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E21ECE24DA0EEE00649DC8 /* IconView.swift */; }; + 36E21ED124DC540400649DC8 /* SettingsDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E21ED024DC540400649DC8 /* SettingsDialog.swift */; }; + 36E21ED524DC549800649DC8 /* BankSettingsDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E21ED424DC549800649DC8 /* BankSettingsDialog.swift */; }; + 36E21ED724DC617200649DC8 /* BankAccountSettingsDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E21ED624DC617200649DC8 /* BankAccountSettingsDialog.swift */; }; + 36E21EDB24DC990300649DC8 /* LabelledUIKitTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E21EDA24DC990300649DC8 /* LabelledUIKitTextField.swift */; }; + 36E21EDD24DCA89100649DC8 /* TanProcedurePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E21EDC24DCA89100649DC8 /* TanProcedurePicker.swift */; }; + 36E21EDF24DCCC2700649DC8 /* CheckmarkListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E21EDE24DCCC2700649DC8 /* CheckmarkListItem.swift */; }; + 36E21EE324DEC7A700649DC8 /* CoreDataBankingPersistenceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E21EE224DEC7A700649DC8 /* CoreDataBankingPersistenceTest.swift */; }; + 36E21EE524DEC89400649DC8 /* CoreDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E21EE424DEC89400649DC8 /* CoreDataManager.swift */; }; 36E7BA1424B3D05C00757859 /* ViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E7BA1324B3D05C00757859 /* ViewExtensions.swift */; }; 36FC929C24B39A05002B12E9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36FC929B24B39A05002B12E9 /* AppDelegate.swift */; }; 36FC929E24B39A05002B12E9 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36FC929D24B39A05002B12E9 /* SceneDelegate.swift */; }; @@ -164,6 +172,14 @@ 36C4009C24D3236B005227AD /* UrlUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UrlUtil.swift; sourceTree = ""; }; 36E21ECA24D88DF000649DC8 /* UIKitExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitExtensions.swift; sourceTree = ""; }; 36E21ECE24DA0EEE00649DC8 /* IconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconView.swift; sourceTree = ""; }; + 36E21ED024DC540400649DC8 /* SettingsDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDialog.swift; sourceTree = ""; }; + 36E21ED424DC549800649DC8 /* BankSettingsDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankSettingsDialog.swift; sourceTree = ""; }; + 36E21ED624DC617200649DC8 /* BankAccountSettingsDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankAccountSettingsDialog.swift; sourceTree = ""; }; + 36E21EDA24DC990300649DC8 /* LabelledUIKitTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelledUIKitTextField.swift; sourceTree = ""; }; + 36E21EDC24DCA89100649DC8 /* TanProcedurePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TanProcedurePicker.swift; sourceTree = ""; }; + 36E21EDE24DCCC2700649DC8 /* CheckmarkListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckmarkListItem.swift; sourceTree = ""; }; + 36E21EE224DEC7A700649DC8 /* CoreDataBankingPersistenceTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataBankingPersistenceTest.swift; sourceTree = ""; }; + 36E21EE424DEC89400649DC8 /* CoreDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataManager.swift; sourceTree = ""; }; 36E7BA1324B3D05C00757859 /* ViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExtensions.swift; sourceTree = ""; }; 36E7BA1824B9E70C00757859 /* xcode-frameworks */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "xcode-frameworks"; path = "../../tools/BankFinder/build/xcode-frameworks"; sourceTree = ""; }; 36FC929824B39A05002B12E9 /* BankingiOSApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BankingiOSApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -221,6 +237,21 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 3607829424DF05460098FEFE /* BankIconFinder */ = { + isa = PBXGroup; + children = ( + 36C4009724D23580005227AD /* SwiftBankIconFinderTest.swift */, + ); + path = BankIconFinder; + sourceTree = ""; + }; + 3607829524DF05660098FEFE /* fints4k */ = { + isa = PBXGroup; + children = ( + ); + path = fints4k; + sourceTree = ""; + }; 36BCF87924BFA679005BEC29 /* persistence */ = { isa = PBXGroup; children = ( @@ -246,6 +277,15 @@ path = BankIconFinder; sourceTree = ""; }; + 36E21EE024DEC70C00649DC8 /* persistence */ = { + isa = PBXGroup; + children = ( + 36E21EE224DEC7A700649DC8 /* CoreDataBankingPersistenceTest.swift */, + 36E21EE424DEC89400649DC8 /* CoreDataManager.swift */, + ); + path = persistence; + sourceTree = ""; + }; 36FC928F24B39A05002B12E9 = { isa = PBXGroup; children = ( @@ -300,6 +340,9 @@ 36FC92B424B39A08002B12E9 /* BankingiOSAppTests */ = { isa = PBXGroup; children = ( + 3607829524DF05660098FEFE /* fints4k */, + 3607829424DF05460098FEFE /* BankIconFinder */, + 36E21EE024DEC70C00649DC8 /* persistence */, 36FC92B524B39A08002B12E9 /* BankingiOSAppTests.swift */, 36FC92B724B39A08002B12E9 /* Info.plist */, ); @@ -355,6 +398,8 @@ 36BE069024CEF52800CBBB68 /* UpdateButton.swift */, 36BE06B424CF85A300CBBB68 /* AmountLabel.swift */, 36BE06C724D0DE7400CBBB68 /* UIKitTextField.swift */, + 36E21EDA24DC990300649DC8 /* LabelledUIKitTextField.swift */, + 36E21EDC24DCA89100649DC8 /* TanProcedurePicker.swift */, ); path = ui; sourceTree = ""; @@ -368,19 +413,22 @@ 36BCF88224C098BB005BEC29 /* BankListItem.swift */, 36BCF88424C098C8005BEC29 /* BankAccountListItem.swift */, 36BCF88A24C0BD2D005BEC29 /* AccountTransactionsDialog.swift */, - 36C4009724D23580005227AD /* SwiftBankIconFinderTest.swift */, 36BE066424CDE62800CBBB68 /* AccountTransactionListItem.swift */, 36BCF88C24C1C1EA005BEC29 /* TransferMoneyDialog.swift */, 366FA4DB24C479120094F009 /* BankInfoListItem.swift */, 366FA4DF24C4924A0094F009 /* RemitteeListItem.swift */, 366FA4E124C4ED6C0094F009 /* EnterTanDialog.swift */, 36BE064E24C9A17F00CBBB68 /* ImageTanView.swift */, + 36E21ED024DC540400649DC8 /* SettingsDialog.swift */, + 36E21ED424DC549800649DC8 /* BankSettingsDialog.swift */, + 36E21ED624DC617200649DC8 /* BankAccountSettingsDialog.swift */, 36BE065624C9E04800CBBB68 /* UIKitImageView.swift */, 36BE065824CA3CAB00CBBB68 /* UIKitSearchBar.swift */, 36BE065A24CA4B3500CBBB68 /* SelectBankDialog.swift */, 36BE065C24CB08FB00CBBB68 /* LazyView.swift */, 36C4009A24D2F9E4005227AD /* IconedTitleView.swift */, 36E21ECE24DA0EEE00649DC8 /* IconView.swift */, + 36E21EDE24DCCC2700649DC8 /* CheckmarkListItem.swift */, ); path = views; sourceTree = ""; @@ -550,6 +598,7 @@ 36BE065924CA3CAB00CBBB68 /* UIKitSearchBar.swift in Sources */, 36BE06C824D0DE7400CBBB68 /* UIKitTextField.swift in Sources */, 36BE064F24C9A17F00CBBB68 /* ImageTanView.swift in Sources */, + 36E21EDB24DC990300649DC8 /* LabelledUIKitTextField.swift in Sources */, 36BE068D24CE41E700CBBB68 /* Styles.swift in Sources */, 36E21ECB24D88DF000649DC8 /* UIKitExtensions.swift in Sources */, 366FA4E224C4ED6C0094F009 /* EnterTanDialog.swift in Sources */, @@ -565,11 +614,14 @@ 36BE06B324CF133400CBBB68 /* JsonEncoderSerializer.swift in Sources */, 36BE06BA24D0783900CBBB68 /* FaviconFinder.swift in Sources */, 36BCF89524C31F02005BEC29 /* AppData.swift in Sources */, + 36E21EDD24DCA89100649DC8 /* TanProcedurePicker.swift in Sources */, 36BE065B24CA4B3500CBBB68 /* SelectBankDialog.swift in Sources */, + 36E21ED524DC549800649DC8 /* BankSettingsDialog.swift in Sources */, 36BE068924CE288800CBBB68 /* CollapsibleText.swift in Sources */, 36BE06B524CF85A300CBBB68 /* AmountLabel.swift in Sources */, 36BCF88324C098BB005BEC29 /* BankListItem.swift in Sources */, 36BCF88D24C1C1EA005BEC29 /* TransferMoneyDialog.swift in Sources */, + 36E21EDF24DCCC2700649DC8 /* CheckmarkListItem.swift in Sources */, 36E7BA1424B3D05C00757859 /* ViewExtensions.swift in Sources */, 36BCF88924C0A7D7005BEC29 /* Message.swift in Sources */, 366FA4E024C4924A0094F009 /* RemitteeListItem.swift in Sources */, @@ -586,9 +638,11 @@ 36C4009B24D2F9E4005227AD /* IconedTitleView.swift in Sources */, 36BE065724C9E04800CBBB68 /* UIKitImageView.swift in Sources */, 36BCF88724C0A310005BEC29 /* PreviewData.swift in Sources */, + 36E21ED724DC617200649DC8 /* BankAccountSettingsDialog.swift in Sources */, 366FA4DA24C472A90094F009 /* Extensions.swift in Sources */, 36BE068F24CEE1BD00CBBB68 /* AllBanksListItem.swift in Sources */, 36BE069124CEF52800CBBB68 /* UpdateButton.swift in Sources */, + 36E21ED124DC540400649DC8 /* SettingsDialog.swift in Sources */, 366FA4DC24C479120094F009 /* BankInfoListItem.swift in Sources */, 36FC929E24B39A05002B12E9 /* SceneDelegate.swift in Sources */, 36E21ECF24DA0EEE00649DC8 /* IconView.swift in Sources */, @@ -605,6 +659,8 @@ files = ( 36C4009824D23580005227AD /* SwiftBankIconFinderTest.swift in Sources */, 36FC92B624B39A08002B12E9 /* BankingiOSAppTests.swift in Sources */, + 36E21EE524DEC89400649DC8 /* CoreDataManager.swift in Sources */, + 36E21EE324DEC7A700649DC8 /* CoreDataBankingPersistenceTest.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ui/BankingiOSApp/BankingiOSApp/BankingiOSApp.xcdatamodeld/BankingiOSApp.xcdatamodel/contents b/ui/BankingiOSApp/BankingiOSApp/BankingiOSApp.xcdatamodeld/BankingiOSApp.xcdatamodel/contents index ea23a254..88fe6d96 100644 --- a/ui/BankingiOSApp/BankingiOSApp/BankingiOSApp.xcdatamodeld/BankingiOSApp.xcdatamodel/contents +++ b/ui/BankingiOSApp/BankingiOSApp/BankingiOSApp.xcdatamodeld/BankingiOSApp.xcdatamodel/contents @@ -52,6 +52,7 @@ + diff --git a/ui/BankingiOSApp/BankingiOSApp/Base.lproj/Localizable.strings b/ui/BankingiOSApp/BankingiOSApp/Base.lproj/Localizable.strings index 4494a826..cfdfdc31 100644 --- a/ui/BankingiOSApp/BankingiOSApp/Base.lproj/Localizable.strings +++ b/ui/BankingiOSApp/BankingiOSApp/Base.lproj/Localizable.strings @@ -1,6 +1,7 @@ "OK" = "OK"; "Cancel" = "Cancel"; +"Done" = "Done"; "Add" = "Add"; "New" = "New"; @@ -15,6 +16,12 @@ "All accounts" = "All accounts"; "Add account" = "Add account"; +"IBAN" = "IBAN"; +"BIC" = "BIC"; +"Bank Code" = "Bank Code"; + +"Settings" = "Settings"; + /* SelectBankDialog */ @@ -73,3 +80,14 @@ "TAN medium change" = "TAN medium change"; "TAN medium successfully changed to %@." = "TAN medium successfully changed to '%@'."; "Could not change TAN medium to %@. Error: %@." = "Could not change TAN medium to '%@'.\n\nError message from your bank:\n\n%@."; + + +/* BankSettingsDialog */ + +"Credentials" = "Credentials"; + +"Customer name" = "Customer name"; +"FinTS server address" = "FinTS server address"; + +"Unsaved changes" = "Unsaved changes"; +"Changed data hasn't been saved. Are you sure you want to discard them?" = "Changed data hasn't been saved. Are you sure you want to discard them?"; diff --git a/ui/BankingiOSApp/BankingiOSApp/ContentView.swift b/ui/BankingiOSApp/BankingiOSApp/ContentView.swift index 8e367f78..f43c7c16 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ContentView.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ContentView.swift @@ -29,6 +29,7 @@ struct ContentView: View { @State private var navigationBarTitle = "" @State private var leadingNavigationBarItem: AnyView? = nil + @State private var trailingNavigationBarItem: AnyView? = nil @State private var showNewOptionsActionSheet = false @@ -53,10 +54,6 @@ struct ContentView: View { .onAppear { self.savelySetAccountsTabNavigationBar() } - .onDisappear { - self.navigationBarTitle = "" - self.leadingNavigationBarItem = nil - } .tabItem { VStack { Image("accounts") @@ -80,6 +77,9 @@ struct ContentView: View { self.generateNewActionSheet() }) } + .onAppear { + self.resetNavigationBar() + } .tabItem { VStack { Image(systemName: "plus.circle.fill") @@ -88,10 +88,23 @@ struct ContentView: View { } .tag(Self.OverlayTabIndex) + + /* Third tab: Settings dialog */ + + SettingsDialog(data: data) + .onAppear { + self.savelySetSettingsTabNavigationBar() + } + .tabItem { + VStack { + Image("gear.fill") + Text("Settings") + } + } + .tag(2) + } - .navigationBarHidden(false) - .navigationBarTitle(navigationBarTitle.localize()) - .navigationBarItems(leading: leadingNavigationBarItem) + .showNavigationBarTitle(LocalizedStringKey(navigationBarTitle)) } } @@ -122,23 +135,41 @@ struct ContentView: View { self.selectedTab = self.previousSelectedTab } + private func savelySetAccountsTabNavigationBar() { - setAccountsTabNavigationBar() + let leadingItem = data.hasAtLeastOneAccountBeenAdded == false ? nil : AnyView(UpdateButton { _ in + self.presenter.updateAccountsTransactionsAsync { _ in } + }) + + savelySetNavigationBar("Accounts", leadingItem) + } + + private func savelySetSettingsTabNavigationBar() { + savelySetNavigationBar("Settings", nil, nil) + } + + private func savelySetNavigationBar(_ title: String, _ leadingNavigationBarItem: AnyView? = nil, _ trailingNavigationBarItem: AnyView? = nil) { + setNavigationBar(title, leadingNavigationBarItem, trailingNavigationBarItem) DispatchQueue.main.async { // when pressing 'Cancel' on ActionSheet navigation bar has to be set asynchronously (why, SwiftUI?) - self.setAccountsTabNavigationBar() + self.setNavigationBar(title, leadingNavigationBarItem, trailingNavigationBarItem) + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { // can't believe it, sometimes even DispatchQueue.main.async() doesn't work. Ok, so let's do it 1 second later again, then it works + self.setNavigationBar(title, leadingNavigationBarItem, trailingNavigationBarItem) } } - private func setAccountsTabNavigationBar() { + private func setNavigationBar(_ title: String, _ leadingNavigationBarItem: AnyView? = nil, _ trailingNavigationBarItem: AnyView? = nil) { // due to a SwiftUI bug this cannot be set in AccountsTab directly, so i have to do it via the indirection of navigationBarTitle property - self.navigationBarTitle = "Accounts" + self.navigationBarTitle = title - if data.hasAtLeastOneAccountBeenAdded { - self.leadingNavigationBarItem = AnyView(UpdateButton { _ in - self.presenter.updateAccountsTransactionsAsync { _ in } - }) - } + self.leadingNavigationBarItem = leadingNavigationBarItem + self.trailingNavigationBarItem = trailingNavigationBarItem + } + + private func resetNavigationBar() { + self.setNavigationBar("", nil, nil) } } diff --git a/ui/BankingiOSApp/BankingiOSApp/de.lproj/Localizable.strings b/ui/BankingiOSApp/BankingiOSApp/de.lproj/Localizable.strings index 2601c4ec..6782f8a0 100644 --- a/ui/BankingiOSApp/BankingiOSApp/de.lproj/Localizable.strings +++ b/ui/BankingiOSApp/BankingiOSApp/de.lproj/Localizable.strings @@ -1,6 +1,7 @@ "OK" = "OK"; "Cancel" = "Abbrechen"; +"Done" = "Fertig"; "Add" = "Hinzufügen"; "New" = "Neu"; @@ -15,6 +16,12 @@ "All accounts" = "Alle Konten"; "Add account" = "Konto hinzufügen"; +"IBAN" = "IBAN"; +"BIC" = "BIC"; +"Bank Code" = "Bankleitzahl"; + +"Settings" = "Einstellungen"; + /* SelectBankDialog */ @@ -73,3 +80,14 @@ "TAN medium change" = "TAN Medium Wechsel"; "TAN medium successfully changed to %@." = "TAN Medium erfolgreich zu '%@' geändert."; "Could not change TAN medium to %@. Error: %@." = "TAN medium konnte nicht zu '%@' geändert werden.\n\nFehlermeldung Ihrer Bank:\n\n%@."; + + +/* BankSettingsDialog */ + +"Credentials" = "Zugangsdaten"; + +"Customer name" = "Kontoinhaber"; +"FinTS server address" = "FinTS Server"; + +"Unsaved changes" = "Nicht gespeicherte Änderungen"; +"Changed data hasn't been saved. Are you sure you want to discard them?" = "Es wurden nicht alle Änderungen gespeichert. Sind Sie sich sicher, dass Sie sie verwerfen möchten?"; diff --git a/ui/BankingiOSApp/BankingiOSApp/persistence/Mapper.swift b/ui/BankingiOSApp/BankingiOSApp/persistence/Mapper.swift index 5a50bcea..58e31db7 100644 --- a/ui/BankingiOSApp/BankingiOSApp/persistence/Mapper.swift +++ b/ui/BankingiOSApp/BankingiOSApp/persistence/Mapper.swift @@ -17,6 +17,8 @@ class Mapper { func map(_ customer: PersistedCustomer) -> Customer { 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.accounts = map(mapped, customer.accounts?.array as? [PersistedBankAccount]) @@ -40,6 +42,8 @@ class Mapper { mapped.customerName = customer.customerName mapped.userId = customer.userId mapped.iconUrl = customer.iconUrl + + mapped.userSetDisplayName = customer.userSetDisplayName mapped.accounts = NSOrderedSet(array: map(mapped, customer.accounts, context)) @@ -59,6 +63,8 @@ class Mapper { func map(_ customer: Customer, _ account: PersistedBankAccount) -> BankAccount { let mapped = BankAccount(customer: customer, identifier: map(account.identifier), accountHolderName: map(account.accountHolderName), iban: account.iban, subAccountNumber: account.subAccountNumber, customerId: map(account.customerId), balance: map(account.balance), currency: map(account.currency), type: map(account.type), productName: account.productName, accountLimit: account.accountLimit, lastRetrievedTransactionsTimestamp: map(account.lastRetrievedTransactionsTimestamp), supportsRetrievingAccountTransactions: account.supportsRetrievingAccountTransactions, supportsRetrievingBalance: account.supportsRetrievingBalance, supportsTransferringMoney: account.supportsTransferringMoney, supportsInstantPaymentMoneyTransfer: account.supportsInstantPaymentMoneyTransfer, bookedTransactions: [], unbookedTransactions: []) + mapped.userSetDisplayName = account.userSetDisplayName + mapped.bookedTransactions = map(mapped, account.transactions as? Set) mappedAccounts[mapped] = account @@ -90,6 +96,8 @@ class Mapper { mapped.supportsTransferringMoney = account.supportsTransferringMoney mapped.supportsInstantPaymentMoneyTransfer = account.supportsInstantPaymentMoneyTransfer + mapped.userSetDisplayName = account.userSetDisplayName + mapped.transactions = NSSet(array: map(mapped, account.bookedTransactions, context)) mappedAccounts[account] = mapped diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/ViewExtensions.swift b/ui/BankingiOSApp/BankingiOSApp/ui/ViewExtensions.swift index 87119b56..b2d690fa 100644 --- a/ui/BankingiOSApp/BankingiOSApp/ui/ViewExtensions.swift +++ b/ui/BankingiOSApp/BankingiOSApp/ui/ViewExtensions.swift @@ -16,23 +16,43 @@ extension View { .navigationBarTitle(title, displayMode: displayMode) } + func customNavigationBarBackButton(onBackButtonPressed: @escaping () -> Void) -> some View { return self .navigationBarBackButtonHidden(true) - .navigationBarItems(leading: Button(action: onBackButtonPressed) { - HStack { - Image(systemName: "chevron.left") - .font(.headline) - .padding(.horizontal, 0) - - Text("Cancel") - .padding(.leading, 0) - } - .edgesIgnoringSafeArea(.leading) - .padding(.leading, 0) - }) + .navigationBarItems(leading: createCancelButton(onBackButtonPressed)) } + func setCancelAndDoneNavigationBarButtons(onCancelPressed: @escaping () -> Void, onDonePressed: @escaping () -> Void) -> some View { + return self + .navigationBarHidden(false) + .navigationBarItems(leading: createCancelButton(onCancelPressed), trailing: createDoneButton(onDonePressed)) + } + + func createDoneButton(_ onDoneButtonPressed: @escaping () -> Void) -> some View { + return Button(action: onDoneButtonPressed) { + Text("Done") + .edgesIgnoringSafeArea(.leading) + .padding(.leading, 0) + } + } + + func createCancelButton(_ onCancelButtonPressed: @escaping () -> Void) -> some View { + return Button(action: onCancelButtonPressed) { + HStack { + Image(systemName: "chevron.left") + .font(.headline) + .padding(.horizontal, 0) + + Text("Cancel") + .padding(.leading, 0) + } + .edgesIgnoringSafeArea(.leading) + .padding(.leading, 0) + } + } + + func detailForegroundColor() -> some View { return self .foregroundColor(Color.secondary) @@ -117,3 +137,25 @@ extension Binding { } } + + +extension NumberFormatter { + + static var currency: NumberFormatter { + let formatter = NumberFormatter() + + formatter.numberStyle = .currency + + return formatter + } + + static func decimal(_ countMaxDecimalPlaces: Int) -> NumberFormatter { + let formatter = NumberFormatter() + + formatter.numberStyle = .decimal + formatter.maximumFractionDigits = countMaxDecimalPlaces + + return formatter + } + +} diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/BankSettingsDialog.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/BankSettingsDialog.swift new file mode 100644 index 00000000..87a0c5f4 --- /dev/null +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/BankSettingsDialog.swift @@ -0,0 +1,124 @@ +import SwiftUI +import BankingUiSwift +import UIKit + + +struct BankSettingsDialog: View { + + @Environment(\.presentationMode) var presentation + + @Inject private var presenter: BankingPresenterSwift + + + private let bank: Customer + + @State private var displayName: String + + @State private var customerId: String + @State private var password: String + + @State private var selectedTanProcedure: TanProcedure? + + @State private var unsavedChangesMessage: Message? = nil + + + private var hasUnsavedData: Bool { + return bank.displayName != displayName + || bank.customerId != customerId + || bank.password != password + || bank.selectedTanProcedure != selectedTanProcedure + } + + + init(_ bank: Customer) { + self.bank = bank + + _displayName = State(initialValue: bank.displayName) + + _customerId = State(initialValue: bank.customerId) + _password = State(initialValue: bank.password) + + _selectedTanProcedure = State(initialValue: bank.selectedTanProcedure) + } + + + var body: some View { + Form { + Section { + LabelledUIKitTextField(label: "Name", text: $displayName) + } + + Section(header: Text("Credentials")) { + LabelledUIKitTextField(label: "Online banking login name", text: $customerId) + + LabelledUIKitTextField(label: "Online banking login password", text: $password, isPasswordField: true) + } + + Section { + TanProcedurePicker(bank) { selectedTanProcedure in + self.selectedTanProcedure = selectedTanProcedure + } + } + + Section { + LabelledUIKitTextField(label: "Bank Code", value: bank.bankCode) + + LabelledUIKitTextField(label: "BIC", value: bank.bic) + + LabelledUIKitTextField(label: "Customer name", value: bank.customerName) // TODO: senseful? + + LabelledUIKitTextField(label: "FinTS server address", value: bank.finTsServerAddress) // TODO: senseful? + } + + Section(header: Text("Accounts")) { + ForEach(bank.accounts) { account in + Text(account.displayName) + } + } + } + .alert(item: $unsavedChangesMessage) { message in + Alert(title: message.title, message: message.message, primaryButton: message.primaryButton, secondaryButton: message.secondaryButton!) + } + .showNavigationBarTitle(LocalizedStringKey(bank.displayName)) + .setCancelAndDoneNavigationBarButtons(onCancelPressed: cancelPressed, onDonePressed: donePressed) + } + + + private func cancelPressed() { + if hasUnsavedData { + self.unsavedChangesMessage = Message(title: Text("Unsaved changes"), message: Text("Changed data hasn't been saved. Are you sure you want to discard them?"), primaryButton: .ok(closeDialog), secondaryButton: .cancel()) + } + else { + closeDialog() + } + } + + private func donePressed() { + if hasUnsavedData { + bank.userSetDisplayName = displayName + + bank.customerId = customerId + bank.password = password + + bank.selectedTanProcedure = selectedTanProcedure + + presenter.accountUpdated(account: bank) + } + + closeDialog() + } + + private func closeDialog() { + presentation.wrappedValue.dismiss() + } + +} + + +struct BankSettingsDialog_Previews: PreviewProvider { + + static var previews: some View { + BankSettingsDialog(previewBanks[0]) + } + +} diff --git a/ui/BankingiOSApp/BankingiOSApp/ui/views/SettingsDialog.swift b/ui/BankingiOSApp/BankingiOSApp/ui/views/SettingsDialog.swift new file mode 100644 index 00000000..cca0b80a --- /dev/null +++ b/ui/BankingiOSApp/BankingiOSApp/ui/views/SettingsDialog.swift @@ -0,0 +1,31 @@ +import SwiftUI +import BankingUiSwift + + +struct SettingsDialog: View { + + @ObservedObject var data: AppData + + @Inject var presenter: BankingPresenterSwift + + + var body: some View { + Form { + ForEach(data.banks.sorted(by: { $0.displayIndex >= $1.displayIndex })) { bank in + NavigationLink(destination: LazyView(BankSettingsDialog(bank))) { + IconedTitleView(bank) + } + } + } + } + +} + + +struct SettingsDialog_Previews: PreviewProvider { + + static var previews: some View { + SettingsDialog(data: AppData()) + } + +}