157 lines
6.2 KiB
Swift
157 lines
6.2 KiB
Swift
import SwiftUI
|
|
|
|
struct LoginView: View {
|
|
@EnvironmentObject var authManager: AuthManager
|
|
@State private var identifier = ""
|
|
@State private var password = ""
|
|
@State private var showingPassword = false
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
// Background
|
|
LinearGradient(
|
|
gradient: Gradient(colors: [
|
|
Color(hex: "0a0a0a"),
|
|
Color(hex: "1a1a1a")
|
|
]),
|
|
startPoint: .top,
|
|
endPoint: .bottom
|
|
)
|
|
.ignoresSafeArea()
|
|
|
|
VStack(spacing: 40) {
|
|
// Logo and title
|
|
VStack(spacing: 20) {
|
|
Text("ai.card")
|
|
.font(.system(size: 48, weight: .bold, design: .rounded))
|
|
.foregroundStyle(
|
|
LinearGradient(
|
|
gradient: Gradient(colors: [
|
|
Color(hex: "fff700"),
|
|
Color(hex: "ff00ff")
|
|
]),
|
|
startPoint: .leading,
|
|
endPoint: .trailing
|
|
)
|
|
)
|
|
|
|
Text("atprotoベースカードゲーム")
|
|
.font(.title3)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
|
|
// Login form
|
|
VStack(spacing: 24) {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text("ハンドル または DID")
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
|
|
TextField("your.bsky.social", text: $identifier)
|
|
.textFieldStyle(CustomTextFieldStyle())
|
|
.autocapitalization(.none)
|
|
.disableAutocorrection(true)
|
|
}
|
|
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
Text("アプリパスワード")
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
|
|
HStack {
|
|
if showingPassword {
|
|
TextField("アプリパスワード", text: $password)
|
|
} else {
|
|
SecureField("アプリパスワード", text: $password)
|
|
}
|
|
|
|
Button(action: {
|
|
showingPassword.toggle()
|
|
}) {
|
|
Image(systemName: showingPassword ? "eye.slash" : "eye")
|
|
.foregroundColor(.secondary)
|
|
}
|
|
}
|
|
.textFieldStyle(CustomTextFieldStyle())
|
|
|
|
Text("メインパスワードではなく、アプリパスワードを使用してください")
|
|
.font(.caption2)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
|
|
if let errorMessage = authManager.errorMessage {
|
|
Text(errorMessage)
|
|
.font(.caption)
|
|
.foregroundColor(.red)
|
|
.padding(.horizontal)
|
|
}
|
|
|
|
Button(action: {
|
|
authManager.login(identifier: identifier, password: password)
|
|
}) {
|
|
HStack {
|
|
if authManager.isLoading {
|
|
ProgressView()
|
|
.progressViewStyle(CircularProgressViewStyle(tint: .black))
|
|
.scaleEffect(0.8)
|
|
}
|
|
|
|
Text(authManager.isLoading ? "ログイン中..." : "ログイン")
|
|
.font(.headline)
|
|
.foregroundColor(.black)
|
|
}
|
|
.frame(maxWidth: .infinity)
|
|
.frame(height: 50)
|
|
.background(
|
|
LinearGradient(
|
|
gradient: Gradient(colors: [
|
|
Color(hex: "fff700"),
|
|
Color(hex: "ffd700")
|
|
]),
|
|
startPoint: .leading,
|
|
endPoint: .trailing
|
|
)
|
|
)
|
|
.cornerRadius(12)
|
|
}
|
|
.disabled(authManager.isLoading || identifier.isEmpty || password.isEmpty)
|
|
.opacity(authManager.isLoading || identifier.isEmpty || password.isEmpty ? 0.6 : 1.0)
|
|
}
|
|
.padding(.horizontal, 32)
|
|
|
|
Spacer()
|
|
|
|
VStack(spacing: 12) {
|
|
Text("ai.cardはatprotoアカウントを使用します")
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
|
|
Text("データはあなたのPDSに保存されます")
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
}
|
|
.padding()
|
|
}
|
|
}
|
|
}
|
|
|
|
struct CustomTextFieldStyle: TextFieldStyle {
|
|
func _body(configuration: TextField<Self._Label>) -> some View {
|
|
configuration
|
|
.padding()
|
|
.background(Color.white.opacity(0.1))
|
|
.cornerRadius(12)
|
|
.overlay(
|
|
RoundedRectangle(cornerRadius: 12)
|
|
.stroke(Color.white.opacity(0.2), lineWidth: 1)
|
|
)
|
|
}
|
|
}
|
|
|
|
struct LoginView_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
LoginView()
|
|
.environmentObject(AuthManager())
|
|
}
|
|
} |