aboutsummaryrefslogtreecommitdiff
path: root/Hutch/Views/Repositories/LinkedFileSheetView.swift
blob: 4f176be5912c7ea4be07275580beb50473fe4391 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import SwiftUI

/// A sheet that fetches and displays a single repository file by path,
/// using the `repository.path()` GraphQL field — one API call, no tree traversal.
struct LinkedFileSheetView: View {
    let rid: String
    let service: SRHTService
    let client: SRHTClient
    let request: LinkedFileRequest

    @AppStorage(AppStorageKeys.wrapRepositoryFileLines) private var wrapLines = false
    @Environment(\.dismiss) private var dismiss

    @State private var entry: TreeEntry?
    @State private var isLoading = true
    @State private var error: String?

    var body: some View {
        NavigationStack {
            Group {
                if isLoading {
                    SRHTLoadingStateView(message: "Loading \(request.fileName)…")
                } else if let error {
                    SRHTErrorStateView(
                        title: "Couldn't Load File",
                        message: error,
                        retryAction: { await load() }
                    )
                } else if let entry, let object = entry.object {
                    fileContentView(entry: entry, object: object)
                } else {
                    ContentUnavailableView(
                        "File Not Found",
                        systemImage: "doc.questionmark",
                        description: Text("\(request.path) could not be found in this repository.")
                    )
                }
            }
            .navigationTitle(request.fileName)
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .topBarTrailing) {
                    Button("Done") { dismiss() }
                }
            }
        }
        .task { await load() }
    }

    private func load() async {
        isLoading = true
        error = nil
        let vm = FileTreeViewModel(repositoryRid: rid, service: service, client: client)
        do {
            entry = try await vm.fetchLinkedFile(path: request.path, revspec: request.revspec)
        } catch {
            self.error = error.userFacingMessage
        }
        isLoading = false
    }

    @ViewBuilder
    private func fileContentView(entry: TreeEntry, object: GitObject) -> some View {
        switch object {
        case .textBlob(let blob):
            CodeFileTextView(
                text: blob.text ?? "",
                fileName: entry.name,
                wrapLines: wrapLines
            )
        case .binaryBlob:
            ContentUnavailableView(
                "Binary File",
                systemImage: "doc.zipper",
                description: Text("Binary files cannot be displayed inline.")
            )
        default:
            ContentUnavailableView(
                "Unknown File",
                systemImage: "questionmark.folder",
                description: Text("This object type cannot be displayed.")
            )
        }
    }
}