Files
andros 7c0fc19b79 Add Discover tab, change default history to 14 days, add Settings/About section, reorganize tabs to 5
- 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
2026-04-19 19:59:35 +02:00

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)
}
}