taler-ios

iOS apps for GNU Taler (wallet)
Log | Files | Refs | README | LICENSE

commit 3a8b484256493117c1ade4d906253ca99160531b
parent d37deaad8d4e58222e5200a8d8f7ccfa2c005aa8
Author: Jonathan Buchanan <jonathan.russ.buchanan@gmail.com>
Date:   Wed, 10 Aug 2022 21:09:54 -0400

pending operations view

Diffstat:
DTaler/BackendManager.swift | 28----------------------------
DTaler/BalanceList.swift | 76----------------------------------------------------------------------------
DTaler/BalanceRow.swift | 38--------------------------------------
DTaler/ContentView.swift | 83-------------------------------------------------------------------------------
ATaler/Model/BackendManager.swift | 30++++++++++++++++++++++++++++++
RTaler/ExchangeManager.swift -> Taler/Model/ExchangeManager.swift | 0
ATaler/Model/PendingManager.swift | 49+++++++++++++++++++++++++++++++++++++++++++++++++
DTaler/SettingsView.swift | 276-------------------------------------------------------------------------------
ATaler/Views/ContentView.swift | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ATaler/Views/PendingView.swift | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ATaler/Views/SettingsView.swift | 295+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
MTaler/WalletBackend.swift | 21+++++++++++++++++++++
12 files changed, 550 insertions(+), 501 deletions(-)

diff --git a/Taler/BackendManager.swift b/Taler/BackendManager.swift @@ -1,28 +0,0 @@ -/* - * This file is part of GNU Taler - * (C) 2022 Taler Systems S.A. - * - * GNU Taler is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3, or (at your option) any later version. - * - * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -import Foundation - -class BackendManager: ObservableObject { - var backend: WalletBackend - - @Published var exchangeManager: ExchangeManager - - init() { - self.backend = try! WalletBackend() - self.exchangeManager = ExchangeManager(_backend: self.backend) - } -} diff --git a/Taler/BalanceList.swift b/Taler/BalanceList.swift @@ -1,76 +0,0 @@ -/* - * This file is part of GNU Taler - * (C) 2021 Taler Systems S.A. - * - * GNU Taler is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3, or (at your option) any later version. - * - * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -import SwiftUI - -/*struct IdentifiedArray<T>: RandomAccessCollection { - struct Item { - var id: Int - var item: T - } - - typealias Element = Item - typealias ItemArray = [Item] - typealias Index = ItemArray.Index - typealias SubSequence = IdentifiedArray<T> - typealias Indices = ItemArray.Indices - - var items: ItemArray - var startIndex: ItemArray.Index { - get { - return items.startIndex - } - } - var endIndex: ItemArray.Index { - get { - return items.endIndex - } - } - - subscript(position: ItemArray.Index) -> Element { - get { - return items[position] - } - set(value) { - items[position] = value - } - } - - init(_ array: [T]) { - self.items = array.enumerated().map({ (index, element) in - return Item(id: index, item: element) - }) - } -} - -struct BalanceList: View { - var balances: IdentifiedArray<Balance> - - var body: some View { - List(balances, id: \.id) { balance in - BalanceRow(balance: balance.item) - } - } -} - -struct BalanceList_Previews: PreviewProvider { - static var previews: some View { - try! BalanceList(balances: IdentifiedArray<Balance>([ - Balance(available: Amount(fromString: "USD:0.01"), pendingIncoming: Amount(fromString: "USD:0.02"), pendingOutgoing: Amount(fromString: "USD:0.03"), requiresUserInput: true), - Balance(available: Amount(fromString: "EUR:0.02"), pendingIncoming: Amount(fromString: "EUR:0.01"), pendingOutgoing: Amount(fromString: "EUR:0.03"), requiresUserInput: false) - ])) - } -}*/ diff --git a/Taler/BalanceRow.swift b/Taler/BalanceRow.swift @@ -1,38 +0,0 @@ -/* - * This file is part of GNU Taler - * (C) 2021 Taler Systems S.A. - * - * GNU Taler is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3, or (at your option) any later version. - * - * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -import SwiftUI -import taler_swift - -/*struct BalanceRow: View { - var balance: Balance - - var body: some View { - VStack(alignment: .leading, spacing: /*@START_MENU_TOKEN@*/nil/*@END_MENU_TOKEN@*/, content: { - Text("Available: \(balance.available.description)") - Text("Pending Incoming: \(balance.pendingIncoming.description)") - Text("Pending Outgoing: \(balance.pendingOutgoing.description)") - Text("Requires User Input: \(balance.requiresUserInput.description)") - }) - .padding() - } -} - -struct BalanceRow_Previews: PreviewProvider { - static var previews: some View { - try! BalanceRow(balance: Balance(available: Amount(fromString: "USD:0.01"), pendingIncoming: Amount(fromString: "USD:0.02"), pendingOutgoing: Amount(fromString: "USD:0.03"), requiresUserInput: true)) - } -}*/ diff --git a/Taler/ContentView.swift b/Taler/ContentView.swift @@ -1,83 +0,0 @@ -/* - * This file is part of GNU Taler - * (C) 2022 Taler Systems S.A. - * - * GNU Taler is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3, or (at your option) any later version. - * - * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -import SwiftUI - -struct SidebarItem { - var name: String - var view: AnyView -} - -struct ContentView: View { - @StateObject var backend: BackendManager = BackendManager() - - @State var sidebarVisible: Bool = false - var views: [SidebarItem] {[ - SidebarItem(name: "Main", - view: AnyView(Button { [self] in - self.sidebarVisible = true - } label: { - Text("Open Sidebar") - })), - SidebarItem(name: "Settings", - view: AnyView(SettingsView { - self.sidebarVisible = true - }.environmentObject(backend))) - ]} - @State var currentView: Int = 0 - - var body: some View { - ZStack(alignment: .leading) { - - views[currentView].view - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) - - VStack { - Spacer() - - Button { - self.sidebarVisible = false - } label: { - Text("Close") - } - Divider() - - ForEach(0..<views.count, id: \.self) { i in - Button { - self.sidebarVisible = false - self.currentView = i - } label: { - Text(views[i].name) - } - Divider() - } - - Spacer() - } - .background(Color.gray) - .frame(width: 100, alignment: .center) - .offset(x: sidebarVisible ? 0 : -100) - .animation(.easeInOut, value: sidebarVisible) - .ignoresSafeArea() - } - } -} - -struct ContentView_Previews: PreviewProvider { - static var previews: some View { - ContentView() - } -} diff --git a/Taler/Model/BackendManager.swift b/Taler/Model/BackendManager.swift @@ -0,0 +1,30 @@ +/* + * This file is part of GNU Taler + * (C) 2022 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +import Foundation + +class BackendManager: ObservableObject { + var backend: WalletBackend + + @Published var exchangeManager: ExchangeManager + @Published var pendingManager: PendingManager + + init() { + self.backend = try! WalletBackend() + self.exchangeManager = ExchangeManager(_backend: self.backend) + self.pendingManager = PendingManager(_backend: self.backend) + } +} diff --git a/Taler/ExchangeManager.swift b/Taler/Model/ExchangeManager.swift diff --git a/Taler/Model/PendingManager.swift b/Taler/Model/PendingManager.swift @@ -0,0 +1,49 @@ +/* + * This file is part of GNU Taler + * (C) 2022 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +import Foundation +import AnyCodable + +class PendingManager: ObservableObject { + var backend: WalletBackend + + @Published var loading: Bool + @Published var items: [String]? + + init(_backend: WalletBackend) { + self.backend = _backend + self.loading = false + self.items = nil + } + + func update() { + let req = WalletBackendPendingRequest() + backend.sendFormattedRequest(request: req) { response, err in + // TODO: Use Combine instead. + DispatchQueue.main.async { + self.loading = false + if let x = response { + self.items = x.pendingOperations.map({ op in + let encoded = try! JSONEncoder().encode(op) + let str = String(data: encoded, encoding: .utf8)! + return str + }) + } + } + } + self.loading = true + } +} diff --git a/Taler/SettingsView.swift b/Taler/SettingsView.swift @@ -1,276 +0,0 @@ -/* - * This file is part of GNU Taler - * (C) 2022 Taler Systems S.A. - * - * GNU Taler is free software; you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation; either version 3, or (at your option) any later version. - * - * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -import SwiftUI - -struct TextInputPopup: ViewModifier { - @State var exchangeUrl: String = "https://" - var onCancel: () -> Void - var onOk: (String) -> Void - - init(cancel: @escaping () -> Void, ok: @escaping (String) -> Void) { - self.onCancel = cancel - self.onOk = ok - } - - func body(content: Content) -> some View { - return content - .overlay( - VStack { - Text("Add Exchange") - TextField("Exchange URL", text: $exchangeUrl) - HStack { - Button { - self.onCancel() - } label: { - Text("Cancel") - } - Button { - self.onOk(exchangeUrl) - } label: { - Text("Ok") - } - } - } - .padding(8) - .frame(width: UIScreen.main.bounds.width - 100, height: 150, alignment: .center) - .background(Color.green) - .cornerRadius(8) - , alignment: .center) - .animation(.easeIn) - } -} - -extension View { - func textInputPopup(cancel: @escaping () -> Void, ok: @escaping (String) -> Void, showing: Bool) -> some View { - if showing { - return AnyView(modifier(TextInputPopup(cancel: cancel, ok: ok))) - } else { - return AnyView(self) - } - } -} - -struct WithdrawView: View { - let exchange: ExchangeItem - @State var amount: String = "" - - var body: some View { - VStack { - Button { - - } label: { - Text("Scan Taler QR Code") - } - Text("Or transfer manually:") - HStack { - TextField(exchange.currency, text: $amount) - } - Button { - - } label: { - Text("Check Fees") - } - } - .navigationTitle("Withdraw") - } -} - -struct ExchangeListView: View { - @ObservedObject var exchangeManager: ExchangeManager - @State var showPopup: Bool = false - - var body: some View { - if exchangeManager.exchanges == nil { - ProgressView() - .navigationTitle("Exchanges") - .onAppear { - exchangeManager.updateList() - } - } else if exchangeManager.loading { - ProgressView() - .navigationTitle("Exchanges") - } else { - let exchanges = exchangeManager.exchanges! - if exchanges.count == 0 { - Text("No Exchanges") - .navigationTitle("Exchanges") - .navigationBarItems(trailing: Button(action: { - withAnimation { - showPopup = true - } - }, label: { - Image(systemName: "plus") - })) - .textInputPopup(cancel: { - self.showPopup = false - }, ok: { exchangeUrl in - self.showPopup = false - exchangeManager.add(url: exchangeUrl) - print(exchangeUrl) - }, showing: showPopup) - } else { - List(exchanges, id: \.self) { exchange in - VStack { - Text(exchange.exchangeBaseUrl) - .frame(maxWidth: .infinity) - Text("Currency: " + exchange.currency) - .frame(maxWidth: .infinity) - NavigationLink { - WithdrawView(exchange: exchange) - } label: { - Text("Withdraw") - } - } - } - .navigationTitle("Exchanges") - .navigationBarItems(trailing: Button(action: { - withAnimation { - showPopup = true - } - }, label: { - Image(systemName: "plus") - })) - .textInputPopup(cancel: { - self.showPopup = false - }, ok: { exchangeUrl in - self.showPopup = false - exchangeManager.add(url: exchangeUrl) - print(exchangeUrl) - }, showing: showPopup) - } - } - } -} - -/* - * Exchanges - * Manage list of exchanges known to this wallet - * - * Backup - * Last backup: 5 hr. ago - * - * Developer Mode [toggle] - * Shows more information intended for debugging - * - * Withdraw TESTKUDOS - * Get money for testing - * - * Debug log - * View/send internal log - * - * App Version - * v0.9.0-dev.11 (fdroid 11) - * - * Wallet Core Version - * v0.9.0-dev.11 - * - * Supported Exchange Versions - * 12:0:0 - * - * Supported Merchant Versions - * 2:0:1 - * - * Reset Wallet (dangerous!) - * Throws away your money - */ - -struct SettingsItem<Content: View>: View { - var name: String - var description: String? - var content: () -> Content - - init(name: String, description: String? = nil, @ViewBuilder content: @escaping () -> Content) { - self.name = name - self.description = description - self.content = content - } - - var body: some View { - HStack { - Image(systemName: "line.3.horizontal") - VStack { - Text(name) - .frame(maxWidth: .infinity, alignment: .leading) - .font(.title2) - if let desc = description { - Text(desc) - .frame(maxWidth: .infinity, alignment: .leading) - .font(.caption) - } - } - content() - } - .padding([.bottom], 8) - } -} - -struct SettingsView: View { - @EnvironmentObject var backend: BackendManager - @AppStorage("developerMode") var developerMode: Bool = false - - var showSidebar: () -> Void - var body: some View { - NavigationView { - VStack { - SettingsItem(name: "Exchanges", description: "Manage list of exchanges known to this wallet") { - NavigationLink { - ExchangeListView(exchangeManager: backend.exchangeManager) - } label: { - Text("View") - } - } - SettingsItem(name: "Developer Mode", description: "Shows more information intended for debugging") { - Toggle(isOn: $developerMode) { } - } - if developerMode { - SettingsItem(name: "App Version") { - Text("v0.9.0-dev.11") - } - SettingsItem(name: "Wallet Core Version") { - Text("v0.9.0-dev.11") - } - SettingsItem(name: "Supported Exchange Versions") { - Text("12:0:0") - } - SettingsItem(name: "Supported Merchant Versions") { - Text("2:0:1") - } - } - Spacer() - } - .padding(16) - .navigationTitle("Settings") - .navigationBarItems( - leading: Button(action: self.showSidebar, label: { - Image(systemName: "line.3.horizontal") - }) - ) - } - } - - init(_showSidebar: @escaping () -> Void) { - self.showSidebar = _showSidebar - } -} - -struct SettingsView_Previews: PreviewProvider { - static var previews: some View { - SettingsView { - - } - } -} diff --git a/Taler/Views/ContentView.swift b/Taler/Views/ContentView.swift @@ -0,0 +1,87 @@ +/* + * This file is part of GNU Taler + * (C) 2022 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +import SwiftUI + +struct SidebarItem { + var name: String + var view: AnyView +} + +struct ContentView: View { + @StateObject var backend: BackendManager = BackendManager() + + @State var sidebarVisible: Bool = false + var views: [SidebarItem] {[ + SidebarItem(name: "Main", + view: AnyView(Button { [self] in + self.sidebarVisible = true + } label: { + Text("Open Sidebar") + })), + SidebarItem(name: "Settings", + view: AnyView(SettingsView { + self.sidebarVisible = true + }.environmentObject(backend))), + SidebarItem(name: "Pending Operations", + view: AnyView(PendingView(_showSidebar: { + self.sidebarVisible = true + }, pending: backend.pendingManager).environmentObject(backend))) + ]} + @State var currentView: Int = 0 + + var body: some View { + ZStack(alignment: .leading) { + + views[currentView].view + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) + + VStack { + Spacer() + + Button { + self.sidebarVisible = false + } label: { + Text("Close") + } + Divider() + + ForEach(0..<views.count, id: \.self) { i in + Button { + self.sidebarVisible = false + self.currentView = i + } label: { + Text(views[i].name) + } + Divider() + } + + Spacer() + } + .background(Color.gray) + .frame(width: 100, alignment: .center) + .offset(x: sidebarVisible ? 0 : -100) + .animation(.easeInOut, value: sidebarVisible) + .ignoresSafeArea() + } + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff --git a/Taler/Views/PendingView.swift b/Taler/Views/PendingView.swift @@ -0,0 +1,68 @@ +/* + * This file is part of GNU Taler + * (C) 2022 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +import SwiftUI + +struct PendingView: View { + @ObservedObject var pendingManager: PendingManager + + var showSidebar: () -> Void + var body: some View { + NavigationView { + if pendingManager.items == nil { + ProgressView() + .navigationTitle("Pending") + .navigationBarItems( + leading: Button(action: self.showSidebar, label: { + Image(systemName: "line.3.horizontal") + })) + .onAppear { + pendingManager.update() + } + } else if pendingManager.loading { + ProgressView() + .navigationTitle("Pending") + .navigationBarItems( + leading: Button(action: self.showSidebar, label: { + Image(systemName: "line.3.horizontal") + })) + } else { + let items = pendingManager.items! + List(items, id: \.self) { item in + VStack { + Text(item) + .font(.system(size: 14, design: .monospaced)) + } + } + .navigationTitle("Pending") + .navigationBarItems( + leading: Button(action: self.showSidebar, label: { + Image(systemName: "line.3.horizontal") + }), + trailing: Button(action: { + pendingManager.update() + }, label: { + Image(systemName: "arrow.clockwise") + })) + } + } + } + + init(_showSidebar: @escaping () -> Void, pending: PendingManager) { + self.showSidebar = _showSidebar + self.pendingManager = pending + } +} diff --git a/Taler/Views/SettingsView.swift b/Taler/Views/SettingsView.swift @@ -0,0 +1,295 @@ +/* + * This file is part of GNU Taler + * (C) 2022 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +import SwiftUI +import taler_swift + +struct TextInputPopup: ViewModifier { + @State var exchangeUrl: String = "https://" + var onCancel: () -> Void + var onOk: (String) -> Void + + init(cancel: @escaping () -> Void, ok: @escaping (String) -> Void) { + self.onCancel = cancel + self.onOk = ok + } + + func body(content: Content) -> some View { + return content + .overlay( + VStack { + Text("Add Exchange") + TextField("Exchange URL", text: $exchangeUrl) + HStack { + Button { + self.onCancel() + } label: { + Text("Cancel") + } + Button { + self.onOk(exchangeUrl) + } label: { + Text("Ok") + } + } + } + .padding(8) + .frame(width: UIScreen.main.bounds.width - 100, height: 150, alignment: .center) + .background(Color.green) + .cornerRadius(8) + , alignment: .center) + .animation(.easeIn) + } +} + +extension View { + func textInputPopup(cancel: @escaping () -> Void, ok: @escaping (String) -> Void, showing: Bool) -> some View { + if showing { + return AnyView(modifier(TextInputPopup(cancel: cancel, ok: ok))) + } else { + return AnyView(self) + } + } +} + +struct PromptWithdrawView: View { + let exchange: ExchangeItem + let amount: Amount + + var body: some View { + VStack { + Text("Fees or something") + } + .navigationTitle("Withdraw Digital Cash") + } +} + +struct WithdrawView: View { + let exchange: ExchangeItem + @State var amount: String = "" + + var body: some View { + VStack { + Button { + + } label: { + Text("Scan Taler QR Code") + } + Text("Or transfer manually:") + HStack { + TextField(exchange.currency, text: $amount) + } + NavigationLink { + // TODO: Handle when the user inputs a non-valid amount + /*do { + let am = try Amount.init(fromString: exchange.currency + ":" + amount) + PromptWithdrawView(exchange: exchange, amount: am) + } catch { + + }*/ + } label: { + Text("Check Fees") + } + } + .navigationTitle("Withdraw") + } +} + +struct ExchangeListView: View { + @ObservedObject var exchangeManager: ExchangeManager + @State var showPopup: Bool = false + + var body: some View { + if exchangeManager.exchanges == nil { + ProgressView() + .navigationTitle("Exchanges") + .onAppear { + exchangeManager.updateList() + } + } else if exchangeManager.loading { + ProgressView() + .navigationTitle("Exchanges") + } else { + let exchanges = exchangeManager.exchanges! + if exchanges.count == 0 { + Text("No Exchanges") + .navigationTitle("Exchanges") + .navigationBarItems(trailing: Button(action: { + withAnimation { + showPopup = true + } + }, label: { + Image(systemName: "plus") + })) + .textInputPopup(cancel: { + self.showPopup = false + }, ok: { exchangeUrl in + self.showPopup = false + exchangeManager.add(url: exchangeUrl) + print(exchangeUrl) + }, showing: showPopup) + } else { + List(exchanges, id: \.self) { exchange in + VStack { + Text(exchange.exchangeBaseUrl) + .frame(maxWidth: .infinity) + Text("Currency: " + exchange.currency) + .frame(maxWidth: .infinity) + NavigationLink { + WithdrawView(exchange: exchange) + } label: { + Text("Withdraw") + } + } + } + .navigationTitle("Exchanges") + .navigationBarItems(trailing: Button(action: { + withAnimation { + showPopup = true + } + }, label: { + Image(systemName: "plus") + })) + .textInputPopup(cancel: { + self.showPopup = false + }, ok: { exchangeUrl in + self.showPopup = false + exchangeManager.add(url: exchangeUrl) + print(exchangeUrl) + }, showing: showPopup) + } + } + } +} + +/* + * Exchanges + * Manage list of exchanges known to this wallet + * + * Backup + * Last backup: 5 hr. ago + * + * Developer Mode [toggle] + * Shows more information intended for debugging + * + * Withdraw TESTKUDOS + * Get money for testing + * + * Debug log + * View/send internal log + * + * App Version + * v0.9.0-dev.11 (fdroid 11) + * + * Wallet Core Version + * v0.9.0-dev.11 + * + * Supported Exchange Versions + * 12:0:0 + * + * Supported Merchant Versions + * 2:0:1 + * + * Reset Wallet (dangerous!) + * Throws away your money + */ + +struct SettingsItem<Content: View>: View { + var name: String + var description: String? + var content: () -> Content + + init(name: String, description: String? = nil, @ViewBuilder content: @escaping () -> Content) { + self.name = name + self.description = description + self.content = content + } + + var body: some View { + HStack { + Image(systemName: "line.3.horizontal") + VStack { + Text(name) + .frame(maxWidth: .infinity, alignment: .leading) + .font(.title2) + if let desc = description { + Text(desc) + .frame(maxWidth: .infinity, alignment: .leading) + .font(.caption) + } + } + content() + } + .padding([.bottom], 8) + } +} + +struct SettingsView: View { + @EnvironmentObject var backend: BackendManager + @AppStorage("developerMode") var developerMode: Bool = false + + var showSidebar: () -> Void + var body: some View { + NavigationView { + VStack { + SettingsItem(name: "Exchanges", description: "Manage list of exchanges known to this wallet") { + NavigationLink { + ExchangeListView(exchangeManager: backend.exchangeManager) + } label: { + Text("View") + } + } + SettingsItem(name: "Developer Mode", description: "Shows more information intended for debugging") { + Toggle(isOn: $developerMode) { } + } + if developerMode { + SettingsItem(name: "App Version") { + Text("v0.9.0-dev.11") + } + SettingsItem(name: "Wallet Core Version") { + Text("v0.9.0-dev.11") + } + SettingsItem(name: "Supported Exchange Versions") { + Text("12:0:0") + } + SettingsItem(name: "Supported Merchant Versions") { + Text("2:0:1") + } + } + Spacer() + } + .padding(16) + .navigationTitle("Settings") + .navigationBarItems( + leading: Button(action: self.showSidebar, label: { + Image(systemName: "line.3.horizontal") + }) + ) + } + } + + init(_showSidebar: @escaping () -> Void) { + self.showSidebar = _showSidebar + } +} + +struct SettingsView_Previews: PreviewProvider { + static var previews: some View { + SettingsView { + + } + } +} diff --git a/Taler/WalletBackend.swift b/Taler/WalletBackend.swift @@ -785,6 +785,27 @@ struct WalletBackendSuspendCoinRequest: WalletBackendFormattedRequest { } } +typealias PendingOperation = AnyCodable + +/// A request to list the backend's currently pending operations. +struct WalletBackendPendingRequest: WalletBackendFormattedRequest { + struct Args: Encodable { + + } + + struct Response: Decodable { + var pendingOperations: [PendingOperation] + } + + func operation() -> String { + return "getPendingOperations" + } + + func args() -> Args { + Args() + } +} + /// Errors for `WalletBackend`. enum WalletBackendError: Error { /// An error that prevented the wallet from being initialized occurred.