32ffe782ea
Flatten widget layouts and use system colors so banner and expanded regions render on vibrant lock screens; migrate auth sessions per account to prevent double sign-in; scope app lock PIN to accounts; default clock description to "Clock In"; add architecture docs and deferred form validation on auth screens. Co-authored-by: Cursor <cursoragent@cursor.com>
80 lines
2.7 KiB
TypeScript
80 lines
2.7 KiB
TypeScript
import * as SecureStore from "expo-secure-store";
|
|
|
|
import { normalizeSecureStoreKey } from "@/lib/secure-store-keys";
|
|
|
|
function lockKey(accountId: string, field: "enabled" | "pin" | "biometric") {
|
|
return normalizeSecureStoreKey(`beenvoice.app-lock.${accountId}.${field}`);
|
|
}
|
|
|
|
const LEGACY_ENABLED_KEY = "beenvoice_app_lock_enabled";
|
|
const LEGACY_PIN_KEY = "beenvoice_app_lock_pin";
|
|
const LEGACY_BIOMETRIC_KEY = "beenvoice_app_lock_biometric";
|
|
|
|
async function migrateLegacyLockIfNeeded(accountId: string): Promise<void> {
|
|
const [legacyEnabled, legacyPin, legacyBiometric, accountEnabled] = await Promise.all([
|
|
SecureStore.getItemAsync(LEGACY_ENABLED_KEY),
|
|
SecureStore.getItemAsync(LEGACY_PIN_KEY),
|
|
SecureStore.getItemAsync(LEGACY_BIOMETRIC_KEY),
|
|
SecureStore.getItemAsync(lockKey(accountId, "enabled")),
|
|
]);
|
|
|
|
if (accountEnabled != null || legacyEnabled !== "1") return;
|
|
|
|
if (legacyPin) {
|
|
await setStoredPin(accountId, legacyPin);
|
|
}
|
|
await setAppLockEnabled(accountId, true);
|
|
if (legacyBiometric === "1") {
|
|
await setBiometricEnabled(accountId, true);
|
|
}
|
|
|
|
await Promise.all([
|
|
SecureStore.deleteItemAsync(LEGACY_ENABLED_KEY),
|
|
SecureStore.deleteItemAsync(LEGACY_PIN_KEY),
|
|
SecureStore.deleteItemAsync(LEGACY_BIOMETRIC_KEY),
|
|
]);
|
|
}
|
|
|
|
export async function getAppLockEnabled(accountId: string): Promise<boolean> {
|
|
await migrateLegacyLockIfNeeded(accountId);
|
|
const value = await SecureStore.getItemAsync(lockKey(accountId, "enabled"));
|
|
return value === "1";
|
|
}
|
|
|
|
export async function setAppLockEnabled(accountId: string, enabled: boolean): Promise<void> {
|
|
if (enabled) {
|
|
await SecureStore.setItemAsync(lockKey(accountId, "enabled"), "1");
|
|
} else {
|
|
await SecureStore.deleteItemAsync(lockKey(accountId, "enabled"));
|
|
}
|
|
}
|
|
|
|
export async function getStoredPin(accountId: string): Promise<string | null> {
|
|
return SecureStore.getItemAsync(lockKey(accountId, "pin"));
|
|
}
|
|
|
|
export async function setStoredPin(accountId: string, pin: string): Promise<void> {
|
|
await SecureStore.setItemAsync(lockKey(accountId, "pin"), pin);
|
|
}
|
|
|
|
export async function clearStoredPin(accountId: string): Promise<void> {
|
|
await SecureStore.deleteItemAsync(lockKey(accountId, "pin"));
|
|
}
|
|
|
|
export async function getBiometricEnabled(accountId: string): Promise<boolean> {
|
|
const value = await SecureStore.getItemAsync(lockKey(accountId, "biometric"));
|
|
return value === "1";
|
|
}
|
|
|
|
export async function setBiometricEnabled(accountId: string, enabled: boolean): Promise<void> {
|
|
if (enabled) {
|
|
await SecureStore.setItemAsync(lockKey(accountId, "biometric"), "1");
|
|
} else {
|
|
await SecureStore.deleteItemAsync(lockKey(accountId, "biometric"));
|
|
}
|
|
}
|
|
|
|
export function isValidPin(pin: string): boolean {
|
|
return /^\d{4,6}$/.test(pin);
|
|
}
|