135 lines
5.1 KiB
Swift
135 lines
5.1 KiB
Swift
import SwiftUI
|
|
|
|
struct LoginView: View {
|
|
@Bindable var viewModel: AuthViewModel
|
|
var appState: AppState
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
ScrollView {
|
|
VStack(spacing: 20) {
|
|
Image(systemName: "lock.shield.fill")
|
|
.font(.system(size: 60))
|
|
.foregroundStyle(.blue)
|
|
.padding(.top, 40)
|
|
|
|
Text("Encrypted Chat")
|
|
.font(.largeTitle.bold())
|
|
|
|
Text("End-to-end encrypted messaging")
|
|
.font(.subheadline)
|
|
.foregroundStyle(.secondary)
|
|
|
|
VStack(spacing: 16) {
|
|
// Server config
|
|
DisclosureGroup("Server") {
|
|
TextField("Host", text: $viewModel.serverHost)
|
|
.textContentType(.URL)
|
|
.autocapitalization(.none)
|
|
TextField("Port", text: $viewModel.serverPort)
|
|
.keyboardType(.numberPad)
|
|
Toggle("Use TLS", isOn: $viewModel.useTLS)
|
|
}
|
|
.padding(.horizontal)
|
|
|
|
TextField("Email", text: $viewModel.email)
|
|
.textContentType(.emailAddress)
|
|
.keyboardType(.emailAddress)
|
|
.autocapitalization(.none)
|
|
.textFieldStyle(.roundedBorder)
|
|
|
|
SecureField("Password", text: $viewModel.password)
|
|
.textContentType(.password)
|
|
.textFieldStyle(.roundedBorder)
|
|
|
|
if viewModel.mode == .register {
|
|
TextField("Username", text: $viewModel.username)
|
|
.textContentType(.username)
|
|
.autocapitalization(.none)
|
|
.textFieldStyle(.roundedBorder)
|
|
|
|
SecureField("Confirm Password", text: $viewModel.confirmPassword)
|
|
.textFieldStyle(.roundedBorder)
|
|
}
|
|
|
|
if let error = viewModel.errorMessage {
|
|
Text(error)
|
|
.foregroundStyle(.red)
|
|
.font(.caption)
|
|
.multilineTextAlignment(.center)
|
|
}
|
|
|
|
Button(action: {
|
|
Task {
|
|
if viewModel.mode == .login {
|
|
await viewModel.login(appState: appState)
|
|
} else {
|
|
await viewModel.register(appState: appState)
|
|
}
|
|
}
|
|
}) {
|
|
if viewModel.isLoading {
|
|
ProgressView()
|
|
.frame(maxWidth: .infinity)
|
|
} else {
|
|
Text(viewModel.mode == .login ? "Login" : "Register")
|
|
.frame(maxWidth: .infinity)
|
|
}
|
|
}
|
|
.buttonStyle(.borderedProminent)
|
|
.disabled(viewModel.isLoading)
|
|
|
|
Button(viewModel.mode == .login ? "Don't have an account? Register" : "Already have an account? Login") {
|
|
viewModel.mode = viewModel.mode == .login ? .register : .login
|
|
viewModel.errorMessage = nil
|
|
}
|
|
.font(.caption)
|
|
}
|
|
.padding(.horizontal, 32)
|
|
}
|
|
}
|
|
.sheet(isPresented: $viewModel.showConfirmation) {
|
|
ConfirmationSheet(viewModel: viewModel, appState: appState)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ConfirmationSheet: View {
|
|
@Bindable var viewModel: AuthViewModel
|
|
var appState: AppState
|
|
|
|
var body: some View {
|
|
VStack(spacing: 20) {
|
|
Text("Confirm Registration")
|
|
.font(.title2.bold())
|
|
|
|
if let msg = viewModel.registrationMessage {
|
|
Text(msg)
|
|
.font(.subheadline)
|
|
.foregroundStyle(.secondary)
|
|
.multilineTextAlignment(.center)
|
|
}
|
|
|
|
TextField("Confirmation Code", text: $viewModel.confirmationCode)
|
|
.textFieldStyle(.roundedBorder)
|
|
.keyboardType(.numberPad)
|
|
|
|
if let error = viewModel.errorMessage {
|
|
Text(error)
|
|
.foregroundStyle(.red)
|
|
.font(.caption)
|
|
}
|
|
|
|
Button("Confirm") {
|
|
Task {
|
|
await viewModel.confirmRegistration(appState: appState)
|
|
}
|
|
}
|
|
.buttonStyle(.borderedProminent)
|
|
.disabled(viewModel.isLoading)
|
|
}
|
|
.padding(32)
|
|
}
|
|
}
|