1.0.2
This commit is contained in:
parent
3b593f564e
commit
48f5c0309d
3 changed files with 483 additions and 622 deletions
|
@ -18,6 +18,11 @@ struct PDSCredentials: Sendable {
|
|||
}
|
||||
}
|
||||
|
||||
// Shared date formatters to improve performance
|
||||
extension ISO8601DateFormatter {
|
||||
static let shared = ISO8601DateFormatter()
|
||||
}
|
||||
|
||||
// Invite code model
|
||||
struct InviteCode: Identifiable, Sendable {
|
||||
var id: String
|
||||
|
@ -26,6 +31,20 @@ struct InviteCode: Identifiable, Sendable {
|
|||
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
|
||||
|
@ -33,6 +52,20 @@ 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
|
||||
|
@ -46,6 +79,14 @@ class PDSUser: Identifiable, Hashable, Sendable, ObservableObject {
|
|||
@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
|
||||
|
@ -66,6 +107,23 @@ class PDSUser: Identifiable, Hashable, Sendable, ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -27,6 +27,9 @@ struct LoginView: View {
|
|||
.autocapitalization(.none)
|
||||
.disableAutocorrection(true)
|
||||
.keyboardType(.URL)
|
||||
.placeholder(when: serverURL.isEmpty) {
|
||||
Text("example.com").foregroundColor(.gray.opacity(0.5))
|
||||
}
|
||||
|
||||
SecureField("Admin Password", text: $password)
|
||||
.textFieldStyle(RoundedBorderTextFieldStyle())
|
||||
|
@ -114,9 +117,9 @@ struct LoginView: View {
|
|||
.onAppear {
|
||||
// Add some example connection info
|
||||
debugLogs.append("Example URLs:")
|
||||
debugLogs.append("https://bsky.social - Main Bluesky server")
|
||||
debugLogs.append("https://bsky.atri.dad - Your local server")
|
||||
debugLogs.append("http://localhost:3000 - Local development PDS")
|
||||
debugLogs.append("bsky.social - Main Bluesky server")
|
||||
debugLogs.append("bsky.atri.dad - Your local server")
|
||||
debugLogs.append("localhost:3000 - Local development PDS")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,19 +134,8 @@ struct LoginView: View {
|
|||
// Add debug info
|
||||
debugLogs.append("Attempting login to: \(serverURL)")
|
||||
|
||||
// Sanitize the URL to ensure it has the correct format
|
||||
var cleanURL = serverURL.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if !cleanURL.hasPrefix("http://") && !cleanURL.hasPrefix("https://") {
|
||||
cleanURL = "http://\(cleanURL)"
|
||||
debugLogs.append("Added http:// prefix: \(cleanURL)")
|
||||
}
|
||||
|
||||
// Remove trailing slash if present
|
||||
if cleanURL.hasSuffix("/") {
|
||||
cleanURL.removeLast()
|
||||
debugLogs.append("Removed trailing slash: \(cleanURL)")
|
||||
}
|
||||
|
||||
// Normalize the URL to ensure it has the correct format
|
||||
var cleanURL = normalizeServerURL(serverURL)
|
||||
debugLogs.append("Using final URL: \(cleanURL)")
|
||||
|
||||
Task {
|
||||
|
@ -157,4 +149,44 @@ struct LoginView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func normalizeServerURL(_ url: String) -> String {
|
||||
// Remove any leading/trailing whitespace
|
||||
var cleanURL = url.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
// Strip any existing protocol prefix
|
||||
if cleanURL.hasPrefix("http://") {
|
||||
cleanURL = String(cleanURL.dropFirst(7))
|
||||
debugLogs.append("Removed http:// prefix")
|
||||
} else if cleanURL.hasPrefix("https://") {
|
||||
cleanURL = String(cleanURL.dropFirst(8))
|
||||
debugLogs.append("Removed https:// prefix")
|
||||
}
|
||||
|
||||
// Remove trailing slash if present
|
||||
if cleanURL.hasSuffix("/") {
|
||||
cleanURL.removeLast()
|
||||
debugLogs.append("Removed trailing slash")
|
||||
}
|
||||
|
||||
// Add https:// prefix (always use HTTPS by default)
|
||||
cleanURL = "https://\(cleanURL)"
|
||||
debugLogs.append("Added https:// prefix")
|
||||
|
||||
return cleanURL
|
||||
}
|
||||
}
|
||||
|
||||
// Placeholder extension for TextField
|
||||
extension View {
|
||||
func placeholder<Content: View>(
|
||||
when shouldShow: Bool,
|
||||
alignment: Alignment = .leading,
|
||||
@ViewBuilder placeholder: () -> Content
|
||||
) -> some View {
|
||||
ZStack(alignment: alignment) {
|
||||
placeholder().opacity(shouldShow ? 1 : 0)
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue