Fix password visibility toggle position in LoginView

Add SecureIconTextField component that includes the eye toggle button
inside the text field (matching RegisterView's OrganicSecureField).

Update LoginView to use SecureIconTextField instead of IconTextField
with an external button, ensuring consistent UI across auth screens.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-12-17 13:35:21 -06:00
parent 338c9a6d09
commit 7d2ac309ab
2 changed files with 67 additions and 22 deletions

View File

@@ -97,28 +97,17 @@ struct LoginView: View {
VStack(alignment: .leading, spacing: 8) {
FieldLabel(text: L10n.Auth.loginPasswordLabel)
HStack(spacing: 12) {
IconTextField(
icon: "lock.fill",
placeholder: L10n.Auth.enterPassword,
text: $viewModel.password,
isSecure: !isPasswordVisible,
textContentType: .password,
onSubmit: { viewModel.login() }
)
.onChange(of: viewModel.password) { _, _ in
viewModel.clearError()
}
.accessibilityIdentifier(AccessibilityIdentifiers.Authentication.passwordField)
Button(action: {
isPasswordVisible.toggle()
}) {
Image(systemName: isPasswordVisible ? "eye.slash.fill" : "eye.fill")
.font(.system(size: 16, weight: .medium))
.foregroundColor(Color.appTextSecondary)
}
.accessibilityIdentifier(AccessibilityIdentifiers.Authentication.passwordVisibilityToggle)
SecureIconTextField(
icon: "lock.fill",
placeholder: L10n.Auth.enterPassword,
text: $viewModel.password,
isVisible: $isPasswordVisible,
textContentType: .password,
onSubmit: { viewModel.login() },
accessibilityId: AccessibilityIdentifiers.Authentication.passwordField
)
.onChange(of: viewModel.password) { _, _ in
viewModel.clearError()
}
}

View File

@@ -288,6 +288,62 @@ struct IconTextField: View {
}
}
// MARK: - Secure TextField with Icon and Visibility Toggle
struct SecureIconTextField: View {
let icon: String
let placeholder: String
@Binding var text: String
@Binding var isVisible: Bool
var textContentType: UITextContentType? = nil
var onSubmit: (() -> Void)? = nil
var accessibilityId: String? = nil
@FocusState private var isFocused: Bool
var body: some View {
HStack(spacing: 12) {
ZStack {
Circle()
.fill(Color.appPrimary.opacity(0.1))
.frame(width: 32, height: 32)
Image(systemName: icon)
.font(.system(size: 14, weight: .medium))
.foregroundColor(Color.appPrimary)
}
Group {
if isVisible {
TextField(placeholder, text: $text)
.accessibilityIdentifier(accessibilityId ?? "")
} else {
SecureField(placeholder, text: $text)
.accessibilityIdentifier(accessibilityId ?? "")
}
}
.font(.system(size: 16, weight: .medium))
.textContentType(textContentType)
.focused($isFocused)
.submitLabel(.go)
.onSubmit { onSubmit?() }
Button(action: { isVisible.toggle() }) {
Image(systemName: isVisible ? "eye.slash.fill" : "eye.fill")
.font(.system(size: 16, weight: .medium))
.foregroundColor(Color.appTextSecondary)
}
}
.padding(16)
.background(Color.appBackgroundPrimary.opacity(0.5))
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
.overlay(
RoundedRectangle(cornerRadius: 16, style: .continuous)
.stroke(isFocused ? Color.appPrimary : Color.appTextSecondary.opacity(0.15), lineWidth: 1.5)
)
.animation(.easeInOut(duration: 0.2), value: isFocused)
}
}
// MARK: - Field Label
struct FieldLabel: View {