7c0fc19b79
- Discover: fetches all relay feeds, shuffles randomly, shows avatar/nick/bio with Follow/Unfollow per user - Default post history: 7 → 14 days (OrgSocialApp.swift + TimelineViewModel fallback) - Settings About section: Donate (liberapay.com/org-social) + Issue/PR (git.andros.dev/andros/contribute) links - Tab bar: 5 tabs (Timeline, Notifications, Discover, Groups, Profile) — Settings accessible via gear in Profile toolbar - OwnProfileView: shows Settings gear in toolbar; shows action button to open Settings when feed URL not configured
120 lines
4.4 KiB
Swift
120 lines
4.4 KiB
Swift
import SwiftUI
|
|
import OrgSocialKit
|
|
|
|
struct DiscoverView: View {
|
|
@State private var viewModel = DiscoverViewModel()
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
Group {
|
|
if viewModel.isLoading && viewModel.users.isEmpty {
|
|
VStack(spacing: 16) {
|
|
ProgressView()
|
|
Text("Loading profiles from relay...")
|
|
.font(.subheadline)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
} else if let error = viewModel.errorMessage, viewModel.users.isEmpty {
|
|
ContentUnavailableView {
|
|
Label("Discover unavailable", systemImage: "person.slash")
|
|
} description: {
|
|
Text(error)
|
|
} actions: {
|
|
Button("Retry") { Task { await viewModel.load() } }
|
|
.buttonStyle(.bordered)
|
|
}
|
|
} else if viewModel.users.isEmpty {
|
|
ContentUnavailableView(
|
|
"No profiles found",
|
|
systemImage: "person.3",
|
|
description: Text("No other users registered on this relay.")
|
|
)
|
|
} else {
|
|
List(viewModel.users) { user in
|
|
DiscoverUserRow(user: user, viewModel: viewModel)
|
|
}
|
|
.listStyle(.plain)
|
|
.navigationDestination(for: URL.self) { ProfileView(feedURL: $0) }
|
|
}
|
|
}
|
|
.navigationTitle("Discover")
|
|
.toolbar {
|
|
ToolbarItem(placement: .navigationBarTrailing) {
|
|
if viewModel.isLoading {
|
|
ProgressView()
|
|
} else {
|
|
Button {
|
|
Task { await viewModel.load() }
|
|
} label: {
|
|
Image(systemName: "arrow.clockwise")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.task { await viewModel.load() }
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct DiscoverUserRow: View {
|
|
let user: DiscoverViewModel.DiscoverUser
|
|
var viewModel: DiscoverViewModel
|
|
@State private var isToggling = false
|
|
|
|
var body: some View {
|
|
HStack(spacing: 12) {
|
|
NavigationLink(value: user.feedURL) {
|
|
HStack(spacing: 12) {
|
|
AvatarView(url: user.avatar, nick: user.nick, size: 44)
|
|
|
|
VStack(alignment: .leading, spacing: 3) {
|
|
if let nick = user.nick {
|
|
Text("@\(nick)")
|
|
.font(.subheadline.weight(.semibold))
|
|
}
|
|
if let desc = user.description {
|
|
Text(desc)
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
.lineLimit(2)
|
|
} else {
|
|
Text(user.feedURL.host ?? user.feedURL.absoluteString)
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
.lineLimit(1)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Spacer()
|
|
|
|
if isToggling {
|
|
ProgressView()
|
|
.frame(width: 72)
|
|
} else {
|
|
Button {
|
|
isToggling = true
|
|
Task {
|
|
if user.isFollowing {
|
|
await viewModel.unfollow(user: user)
|
|
} else {
|
|
await viewModel.follow(user: user)
|
|
}
|
|
isToggling = false
|
|
}
|
|
} label: {
|
|
Text(user.isFollowing ? "Unfollow" : "Follow")
|
|
.font(.subheadline)
|
|
.fontWeight(user.isFollowing ? .regular : .semibold)
|
|
.frame(width: 72)
|
|
}
|
|
.buttonStyle(.bordered)
|
|
.tint(user.isFollowing ? .secondary : .accentColor)
|
|
}
|
|
}
|
|
.padding(.vertical, 4)
|
|
}
|
|
}
|