Update credentials list to properly refresh with new vault (#541)

This commit is contained in:
Leendert de Borst
2025-02-21 16:31:30 +01:00
parent 977acf84c5
commit e8314f91dc
4 changed files with 32 additions and 69 deletions

View File

@@ -9,15 +9,11 @@ document.addEventListener('focusin', async (e) => {
const target = e.target as HTMLInputElement;
const textInputTypes = ['text', 'email', 'tel', 'password', 'search', 'url'];
console.log('focusin triggered 1:', target);
if (target.tagName === 'INPUT' &&
textInputTypes.includes(target.type) &&
!target.dataset.aliasvaultIgnore) {
const formDetector = new FormDetector(document, target);
console.log('focusin triggered 2');
if (!formDetector.containsLoginForm()) return;
injectIcon(target);

View File

@@ -57,6 +57,7 @@ export const DbProvider: React.FC<{ children: React.ReactNode }> = ({ children }
// Initialize the SQLite client.
const client = new SqliteClient();
await client.initializeFromBase64(decryptedBlob);
setSqliteClient(client);
setDbInitialized(true);
setDbAvailable(true);
@@ -80,6 +81,7 @@ export const DbProvider: React.FC<{ children: React.ReactNode }> = ({ children }
if (response?.vault) {
const client = new SqliteClient();
await client.initializeFromBase64(response.vault);
setSqliteClient(client);
setDbInitialized(true);
setDbAvailable(true);
@@ -106,26 +108,6 @@ export const DbProvider: React.FC<{ children: React.ReactNode }> = ({ children }
}
}, [dbInitialized, checkStoredVault]);
/**
* Add a listener for when the popup becomes visible
*/
useEffect(() : void => {
/**
* Handles visibility state changes of the document.
* Checks and retrieves stored vault data when document becomes visible and database is not initialized.
*/
const handleVisibilityChange = () : void => {
if (document.visibilityState === 'visible' && !dbInitialized) {
checkStoredVault();
}
};
document.addEventListener('visibilitychange', handleVisibilityChange);
return () : void => {
document.removeEventListener('visibilitychange', handleVisibilityChange);
};
}, [dbInitialized, checkStoredVault]);
/**
* Clear database and remove from background worker, called when logging out.
*/

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback, useRef } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import { useDb } from '../context/DbContext';
import { Credential } from '../../shared/types/Credential';
import { Buffer } from 'buffer';
@@ -22,7 +22,6 @@ const CredentialsList: React.FC = () => {
const navigate = useNavigate();
const { showLoading, hideLoading, setIsInitialLoading } = useLoading();
const authContext = useAuth();
const isMounted = useRef(false);
/**
* Loading state with minimum duration for more fluid UX.
@@ -45,19 +44,18 @@ const CredentialsList: React.FC = () => {
return;
}
// If the vault has not been updated on the server, just load existing credentials
if (statusResponse.vaultRevision <= dbContext.vaultRevision) {
try {
try {
// If the vault revision is the same or lower, (re)load existing credentials.
if (statusResponse.vaultRevision <= dbContext.vaultRevision) {
const results = dbContext.sqliteClient.getAllCredentials();
setCredentials(results);
return;
} catch (err) {
console.error('Error loading credentials:', err);
}
}
try {
// Make API call to get latest vault
/**
* If the vault revision is higher, fetch the latest vault and initialize the SQLite context again.
* This will trigger a new credentials list refresh.
*/
const vaultResponseJson = await webApi.get<VaultResponse>('Vault');
const vaultError = webApi.validateVaultResponse(vaultResponseJson);
@@ -72,42 +70,11 @@ const CredentialsList: React.FC = () => {
// Initialize the SQLite context again with the newly retrieved decrypted blob
await dbContext.initializeDatabase(vaultResponseJson, passwordHashBase64);
// Load credentials
try {
const results = dbContext.sqliteClient.getAllCredentials();
setCredentials(results);
} catch (err) {
console.error('Error loading credentials:', err);
}
} catch (err) {
console.error('Refresh error:', err);
}
}, [dbContext, webApi, authContext, hideLoading]);
/**
* Load the credentials list when the component is mounted (only once).
*/
useEffect(() => {
if (isMounted.current) {
return;
}
isMounted.current = true;
/**
* Asynchronously load the credentials list.
*/
const loadInitialData = async () : Promise<void> => {
setIsLoading(true);
await onRefresh();
setIsLoading(false);
// Hide the global app initial loading state after the credentials list is loaded.
setIsInitialLoading(false);
};
loadInitialData();
}, [onRefresh, setIsLoading, setIsInitialLoading]);
/**
* Manually refresh the credentials list.
*/
@@ -117,6 +84,27 @@ const CredentialsList: React.FC = () => {
hideLoading();
};
/**
* Load credentials list on mount and on sqlite client change.
*/
useEffect(() => {
/**
* Refresh credentials list when sqlite client is available.
*/
const refreshCredentials = async () : Promise<void> => {
if (dbContext?.sqliteClient) {
setIsLoading(true);
await onRefresh();
setIsLoading(false);
// Hide the global app initial loading state after the credentials list is loaded.
setIsInitialLoading(false);
}
};
refreshCredentials();
}, [dbContext?.sqliteClient, onRefresh, setIsLoading, setIsInitialLoading]);
// Add this function to filter credentials
const filteredCredentials = credentials.filter(cred => {
const searchLower = searchTerm.toLowerCase();

View File

@@ -104,10 +104,7 @@ const Unlock: React.FC = () => {
return (
<div className="max-w-md">
<form onSubmit={handleSubmit} className="bg-white dark:bg-gray-700 w-full shadow-md rounded px-8 pt-6 pb-8 mb-4">
<div className="flex space-x-4 mb-6">
<img className="w-8 h-8 rounded-full" src="/assets/images/avatar.webp" alt="User avatar" />
<h2 className="text-2xl font-bold text-gray-900 dark:text-white break-all overflow-hidden">{authContext.username}</h2>
</div>
<h2 className="text-2xl font-bold text-gray-900 dark:text-white break-all overflow-hidden mb-4">{authContext.username}</h2>
<p className="text-base text-gray-500 dark:text-gray-200 mb-6">
Enter your master password to unlock your vault.