import Foundation import Observation /// Fork, watch, mute and bookmark — repository-level actions performed /// against a `RepoDetail` already loaded elsewhere: /// /// - Watch/mute/bookmark state now comes back on `repo show` /// (`RepoDetail.watch`/`.bookmarked`), so this view model only sends /// the write commands; the caller rereads `repo show` to see the result. /// - `fork_of` is also on `repo fork`'s own response, which is why `fork` /// hands its result back to the caller directly. @Observable @MainActor final class RepoActionsViewModel { /// `repo fork`'s result — the only place `fork_of` is reported. nonisolated struct ForkResult: Decodable, Sendable, Hashable { let path: String let forkOf: String enum CodingKeys: String, CodingKey { case path case forkOf = "fork_of" } } private(set) var actionError: String? private(set) var notice: String? private(set) var working = false private let client: GitbayClient let repoPath: String init(client: GitbayClient, repoPath: String) { self.client = client self.repoPath = repoPath } // MARK: - Bookmarks func setBookmarked(_ bookmarked: Bool) async { await perform(["repo", bookmarked ? "bookmark" : "unbookmark", repoPath]) } // MARK: - Watch / mute func watch() async { await perform(["repo", "watch", repoPath]) if actionError == nil { notice = "You are now watching this repository." } } func mute() async { await perform(["repo", "mute", repoPath]) if actionError == nil { notice = "Notifications for this repository are muted." } } func unwatch() async { await perform(["repo", "unwatch", repoPath]) if actionError == nil { notice = "Notifications for this repository are back to the default." } } // MARK: - Fork func fork(named name: String?) async -> ForkResult? { var argv = ["repo", "fork", repoPath] if let name { let trimmed = name.trimmingCharacters(in: .whitespacesAndNewlines) if !trimmed.isEmpty { argv += ["--name", trimmed] } } working = true actionError = nil notice = nil defer { working = false } do { guard let result = try await client.run(argv, as: ForkResult.self) else { actionError = "The fork succeeded, but the server reported nothing back." return nil } return result } catch let error as GitbayError { actionError = error.userFacingMessage return nil } catch { actionError = GitbayError.transport(error).userFacingMessage return nil } } private func perform(_ argv: [String]) async { working = true actionError = nil notice = nil defer { working = false } do { try await client.run(argv) } catch let error as GitbayError { actionError = error.userFacingMessage } catch { actionError = GitbayError.transport(error).userFacingMessage } } }