import Foundation // Credentials for connecting to the PDS struct PDSCredentials: Sendable { var serverURL: String var username: String var password: String init?(serverURL: String, username: String, password: String) { // Validate inputs guard !serverURL.isEmpty, !username.isEmpty, !password.isEmpty else { return nil } self.serverURL = serverURL self.username = username self.password = password } } // Shared date formatters to improve performance extension ISO8601DateFormatter { static let shared = ISO8601DateFormatter() } // Invite code model struct InviteCode: Identifiable, Sendable { var id: String var code: String { id } // For compatibility with the view var uses: [CodeUse]? var createdAt: Date var disabled: Bool var isDisabled: Bool { disabled } // For backwards compatibility // Returns a formatted date string for display var formattedDate: String { let formatter = DateFormatter() formatter.dateStyle = .medium formatter.timeStyle = .short return formatter.string(from: createdAt) } // Returns the number of available uses var availableUses: Int { let usedCount = uses?.count ?? 0 return max(0, 1 - usedCount) // Assuming default of 1 use per code } } // Invite use model struct CodeUse: Codable, Identifiable, Sendable { var id: String { usedBy } var usedBy: String var usedAt: String // Parsed date for the usedAt string var date: Date? { ISO8601DateFormatter.shared.date(from: usedAt) } // Formatted date for display var formattedDate: String { guard let date = date else { return "Unknown date" } let formatter = DateFormatter() formatter.dateStyle = .medium formatter.timeStyle = .short return formatter.string(from: date) } } // User model class PDSUser: Identifiable, Hashable, Sendable, ObservableObject { let id: String // DID let joinedAt: Date let avatar: URL? @Published var handle: String @Published var displayName: String @Published var description: String @Published var isActive: Bool = true // Formatted date for display var formattedJoinDate: String { let formatter = DateFormatter() formatter.dateStyle = .medium formatter.timeStyle = .none return formatter.string(from: joinedAt) } init(id: String, handle: String, displayName: String, description: String, joinedAt: Date, avatar: URL?, isActive: Bool = true) { self.id = id self.handle = handle self.displayName = displayName self.description = description self.joinedAt = joinedAt self.avatar = avatar self.isActive = isActive } // Add Hashable conformance func hash(into hasher: inout Hasher) { hasher.combine(id) } static func == (lhs: PDSUser, rhs: PDSUser) -> Bool { lhs.id == rhs.id } } // Shared DateFormatter for consistent date formatting across the app extension DateFormatter { static let shared: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .medium formatter.timeStyle = .short return formatter }() static let dateOnly: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .medium formatter.timeStyle = .none return formatter }() } // Auth state enum AuthState: Sendable { case loggedOut case loggedIn } // View state enum ViewTab: Sendable { case inviteCodes case userList }