~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
92 lines · 3.3 KB Raw History
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)
    }
}