krz/rune
an ios client for njalla
clone: git clone https://gitbay.org/krz/rune.git
main: Rune/ViewModels/TokenViewModel.swift · raw
1import Combine
2import Foundation
3
4@MainActor
5final class TokenViewModel: ObservableObject {
6 @Published private(set) var tokens: [APIToken] = []
7 @Published private(set) var isLoading = false
8 @Published private(set) var isSaving = false
9 @Published var listErrorMessage: String?
10 @Published var mutationErrorMessage: String?
11
12 func reset() {
13 tokens = []
14 isLoading = false
15 isSaving = false
16 listErrorMessage = nil
17 mutationErrorMessage = nil
18 }
19
20 func loadTokens(client: NjallaClient) async {
21 guard !isLoading else { return }
22
23 isLoading = true
24 defer {
25 isLoading = false
26 }
27
28 do {
29 tokens = try await client.listTokens().sorted {
30 tokenLabel(for: $0).localizedCaseInsensitiveCompare(tokenLabel(for: $1)) == .orderedAscending
31 }
32 listErrorMessage = nil
33 } catch is CancellationError {
34 return
35 } catch {
36 if (error as? URLError)?.code == .cancelled {
37 return
38 }
39 listErrorMessage = error.userFacingMessage
40 }
41 }
42
43 func addToken(request: TokenCreateRequest, client: NjallaClient) async -> Bool {
44 guard !isSaving else { return false }
45
46 isSaving = true
47 mutationErrorMessage = nil
48
49 do {
50 try await client.addToken(request: request)
51 isSaving = false
52 Task {
53 await loadTokens(client: client)
54 }
55 return true
56 } catch is CancellationError {
57 isSaving = false
58 return false
59 } catch {
60 if (error as? URLError)?.code == .cancelled {
61 isSaving = false
62 return false
63 }
64 isSaving = false
65 mutationErrorMessage = error.userFacingMessage
66 return false
67 }
68 }
69
70 func removeToken(_ token: APIToken, client: NjallaClient) async -> Bool {
71 guard !isSaving else { return false }
72
73 isSaving = true
74 mutationErrorMessage = nil
75
76 do {
77 try await client.removeToken(key: token.key)
78 tokens.removeAll { $0.key == token.key }
79 isSaving = false
80 Task {
81 await loadTokens(client: client)
82 }
83 return true
84 } catch is CancellationError {
85 isSaving = false
86 return false
87 } catch {
88 if (error as? URLError)?.code == .cancelled {
89 isSaving = false
90 return false
91 }
92 isSaving = false
93 mutationErrorMessage = error.userFacingMessage
94 return false
95 }
96 }
97
98 func dismissMutationError() {
99 mutationErrorMessage = nil
100 }
101
102 func tokenLabel(for token: APIToken) -> String {
103 let trimmedComment = token.comment?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
104 if !trimmedComment.isEmpty {
105 return trimmedComment
106 }
107
108 return String(token.key.prefix(8))
109 }
110
111 private func sortedTokens(_ tokens: [APIToken]) -> [APIToken] {
112 tokens.sorted {
113 tokenLabel(for: $0).localizedCaseInsensitiveCompare(tokenLabel(for: $1)) == .orderedAscending
114 }
115 }
116}