diff options
Diffstat (limited to 'Hutch/Views/Activity/ActivityView.swift')
| -rw-r--r-- | Hutch/Views/Activity/ActivityView.swift | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/Hutch/Views/Activity/ActivityView.swift b/Hutch/Views/Activity/ActivityView.swift new file mode 100644 index 0000000..6379d45 --- /dev/null +++ b/Hutch/Views/Activity/ActivityView.swift @@ -0,0 +1,96 @@ +import SwiftUI + +struct ActivityView: View { + @Environment(AppState.self) private var appState + @State private var viewModel: ActivityViewModel? + + var body: some View { + Group { + if let viewModel { + content(viewModel) + } else { + SRHTLoadingStateView(message: "Loading Activity…") + } + } + .navigationTitle("Activity") + .navigationBarTitleDisplayMode(.inline) + .task { + let model = viewModel ?? ActivityViewModel(client: appState.client) + viewModel = model + await model.loadIfNeeded() + } + } + + @ViewBuilder + private func content(_ viewModel: ActivityViewModel) -> some View { + List { + ForEach(viewModel.events) { event in + NavigationLink { + TicketDetailView( + ownerUsername: event.ownerUsername, + trackerName: event.trackerName, + trackerId: event.trackerID, + trackerRid: event.trackerRID, + ticketId: event.ticketID + ) + } label: { + ActivityRow(event: event) + } + .themedRow() + } + + if viewModel.hasMore { + HStack { + Spacer() + ProgressView() + Spacer() + } + .themedRow() + .task { await viewModel.loadMore() } + } + } + .themedList() + .listStyle(.plain) + .refreshable { await viewModel.load() } + .overlay { + if viewModel.isLoading, viewModel.events.isEmpty { + SRHTLoadingStateView(message: "Loading Activity…") + } else if let error = viewModel.error, viewModel.events.isEmpty { + SRHTErrorStateView( + title: "Couldn't Load Activity", + message: error, + retryAction: { await viewModel.load() } + ) + } else if viewModel.events.isEmpty { + ContentUnavailableView( + "No Activity", + systemImage: "bell", + description: Text("Ticket activity you are subscribed to or involved in appears here.") + ) + } + } + } +} + +private struct ActivityRow: View { + let event: ActivityEvent + + var body: some View { + VStack(alignment: .leading, spacing: 4) { + Text(event.ticketSubject) + .font(.subheadline.weight(.medium)) + .lineLimit(2) + + Text("\(event.summary) • \(event.created.relativeDescription)") + .font(.caption) + .foregroundStyle(.secondary) + + Text("\(event.trackerOwner.canonicalName)/\(event.trackerName) #\(event.ticketID)") + .font(.caption2) + .foregroundStyle(.tertiary) + } + .padding(.vertical, 2) + .accessibilityElement(children: .combine) + .accessibilityLabel("\(event.ticketSubject), \(event.summary), \(event.created.relativeDescription)") + } +} |
