blob: f57c775cbd5362813b0d2c59203363e935494ff6 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
/*
* This file is part of GNU Taler, ©2022-23 Taler Systems S.A.
* See LICENSE.md
*/
import Foundation
import AVFoundation
import SwiftUI
import SymLog
import os.log
import CoreHaptics
enum BackendState {
case none
case instantiated
case initing
case ready
case error
}
enum UrlCommand {
case unknown
case withdraw
case pay
case payPull
case payPush
case payTemplate
case reward
}
// MARK: -
class Controller: ObservableObject {
public static let shared = Controller()
private let symLog = SymLogC()
@Published var backendState: BackendState = .none // only used for launch animation
@Published var currencyInfos: [CurrencyInfo]
@AppStorage("useHaptics") var useHaptics: Bool = true // extension mustn't define this, so it must be here
@AppStorage("playSounds") var playSounds: Int = 1 // extension mustn't define this, so it must be here
@AppStorage("talerFont") var talerFont: Int = 0 // extension mustn't define this, so it must be here
let hapticCapability = CHHapticEngine.capabilitiesForHardware()
let logger = Logger(subsystem: "net.taler.gnu", category: "Controller")
let player = AVQueuePlayer()
let semaphore = AsyncSemaphore(value: 1)
var messageForSheet: String? = nil
init() {
// for family in UIFont.familyNames {
// print(family)
// for names in UIFont.fontNames(forFamilyName: family) {
// print("== \(names)")
// }
// }
backendState = .instantiated
currencyInfos = []
}
func info(for currency: String) -> CurrencyInfo? {
for currencyInfo in currencyInfos {
if currencyInfo.scope.currency == currency {
return currencyInfo
}
}
return nil
}
@MainActor
func setInfo(_ info: CurrencyInfo) async {
await semaphore.wait()
defer { semaphore.signal() }
var existing: ScopeInfo? = nil
for currencyInfo in currencyInfos {
let scope = currencyInfo.scope
if scope.currency == info.scope.currency {
existing = scope
break
}
}
if existing != nil {
currencyInfos.removeAll(where: { $0.scope == existing })
}
currencyInfos.append(info)
}
@MainActor
func initWalletCore(_ model: WalletModel, sqlite3: Bool)
async throws {
if backendState == .instantiated {
backendState = .initing
do {
let versionInfo = try await model.initWalletCoreT(sqlite3: sqlite3)
WalletCore.shared.versionInfo = versionInfo
backendState = .ready // dismiss the launch animation
} catch { // rethrows
symLog.log(error.localizedDescription) // TODO: .error
backendState = .error // ❗️Yikes app cannot continue
throw error
}
} else {
symLog.log("Yikes❗️ wallet-core already initialized") // TODO: .fault
}
}
}
// MARK: -
extension Controller {
func openURL(_ url: URL, stack: CallStack) -> UrlCommand {
symLog.log(url)
guard let scheme = url.scheme else {return UrlCommand.unknown}
var uncrypted = false
switch scheme {
case "taler+http":
uncrypted = true
fallthrough
case "taler", "ext+taler", "web+taler":
return talerScheme(url, uncrypted)
case "payto":
messageForSheet = url.absoluteString
return paytoScheme(url)
default:
symLog.log("unknown scheme: <\(scheme)>") // should never happen
}
return UrlCommand.unknown
}
}
// MARK: -
extension Controller {
func paytoScheme(_ url:URL) -> UrlCommand {
let logItem = "scheme payto:// is not yet implemented"
// TODO: write logItem to somewhere in Debug section of SettingsView
symLog.log(logItem) // TODO: symLog.error(logItem)
return UrlCommand.unknown
}
func talerScheme(_ url:URL,_ uncrypted: Bool = false) -> UrlCommand {
guard let command = url.host else {return UrlCommand.unknown}
if uncrypted {
symLog.log("uncrypted: taler://\(command)")
// TODO: uncrypted
}
switch command {
case "withdraw":
return UrlCommand.withdraw
case "pay":
return UrlCommand.pay
case "pay-pull":
return UrlCommand.payPull
case "pay-push":
return UrlCommand.payPush
case "pay-template":
return UrlCommand.payTemplate
case "tip":
return UrlCommand.reward
default:
symLog.log("unknown command taler://\(command)")
}
return UrlCommand.unknown
}
}
|