gitbay/Views/Discovery/FeedView.swift

1c5622210d0f72efd86582ce195a6741401e7939
gitbay-ios/gitbay/Views/Discovery/FeedView.swift history · blame · raw

95 lines · 3062 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        .toolbar { AccountMenu() }
26        .task {
27            if list.state.value == nil {
28                await list.reload()
29            }
30        }
31        .refreshable { await list.reload() }
32    }
33
34}
35
36/// One feed event linked to the same native destination everywhere it is
37/// represented, including the dashboard's recent-activity section.
38struct FeedLink: View {
39    let event: FeedEvent
40
41    @ViewBuilder
42    var body: some View {
43        switch event.destination {
44        case .repo(let repo):
45            NavigationLink(value: RepoRoute.repo(repo)) { FeedRow(event: event) }
46        case .mr(let repo, let number):
47            NavigationLink(value: MRRoute.mr(repo: repo, number: number)) { FeedRow(event: event) }
48        case .issue(let repo, let number):
49            NavigationLink(value: IssueRoute.issue(repo: repo, number: number)) { FeedRow(event: event) }
50        case .build(let repo, let number):
51            NavigationLink(value: BuildRoute.log(repo: repo, number: number)) { FeedRow(event: event) }
52        case .release(let repo, let tag):
53            NavigationLink(value: ReleaseRoute.release(repo: repo, tag: tag)) { FeedRow(event: event) }
54        case nil:
55            FeedRow(event: event)
56        }
57    }
58}
59
60struct FeedRow: View {
61    let event: FeedEvent
62
63    var body: some View {
64        HStack(spacing: 10) {
65            Image(systemName: event.icon.name)
66                .foregroundStyle(color)
67                .frame(width: 22)
68            VStack(alignment: .leading, spacing: 2) {
69                Text(event.repo)
70                    .font(.gbSans(.caption))
71                    .foregroundStyle(.secondary)
72                HStack(spacing: 4) {
73                    Text(event.displayActor)
74                        .font(.gbSans(.subheadline).weight(.semibold))
75                    Text(event.phrase)
76                        .font(.gbSans(.subheadline))
77                        .lineLimit(1)
78                }
79            }
80            Spacer()
81            Text(event.createdAt, format: .relative(presentation: .named))
82                .font(.gbSans(.caption))
83                .foregroundStyle(.secondary)
84        }
85        .padding(.vertical, 2)
86    }
87
88    private var color: Color {
89        switch event.icon.isPositive {
90        case true: .gbOK
91        case false: .gbBad
92        default: .secondary
93        }
94    }
95}