mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-18 13:28:12 -04:00
Linting refactor context (#771)
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
import React, { createContext, useContext, useState, useEffect, useMemo, useCallback, useRef } from 'react';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { useDb } from './DbContext';
|
||||
import { AppState, Platform, Linking } from 'react-native';
|
||||
import { AppState, Platform } from 'react-native';
|
||||
import { router, usePathname } from 'expo-router';
|
||||
import { NavigationContainerRef, ParamListBase } from '@react-navigation/native';
|
||||
import * as LocalAuthentication from 'expo-local-authentication';
|
||||
import NativeVaultManager from '../specs/NativeVaultManager';
|
||||
|
||||
import { useDb } from '@/context/DbContext';
|
||||
import NativeVaultManager from '@/specs/NativeVaultManager';
|
||||
|
||||
// Create a navigation reference
|
||||
export const navigationRef = React.createRef<NavigationContainerRef<ParamListBase>>();
|
||||
@@ -214,55 +215,55 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
/**
|
||||
* Get the auto-lock timeout from the iOS credentials manager
|
||||
*/
|
||||
const getAutoLockTimeout = async (): Promise<number> => {
|
||||
const getAutoLockTimeout = useCallback(async (): Promise<number> => {
|
||||
try {
|
||||
return await NativeVaultManager.getAutoLockTimeout();
|
||||
} catch (error) {
|
||||
console.error('Failed to get auto-lock timeout:', error);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Set the auto-lock timeout in the iOS credentials manager
|
||||
*/
|
||||
const setAutoLockTimeout = async (timeout: number) => {
|
||||
const setAutoLockTimeout = useCallback(async (timeout: number): Promise<void> => {
|
||||
try {
|
||||
await NativeVaultManager.setAutoLockTimeout(timeout);
|
||||
} catch (error) {
|
||||
console.error('Failed to update iOS auto-lock timeout:', error);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const isVaultUnlocked = async (): Promise<boolean> => {
|
||||
/**
|
||||
* Check if the vault is unlocked.
|
||||
*/
|
||||
const isVaultUnlocked = useCallback(async (): Promise<boolean> => {
|
||||
try {
|
||||
return await NativeVaultManager.isVaultUnlocked();
|
||||
} catch (error) {
|
||||
console.error('Failed to check vault status:', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Handle app state changes
|
||||
useEffect(() => {
|
||||
const subscription = AppState.addEventListener('change', async (nextAppState) => {
|
||||
if (appState.current.match(/inactive|background/) && nextAppState === 'active') {
|
||||
// App coming to foreground
|
||||
console.log('App coming to foreground in AuthContext');
|
||||
if (!pathname?.includes('unlock') && !pathname?.includes('login')) {
|
||||
try {
|
||||
// Check if vault is unlocked.
|
||||
const isUnlocked = await isVaultUnlocked();
|
||||
if (!isUnlocked) {
|
||||
// Database connection failed, navigate to unlock flow
|
||||
console.log('Vault is not unlocked anymore, navigating to unlock flow');
|
||||
router.replace('/sync');
|
||||
} else {
|
||||
console.log('Vault is still unlocked, staying on current screen');
|
||||
// Vault is still unlocked, staying on current screen
|
||||
}
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// Database query failed, navigate to unlock flow
|
||||
console.log('Failed to check vault status, navigating to unlock flow:', error);
|
||||
router.replace('/sync');
|
||||
}
|
||||
}
|
||||
@@ -270,10 +271,10 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
appState.current = nextAppState;
|
||||
});
|
||||
|
||||
return () => {
|
||||
return (): void => {
|
||||
subscription.remove();
|
||||
};
|
||||
}, [isVaultUnlocked]);
|
||||
}, [isVaultUnlocked, pathname]);
|
||||
|
||||
/**
|
||||
* Load iOS Autofill state from storage
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { createContext, useContext, useState, useEffect, useCallback, useMemo } from 'react';
|
||||
|
||||
import NativeVaultManager from '@/specs/NativeVaultManager';
|
||||
import SqliteClient from '@/utils/SqliteClient';
|
||||
import { VaultResponse } from '@/utils/types/webapi/VaultResponse';
|
||||
import { VaultMetadata } from '@/utils/types/messaging/VaultMetadata';
|
||||
import NativeVaultManager from '../specs/NativeVaultManager';
|
||||
|
||||
type DbContextType = {
|
||||
sqliteClient: SqliteClient | null;
|
||||
@@ -24,7 +25,7 @@ export const DbProvider: React.FC<{ children: React.ReactNode }> = ({ children }
|
||||
/**
|
||||
* SQLite client is initialized in constructor as it passes SQL queries to the native module.
|
||||
*/
|
||||
const sqliteClient = new SqliteClient();
|
||||
const sqliteClient = useMemo(() => new SqliteClient(), []);
|
||||
|
||||
/**
|
||||
* Database initialization state. If true, the database has been initialized and the dbAvailable state is correct.
|
||||
@@ -36,9 +37,25 @@ export const DbProvider: React.FC<{ children: React.ReactNode }> = ({ children }
|
||||
*/
|
||||
const [dbAvailable, setDbAvailable] = useState(false);
|
||||
|
||||
/**
|
||||
* Unlock the vault in the native module which will decrypt the database using the stored encryption key
|
||||
* and load it into memory.
|
||||
*/
|
||||
const unlockVault = useCallback(async () : Promise<boolean> => {
|
||||
try {
|
||||
await NativeVaultManager.unlockVault();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Failed to unlock vault:', error);
|
||||
return false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const initializeDatabase = useCallback(async (vaultResponse: VaultResponse, derivedKey: string | null = null) => {
|
||||
// If the derived key is provided, store it in the keychain.
|
||||
// Otherwise we assume the encryption key is already stored in the keychain.
|
||||
/*
|
||||
* If the derived key is provided, store it in the keychain.
|
||||
* Otherwise we assume the encryption key is already stored in the keychain.
|
||||
*/
|
||||
if (derivedKey) {
|
||||
await sqliteClient.storeEncryptionKey(derivedKey);
|
||||
}
|
||||
@@ -58,7 +75,7 @@ export const DbProvider: React.FC<{ children: React.ReactNode }> = ({ children }
|
||||
|
||||
setDbInitialized(true);
|
||||
setDbAvailable(true);
|
||||
}, []);
|
||||
}, [sqliteClient, unlockVault]);
|
||||
|
||||
const checkStoredVault = useCallback(async () => {
|
||||
try {
|
||||
@@ -67,25 +84,25 @@ export const DbProvider: React.FC<{ children: React.ReactNode }> = ({ children }
|
||||
// Get metadata from SQLite client
|
||||
const metadata = await sqliteClient.getVaultMetadata();
|
||||
if (metadata) {
|
||||
console.log('Vault metadata found, setting dbInitialized and dbAvailable to true');
|
||||
// Vault metadata found, set database initialization state
|
||||
setDbInitialized(true);
|
||||
setDbAvailable(true);
|
||||
} else {
|
||||
console.log('Vault metadata not found, setting dbInitialized and dbAvailable to false');
|
||||
// Vault metadata not found, set database initialization state
|
||||
setDbInitialized(true);
|
||||
setDbAvailable(false);
|
||||
}
|
||||
} else {
|
||||
console.log('Vault not initialized, setting dbInitialized and dbAvailable to false');
|
||||
// Vault not initialized, set database initialization state
|
||||
setDbInitialized(true);
|
||||
setDbAvailable(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking vault initialization:', error);
|
||||
} catch {
|
||||
// Error checking vault initialization, set database initialization state
|
||||
setDbInitialized(true);
|
||||
setDbAvailable(false);
|
||||
}
|
||||
}, []);
|
||||
}, [sqliteClient]);
|
||||
|
||||
/**
|
||||
* Check if database is initialized and try to retrieve vault from background
|
||||
@@ -104,26 +121,12 @@ export const DbProvider: React.FC<{ children: React.ReactNode }> = ({ children }
|
||||
NativeVaultManager.clearVault();
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Unlock the vault in the native module which will decrypt the database using the stored encryption key
|
||||
* and load it into memory.
|
||||
*/
|
||||
const unlockVault = useCallback(async () : Promise<boolean> => {
|
||||
try {
|
||||
await NativeVaultManager.unlockVault();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Failed to unlock vault:', error);
|
||||
return false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Get the current vault metadata directly from SQLite client
|
||||
*/
|
||||
const getVaultMetadata = useCallback(async () : Promise<VaultMetadata | null> => {
|
||||
return await sqliteClient.getVaultMetadata();
|
||||
}, []);
|
||||
}, [sqliteClient]);
|
||||
|
||||
/**
|
||||
* Test if the database is working with the provided (to be stored) encryption key by performing a simple query
|
||||
@@ -146,10 +149,11 @@ export const DbProvider: React.FC<{ children: React.ReactNode }> = ({ children }
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (error) {
|
||||
} catch {
|
||||
// Error testing database connection, return false
|
||||
return false;
|
||||
}
|
||||
}, []);
|
||||
}, [sqliteClient, unlockVault]);
|
||||
|
||||
const contextValue = useMemo(() => ({
|
||||
sqliteClient,
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
import React, { createContext, useContext, useState, useMemo, useEffect, useCallback } from 'react';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
||||
/**
|
||||
* Theme type.
|
||||
*/
|
||||
type Theme = 'light' | 'dark' | 'system';
|
||||
|
||||
/**
|
||||
* Theme preference key in storage.
|
||||
*/
|
||||
const THEME_PREFERENCE_KEY = 'local:theme';
|
||||
|
||||
/**
|
||||
* Theme context type.
|
||||
*/
|
||||
type ThemeContextType = {
|
||||
theme: Theme;
|
||||
setTheme: (theme: Theme) => void;
|
||||
isDarkMode: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Theme context.
|
||||
*/
|
||||
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
||||
|
||||
/**
|
||||
* Theme provider
|
||||
*/
|
||||
export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
/**
|
||||
* Theme state that can be 'light', 'dark', or 'system'.
|
||||
*/
|
||||
const [theme, setTheme] = useState<Theme>('system');
|
||||
|
||||
/**
|
||||
* Tracks whether dark mode is active (based on theme or system preference).
|
||||
*/
|
||||
const [isDarkMode, setIsDarkMode] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
/**
|
||||
* Load theme setting from storage.
|
||||
*/
|
||||
const loadTheme = async () : Promise<void> => {
|
||||
const savedTheme = await getTheme();
|
||||
setTheme(savedTheme);
|
||||
};
|
||||
loadTheme();
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Set the theme and save to storage.
|
||||
*/
|
||||
const updateTheme = useCallback((newTheme: Theme): void => {
|
||||
setTheme(newTheme);
|
||||
setStoredTheme(newTheme);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Get the theme from storage.
|
||||
*/
|
||||
const getTheme = async (): Promise<Theme> => {
|
||||
return (await AsyncStorage.getItem(THEME_PREFERENCE_KEY) as Theme) || 'system';
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the theme in storage.
|
||||
*/
|
||||
const setStoredTheme = async (theme: Theme): Promise<void> => {
|
||||
await AsyncStorage.setItem(THEME_PREFERENCE_KEY, theme);
|
||||
};
|
||||
|
||||
/**
|
||||
* Effect to apply theme to document and handle system preference changes
|
||||
*/
|
||||
useEffect(() => {
|
||||
/**
|
||||
* Update the dark mode status.
|
||||
*/
|
||||
const updateDarkMode = (): void => {
|
||||
if (theme === 'system') {
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
setIsDarkMode(prefersDark);
|
||||
document.documentElement.classList.toggle('dark', prefersDark);
|
||||
} else {
|
||||
const isDark = theme === 'dark';
|
||||
setIsDarkMode(isDark);
|
||||
document.documentElement.classList.toggle('dark', isDark);
|
||||
}
|
||||
};
|
||||
|
||||
// Initial update
|
||||
updateDarkMode();
|
||||
|
||||
// Listen for system preference changes if using 'system' theme
|
||||
if (theme === 'system') {
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
/**
|
||||
* Update the dark mode status when the system preference changes.
|
||||
*/
|
||||
const handler = () : void => updateDarkMode();
|
||||
mediaQuery.addEventListener('change', handler);
|
||||
return () : void => mediaQuery.removeEventListener('change', handler);
|
||||
}
|
||||
}, [theme]);
|
||||
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
theme,
|
||||
setTheme: updateTheme,
|
||||
isDarkMode,
|
||||
}),
|
||||
[theme, isDarkMode, updateTheme]
|
||||
);
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={value}>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to use theme state
|
||||
*/
|
||||
export const useTheme = (): ThemeContextType => {
|
||||
const context = useContext(ThemeContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useTheme must be used within a ThemeProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
|
||||
import { useAuth } from '@/context/AuthContext';
|
||||
import { WebApiService } from '@/utils/WebApiService';
|
||||
import { useAuth } from './AuthContext';
|
||||
|
||||
const WebApiContext = createContext<WebApiService | null>(null);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user