Bug fixes
This commit is contained in:
parent
292180b204
commit
bbad8d6948
5 changed files with 195 additions and 166 deletions
|
@ -395,7 +395,7 @@
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 2;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"PDSMan/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"PDSMan/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
|
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
|
@ -431,7 +431,7 @@
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 2;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"PDSMan/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"PDSMan/Preview Content\"";
|
||||||
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
|
DEVELOPMENT_TEAM = 4BC9Y2LL4B;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
|
|
|
@ -387,76 +387,84 @@ class PDSService: ObservableObject {
|
||||||
// MARK: - Invite Codes
|
// MARK: - Invite Codes
|
||||||
|
|
||||||
func fetchInviteCodes() async {
|
func fetchInviteCodes() async {
|
||||||
guard isAuthenticated, let baseURL = baseURL, let authHeader = authHeader else { return }
|
print("⏳ PDSService: Starting to fetch invite codes")
|
||||||
|
|
||||||
self.isLoading = true
|
self.isLoading = true
|
||||||
|
defer { self.isLoading = false }
|
||||||
|
|
||||||
defer {
|
guard let baseURL = baseURL, let authHeader = authHeader else {
|
||||||
self.isLoading = false
|
print("❌ PDSService: Cannot fetch invite codes - missing authentication")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the URL for the invite codes endpoint
|
// Set up URL components for the request with any needed query parameters
|
||||||
guard let inviteCodesURL = URL(string: "\(baseURL)/xrpc/com.atproto.admin.getInviteCodes") else {
|
guard var components = URLComponents(string: "\(baseURL)/xrpc/com.atproto.admin.getInviteCodes") else {
|
||||||
setError("Invalid invite codes URL")
|
print("❌ PDSService: Invalid invite codes URL")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add query parameters
|
// Add query parameters
|
||||||
var components = URLComponents(url: inviteCodesURL, resolvingAgainstBaseURL: true)
|
components.queryItems = [
|
||||||
components?.queryItems = [
|
|
||||||
URLQueryItem(name: "sort", value: "recent"),
|
URLQueryItem(name: "sort", value: "recent"),
|
||||||
URLQueryItem(name: "limit", value: "100"),
|
URLQueryItem(name: "limit", value: "100"),
|
||||||
URLQueryItem(name: "includeDisabled", value: "true") // Always include disabled codes
|
URLQueryItem(name: "includeDisabled", value: "true")
|
||||||
]
|
]
|
||||||
|
|
||||||
guard let finalURL = components?.url else {
|
guard let url = components.url else {
|
||||||
setError("Invalid invite codes URL with parameters")
|
print("❌ PDSService: Failed to construct URL with query parameters")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var request = URLRequest(url: finalURL)
|
var request = URLRequest(url: url)
|
||||||
request.httpMethod = "GET"
|
request.httpMethod = "GET"
|
||||||
request.addValue(authHeader, forHTTPHeaderField: "Authorization")
|
request.addValue(authHeader, forHTTPHeaderField: "Authorization")
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let (data, response) = try await session.data(for: request)
|
let (data, response) = try await session.data(for: request)
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
|
||||||
setError("Invalid response from server")
|
let statusCode = (response as? HTTPURLResponse)?.statusCode ?? 0
|
||||||
|
print("❌ PDSService: Invite codes fetch failed with status \(statusCode)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if httpResponse.statusCode == 200 {
|
let decoder = JSONDecoder()
|
||||||
let decoder = JSONDecoder()
|
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
||||||
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
|
||||||
|
// Debug: Print raw response
|
||||||
let codesResponse = try decoder.decode(InviteCodesResponse.self, from: data)
|
if let responseString = String(data: data, encoding: .utf8) {
|
||||||
|
print("👀 PDSService: Raw invite codes response: \(responseString)")
|
||||||
|
}
|
||||||
|
|
||||||
|
let codesResponse = try decoder.decode(InviteCodesResponse.self, from: data)
|
||||||
|
|
||||||
|
// Map the response to our model
|
||||||
|
let parsedCodes = codesResponse.codes.map { codeResp -> InviteCode in
|
||||||
let dateFormatter = ISO8601DateFormatter()
|
let dateFormatter = ISO8601DateFormatter()
|
||||||
|
let createdDate = dateFormatter.date(from: codeResp.createdAt) ?? Date()
|
||||||
|
|
||||||
let parsedCodes = codesResponse.codes.map { codeResp -> InviteCode in
|
// Convert the uses array
|
||||||
let createdDate = dateFormatter.date(from: codeResp.createdAt) ?? Date()
|
let inviteUses = codeResp.uses?.map { use -> PDSMan.CodeUse in
|
||||||
|
return PDSMan.CodeUse(usedBy: use.usedBy, usedAt: use.usedAt)
|
||||||
// Convert the uses array
|
|
||||||
let inviteUses = codeResp.uses?.map { use -> PDSMan.CodeUse in
|
|
||||||
return PDSMan.CodeUse(usedBy: use.usedBy, usedAt: use.usedAt)
|
|
||||||
}
|
|
||||||
|
|
||||||
return InviteCode(
|
|
||||||
id: codeResp.code,
|
|
||||||
uses: inviteUses,
|
|
||||||
createdAt: createdDate,
|
|
||||||
disabled: codeResp.disabled
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.inviteCodes = parsedCodes
|
return InviteCode(
|
||||||
} else {
|
id: codeResp.code,
|
||||||
let responseString = String(data: data, encoding: .utf8) ?? "Unknown error"
|
uses: inviteUses,
|
||||||
setError("Failed to fetch invite codes: \(httpResponse.statusCode) - \(responseString)")
|
createdAt: createdDate,
|
||||||
|
disabled: codeResp.disabled
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the inviteCodes property
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.inviteCodes = parsedCodes
|
||||||
|
self.objectWillChange.send()
|
||||||
|
print("✅ PDSService: Successfully fetched \(parsedCodes.count) invite codes")
|
||||||
|
print("✅ PDSService: Including \(parsedCodes.filter { !$0.disabled }.count) active codes")
|
||||||
|
}
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
setError("Failed to fetch invite codes: \(error.localizedDescription)")
|
print("❌ PDSService: Error fetching invite codes: \(error.localizedDescription)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,7 +509,13 @@ class PDSService: ObservableObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update the local list
|
// Update the local list
|
||||||
self.inviteCodes.append(newCode)
|
DispatchQueue.main.async {
|
||||||
|
self.inviteCodes.append(newCode)
|
||||||
|
self.objectWillChange.send()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also refresh the full list to ensure we have the most up-to-date data
|
||||||
|
await fetchInviteCodes()
|
||||||
|
|
||||||
return newCode
|
return newCode
|
||||||
} else {
|
} else {
|
||||||
|
@ -516,10 +530,17 @@ class PDSService: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
func disableInviteCode(_ code: String) async -> Bool {
|
func disableInviteCode(_ code: String) async -> Bool {
|
||||||
guard isAuthenticated, let baseURL = baseURL, let authHeader = authHeader else { return false }
|
print("⏳ PDSService: Attempting to disable invite code: \(code)")
|
||||||
|
self.isLoading = true
|
||||||
|
defer { self.isLoading = false }
|
||||||
|
|
||||||
|
guard let baseURL = baseURL, let authHeader = authHeader else {
|
||||||
|
print("❌ PDSService: Cannot disable code - missing authentication")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
guard let disableURL = URL(string: "\(baseURL)/xrpc/com.atproto.admin.disableInviteCodes") else {
|
guard let disableURL = URL(string: "\(baseURL)/xrpc/com.atproto.admin.disableInviteCodes") else {
|
||||||
setError("Invalid disable invite code URL")
|
print("❌ PDSService: Invalid disable code URL")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -528,7 +549,7 @@ class PDSService: ObservableObject {
|
||||||
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||||
request.addValue(authHeader, forHTTPHeaderField: "Authorization")
|
request.addValue(authHeader, forHTTPHeaderField: "Authorization")
|
||||||
|
|
||||||
// Create the request body with an array of codes
|
// Create the request body
|
||||||
let disableBody = ["codes": [code]]
|
let disableBody = ["codes": [code]]
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
@ -538,21 +559,29 @@ class PDSService: ObservableObject {
|
||||||
let (data, response) = try await session.data(for: request)
|
let (data, response) = try await session.data(for: request)
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
setError("Invalid response from server")
|
print("❌ PDSService: Invalid response from server")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Debug: Print response details
|
||||||
|
if let responseString = String(data: data, encoding: .utf8) {
|
||||||
|
print("👀 PDSService: Disable code response: \(responseString)")
|
||||||
|
}
|
||||||
|
|
||||||
if httpResponse.statusCode == 200 {
|
if httpResponse.statusCode == 200 {
|
||||||
// Refresh the invite codes
|
print("✅ PDSService: Successfully disabled code: \(code)")
|
||||||
|
|
||||||
|
// Refresh the invite codes list
|
||||||
await fetchInviteCodes()
|
await fetchInviteCodes()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
let responseString = String(data: data, encoding: .utf8) ?? "Unknown error"
|
let responseString = String(data: data, encoding: .utf8) ?? "Unknown error"
|
||||||
setError("Failed to disable invite code: \(httpResponse.statusCode) - \(responseString)")
|
print("❌ PDSService: Failed to disable code: \(httpResponse.statusCode) - \(responseString)")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
setError("Failed to disable invite code: \(error.localizedDescription)")
|
print("❌ PDSService: Error disabling code: \(error.localizedDescription)")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -560,68 +589,67 @@ class PDSService: ObservableObject {
|
||||||
// MARK: - Users
|
// MARK: - Users
|
||||||
|
|
||||||
func fetchUsers() async {
|
func fetchUsers() async {
|
||||||
guard isAuthenticated, let baseURL = baseURL, let authHeader = authHeader else { return }
|
print("⏳ PDSService: Starting to fetch users")
|
||||||
|
|
||||||
self.isLoading = true
|
self.isLoading = true
|
||||||
|
defer { self.isLoading = false }
|
||||||
|
|
||||||
defer {
|
guard let baseURL = baseURL, let authHeader = authHeader else {
|
||||||
self.isLoading = false
|
print("❌ PDSService: Cannot fetch users - missing authentication")
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the URL for the repos endpoint
|
|
||||||
guard let reposURL = URL(string: "\(baseURL)/xrpc/com.atproto.sync.listRepos") else {
|
|
||||||
setError("Invalid list repos URL")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add query parameters
|
// First, get a list of all repos (users) on the server
|
||||||
var components = URLComponents(url: reposURL, resolvingAgainstBaseURL: true)
|
guard let repoURL = URL(string: "\(baseURL)/xrpc/com.atproto.sync.listRepos") else {
|
||||||
components?.queryItems = [
|
print("❌ PDSService: Invalid list repos URL")
|
||||||
URLQueryItem(name: "limit", value: "100")
|
|
||||||
]
|
|
||||||
|
|
||||||
guard let finalURL = components?.url else {
|
|
||||||
setError("Invalid repos URL with parameters")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var request = URLRequest(url: finalURL)
|
var repoRequest = URLRequest(url: repoURL)
|
||||||
request.httpMethod = "GET"
|
repoRequest.httpMethod = "GET"
|
||||||
request.addValue(authHeader, forHTTPHeaderField: "Authorization")
|
repoRequest.addValue(authHeader, forHTTPHeaderField: "Authorization")
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let (data, response) = try await session.data(for: request)
|
let (repoData, repoResponse) = try await session.data(for: repoRequest)
|
||||||
|
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
guard let httpResponse = repoResponse as? HTTPURLResponse, httpResponse.statusCode == 200 else {
|
||||||
setError("Invalid response from server")
|
let statusCode = (repoResponse as? HTTPURLResponse)?.statusCode ?? 0
|
||||||
|
print("❌ PDSService: Repos fetch failed with status \(statusCode)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if httpResponse.statusCode == 200 {
|
// Debug: Print raw response
|
||||||
let decoder = JSONDecoder()
|
if let responseString = String(data: repoData, encoding: .utf8) {
|
||||||
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
print("👀 PDSService: Raw repos response: \(responseString)")
|
||||||
|
|
||||||
let reposResponse = try decoder.decode(RepoResponse.self, from: data)
|
|
||||||
|
|
||||||
// Fetch details for each user
|
|
||||||
var fetchedUsers: [PDSUser] = []
|
|
||||||
|
|
||||||
for repo in reposResponse.repos {
|
|
||||||
if let user = await fetchUserProfile(did: repo.did, isActive: repo.active) {
|
|
||||||
fetchedUsers.append(user)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort users by join date (newest first)
|
|
||||||
fetchedUsers.sort { $0.joinedAt > $1.joinedAt }
|
|
||||||
|
|
||||||
self.users = fetchedUsers
|
|
||||||
} else {
|
|
||||||
let responseString = String(data: data, encoding: .utf8) ?? "Unknown error"
|
|
||||||
setError("Failed to fetch users: \(httpResponse.statusCode) - \(responseString)")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let decoder = JSONDecoder()
|
||||||
|
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
||||||
|
|
||||||
|
let reposResult = try decoder.decode(RepoResponse.self, from: repoData)
|
||||||
|
print("📊 PDSService: Found \(reposResult.repos.count) repos")
|
||||||
|
|
||||||
|
// Fetch individual user profiles
|
||||||
|
var fetchedUsers: [PDSUser] = []
|
||||||
|
|
||||||
|
for repo in reposResult.repos {
|
||||||
|
print("🔍 PDSService: Fetching profile for \(repo.did)")
|
||||||
|
if let user = await fetchUserProfile(did: repo.did, isActive: repo.active) {
|
||||||
|
fetchedUsers.append(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort users by join date (newest first)
|
||||||
|
fetchedUsers.sort { $0.joinedAt > $1.joinedAt }
|
||||||
|
|
||||||
|
// Update the users property
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.users = fetchedUsers
|
||||||
|
self.objectWillChange.send()
|
||||||
|
print("✅ PDSService: Successfully fetched \(fetchedUsers.count) user profiles")
|
||||||
|
}
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
setError("Failed to fetch users: \(error.localizedDescription)")
|
print("❌ PDSService: Error fetching repos: \(error.localizedDescription)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,47 @@ class PDSViewModel: ObservableObject {
|
||||||
pdsService.inviteCodes
|
pdsService.inviteCodes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add listeners for PDSService changes
|
||||||
|
init() {
|
||||||
|
// Subscribe to PDSService objectWillChange events
|
||||||
|
pdsService.objectWillChange.sink { [weak self] _ in
|
||||||
|
// Forward the change notification to our own objectWillChange
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self?.objectWillChange.send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage for cancellables
|
||||||
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
// Method to manually refresh UI data
|
||||||
|
func refreshUI() {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.objectWillChange.send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh invite codes with UI update
|
||||||
|
func refreshInviteCodes() async {
|
||||||
|
await pdsService.fetchInviteCodes()
|
||||||
|
refreshUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh users with UI update
|
||||||
|
func refreshUsers() async {
|
||||||
|
await pdsService.fetchUsers()
|
||||||
|
refreshUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable invite code with guaranteed UI update
|
||||||
|
func disableInviteCode(_ code: String) async -> Bool {
|
||||||
|
let result = await pdsService.disableInviteCode(code)
|
||||||
|
refreshUI()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func login(serverURL: String, username: String, password: String) async {
|
func login(serverURL: String, username: String, password: String) async {
|
||||||
print("PDSViewModel: login called")
|
print("PDSViewModel: login called")
|
||||||
if let credentials = PDSCredentials(serverURL: serverURL, username: username, password: password) {
|
if let credentials = PDSCredentials(serverURL: serverURL, username: username, password: password) {
|
||||||
|
|
|
@ -34,7 +34,9 @@ struct InviteCodesView: View {
|
||||||
Text("No invite codes found")
|
Text("No invite codes found")
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
Button("Refresh") {
|
Button("Refresh") {
|
||||||
refreshInviteCodes()
|
Task {
|
||||||
|
await viewModel.refreshInviteCodes()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
|
@ -49,7 +51,8 @@ struct InviteCodesView: View {
|
||||||
.contextMenu {
|
.contextMenu {
|
||||||
Button(role: .destructive) {
|
Button(role: .destructive) {
|
||||||
Task {
|
Task {
|
||||||
await viewModel.pdsService.disableInviteCode(code.id)
|
// After disabling the code, refresh the list
|
||||||
|
await viewModel.disableInviteCode(code.id)
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
Label("Disable Code", systemImage: "xmark.circle")
|
Label("Disable Code", systemImage: "xmark.circle")
|
||||||
|
@ -71,6 +74,10 @@ struct InviteCodesView: View {
|
||||||
Toggle("Show Disabled Codes (\(disabledCodes.count))", isOn: $showDisabledCodes)
|
Toggle("Show Disabled Codes (\(disabledCodes.count))", isOn: $showDisabledCodes)
|
||||||
.padding()
|
.padding()
|
||||||
.background(Color(.systemBackground))
|
.background(Color(.systemBackground))
|
||||||
|
.onChange(of: showDisabledCodes) { _ in
|
||||||
|
// Refresh UI when toggle changes
|
||||||
|
viewModel.refreshUI()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,6 +88,7 @@ struct InviteCodesView: View {
|
||||||
isCreatingCode = true
|
isCreatingCode = true
|
||||||
Task {
|
Task {
|
||||||
await viewModel.pdsService.createInviteCode()
|
await viewModel.pdsService.createInviteCode()
|
||||||
|
await viewModel.refreshInviteCodes()
|
||||||
isCreatingCode = false
|
isCreatingCode = false
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
|
@ -92,43 +100,18 @@ struct InviteCodesView: View {
|
||||||
}
|
}
|
||||||
.disabled(isCreatingCode)
|
.disabled(isCreatingCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolbarItem(placement: .navigationBarLeading) {
|
|
||||||
Button {
|
|
||||||
refreshInviteCodes()
|
|
||||||
} label: {
|
|
||||||
if isRefreshing {
|
|
||||||
ProgressView()
|
|
||||||
} else {
|
|
||||||
Label("Refresh", systemImage: "arrow.clockwise")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.disabled(isRefreshing)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.refreshable {
|
.refreshable {
|
||||||
await viewModel.pdsService.fetchInviteCodes()
|
print("⏳ Pull-to-refresh: Fetching invite codes")
|
||||||
|
await viewModel.refreshInviteCodes()
|
||||||
|
print("✅ Pull-to-refresh completed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
// Only fetch if we don't already have data
|
print("⏳ Task: Fetching invite codes")
|
||||||
if viewModel.inviteCodes.isEmpty && !viewModel.isLoading {
|
// Always fetch on initial load
|
||||||
await viewModel.pdsService.fetchInviteCodes()
|
await viewModel.refreshInviteCodes()
|
||||||
}
|
print("✅ Task fetch completed")
|
||||||
}
|
|
||||||
.onAppear {
|
|
||||||
// Always attempt to refresh when view appears
|
|
||||||
if !viewModel.isLoading {
|
|
||||||
refreshInviteCodes()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func refreshInviteCodes() {
|
|
||||||
isRefreshing = true
|
|
||||||
Task {
|
|
||||||
await viewModel.pdsService.fetchInviteCodes()
|
|
||||||
isRefreshing = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,9 @@ struct UserListView: View {
|
||||||
Text("No users found")
|
Text("No users found")
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
Button("Refresh") {
|
Button("Refresh") {
|
||||||
refreshUsers()
|
Task {
|
||||||
|
await viewModel.refreshUsers()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
}
|
}
|
||||||
|
@ -70,22 +72,10 @@ struct UserListView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("Users")
|
.navigationTitle("Users")
|
||||||
.toolbar {
|
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
|
||||||
Button {
|
|
||||||
refreshUsers()
|
|
||||||
} label: {
|
|
||||||
if isRefreshing {
|
|
||||||
ProgressView()
|
|
||||||
} else {
|
|
||||||
Label("Refresh", systemImage: "arrow.clockwise")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.disabled(isRefreshing)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.refreshable {
|
.refreshable {
|
||||||
await viewModel.pdsService.fetchUsers()
|
print("⏳ Pull-to-refresh: Fetching users")
|
||||||
|
await viewModel.refreshUsers()
|
||||||
|
print("✅ Pull-to-refresh completed")
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $showingEditSheet) {
|
.sheet(isPresented: $showingEditSheet) {
|
||||||
if let user = editingUser {
|
if let user = editingUser {
|
||||||
|
@ -105,6 +95,7 @@ struct UserListView: View {
|
||||||
Task {
|
Task {
|
||||||
if await viewModel.pdsService.editUserHandle(userId: user.id, newHandle: newHandle) {
|
if await viewModel.pdsService.editUserHandle(userId: user.id, newHandle: newHandle) {
|
||||||
showingEditSheet = false
|
showingEditSheet = false
|
||||||
|
await viewModel.refreshUsers() // Refresh to show updated handle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +114,7 @@ struct UserListView: View {
|
||||||
if let user = selectedUser {
|
if let user = selectedUser {
|
||||||
Task {
|
Task {
|
||||||
await viewModel.pdsService.suspendUser(userId: user.id, reason: suspensionReason)
|
await viewModel.pdsService.suspendUser(userId: user.id, reason: suspensionReason)
|
||||||
await viewModel.pdsService.fetchUsers() // Refresh after suspension
|
await viewModel.refreshUsers() // Refresh after suspension
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +129,7 @@ struct UserListView: View {
|
||||||
if let user = selectedUser {
|
if let user = selectedUser {
|
||||||
Task {
|
Task {
|
||||||
await viewModel.pdsService.reactivateUser(userId: user.id)
|
await viewModel.pdsService.reactivateUser(userId: user.id)
|
||||||
await viewModel.pdsService.fetchUsers() // Refresh after reactivation
|
await viewModel.refreshUsers() // Refresh after reactivation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,24 +140,10 @@ struct UserListView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
// Only fetch if we don't already have data
|
print("⏳ Task: Fetching users")
|
||||||
if viewModel.users.isEmpty && !viewModel.isLoading {
|
// Always fetch on initial load
|
||||||
await viewModel.pdsService.fetchUsers()
|
await viewModel.refreshUsers()
|
||||||
}
|
print("✅ Task fetch completed")
|
||||||
}
|
|
||||||
.onAppear {
|
|
||||||
// Always attempt to refresh when view appears
|
|
||||||
if !viewModel.isLoading {
|
|
||||||
refreshUsers()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func refreshUsers() {
|
|
||||||
isRefreshing = true
|
|
||||||
Task {
|
|
||||||
await viewModel.pdsService.fetchUsers()
|
|
||||||
isRefreshing = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue