gitbay/MRs/MRListViewModel.swift

v1.1.1
gitbay-ios/gitbay/MRs/MRListViewModel.swift history · blame · raw

73 lines · 2267 bytes

 1import Foundation
 2import Observation
 3
 4/// `mr list <repo> --state <s>`, paginated.
 5@Observable
 6@MainActor
 7final class MRListViewModel {
 8
 9    let list: PagedListModel<MergeRequest>
10    let repoPath: String
11    private let client: GitbayClient
12    var filter = MRFilter() {
13        didSet {
14            guard filter != oldValue else { return }
15            configureList()
16            // Only the free-text fields changed: debounce, so typing does
17            // not fire a request per keystroke. Anything else — a picker —
18            // reloads now. MRFilter has no `assignee` field: `mr list`
19            // rejects it.
20            var typed = oldValue
21            typed.search = filter.search
22            typed.author = filter.author
23            if typed == filter {
24                scheduleSearchReload()
25            } else {
26                reloadTask?.cancel()
27                reloadTask = Task { await list.reload() }
28            }
29        }
30    }
31
32    /// Open and closed milestones for the filter picker; fetched on first use.
33    private(set) var milestones: [Milestone] = []
34    private var reloadTask: Task<Void, Never>?
35
36    init(client: GitbayClient, repoPath: String) {
37        self.client = client
38        self.repoPath = repoPath
39        list = PagedListModel(
40            client: client,
41            argv: ["mr", "list", repoPath] + MRFilter().flags(),
42            emptyMessage: MRFilter().emptyMessage
43        )
44    }
45
46    var state: LoadState<[MergeRequest]> { list.state }
47
48    func load() async {
49        reloadTask?.cancel()
50        await list.reload()
51    }
52
53    func loadMilestones() async {
54        guard milestones.isEmpty else { return }
55        milestones = (try? await client.readList(
56            ["milestone", "list", repoPath, "--state", "all"], of: Milestone.self
57        )) ?? []
58    }
59
60    private func scheduleSearchReload() {
61        reloadTask?.cancel()
62        reloadTask = Task {
63            guard (try? await Task.sleep(for: .milliseconds(300))) != nil else { return }
64            guard !Task.isCancelled else { return }
65            await list.reload()
66        }
67    }
68
69    private func configureList() {
70        list.argv = ["mr", "list", repoPath] + filter.flags()
71        list.emptyMessage = filter.emptyMessage
72    }
73}