gitbay/Views/Discovery/FeedView.swift

5411753023a5e2958f17b523903abce380941107
gitbay-ios/gitbay/Views/Discovery/FeedView.swift history · blame · raw

90 lines · 2946 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                link(for: event)
20            }
21            PageFooter(list: list)
22        }
23        .overlay { LoadStateOverlay(state: list.state) }
24        .navigationTitle("Feed")
25        .toolbar { AccountMenu() }
26        .task {
27            if list.state.value == nil {
28                await list.reload()
29            }
30        }
31        .refreshable { await list.reload() }
32    }
33
34    @ViewBuilder
35    private func link(for event: FeedEvent) -> some View {
36        switch event.destination {
37        case .repo(let repo):
38            NavigationLink(value: RepoRoute.repo(repo)) { FeedRow(event: event) }
39        case .mr(let repo, let number):
40            NavigationLink(value: MRRoute.mr(repo: repo, number: number)) { FeedRow(event: event) }
41        case .issue(let repo, let number):
42            NavigationLink(value: IssueRoute.issue(repo: repo, number: number)) { FeedRow(event: event) }
43        case .build(let repo, let number):
44            NavigationLink(value: BuildRoute.log(repo: repo, number: number)) { FeedRow(event: event) }
45        case .release(let repo, let tag):
46            NavigationLink(value: ReleaseRoute.release(repo: repo, tag: tag)) { FeedRow(event: event) }
47        case nil:
48            FeedRow(event: event)
49        }
50    }
51}
52
53private struct FeedRow: View {
54    let event: FeedEvent
55
56    var body: some View {
57        HStack(spacing: 10) {
58            Image(systemName: event.icon.name)
59                .foregroundStyle(color)
60                .frame(width: 22)
61            VStack(alignment: .leading, spacing: 2) {
62                Text(event.repo)
63                    .font(.caption)
64                    .foregroundStyle(.secondary)
65                HStack(spacing: 4) {
66                    if let actor = event.actor, !actor.isEmpty {
67                        Text(actor)
68                            .font(.subheadline.weight(.semibold))
69                    }
70                    Text(event.phrase)
71                        .font(.subheadline)
72                        .lineLimit(1)
73                }
74            }
75            Spacer()
76            Text(event.createdAt, format: .relative(presentation: .named))
77                .font(.caption)
78                .foregroundStyle(.tertiary)
79        }
80        .padding(.vertical, 2)
81    }
82
83    private var color: Color {
84        switch event.icon.isPositive {
85        case true: .green
86        case false: .red
87        default: .secondary
88        }
89    }
90}