summaryrefslogtreecommitdiff
path: root/Rune/ViewModels/SettingsViewModel.swift
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-03-24 21:48:23 -0500
committerChristian Cleberg <[email protected]>2026-03-24 21:48:23 -0500
commitcf587aa0573ac4f34e1effe70108a9eca82093ac (patch)
tree833e6f4961522e35b08af01b5f012983f8e34678 /Rune/ViewModels/SettingsViewModel.swift
downloadrune-cf587aa0573ac4f34e1effe70108a9eca82093ac.tar.gz
rune-cf587aa0573ac4f34e1effe70108a9eca82093ac.tar.bz2
rune-cf587aa0573ac4f34e1effe70108a9eca82093ac.zip
v1.0v1.0.0
Diffstat (limited to 'Rune/ViewModels/SettingsViewModel.swift')
-rw-r--r--Rune/ViewModels/SettingsViewModel.swift102
1 files changed, 102 insertions, 0 deletions
diff --git a/Rune/ViewModels/SettingsViewModel.swift b/Rune/ViewModels/SettingsViewModel.swift
new file mode 100644
index 0000000..6b773eb
--- /dev/null
+++ b/Rune/ViewModels/SettingsViewModel.swift
@@ -0,0 +1,102 @@
+import Combine
+import Foundation
+
+@MainActor
+final class SettingsViewModel: ObservableObject {
+ @Published private(set) var client: NjallaClient?
+ @Published private(set) var balance: WalletBalance?
+ @Published private(set) var isAuthenticated = false
+ @Published private(set) var isBootstrapped = false
+ @Published private(set) var isLoadingBalance = false
+ @Published var errorMessage: String?
+
+ private let keychainManager = KeychainManager()
+
+ var requiresOnboarding: Bool {
+ isBootstrapped && !isAuthenticated
+ }
+
+ func bootstrap() async {
+ guard !isBootstrapped else { return }
+
+ defer {
+ isBootstrapped = true
+ }
+
+ do {
+ guard let token = try keychainManager.readToken(), !token.isEmpty else {
+ isAuthenticated = false
+ client = nil
+ balance = nil
+ return
+ }
+
+ let client = NjallaClient(token: token)
+ self.client = client
+ isAuthenticated = true
+ try await refreshBalance()
+ } catch is CancellationError {
+ return
+ } catch {
+ if (error as? URLError)?.code == .cancelled {
+ return
+ }
+ errorMessage = error.localizedDescription
+ client = nil
+ balance = nil
+ isAuthenticated = false
+ }
+ }
+
+ func login(token: String) async throws {
+ let trimmedToken = token.trimmingCharacters(in: .whitespacesAndNewlines)
+ let client = NjallaClient(token: trimmedToken)
+ let balance = try await client.getBalance()
+ try keychainManager.saveToken(trimmedToken)
+
+ self.client = client
+ self.balance = balance
+ isAuthenticated = true
+ isBootstrapped = true
+ errorMessage = nil
+ }
+
+ func refreshBalance() async throws {
+ guard let client else { return }
+
+ isLoadingBalance = true
+ defer {
+ isLoadingBalance = false
+ }
+
+ do {
+ balance = try await client.getBalance()
+ errorMessage = nil
+ } catch is CancellationError {
+ throw CancellationError()
+ } catch {
+ if (error as? URLError)?.code == .cancelled {
+ throw error
+ }
+ errorMessage = error.localizedDescription
+ throw error
+ }
+ }
+
+ func logout() throws {
+ try keychainManager.deleteToken()
+ client = nil
+ balance = nil
+ isAuthenticated = false
+ errorMessage = nil
+ }
+
+ func logoutIfCurrentTokenDeleted(_ key: String) throws -> Bool {
+ guard let storedToken = try keychainManager.readToken(), storedToken == key else {
+ return false
+ }
+
+ try logout()
+ return true
+ }
+}