1
0
Fork 0
pdsman-ios/PDSMan/Views/InviteCodesView.swift
2025-03-19 01:55:58 -06:00

210 lines
No EOL
8.2 KiB
Swift

import SwiftUI
struct InviteCodesView: View {
@EnvironmentObject var viewModel: PDSViewModel
@State private var isCreatingCode = false
@State private var isRefreshing = false
@State private var showDisabledCodes = false
// Split codes into active and disabled
var activeCodes: [InviteCode] {
return viewModel.inviteCodes.filter { !$0.disabled }
}
var disabledCodes: [InviteCode] {
return viewModel.inviteCodes.filter { $0.disabled }
}
var filteredCodes: [InviteCode] {
if showDisabledCodes {
return viewModel.inviteCodes
} else {
return viewModel.inviteCodes.filter { !$0.disabled }
}
}
var body: some View {
NavigationView {
Group {
if viewModel.isLoading && viewModel.inviteCodes.isEmpty {
ProgressView("Loading invite codes...")
.frame(maxWidth: .infinity, maxHeight: .infinity)
} else if filteredCodes.isEmpty {
VStack {
Text("No invite codes found")
.foregroundColor(.secondary)
Button("Refresh") {
Task {
await viewModel.refreshInviteCodes()
}
}
.padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
} else {
VStack(spacing: 0) {
List {
// Active codes section
Section(header: Text("Active Codes")) {
ForEach(activeCodes) { code in
InviteCodeRow(code: code)
.contextMenu {
Button(role: .destructive) {
Task {
// After disabling the code, refresh the list
await viewModel.disableInviteCode(code.id)
}
} label: {
Label("Disable Code", systemImage: "xmark.circle")
}
}
}
}
// Disabled codes section (only shown if toggle is on)
if showDisabledCodes && !disabledCodes.isEmpty {
Section(header: Text("Disabled Codes")) {
ForEach(disabledCodes) { code in
InviteCodeRow(code: code)
}
}
}
}
Toggle("Show Disabled Codes (\(disabledCodes.count))", isOn: $showDisabledCodes)
.padding()
.background(Color(.systemBackground))
.onChange(of: showDisabledCodes) { _ in
// Refresh UI when toggle changes
viewModel.refreshUI()
}
}
}
}
.navigationTitle("Invite Codes")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
isCreatingCode = true
Task {
await viewModel.pdsService.createInviteCode()
await viewModel.refreshInviteCodes()
isCreatingCode = false
}
} label: {
if isCreatingCode {
ProgressView()
} else {
Label("Create Code", systemImage: "plus")
}
}
.disabled(isCreatingCode)
}
}
.refreshable {
print("⏳ Pull-to-refresh: Fetching invite codes")
await viewModel.refreshInviteCodes()
print("✅ Pull-to-refresh completed")
}
}
.task {
print("⏳ Task: Fetching invite codes")
// Always fetch on initial load
await viewModel.refreshInviteCodes()
print("✅ Task fetch completed")
}
}
}
struct InviteCodeRow: View {
let code: InviteCode
@State private var copySuccess = false
var formattedDate: String {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
return formatter.string(from: code.createdAt)
}
var body: some View {
VStack(alignment: .leading, spacing: 8) {
HStack {
Text(code.id)
.font(.system(.headline, design: .monospaced))
.foregroundColor(code.disabled ? .gray : .primary)
Spacer()
if code.disabled {
Text("DISABLED")
.font(.caption)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(Color.red.opacity(0.2))
.foregroundColor(.red)
.cornerRadius(4)
} else {
Button {
UIPasteboard.general.string = code.id
withAnimation {
copySuccess = true
}
// Reset the success message after a delay
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
withAnimation {
copySuccess = false
}
}
} label: {
HStack(spacing: 4) {
Image(systemName: copySuccess ? "checkmark" : "doc.on.doc")
if copySuccess {
Text("Copied!")
}
}
.foregroundColor(copySuccess ? .green : .blue)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(
(copySuccess ? Color.green : Color.blue)
.opacity(0.1)
.cornerRadius(4)
)
}
.buttonStyle(BorderlessButtonStyle())
}
}
HStack {
Text("Created: \(formattedDate)")
.font(.caption)
.foregroundColor(.secondary)
Spacer()
if let uses = code.uses {
if uses.isEmpty {
Text("Unused")
.font(.caption)
.foregroundColor(.secondary)
.padding(.horizontal, 6)
.padding(.vertical, 2)
.background(Color.green.opacity(0.1))
.cornerRadius(4)
} else {
Text("Uses: \(uses.count)")
.font(.caption)
.foregroundColor(.secondary)
.padding(.horizontal, 6)
.padding(.vertical, 2)
.background(Color.blue.opacity(0.1))
.cornerRadius(4)
}
}
}
}
.padding(.vertical, 6)
.opacity(code.disabled ? 0.7 : 1.0)
}
}