From ce52f8a4f708b95fe8aed4ccf1399e30db82d5f1 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Sun, 1 Mar 2026 15:56:36 +0100 Subject: [PATCH] Tweak mobile app sync queue --- apps/mobile-app/context/DbContext.tsx | 3 +- apps/mobile-app/hooks/useVaultMutate.ts | 41 ++++++++++++++++++------- apps/mobile-app/hooks/useVaultSync.ts | 2 -- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/apps/mobile-app/context/DbContext.tsx b/apps/mobile-app/context/DbContext.tsx index a4cd3de3e..6f6359531 100644 --- a/apps/mobile-app/context/DbContext.tsx +++ b/apps/mobile-app/context/DbContext.tsx @@ -187,9 +187,8 @@ export const DbProvider: React.FC<{ children: React.ReactNode }> = ({ children } const refreshSyncState = useCallback(async (): Promise => { try { const syncState = await NativeVaultManager.getSyncState(); - setIsDirty(syncState.isDirty); - // Also refresh offline mode from native const offline = await NativeVaultManager.getOfflineMode(); + setIsDirty(syncState.isDirty); setIsOfflineState(offline); } catch (error) { console.error('Failed to refresh sync state:', error); diff --git a/apps/mobile-app/hooks/useVaultMutate.ts b/apps/mobile-app/hooks/useVaultMutate.ts index 3e646d6d7..d30dccec4 100644 --- a/apps/mobile-app/hooks/useVaultMutate.ts +++ b/apps/mobile-app/hooks/useVaultMutate.ts @@ -1,6 +1,6 @@ import { Buffer } from 'buffer'; -import { useCallback, useState } from 'react'; +import { useCallback, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import Toast from 'react-native-toast-message'; @@ -43,6 +43,8 @@ export function useVaultMutate() : { const dbContext = useDb(); const webApi = useWebApi(); const { syncVault } = useVaultSync(); + const syncInProgressRef = useRef(false); + const syncQueuedRef = useRef(false); /** * Prepare vault for password change operation. @@ -91,8 +93,14 @@ export function useVaultMutate() : { * This is fire-and-forget - the ServerSyncIndicator shows progress. */ const triggerBackgroundSync = useCallback(async (options: VaultMutationOptions): Promise => { - // Show uploading indicator since we're uploading local changes - dbContext.setIsUploading(true); + // If sync already in progress, queue this request + if (syncInProgressRef.current) { + syncQueuedRef.current = true; + console.log('[useVaultMutate] Sync already in progress, queuing sync request'); + return; + } + + syncInProgressRef.current = true; try { await syncVault({ @@ -103,24 +111,32 @@ export function useVaultMutate() : { } catch (error) { console.warn('VaultMutate: Failed to register credential identities:', error); } - await dbContext.refreshSyncState(); options.onSuccess?.(); }, onError: async (error) => { - await dbContext.refreshSyncState(); - // Don't show error toast - the indicator shows offline/pending state console.warn('Background sync failed:', error); options.onError?.(new Error(error)); }, onOffline: async () => { - // Local change is saved and isDirty is set - will sync when back online - await dbContext.refreshSyncState(); options.onSuccess?.(); } }); } finally { - dbContext.setIsUploading(false); - await dbContext.refreshSyncState(); + syncInProgressRef.current = false; + + // Process queued sync if one was requested while we were syncing + if (syncQueuedRef.current) { + syncQueuedRef.current = false; + console.log('[useVaultMutate] Processing queued sync request'); + // Small delay to avoid rapid-fire syncs + setTimeout(() => { + void triggerBackgroundSync(options); + }, 100); + } else { + // Only refresh state and clear uploading if no queued sync + await dbContext.refreshSyncState(); + dbContext.setIsUploading(false); + } } }, [syncVault, dbContext]); @@ -133,7 +149,10 @@ export function useVaultMutate() : { ): Promise => { await operation(); - // Refresh sync state to show "Pending" indicator immediately + // Set uploading state BEFORE refreshing sync state to prevent "pending" flash + dbContext.setIsUploading(true); + + // Refresh sync state to update isDirty flag await dbContext.refreshSyncState(); // Trigger background sync - fire-and-forget, don't await diff --git a/apps/mobile-app/hooks/useVaultSync.ts b/apps/mobile-app/hooks/useVaultSync.ts index be84b7c6a..adda8f8d4 100644 --- a/apps/mobile-app/hooks/useVaultSync.ts +++ b/apps/mobile-app/hooks/useVaultSync.ts @@ -238,9 +238,7 @@ export const useVaultSync = (): { return false; } finally { syncInProgressRef.current = false; - // Always clear syncing/uploading states when done dbContext.setIsSyncing(false); - dbContext.setIsUploading(false); } }, [app, dbContext, t]);