gitbay/Views/Repos/WikiView.swift
79 lines · 2762 bytes
1import SwiftUI
2
3/// A repository's wiki pages, landing page first.
4struct WikiView: View {
5
6 @State private var model: WikiViewModel
7
8 init(client: GitbayClient, repo: String) {
9 _model = State(initialValue: WikiViewModel(client: client, repoPath: repo))
10 }
11
12 /// The landing page leads, then the rest in the order the server
13 /// gave them — the same order the web's sidebar shows.
14 private var ordered: [String] {
15 guard let listing = model.state.value else { return [] }
16 guard let home = listing.home else { return listing.pages }
17 return [home] + listing.pages.filter { $0 != home }
18 }
19
20 var body: some View {
21 List {
22 ForEach(ordered, id: \.self) { page in
23 NavigationLink(value: RepoRoute.wikiPage(
24 repo: model.repoPath, page: page
25 )) {
26 HStack(spacing: 8) {
27 Label(page, systemImage: "doc.text")
28 .font(.gbSans(.subheadline))
29 if page == model.state.value?.home {
30 GBChip("home", .gbAccent)
31 }
32 }
33 }
34 }
35 }
36 .overlay { LoadStateOverlay(state: model.state) }
37 .navigationTitle("Wiki")
38 .navigationBarTitleDisplayMode(.inline)
39 .task { await model.load() }
40 .refreshable { await model.load() }
41 }
42}
43
44/// One wiki page, rendered by its format — the pages here are .org as
45/// often as .md, so ReadmeView picks the renderer.
46struct WikiPageView: View {
47
48 @State private var model: WikiPageViewModel
49
50 init(client: GitbayClient, repo: String, page: String) {
51 _model = State(initialValue: WikiPageViewModel(
52 client: client, repoPath: repo, page: page
53 ))
54 }
55
56 var body: some View {
57 ScrollView {
58 if let page = model.state.value {
59 if page.isBinary {
60 ContentUnavailableView {
61 Label("Not a page", systemImage: "doc.zipper")
62 } description: {
63 Text(page.file)
64 }
65 } else if let content = page.content {
66 ReadmeView(name: page.file, content: content)
67 .padding(.horizontal, 16)
68 .padding(.vertical, 12)
69 .frame(maxWidth: .infinity, alignment: .leading)
70 }
71 }
72 }
73 .overlay { LoadStateOverlay(state: model.state) }
74 .navigationTitle(model.page)
75 .navigationBarTitleDisplayMode(.inline)
76 .task { await model.load() }
77 .refreshable { await model.load() }
78 }
79}