krz/rune

an ios client for njalla

clone: git clone https://gitbay.org/krz/rune.git

v1.2.0: 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}