gitbay/Views/Repos/FileView.swift
80 lines · 2624 bytes
1import SwiftUI
2
3struct FileView: View {
4
5 @Environment(\.colorScheme) private var colorScheme
6 @State private var model: FileViewModel
7
8 init(client: GitbayClient, repo: String, path: String, ref: String?) {
9 _model = State(initialValue: FileViewModel(
10 client: client, repoPath: repo, filePath: path, ref: ref
11 ))
12 }
13
14 var body: some View {
15 Group {
16 if let file = model.state.value {
17 content(file)
18 } else {
19 Color.clear
20 }
21 }
22 .overlay { LoadStateOverlay(state: model.state) }
23 .navigationTitle(model.fileName)
24 .navigationBarTitleDisplayMode(.inline)
25 .task { await model.load() }
26 }
27
28 @ViewBuilder
29 private func content(_ file: FileContent) -> some View {
30 if file.binary {
31 ContentUnavailableView {
32 Label("Binary file", systemImage: "doc.zipper")
33 } description: {
34 Text("\(file.file) · \(Int64(file.size).formatted(.byteCount(style: .file)))")
35 }
36 } else if let text = file.content {
37 VStack(spacing: 0) {
38 if file.isTruncated {
39 Label(
40 "Truncated — showing the first \(Int64(file.size).formatted(.byteCount(style: .file))).",
41 systemImage: "scissors"
42 )
43 .font(.caption)
44 .frame(maxWidth: .infinity, alignment: .leading)
45 .padding(8)
46 .background(.yellow.opacity(0.15))
47 }
48 CodeScrollView(text: text, fileName: model.fileName, colorScheme: colorScheme)
49 }
50 }
51 }
52}
53
54/// Both-axis scrolling for code, highlighted when the language is known.
55private struct CodeScrollView: View {
56
57 let text: String
58 let fileName: String
59 let colorScheme: ColorScheme
60
61 var body: some View {
62 ScrollView([.horizontal, .vertical]) {
63 code
64 .padding(12)
65 .frame(maxWidth: .infinity, alignment: .leading)
66 }
67 .font(.caption.monospaced())
68 }
69
70 private var code: some View {
71 let font = UIFont.monospacedSystemFont(
72 ofSize: UIFont.preferredFont(forTextStyle: .caption1).pointSize, weight: .regular
73 )
74 let highlighter = SyntaxHighlighter(colorScheme: colorScheme)
75 if let highlighted = highlighter.highlight(text, fileName: fileName, font: font) {
76 return Text(AttributedString(highlighted))
77 }
78 return Text(text)
79 }
80}