diff --git a/apps/browser-extension/src/entrypoints/popup/components/Forms/PasswordField.tsx b/apps/browser-extension/src/entrypoints/popup/components/Forms/PasswordField.tsx index 275991d69..e1c692ee3 100644 --- a/apps/browser-extension/src/entrypoints/popup/components/Forms/PasswordField.tsx +++ b/apps/browser-extension/src/entrypoints/popup/components/Forms/PasswordField.tsx @@ -16,6 +16,10 @@ interface IPasswordFieldProps { error?: string; showPassword?: boolean; onShowPasswordChange?: (show: boolean) => void; + /** Initial password settings to use (e.g., from persisted form state) */ + initialSettings?: PasswordSettings; + /** Callback when password settings change */ + onSettingsChange?: (settings: PasswordSettings) => void; } /** @@ -29,7 +33,9 @@ const PasswordField: React.FC = ({ placeholder, error, showPassword: controlledShowPassword, - onShowPasswordChange + onShowPasswordChange, + initialSettings, + onSettingsChange }) => { const { t } = useTranslation(); const dbContext = useDb(); @@ -56,12 +62,18 @@ const PasswordField: React.FC = ({ useEffect(() => { /** * Load password settings from the database. + * If initialSettings is provided (e.g., from persisted form state), use it instead of the database default. */ const loadSettings = async (): Promise => { try { if (dbContext.sqliteClient) { - const settings = dbContext.sqliteClient.settings.getPasswordSettings(); - setCurrentSettings(settings); + // Use initialSettings if provided, otherwise use database settings + if (initialSettings !== undefined) { + setCurrentSettings(initialSettings); + } else { + const settings = dbContext.sqliteClient.settings.getPasswordSettings(); + setCurrentSettings(settings); + } setIsLoaded(true); } } catch (error) { @@ -69,7 +81,7 @@ const PasswordField: React.FC = ({ } }; void loadSettings(); - }, [dbContext.sqliteClient]); + }, [dbContext.sqliteClient, initialSettings]); const generatePassword = useCallback((settings: PasswordSettings) => { try { @@ -90,9 +102,12 @@ const PasswordField: React.FC = ({ const newSettings = { ...currentSettings, Length: length }; setCurrentSettings(newSettings); + // Notify parent of settings change for persistence + onSettingsChange?.(newSettings); + // Always generate password when length changes generatePassword(newSettings); - }, [currentSettings, generatePassword]); + }, [currentSettings, generatePassword, onSettingsChange]); const handleRegeneratePassword = useCallback(() => { if (!currentSettings) { @@ -108,7 +123,9 @@ const PasswordField: React.FC = ({ const handleAdvancedSettingsChange = useCallback((newSettings: PasswordSettings) => { setCurrentSettings(newSettings); - }, []); + // Notify parent of settings change for persistence + onSettingsChange?.(newSettings); + }, [onSettingsChange]); const togglePasswordVisibility = useCallback(() => { setShowPassword(!showPassword); diff --git a/apps/browser-extension/src/entrypoints/popup/pages/items/ItemAddEdit.tsx b/apps/browser-extension/src/entrypoints/popup/pages/items/ItemAddEdit.tsx index df9268694..f5027e346 100644 --- a/apps/browser-extension/src/entrypoints/popup/pages/items/ItemAddEdit.tsx +++ b/apps/browser-extension/src/entrypoints/popup/pages/items/ItemAddEdit.tsx @@ -29,7 +29,7 @@ import useServiceDetection from '@/entrypoints/popup/hooks/useServiceDetection'; import { useVaultMutate } from '@/entrypoints/popup/hooks/useVaultMutate'; import { SKIP_FORM_RESTORE_KEY } from '@/utils/Constants'; -import type { Item, ItemField, ItemType, FieldType, Attachment, TotpCode } from '@/utils/dist/core/models/vault'; +import type { Item, ItemField, ItemType, FieldType, Attachment, TotpCode, PasswordSettings } from '@/utils/dist/core/models/vault'; import { FieldCategories, FieldTypes, ItemTypes, getSystemFieldsForItemType, getOptionalFieldsForItemType, isFieldShownByDefault, getSystemField, fieldAppliesToType } from '@/utils/dist/core/models/vault'; import { FaviconService } from '@/utils/FaviconService'; @@ -71,6 +71,7 @@ type PersistedFormData = { showAttachments: boolean; manuallyAddedFields: string[]; isLoginEmailInEmailMode?: boolean; + passwordSettings?: PasswordSettings; }; /** @@ -152,6 +153,9 @@ const ItemAddEdit: React.FC = () => { // Track email field mode for Login type (true = free text "Email", false = domain chooser "Alias") const [isLoginEmailInEmailMode, setIsLoginEmailInEmailMode] = useState(true); + // Track password settings for persistence (so slider position and options are remembered) + const [passwordSettings, setPasswordSettings] = useState(undefined); + // Track whether to skip form restoration (set during initialization) const [skipFormRestore] = useState(false); @@ -184,6 +188,9 @@ const ItemAddEdit: React.FC = () => { if (data.isLoginEmailInEmailMode !== undefined) { setIsLoginEmailInEmailMode(data.isLoginEmailInEmailMode); } + if (data.passwordSettings !== undefined) { + setPasswordSettings(data.passwordSettings); + } }, []); /** @@ -202,6 +209,7 @@ const ItemAddEdit: React.FC = () => { showAttachments, manuallyAddedFields: Array.from(manuallyAddedFields), isLoginEmailInEmailMode, + passwordSettings, }, onRestore: handleFormRestore, skipRestore: skipFormRestore, @@ -1026,6 +1034,8 @@ const ItemAddEdit: React.FC = () => { onChange={(val) => handleFieldChange(fieldKey, val)} showPassword={showPassword} onShowPasswordChange={setShowPassword} + initialSettings={passwordSettings} + onSettingsChange={setPasswordSettings} /> ); @@ -1103,7 +1113,7 @@ const ItemAddEdit: React.FC = () => { ); } - }, [fieldValues, handleFieldChange, showPassword, t, handleGenerateAliasEmail, aliasFieldsShownByDefault, generateRandomUsername, isLoginEmailInEmailMode]); + }, [fieldValues, handleFieldChange, showPassword, t, handleGenerateAliasEmail, aliasFieldsShownByDefault, generateRandomUsername, isLoginEmailInEmailMode, passwordSettings]); /** * Handle form submission via Enter key.