78ba61034f
- Remove .toolbarBackground(.visible) from all NavigationStack views: this modifier suppresses large title rendering in iOS 26. - Discover: switch to always-present List + overlay pattern so SwiftUI never loses the scroll context during loading transitions. - Discover, Notifications, Groups: use .inline title mode; the tab bar already identifies these screens, large titles are redundant. - RootView: add .toolbarColorScheme propagation for nav bar foreground. - Settings sheet: apply theme background and toolbar color to the Form.
115 lines
4.3 KiB
Swift
115 lines
4.3 KiB
Swift
import SwiftUI
|
|
import OrgSocialKit
|
|
|
|
struct DiscoverView: View {
|
|
@State private var viewModel = DiscoverViewModel()
|
|
@Environment(\.appTheme) private var theme
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
List(viewModel.users) { user in
|
|
DiscoverUserRow(user: user, viewModel: viewModel)
|
|
.listRowBackground(theme.background)
|
|
}
|
|
.listStyle(.plain)
|
|
.scrollContentBackground(.hidden)
|
|
.background(theme.background)
|
|
.overlay {
|
|
if viewModel.isLoading && viewModel.users.isEmpty {
|
|
VStack(spacing: 16) {
|
|
ProgressView()
|
|
Text("Loading profiles from relay...")
|
|
.font(.subheadline)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
.background(theme.background)
|
|
} 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)
|
|
}
|
|
.background(theme.background)
|
|
} else if viewModel.users.isEmpty {
|
|
ContentUnavailableView(
|
|
"No profiles found",
|
|
systemImage: "person.3",
|
|
description: Text("No other users registered on this relay.")
|
|
)
|
|
.background(theme.background)
|
|
}
|
|
}
|
|
.navigationTitle("Discover")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.task { await viewModel.load() }
|
|
.toolbarBackground(theme.secondaryBackground, for: .navigationBar)
|
|
.navigationDestination(for: URL.self) { ProfileView(feedURL: $0) }
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|