Files
ladill-mini/apps/ios/LadillMini/Features/Security/PinViews.swift
T
isaaccladandCursor ea6afb80a4
Deploy Ladill Mini / deploy (push) Successful in 35s
Add Ladill Mini native Android and iOS apps.
Ship full-featured mobile clients with auth, payments, QRs, push notifications, and iOS polish including Figtree typography, app icons, and QR previews.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-11 22:30:35 +00:00

126 lines
3.9 KiB
Swift

import SwiftUI
struct PinLockView: View {
let onUnlocked: () -> Void
@State private var pin = ""
@State private var error: String?
var body: some View {
PinPadView(title: "Enter PIN", pin: $pin, error: $error) {
if PinStore.shared.verifyPin(pin) {
onUnlocked()
} else {
error = "Incorrect PIN. Try again."
pin = ""
}
}
}
}
struct PinLockOverlay: View {
let onUnlocked: () -> Void
@State private var pin = ""
@State private var error: String?
var body: some View {
ZStack {
LadillColors.page.ignoresSafeArea()
PinPadView(title: "Enter PIN", pin: $pin, error: $error) {
if PinStore.shared.verifyPin(pin) {
onUnlocked()
} else {
error = "Incorrect PIN. Try again."
pin = ""
}
}
}
}
}
struct PinSetupView: View {
let onPinSet: () -> Void
@State private var pin = ""
@State private var confirm = ""
@State private var step = 0
@State private var error: String?
var body: some View {
PinPadView(title: step == 0 ? "Set a PIN" : "Confirm PIN", pin: step == 0 ? $pin : $confirm, error: $error) {
if step == 0 {
step = 1
} else if pin == confirm {
PinStore.shared.savePin(pin)
onPinSet()
} else {
error = "PINs don't match."
confirm = ""
step = 0
pin = ""
}
}
}
}
private struct PinPadView: View {
let title: String
@Binding var pin: String
@Binding var error: String?
let onComplete: () -> Void
var body: some View {
VStack(spacing: 24) {
Spacer()
Image(systemName: "lock.fill")
.font(.system(size: 32))
.foregroundStyle(LadillColors.indigo)
Text(title)
.font(.ladill(size: 22, weight: .semibold))
.foregroundStyle(LadillColors.ink)
HStack(spacing: 12) {
ForEach(0..<4, id: \.self) { i in
Circle()
.fill(i < pin.count ? LadillColors.indigo : LadillColors.border)
.frame(width: 14, height: 14)
}
}
if let error {
Text(error)
.font(.ladill(size: 13))
.foregroundStyle(LadillColors.red)
}
let keys = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "", "0", "⌫"]
LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 3), spacing: 16) {
ForEach(keys, id: \.self) { key in
if key.isEmpty {
Color.clear.frame(height: 56)
} else {
Button {
if key == "⌫" {
if !pin.isEmpty { pin.removeLast() }
} else if pin.count < 4 {
pin.append(key)
if pin.count == 4 { onComplete() }
}
} label: {
Text(key)
.font(.ladill(size: 24, weight: .medium))
.frame(maxWidth: .infinity)
.frame(height: 56)
.background(LadillColors.surface)
.clipShape(Circle())
}
.buttonStyle(.plain)
}
}
}
.padding(.horizontal, 40)
Spacer()
}
.background(LadillColors.page)
}
}