From c1aa28b29526f4986dc96d397e7550ee12d3c6d2 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Sun, 1 Feb 2026 15:24:15 +0100 Subject: [PATCH] Update upgrade.tsx and NativeVaultManager.ts (#1576) --- apps/mobile-app/app/upgrade.tsx | 22 ++++++++++++--------- apps/mobile-app/specs/NativeVaultManager.ts | 3 +++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/apps/mobile-app/app/upgrade.tsx b/apps/mobile-app/app/upgrade.tsx index 8ede7e4dc..7a1b1b94e 100644 --- a/apps/mobile-app/app/upgrade.tsx +++ b/apps/mobile-app/app/upgrade.tsx @@ -177,13 +177,14 @@ export default function UpgradeScreen() : React.ReactNode { return; } - // Use the useVaultMutate hook to handle the upgrade and vault upload + /* + * Use the useVaultMutate hook to handle the upgrade and vault upload. + * IMPORTANT: Do NOT wrap migration SQL in beginTransaction/commitTransaction! + * The migration SQL contains PRAGMA foreign_keys statements that MUST be executed + * outside of any transaction to take effect. The SQL handles its own transactions. + */ await executeVaultMutation(async () => { - // Begin transaction - setUpgradeStatus(t('upgrade.status.startingDatabaseTransaction')); - await NativeVaultManager.beginTransaction(); - - // Execute each SQL command + // Execute each SQL command (each migration script handles its own transactions) setUpgradeStatus(t('upgrade.status.applyingDatabaseMigrations')); for (let i = 0; i < upgradeResult.sqlCommands.length; i++) { const sqlCommand = upgradeResult.sqlCommands[i]; @@ -194,14 +195,17 @@ export default function UpgradeScreen() : React.ReactNode { } catch (error) { console.error(`Error executing SQL command ${i + 1}:`, sqlCommand, error); const errorMessage = error instanceof Error ? error.message : 'Unknown error'; - await NativeVaultManager.rollbackTransaction(); throw new Error(`${t('upgrade.alerts.failedToApplyMigration', { current: i + 1, total: upgradeResult.sqlCommands.length })}\n\nDetails: ${errorMessage}`); } } - // Commit transaction + /* + * Persist the database to encrypted storage and mark as dirty. + * This is needed because we're not using beginTransaction/commitTransaction. + * The executeVaultMutation hook will handle the upload. + */ setUpgradeStatus(t('upgrade.status.committingChanges')); - await NativeVaultManager.commitTransaction(); + await NativeVaultManager.persistAndMarkDirty(); }, { skipSyncCheck: true, // Skip sync check during upgrade to prevent loop /** diff --git a/apps/mobile-app/specs/NativeVaultManager.ts b/apps/mobile-app/specs/NativeVaultManager.ts index 1ddec6047..c01182947 100644 --- a/apps/mobile-app/specs/NativeVaultManager.ts +++ b/apps/mobile-app/specs/NativeVaultManager.ts @@ -38,6 +38,9 @@ export interface Spec extends TurboModule { beginTransaction(): Promise; commitTransaction(): Promise; rollbackTransaction(): Promise; + // Persist the in-memory database to encrypted storage and mark as dirty. + // Used after migrations where SQL handles its own transactions but we need to persist and sync. + persistAndMarkDirty(): Promise; // Cryptography operations deriveKeyFromPassword(password: string, salt: string, encryptionType: string, encryptionSettings: string): Promise;