summaryrefslogtreecommitdiff
path: root/TalerWallet1/Controllers/Controller.swift
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
    }
}