The sync system has been completely rewritten to provide a unified, robust, and conflict-free synchronization mechanism across devices without data corruption.
- Uploads new/modified local records to cloud
- Downloads new/modified cloud records to local
- Intelligently merges changes based on timestamps
- Soft Delete Pattern: Records are marked as
deleted: trueinstead of being permanently removed - Deletions sync across all devices in real-time
- Automatic cleanup of old deleted records after 24 hours
- No more zombie records appearing after deletion
- Automatic sync when cloud changes are detected
- Uses Firestore's
onSnapshot()listener - Debounced to prevent excessive syncs (1 second delay)
- Only syncs when changes come from other devices
- No manual "Sync Now" needed for most cases
- Last-Write-Wins: Most recent timestamp takes precedence
- Device tracking prevents sync loops
- Data integrity validation before upload
- Conflict counter in sync results
- Validates record structure before upload
- Atomic operations (all-or-nothing)
- Error tracking per record
- Comprehensive error reporting
interface CloudRecord {
// Standard fields
id: number;
type: 'card' | 'netbanking' | 'note' | 'password';
data: string; // Encrypted
createdAt: number;
updatedAt: number;
// Cloud sync fields
cloudUpdatedAt: number; // Timestamp when uploaded to cloud
deviceId: string; // Which device uploaded this
deleted?: boolean; // Soft delete flag
serverTimestamp: any; // Firebase server timestamp
}interface SyncResult {
uploaded: number; // Records uploaded to cloud
downloaded: number; // Records downloaded from cloud
deleted: number; // Records deleted during sync
conflicts: number; // Conflicts detected (future use)
errors: string[]; // Any errors encountered
}- Get sync metadata (last sync time)
- Load all local records
- Download all cloud records
- Build comparison maps
- For each local record:
- If not in cloud → Upload
- If in cloud and deleted → Delete local
- If in cloud and local is newer → Upload
- If in cloud and cloud is newer → Download
- For each cloud-only record:
- If not deleted → Download
- Update sync metadata
- Dispatch sync-complete event
- Firestore listener detects changes
- Check if changes are from another device
- Debounce for 1 second
- Trigger full bidirectional sync
- UI automatically updates
- User deletes record locally
- Local IndexedDB record removed
- Cloud record marked with
deleted: true - Real-time listener on other devices detects change
- Other devices sync and remove local record
- After 24 hours, cleanup removes cloud marker
Main sync function. Performs bidirectional sync with conflict resolution.
Marks a record as deleted in the cloud (soft delete).
Enables real-time sync listener. Callback is called when external changes detected.
Disables real-time sync listener (e.g., when user signs out).
Resets sync metadata to force a fresh sync (troubleshooting).
Removes deleted records older than 24 hours from cloud.
Returns true if user is signed in and sync is available.
Returns true if a sync operation is currently running.
// Deletion now automatically syncs
await storageService.deleteRecord(recordId);
await firebaseSyncService.deleteRecord(recordId); // Marks as deleted in cloud// Real-time sync enabled on sign-in
useEffect(() => {
if (user) {
firebaseSyncService.enableRealtimeSync(() => {
// Real-time update detected, sync will happen automatically
});
}
return () => firebaseSyncService.disableRealtimeSync();
}, [user]);const { syncNow } = useSyncContext();
await syncNow(); // Manually trigger syncconst { resetAndSync } = useSyncContext();
await resetAndSync(); // Force fresh sync- Uploaded: Blue gradient card
- Downloaded: Green gradient card
- Deleted: Red gradient card (shows if > 0)
- Conflicts: Yellow gradient card (shows if > 0)
- Errors: Red alert box with details
- Sync happens automatically in background
- No "Sync Now" needed for most operations
- Last sync time displayed
- Sync status shows "Syncing..." during operation
- Atomic operations ensure all-or-nothing updates
- Validation before upload prevents malformed data
- Soft deletes prevent accidental permanent data loss
- Changes appear on all devices within 1-2 seconds
- Deletions propagate instantly
- No manual intervention needed
- Last-write-wins prevents conflicts
- Device tracking prevents sync loops
- Timestamp-based resolution is deterministic
- Comprehensive error handling
- Error tracking per record
- Sync can continue even if one record fails
- Only syncs changed records
- Debounced real-time updates
- Cleanup removes old deleted records
- Device A adds new note
- Note saved locally and uploaded to cloud
- Device B's real-time listener detects change
- Device B automatically syncs and downloads note
- Note appears on Device B within 1-2 seconds
- Device A deletes note
- Note removed from local storage
- Cloud record marked as
deleted: true - Device B's listener detects change
- Device B syncs and removes local note
- Note disappears from Device B
- Device A modifies note content
- Local record updated with new timestamp
- Cloud record updated
- Device B's listener detects change
- Device B downloads updated content
- Updated content appears on Device B
- Device A goes offline
- User edits multiple records
- Device A comes back online
- Manual sync uploads all changes
- Device B receives all updates
- Check if user is signed in
- Check console for sync errors
- Try "Reset & Sync" button
- Verify Firebase rules allow access
- Check if deleteRecord() is called
- Verify soft delete marker in Firestore
- Check real-time listener is active
- Check device connectivity
- Verify real-time listener is enabled
- Check console for listener errors
- Verify Firebase connection
- Try manual sync
- Offline sync queue
- Exponential backoff for retries
- Batch operations for efficiency
- Sync conflict UI for manual resolution
- Sync history/audit log
- Compression for large datasets
- Selective sync (by record type)
- Unique identifier stored in
localStorage - Format:
device_${timestamp}_${random} - Used to prevent sync loops
- Stored in Firestore:
/users/{userId}/meta/sync - Contains:
lastSyncTime,deviceId,serverTimestamp - Used for incremental syncs
createdAt: When record was created locallyupdatedAt: When record was last modified locallycloudUpdatedAt: When record was last uploaded to cloud- All timestamps are Unix milliseconds
- Client-side encryption before upload
- Zero-knowledge architecture maintained
- Firebase only sees encrypted blobs
- Decryption happens on download