169 lines
4.3 KiB
Swift
169 lines
4.3 KiB
Swift
//
|
|
// LoginForm.swift
|
|
// Login
|
|
//
|
|
// Created by Javier Cicchelli on 01/12/2022.
|
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct LoginForm: View {
|
|
|
|
// MARK: States
|
|
|
|
@FocusState private var focusedField: Field?
|
|
|
|
// MARK: Bindings
|
|
|
|
@Binding var username: String
|
|
@Binding var password: String
|
|
@Binding var errorMessage: String?
|
|
|
|
// MARK: Properties
|
|
|
|
let onReturn: () -> Void
|
|
|
|
// MARK: Body
|
|
|
|
var body: some View {
|
|
VStack(
|
|
alignment: .leading,
|
|
spacing: 16
|
|
) {
|
|
TextField(
|
|
NSLocalizedString(
|
|
"login.text_field.username.placeholder",
|
|
bundle: .module,
|
|
comment: "The placeholder for the username text field."
|
|
),
|
|
text: $username
|
|
)
|
|
.textContentType(.username)
|
|
.lineLimit(1)
|
|
.autocapitalization(.none)
|
|
.disableAutocorrection(true)
|
|
.keyboardType(.default)
|
|
.focused($focusedField, equals: .username)
|
|
.onSubmit {
|
|
onUsernameReturnPressed()
|
|
}
|
|
|
|
Divider()
|
|
|
|
SecureField(
|
|
NSLocalizedString(
|
|
"login.text_field.password.placeholder",
|
|
bundle: .module,
|
|
comment: "The placeholder for the password text field."
|
|
),
|
|
text: $password
|
|
)
|
|
.textContentType(.password)
|
|
.lineLimit(1)
|
|
.autocapitalization(.none)
|
|
.disableAutocorrection(true)
|
|
.keyboardType(.default)
|
|
.focused($focusedField, equals: .password)
|
|
.onSubmit {
|
|
onPasswordReturnPressed()
|
|
}
|
|
|
|
if let errorMessage {
|
|
Divider()
|
|
|
|
Text(
|
|
NSLocalizedString(
|
|
errorMessage,
|
|
bundle: .module,
|
|
comment: "The error message received from the backend."
|
|
)
|
|
)
|
|
.font(.body)
|
|
.foregroundColor(.red)
|
|
.frame(maxWidth: .infinity)
|
|
.multilineTextAlignment(.center)
|
|
}
|
|
}
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.padding(16)
|
|
.background(Color.primary.colorInvert())
|
|
.cornerRadius(8)
|
|
.onAppear {
|
|
setClearButtonIfNeeded()
|
|
}
|
|
.onChange(of: focusedField) { _ in
|
|
onTextFieldFocused()
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Helpers
|
|
|
|
private extension LoginForm {
|
|
func setClearButtonIfNeeded() {
|
|
let textFieldAppearance = UITextField.appearance()
|
|
guard textFieldAppearance.clearButtonMode != .whileEditing else { return }
|
|
|
|
textFieldAppearance.clearButtonMode = .whileEditing
|
|
}
|
|
|
|
func onTextFieldFocused() {
|
|
guard errorMessage != nil else { return }
|
|
|
|
password = ""
|
|
errorMessage = nil
|
|
}
|
|
|
|
func onUsernameReturnPressed() {
|
|
guard !username.isEmpty else { return }
|
|
|
|
if password.isEmpty {
|
|
focusedField = .password
|
|
} else {
|
|
onReturn()
|
|
}
|
|
}
|
|
|
|
func onPasswordReturnPressed() {
|
|
guard !password.isEmpty else { return }
|
|
|
|
if username.isEmpty {
|
|
focusedField = .username
|
|
} else {
|
|
onReturn()
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Enumerations
|
|
|
|
private extension LoginForm {
|
|
enum Field: Hashable {
|
|
case username
|
|
case password
|
|
}
|
|
}
|
|
|
|
// MARK: - Previews
|
|
|
|
struct LoginForm_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
LoginForm(
|
|
username: .constant("Some username"),
|
|
password: .constant("Some Password"),
|
|
errorMessage: .constant(nil),
|
|
onReturn: {}
|
|
)
|
|
.previewDisplayName("Login form with no error message")
|
|
|
|
LoginForm(
|
|
username: .constant("Some username"),
|
|
password: .constant("Some Password"),
|
|
errorMessage: .constant("Some error goes in here..."),
|
|
onReturn: {}
|
|
)
|
|
.previewDisplayName("Login form with some error message")
|
|
}
|
|
}
|