import SwiftUI struct UserListView: View { @EnvironmentObject var viewModel: PDSViewModel @State private var editingUser: PDSUser? = nil @State private var newHandle: String = "" @State private var showingEditSheet = false @State private var showingSuspendAlert = false @State private var showingReactivateAlert = false @State private var suspensionReason: String = "" @State private var selectedUser: PDSUser? = nil @State private var isRefreshing = false var body: some View { NavigationView { Group { if viewModel.isLoading && viewModel.users.isEmpty { ProgressView("Loading users...") .frame(maxWidth: .infinity, maxHeight: .infinity) } else if viewModel.users.isEmpty { VStack { Text("No users found") .foregroundColor(.secondary) Button("Refresh") { Task { await viewModel.refreshUsers() } } .padding() } .frame(maxWidth: .infinity, maxHeight: .infinity) } else { List { ForEach(viewModel.users) { user in UserRow(user: user) .contentShape(Rectangle()) .onTapGesture { selectedUser = user editingUser = user newHandle = user.handle showingEditSheet = true } .contextMenu { Button(action: { selectedUser = user editingUser = user newHandle = user.handle showingEditSheet = true }) { Label("Edit Handle", systemImage: "pencil") } if user.isActive { Button(action: { selectedUser = user suspensionReason = "" showingSuspendAlert = true }) { Label("Suspend Account", systemImage: "xmark.circle") } } else { Button(action: { selectedUser = user showingReactivateAlert = true }) { Label("Reactivate Account", systemImage: "checkmark.circle") } } } } } } } .navigationTitle("Users") .refreshable { print("⏳ Pull-to-refresh: Fetching users") await viewModel.refreshUsers() print("✅ Pull-to-refresh completed") } .sheet(isPresented: $showingEditSheet) { if let user = editingUser { NavigationView { Form { Section(header: Text("Edit User Handle")) { TextField("Handle", text: $newHandle) HStack { Button("Cancel") { showingEditSheet = false } Spacer() Button("Save") { Task { if await viewModel.pdsService.editUserHandle(userId: user.id, newHandle: newHandle) { showingEditSheet = false await viewModel.refreshUsers() // Refresh to show updated handle } } } .disabled(newHandle.isEmpty || newHandle == user.handle) } } } .navigationTitle("Edit \(user.displayName)") } } } .alert("Suspend User", isPresented: $showingSuspendAlert) { TextField("Reason for suspension", text: $suspensionReason) Button("Cancel", role: .cancel) { } Button("Suspend", role: .destructive) { if let user = selectedUser { Task { await viewModel.pdsService.suspendUser(userId: user.id, reason: suspensionReason) await viewModel.refreshUsers() // Refresh after suspension } } } } message: { if let user = selectedUser { Text("Are you sure you want to suspend \(user.displayName)'s account? This will prevent them from using the service.") } } .alert("Reactivate User", isPresented: $showingReactivateAlert) { Button("Cancel", role: .cancel) { } Button("Reactivate") { if let user = selectedUser { Task { await viewModel.pdsService.reactivateUser(userId: user.id) await viewModel.refreshUsers() // Refresh after reactivation } } } } message: { if let user = selectedUser { Text("Are you sure you want to reactivate \(user.displayName)'s account?") } } } .task { print("⏳ Task: Fetching users") // Always fetch on initial load await viewModel.refreshUsers() print("✅ Task fetch completed") } } } struct UserRow: View { let user: PDSUser var formattedDate: String { let formatter = DateFormatter() formatter.dateStyle = .medium formatter.timeStyle = .none return "Joined: \(formatter.string(from: user.joinedAt))" } var body: some View { VStack(alignment: .leading, spacing: 8) { HStack(spacing: 12) { if let avatarURL = user.avatar { AsyncImage(url: avatarURL) { image in image .resizable() .aspectRatio(contentMode: .fill) } placeholder: { ProgressView() } .frame(width: 60, height: 60) .clipShape(Circle()) .overlay( Circle() .stroke(Color.gray.opacity(0.2), lineWidth: 1) ) } else { Image(systemName: "person.circle.fill") .resizable() .foregroundColor(.gray.opacity(0.7)) .frame(width: 60, height: 60) } VStack(alignment: .leading, spacing: 3) { HStack { Text(user.displayName) .font(.title3) .fontWeight(.semibold) .lineLimit(1) if !user.isActive { Text("SUSPENDED") .font(.caption) .padding(.horizontal, 5) .padding(.vertical, 2) .background(Color.red) .foregroundColor(.white) .cornerRadius(4) } } Text("@\(user.handle)") .font(.subheadline) .foregroundColor(.blue) .lineLimit(1) } } if !user.description.isEmpty { Text(user.description) .font(.subheadline) .foregroundColor(.primary) .lineLimit(3) .padding(.top, 2) } HStack(spacing: 12) { Text(formattedDate) .font(.caption) .foregroundColor(.secondary) Spacer() Text(user.id) .font(.caption) .foregroundColor(.secondary) .lineLimit(1) .truncationMode(.middle) .frame(maxWidth: 160) .opacity(0.7) } } .padding(.vertical, 10) .opacity(user.isActive ? 1.0 : 0.7) } }