a native ios client for gitbay

client ios swift

https://gitbay.org

gitbay/Views/Discovery/FeedView.swift

94 lines · 3030 bytes

 1import SwiftUI
 2
 3/// Activity on everything you can reach  the `feed` command, paginated.
 4struct FeedView: View {
 5
 6    @State private var list: PagedListModel<FeedEvent>
 7
 8    init(client: GitbayClient) {
 9        _list = State(initialValue: PagedListModel(
10            client: client,
11            argv: ["feed"],
12            emptyMessage: "Nothing has happened yet on repositories you can reach."
13        ))
14    }
15
16    var body: some View {
17        List {
18            ForEach(list.state.value ?? []) { event in
19                FeedLink(event: event)
20            }
21            PageFooter(list: list)
22        }
23        .overlay { LoadStateOverlay(state: list.state) }
24        .navigationTitle("Feed")
25        .task {
26            if list.state.value == nil {
27                await list.reload()
28            }
29        }
30        .refreshable { await list.reload() }
31    }
32
33}
34
35/// One feed event linked to the same native destination everywhere it is
36/// represented, including the dashboard's recent-activity section.
37struct FeedLink: View {
38    let event: FeedEvent
39
40    @ViewBuilder
41    var body: some View {
42        switch event.destination {
43        case .repo(let repo):
44            NavigationLink(value: RepoRoute.repo(repo)) { FeedRow(event: event) }
45        case .mr(let repo, let number):
46            NavigationLink(value: MRRoute.mr(repo: repo, number: number)) { FeedRow(event: event) }
47        case .issue(let repo, let number):
48            NavigationLink(value: IssueRoute.issue(repo: repo, number: number)) { FeedRow(event: event) }
49        case .build(let repo, let number):
50            NavigationLink(value: BuildRoute.detail(repo: repo, number: number)) { FeedRow(event: event) }
51        case .release(let repo, let tag):
52            NavigationLink(value: ReleaseRoute.release(repo: repo, tag: tag)) { FeedRow(event: event) }
53        case nil:
54            FeedRow(event: event)
55        }
56    }
57}
58
59struct FeedRow: View {
60    let event: FeedEvent
61
62    var body: some View {
63        HStack(spacing: 10) {
64            Image(systemName: event.icon.name)
65                .foregroundStyle(color)
66                .frame(width: 22)
67            VStack(alignment: .leading, spacing: 2) {
68                Text(event.repo)
69                    .font(.gbSans(.caption))
70                    .foregroundStyle(.secondary)
71                HStack(spacing: 4) {
72                    Text(event.displayActor)
73                        .font(.gbSans(.subheadline).weight(.semibold))
74                    Text(event.phrase)
75                        .font(.gbSans(.subheadline))
76                        .lineLimit(1)
77                }
78            }
79            Spacer()
80            Text(event.createdAt, format: .relative(presentation: .named))
81                .font(.gbSans(.caption))
82                .foregroundStyle(.secondary)
83        }
84        .padding(.vertical, 2)
85    }
86
87    private var color: Color {
88        switch event.icon.isPositive {
89        case true: .gbOK
90        case false: .gbBad
91        default: .secondary
92        }
93    }
94}