commit cd52ab1e95e9998632fd30a8215f3439074ea68f
parent d25e0bc71c6a8c53af084a74fd94cd83a180032a
Author: Marc Stibane <marc@taler.net>
Date: Mon, 22 Jun 2026 09:55:27 +0200
Subscriptions
Diffstat:
3 files changed, 61 insertions(+), 6 deletions(-)
diff --git a/TalerWallet1/Controllers/Controller.swift b/TalerWallet1/Controllers/Controller.swift
@@ -125,7 +125,8 @@ class Controller: ObservableObject {
@Published var haveProdBalance: Bool = false
@Published var balances: [Balance] = []
- @Published var discounts: [Discount] = []
+ @Published var discounts: [TalerToken] = []
+ @Published var subscriptions: [TalerToken] = []
@Published var defaultExchanges: [DefaultExchange] = []
@Published var scannedURLs: [ScannedURL] = []
@@ -307,6 +308,7 @@ class Controller: ObservableObject {
exchanges = []
balances = []
discounts = []
+ subscriptions = []
defaultExchanges = []
// printFonts()
// checkInternetConnection()
@@ -358,6 +360,22 @@ class Controller: ObservableObject {
}
return nil
}
+ // MARK: -
+ @MainActor
+ @discardableResult
+ func loadSubscriptions(_ stack: CallStack,_ model: WalletModel) async -> Int? {
+ if let response = try? await model.listSubscriptions(stack.push()) {
+ let reloaded = response.subscriptions
+ if reloaded != subscriptions {
+ self.logger.log("••Got new passes, will redraw")
+ subscriptions = reloaded // redraw
+ } else {
+ self.logger.log("••Same passes, no redraw")
+ }
+ return reloaded.count
+ }
+ return nil
+ }
// MARK: -
func exchange(for baseUrl: String) -> Exchange? {
for exchange in exchanges {
diff --git a/TalerWallet1/Model/Model+Balances.swift b/TalerWallet1/Model/Model+Balances.swift
@@ -83,7 +83,7 @@ fileprivate struct Balances: WalletBackendFormattedRequest {
typealias Response = BalancesResponse
}
// MARK: -
-struct Discount: Identifiable, Codable, Hashable, Sendable {
+struct TalerToken: Identifiable, Codable, Hashable, Sendable {
// Hash of token family info.
var tokenFamilyHash: String
// Hash of token issue public key.
@@ -103,7 +103,7 @@ struct Discount: Identifiable, Codable, Hashable, Sendable {
// End time of the token's validity period.
var validityEnd: Timestamp
// Number of tokens available to use.
- var tokensAvailable: Int
+ var tokensAvailable: Int? // Only for Discount
var id: String {
return tokenFamilyHash
@@ -111,7 +111,7 @@ struct Discount: Identifiable, Codable, Hashable, Sendable {
}
// MARK: -
struct DiscountsResponse: Decodable, Hashable, Sendable {
- let discounts: [Discount]
+ let discounts: [TalerToken]
}
/// A request to get the balances held in the wallet.
@@ -135,6 +135,31 @@ struct DeleteDiscount: WalletBackendFormattedRequest {
}
}
// MARK: -
+struct SubscriptionsResponse: Decodable, Hashable, Sendable {
+ let subscriptions: [TalerToken]
+}
+
+/// A request to get the balances held in the wallet.
+fileprivate struct ListSubscriptions: WalletBackendFormattedRequest {
+ func operation() -> String { "listSubscriptions" }
+ func args() -> Args { Args() }
+
+ struct Args: Encodable {} // no arguments needed
+
+ typealias Response = SubscriptionsResponse
+}
+/// A request to delete a subscription by ID.
+struct DeleteSubscription: WalletBackendFormattedRequest {
+ struct Response: Decodable {} // no result - getting no error back means success
+ func operation() -> String { "deleteSubscription" }
+ func args() -> Args { Args(tokenFamilyHash: tokenFamilyHash) }
+
+ var tokenFamilyHash: String
+ struct Args: Encodable {
+ var tokenFamilyHash: String
+ }
+}
+// MARK: -
extension WalletModel {
/// fetch Balances from Wallet-Core. No networking involved
nonisolated func getBalances(_ stack: CallStack, viewHandles: Bool = false) async throws -> BalancesResponse {
@@ -148,10 +173,22 @@ extension WalletModel {
let response = try await sendRequest(request, viewHandles: viewHandles)
return response
}
- /// fetch Discounts from Wallet-Core. No networking involved
+ /// delete a discount. No networking involved
nonisolated func deleteDiscount(tokenFamilyHash: String, viewHandles: Bool = false) async throws {
let request = DeleteDiscount(tokenFamilyHash: tokenFamilyHash)
logger.notice("deleteDiscount: \(tokenFamilyHash, privacy: .private(mask: .hash))")
let _ = try await sendRequest(request, viewHandles: viewHandles)
}
+ /// fetch Subscriptions from Wallet-Core. No networking involved
+ nonisolated func listSubscriptions(_ stack: CallStack, viewHandles: Bool = false) async throws -> SubscriptionsResponse {
+ let request = ListSubscriptions()
+ let response = try await sendRequest(request, viewHandles: viewHandles)
+ return response
+ }
+ /// delete a subscription. No networking involved
+ nonisolated func deleteSubscription(tokenFamilyHash: String, viewHandles: Bool = false) async throws {
+ let request = DeleteSubscription(tokenFamilyHash: tokenFamilyHash)
+ logger.notice("deleteSubscription: \(tokenFamilyHash, privacy: .private(mask: .hash))")
+ let _ = try await sendRequest(request, viewHandles: viewHandles)
+ }
}
diff --git a/TalerWallet1/Views/Balances/DiscountPasses.swift b/TalerWallet1/Views/Balances/DiscountPasses.swift
@@ -27,7 +27,7 @@ struct DiscountsPassesList: View {
if isDiscount {
await controller.loadDiscounts(stack.push("refreshing discounts"), model)
} else {
-// await controller.loadPasses(stack.push("refreshing passes"), model)
+ await controller.loadSubscriptions(stack.push("refreshing passes"), model)
}
}