import SwiftUI /// Activity on everything you can reach — the `feed` command, paginated. struct FeedView: View { @State private var list: PagedListModel init(client: GitbayClient) { _list = State(initialValue: PagedListModel( client: client, argv: ["feed"], emptyMessage: "Nothing has happened yet on repositories you can reach." )) } var body: some View { List { ForEach(list.state.value ?? []) { event in FeedLink(event: event) } PageFooter(list: list) } .overlay { LoadStateOverlay(state: list.state) } .navigationTitle("Feed") .task { if list.state.value == nil { await list.reload() } } .refreshable { await list.reload() } } } /// One feed event linked to the same native destination everywhere it is /// represented, including the dashboard's recent-activity section. struct FeedLink: View { let event: FeedEvent @ViewBuilder var body: some View { switch event.destination { case .repo(let repo): NavigationLink(value: RepoRoute.repo(repo)) { FeedRow(event: event) } case .mr(let repo, let number): NavigationLink(value: MRRoute.mr(repo: repo, number: number)) { FeedRow(event: event) } case .issue(let repo, let number): NavigationLink(value: IssueRoute.issue(repo: repo, number: number)) { FeedRow(event: event) } case .build(let repo, let number): NavigationLink(value: BuildRoute.detail(repo: repo, number: number)) { FeedRow(event: event) } case .release(let repo, let tag): NavigationLink(value: ReleaseRoute.release(repo: repo, tag: tag)) { FeedRow(event: event) } case nil: FeedRow(event: event) } } } struct FeedRow: View { let event: FeedEvent var body: some View { HStack(spacing: 10) { Image(systemName: event.icon.name) .foregroundStyle(color) .frame(width: 22) VStack(alignment: .leading, spacing: 2) { Text(event.repo) .font(.gbSans(.caption)) .foregroundStyle(.secondary) HStack(spacing: 4) { Text(event.displayActor) .font(.gbSans(.subheadline).weight(.semibold)) Text(event.phrase) .font(.gbSans(.subheadline)) .lineLimit(1) } } Spacer() Text(event.createdAt, format: .relative(presentation: .named)) .font(.gbSans(.caption)) .foregroundStyle(.secondary) } .padding(.vertical, 2) } private var color: Color { switch event.icon.isPositive { case true: .gbOK case false: .gbBad default: .secondary } } }