import React, { useState } from 'react'; import { Pressable, StyleSheet, Text, View } from 'react-native'; import * as Haptics from 'expo-haptics'; import { theme } from '../theme'; interface Props { title: string; error?: string | null; onSubmit: (pin: string) => Promise | void; onCancel: () => void; } const KEYS: (string | null)[] = ['1', '2', '3', '4', '5', '6', '7', '8', '9', null, '0', '⌫']; export function PinEntry({ title, error, onSubmit, onCancel }: Props) { const [pin, setPin] = useState(''); function press(k: string) { if (k === '⌫') { setPin((p) => p.slice(0, -1)); Haptics.selectionAsync().catch(() => {}); return; } if (pin.length >= 8) return; setPin((p) => p + k); Haptics.selectionAsync().catch(() => {}); } return ( {title} {error ? {error} : } {Array.from({ length: Math.max(4, pin.length) }).map((_, i) => ( ))} {KEYS.map((k, i) => k === null ? ( ) : ( [styles.key, pressed && styles.keyPressed]} onPress={() => press(k)} > {k} ), )} Cancel onSubmit(pin)} > Next ); } const KEY_SIZE = 72; const styles = StyleSheet.create({ root: { flex: 1, backgroundColor: theme.bg, alignItems: 'center', paddingTop: 60 }, title: { color: theme.fg, fontSize: 18, fontWeight: '700' }, error: { color: theme.bad, fontSize: 13, marginTop: 8 }, dots: { flexDirection: 'row', gap: 14, marginTop: 14, minHeight: 16 }, dot: { width: 12, height: 12, borderRadius: 6, borderWidth: 1.5, borderColor: theme.border, }, dotFilled: { backgroundColor: theme.accent, borderColor: theme.accent }, pad: { marginTop: 24, flexDirection: 'row', flexWrap: 'wrap', width: KEY_SIZE * 3 + 36, justifyContent: 'space-between', rowGap: 12, }, key: { width: KEY_SIZE, height: KEY_SIZE, borderRadius: KEY_SIZE / 2, backgroundColor: theme.card, borderWidth: 1, borderColor: theme.border, alignItems: 'center', justifyContent: 'center', }, keyEmpty: { backgroundColor: 'transparent', borderColor: 'transparent' }, keyPressed: { backgroundColor: theme.bgElevated, borderColor: theme.borderFocus }, keyText: { color: theme.fg, fontSize: 26, fontWeight: '500' }, keyBack: { color: theme.muted, fontSize: 22 }, actions: { flexDirection: 'row', gap: 12, marginTop: 28, paddingHorizontal: 24, }, ghostBtn: { paddingVertical: 12, paddingHorizontal: 22, borderRadius: 10, borderWidth: 1, borderColor: theme.border, }, ghostText: { color: theme.muted, fontSize: 14, fontWeight: '600' }, primaryBtn: { paddingVertical: 12, paddingHorizontal: 30, borderRadius: 10, backgroundColor: theme.accent, }, primaryBtnDisabled: { backgroundColor: theme.border }, primaryText: { color: '#fff', fontSize: 14, fontWeight: '700' }, });