gitbay/Repos/TreeViewModel.swift
48 lines · 1514 bytes
1import Foundation
2import Observation
3
4/// One directory of `repo tree`. Navigation pushes a new instance per
5/// level, so back is free and each level revalidates independently.
6@Observable
7@MainActor
8final class TreeViewModel {
9
10 private(set) var state: LoadState<TreeListing> = .loading
11
12 private let client: GitbayClient
13 let repoPath: String
14 let directory: String
15 let ref: String?
16
17 init(client: GitbayClient, repoPath: String, directory: String = "", ref: String? = nil) {
18 self.client = client
19 self.repoPath = repoPath
20 self.directory = directory
21 self.ref = ref
22 }
23
24 /// Directories first, then files, both alphabetically — the ordering
25 /// every forge uses.
26 var entries: [TreeEntry] {
27 guard let listing = state.value else { return [] }
28 return listing.entries.sorted {
29 if $0.isDirectory != $1.isDirectory { return $0.isDirectory }
30 return $0.name.localizedStandardCompare($1.name) == .orderedAscending
31 }
32 }
33
34 func load() async {
35 var argv = ["repo", "tree", repoPath]
36 if !directory.isEmpty { argv.append(directory) }
37 if let ref { argv.append(contentsOf: ["--ref", ref]) }
38 do {
39 state = .loaded(try await client.read(argv, as: TreeListing.self))
40 } catch {
41 state = .from(error)
42 }
43 }
44
45 func childDirectory(_ entry: TreeEntry) -> String {
46 directory.isEmpty ? entry.name : "\(directory)/\(entry.name)"
47 }
48}