gitbay/Account/WebSessions.swift

main
gitbay-ios/gitbay/Account/WebSessions.swift history · blame · raw

66 lines · 1886 bytes

6 symbols in this file
 1import Foundation
 2import Observation
 3
 4/// One row of `web sessions list`: a browser signed in to the web.
 5nonisolated struct WebSession: Decodable, Sendable, Hashable, Identifiable {
 6    /// The first characters of the session's hash, which `revoke` takes.
 7    let id: String
 8    let createdAt: Date
 9    let expiresAt: Date
10    let lastUsedAt: Date
11
12    enum CodingKeys: String, CodingKey {
13        case id
14        case createdAt = "created_at"
15        case expiresAt = "expires_at"
16        case lastUsedAt = "last_used_at"
17    }
18}
19
20/// `web sessions list` and `revoke`. The app signs in with an API token,
21/// so ending a browser session never signs the app out.
22@Observable
23@MainActor
24final class WebSessionsViewModel {
25
26    private(set) var state: LoadState<[WebSession]> = .loading
27    private(set) var actionError: String?
28    private(set) var working = false
29
30    private let client: GitbayClient
31
32    init(client: GitbayClient) {
33        self.client = client
34    }
35
36    func load() async {
37        do {
38            let sessions = try await client.readList(["web", "sessions", "list"], of: WebSession.self)
39            state = sessions.isEmpty ? .empty("No browser is signed in.") : .loaded(sessions)
40        } catch {
41            state = .from(error)
42        }
43    }
44
45    func revoke(id: String) async {
46        await perform(["web", "sessions", "revoke", id])
47    }
48
49    func revokeAll() async {
50        await perform(["web", "sessions", "revoke", "--all"])
51    }
52
53    private func perform(_ argv: [String]) async {
54        working = true
55        actionError = nil
56        defer { working = false }
57        do {
58            try await client.run(argv)
59            await load()
60        } catch let error as GitbayError {
61            actionError = error.userFacingMessage
62        } catch {
63            actionError = GitbayError.transport(error).userFacingMessage
64        }
65    }
66}