johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
import SwiftUI
struct ExploreView: View {
@State private var events: [FeedEvent] = []
@State private var cursor: String?
@State private var failure: String?
@State private var loaded = false
var body: some View {
Group {
if let failure, events.isEmpty {
ContentUnavailableView {
SwiftUI.Label("This didn't load", systemImage: "wifi.exclamationmark")
} description: {
Text(failure)
} actions: {
Button("Try again") { Task { await reload() } }
}
} else if !loaded {
ProgressView()
} else if events.isEmpty {
ContentUnavailableView("Nothing yet", systemImage: "globe", description: Text("Activity on public repositories shows here."))
} else {
List {
Group {
ForEach(events) { event in
NavigationLink(value: target(event)) { EventRow(event: event) }
.onAppear { if event == events.last { Task { await more() } } }
}
}
.listRowBackground(Color.page)
}
.listStyle(.plain)
.refreshable { await reload() }
}
}
.navigationTitle("Explore")
.task { if !loaded { await reload() } }
}
private func target(_ event: FeedEvent) -> Route {
if let subject = event.action.subject, kind(ofURI: subject) != nil {
return .thread(event.repo, uri: subject)
}
return .repo(event.repo)
}
private func reload() async {
do {
let feed: Feed = try await Client.shared.query("getFeed", ["limit": "40"])
events = feed.events
cursor = feed.cursor
failure = nil
} catch is CancellationError {
} catch {
failure = error.localizedDescription
}
loaded = true
}
private func more() async {
guard let cursor else { return }
let feed: Feed? = try? await Client.shared.query("getFeed", ["limit": "40", "cursor": cursor])
guard let feed else { return }
self.cursor = feed.cursor
events += feed.events.filter { !events.contains($0) }
}
}
struct EventRow: View {
let event: FeedEvent
var body: some View {
HStack(alignment: .top, spacing: Space.s3) {
Avatar(url: event.actor.avatar, name: event.actor.handle, did: event.actor.did, size: Size.Avatar.medium)
VStack(alignment: .leading, spacing: Space.s1) {
HStack {
Text(event.repo.label).lineLimit(1).foregroundStyle(.secondary)
Spacer()
Text(When.short(event.createdAt)).foregroundStyle(.secondary)
}
.font(.subheadline)
Text("\(Text(event.actor.handle).fontWeight(.medium)) \(event.action.summary)")
.font(.subheadline)
if let title = event.action.title {
Text(title).font(.body.weight(.medium)).lineLimit(2)
}
}
}
.padding(.vertical, Space.s1)
}
}