import React from "react"; import { ActivityIndicator, Pressable, View } from "react-native"; import { Ionicons } from "@expo/vector-icons"; import { useColorScheme } from "nativewind"; import Slider from "@react-native-community/slider"; import { Text } from "./text"; import { usePasswordGenerator } from "../hooks/usePasswordGenerator"; export interface PasswordGeneratorProps { /** Called when the user taps "Use Password" (only shown when `onSelect` is provided). */ onSelect?: (password: string) => void; /** Hide the copy button (useful in embedded / inline mode). */ hideCopy?: boolean; } /** * UI shell over `utils/password.generatePassword`. Owns length slider, * character-set toggles, copy-to-clipboard (via shared util with auto- * clear), and a coarse strength indicator. */ export const PasswordGenerator = React.memo(function PasswordGenerator({ onSelect, hideCopy, }: PasswordGeneratorProps) { const { password, length, setLength, includeUppercase, setIncludeUppercase, includeLowercase, setIncludeLowercase, includeNumbers, setIncludeNumbers, includeSymbols, setIncludeSymbols, excludeSimilar, setExcludeSimilar, breach, strength: passwordStrength, handleGenerate, handleCopy, } = usePasswordGenerator(); const { colorScheme } = useColorScheme(); const isDark = colorScheme === "dark"; return ( <> Generated Password {passwordStrength.label} {password || "Press generate to create a password"} {/* HIBP breach-check status. Fixed height so the layout never shifts. */} {breach === "checking" ? ( <> Checking breach database… ) : breach === "safe" ? ( <> Not found in any known breach ) : breach === "unverified" ? ( <> Couldn't check breaches (offline) ) : null} Generate {!hideCopy && ( Copy )} {onSelect && ( onSelect(password)} disabled={!password} className="active:scale-98 rounded-lg bg-secondary dark:bg-neutral-800 px-4 py-3" accessibilityRole="button" accessibilityLabel="Use password" accessibilityState={{ disabled: !password }} > Use )} Options Password Length {length} 8 128 setIncludeUppercase((v) => !v)} isDark={isDark} /> setIncludeLowercase((v) => !v)} isDark={isDark} /> setIncludeNumbers((v) => !v)} isDark={isDark} /> setIncludeSymbols((v) => !v)} isDark={isDark} /> setExcludeSimilar((v) => !v)} isDark={isDark} /> ); }); export default PasswordGenerator; interface CharsetCheckboxProps { label: string; checked: boolean; onToggle: () => void; isDark: boolean; } function CharsetCheckbox({ label, checked, onToggle, isDark }: CharsetCheckboxProps) { return ( {checked && } {label} ); }