diff --git a/App/app/features/db-sync/DBSyncManager.tsx b/App/app/features/db-sync/DBSyncManager.tsx index 397f2034..bcef68db 100644 --- a/App/app/features/db-sync/DBSyncManager.tsx +++ b/App/app/features/db-sync/DBSyncManager.tsx @@ -15,6 +15,20 @@ import useLogger from '@app/hooks/useLogger'; import { DBSyncServerEditableData } from './slice'; +const SYNC_FILTER_DDOC_NAME = 'app_sync_v0'; +const SYNC_ONLY_PRIMARY_FILTER_NAME = 'only_primary'; +const SYNC_ONLY_IMAGES_FILTER_NAME = 'only_images'; + +const SYNC_FILTER_DDOC = { + _id: `_design/${SYNC_FILTER_DDOC_NAME}`, + filters: { + [SYNC_ONLY_PRIMARY_FILTER_NAME]: + "function (doc) { return !doc._id.startsWith('zz'); }", + [SYNC_ONLY_IMAGES_FILTER_NAME]: + "function (doc) { return doc._id.startsWith('zz20-image'); }", + }, +}; + const BATCH_SIZE = 16; const BATCHES_LIMIT = 4; @@ -137,6 +151,24 @@ export default function DBSyncManager() { return null; } } + + let createSyncFilterDDocRetries = 0; + while (true) { + try { + await remoteDB.get(`_design/${SYNC_FILTER_DDOC_NAME}`); + break; + } catch (e) { + if (createSyncFilterDDocRetries >= 5) throw e; + try { + await remoteDB.put(SYNC_FILTER_DDOC); + } catch (ee) { + if (createSyncFilterDDocRetries >= 5) throw ee; + await new Promise(resolve => setTimeout(resolve, 1000)); + } + createSyncFilterDDocRetries += 1; + } + } + return remoteDB; } catch (e) { fLogger.error( @@ -225,9 +257,11 @@ export default function DBSyncManager() { params: PouchDB.Replication.SyncOptions, server: ServerData, { + filter, onChange, onComplete, }: { + filter?: string; onChange?: (arg: { localDBUpdateSeq?: number | undefined; remoteDBUpdateSeq?: number | undefined; @@ -247,6 +281,7 @@ export default function DBSyncManager() { const syncHandler = localDB.sync(remoteDB, { ...params, retry: true, + filter, // The patched PouchDB will accept a logger for logging during the sync ...({ logger: fLogger } as any), }); @@ -256,21 +291,27 @@ export default function DBSyncManager() { const lastSeq = getSeqValue(info.change.last_seq); let localDBUpdateSeq; let remoteDBUpdateSeq; + let localDBDocCount; + let remoteDBDocCount; const logDetails1: any = { info, lastSeq }; const logDetails2: any = {}; try { const localDBInfo = await localDB.info(); localDBUpdateSeq = getSeqValue(localDBInfo.update_seq); + localDBDocCount = localDBInfo.doc_count; logDetails2.localDBInfo = localDBInfo; logDetails1.localDBUpdateSeq = localDBUpdateSeq; + logDetails1.localDBDocCount = localDBDocCount; } catch (e) { logDetails1.localDBInfoError = e; } try { const remoteDBInfo = await remoteDB.info(); remoteDBUpdateSeq = getSeqValue(remoteDBInfo.update_seq); + remoteDBDocCount = remoteDBInfo.doc_count; logDetails2.remoteDBInfo = remoteDBInfo; logDetails1.remoteDBUpdateSeq = remoteDBUpdateSeq; + logDetails1.remoteDBDocCount = remoteDBDocCount; } catch (e) { logDetails1.remoteDBInfoError = e; } @@ -279,6 +320,8 @@ export default function DBSyncManager() { // Only update the local or remote seq based on the current operation ...(direction === 'push' ? { localDBUpdateSeq } : {}), ...(direction === 'pull' ? { remoteDBUpdateSeq } : {}), + localDBDocCount, + remoteDBDocCount, [direction === 'push' ? 'pushLastSeq' : 'pullLastSeq']: lastSeq, }; updateSyncProgress([server.id, payload]); @@ -418,6 +461,7 @@ export default function DBSyncManager() { } if (shouldCancel) return; dispatch(actions.dbSync.updateServerStatus([server.id, 'Syncing'])); + startupSyncHandler = _startSync( localDB, remoteDB, @@ -428,6 +472,7 @@ export default function DBSyncManager() { }, server, { + filter: `${SYNC_FILTER_DDOC_NAME}/${SYNC_ONLY_PRIMARY_FILTER_NAME}`, onChange: assignSeqs, onComplete: payload => { assignSeqs(payload); @@ -436,6 +481,60 @@ export default function DBSyncManager() { }, }, ); + await new Promise(resolve => { + startupSyncHandler?.on('complete', resolve); + }); + logger.info('Start-up sync: primary data synced', { + function: server.id, + }); + if (shouldCancel) return; + + startupSyncHandler = _startSync( + localDB, + remoteDB, + { + live: false, + batch_size: 1, + batches_limit: 2, + }, + server, + { + filter: `${SYNC_FILTER_DDOC_NAME}/${SYNC_ONLY_IMAGES_FILTER_NAME}`, + onChange: assignSeqs, + onComplete: payload => { + assignSeqs(payload); + initialPullLastSeq = payload.pushLastSeq; + initialPullLastSeq = payload.pullLastSeq; + }, + }, + ); + await new Promise(resolve => { + startupSyncHandler?.on('complete', resolve); + }); + logger.info('Start-up sync: images synced', { + function: server.id, + }); + if (shouldCancel) return; + + startupSyncHandler = _startSync( + localDB, + remoteDB, + { + live: false, + batch_size: BATCH_SIZE, + batches_limit: BATCHES_LIMIT, + }, + server, + { + onChange: assignSeqs, + onComplete: payload => { + assignSeqs(payload); + initialPullLastSeq = payload.pushLastSeq; + initialPullLastSeq = payload.pullLastSeq; + }, + }, + ); + startupSyncHandler.on('complete', info => { if (shouldCancel) return; if ( @@ -465,8 +564,8 @@ export default function DBSyncManager() { remoteDB, { live: true, - batch_size: BATCH_SIZE, - batches_limit: BATCHES_LIMIT, + batch_size: 2, + batches_limit: 2, }, server, { diff --git a/App/app/features/db-sync/screens/DBSyncServerDetailScreen.tsx b/App/app/features/db-sync/screens/DBSyncServerDetailScreen.tsx index 16705ea6..5eb09b3d 100644 --- a/App/app/features/db-sync/screens/DBSyncServerDetailScreen.tsx +++ b/App/app/features/db-sync/screens/DBSyncServerDetailScreen.tsx @@ -48,6 +48,8 @@ function DBSyncServerDetailScreen({ }); }, []); + const [showAdvancedStatus, setShowAdvancedStatus] = useState(false); + return ( - + - + {!showAdvancedStatus ? ( + setShowAdvancedStatus(true)} + button + /> + ) : ( + <> + + + + + )} , @@ -311,6 +315,8 @@ reducer.dehydrateSensitive = (state: DBSyncState) => { remoteDBUpdateSeq: server.remoteDBUpdateSeq, pushLastSeq: server.pushLastSeq, pullLastSeq: server.pullLastSeq, + localDBDocCount: server.localDBDocCount, + remoteDBDocCount: server.remoteDBDocCount, }), ), }; diff --git a/App/app/screens/OnboardingScreen.tsx b/App/app/screens/OnboardingScreen.tsx index d353692c..02bfb8a1 100644 --- a/App/app/screens/OnboardingScreen.tsx +++ b/App/app/screens/OnboardingScreen.tsx @@ -54,7 +54,6 @@ import useScrollViewAutomaticallyAdjustKeyboardInsetsFix from '@app/hooks/useScr import Button from '@app/components/Button'; import Configuration from '@app/components/Configuration'; import FullWidthImage from '@app/components/FullWidthImage'; -import Icon from '@app/components/Icon'; import Text, { Link } from '@app/components/Text'; import UIGroup from '@app/components/UIGroup'; @@ -1445,33 +1444,6 @@ function OnboardingScreen({ return 'ERROR'; }, [syncServerStatus.lastSyncedAt, syncServerStatus.status]); - const [initialPullLastSeq, setInitialPullLastSeq] = useState(-1); - const initialPullLastSeqRef = useRef(initialPullLastSeq); - initialPullLastSeqRef.current = initialPullLastSeq; - useEffect(() => { - if (initialPullLastSeqRef.current >= 0) { - return; - } - - if (typeof syncServerStatus.pullLastSeq === 'number') { - setInitialPullLastSeq(syncServerStatus.pullLastSeq); - } - }, [syncServerStatus.pullLastSeq]); - const initialSyncDoneProgress = - initialPullLastSeq >= 0 - ? (syncServerStatus.pullLastSeq || 0) - initialPullLastSeq - : syncServerStatus.pullLastSeq || 0; - const initialSyncRemainingProgress = - initialPullLastSeq >= 0 - ? (syncServerStatus.remoteDBUpdateSeq || 0) - initialPullLastSeq - : syncServerStatus.remoteDBUpdateSeq || 0; - const prevInitialSyncDoneProgress = useRef(0); - useEffect(() => { - if (initialSyncDoneProgress !== prevInitialSyncDoneProgress.current) { - LayoutAnimation.configureNext(DEFAULT_LAYOUT_ANIMATION_CONFIG); - prevInitialSyncDoneProgress.current = initialSyncDoneProgress; - } - }, [initialSyncDoneProgress]); const restoreFromCouchDBScrollViewRef = useRef(null); const { kiaTextInputProps: restoreFromCouchDBKiaTextInputProps } = @@ -1605,15 +1577,12 @@ function OnboardingScreen({ {(() => { if (dbSyncHasBeenSetupStatus === 'WORKING') { - // TODO: Show progress bar return ( <> 0 - ? initialSyncDoneProgress / - initialSyncRemainingProgress - : 0 + (syncServerStatus.localDBDocCount || 0) / + (syncServerStatus.remoteDBDocCount || 1) } style={[ commonStyles.alignSelfStretch, @@ -1628,8 +1597,9 @@ function OnboardingScreen({ commonStyles.mt2, ]} > - {initialSyncDoneProgress}/{initialSyncRemainingProgress}{' '} - documents synced + {syncServerStatus.localDBDocCount || '?'}/ + {syncServerStatus.remoteDBDocCount || '?'} documents + synced {!isSetupNotDone && ( @@ -1690,13 +1660,13 @@ function OnboardingScreen({ }, [ backgroundColor, + scrollViewContentContainerPaddingTop, dbSyncHasBeenSetupStatus, - initialSyncDoneProgress, - initialSyncRemainingProgress, + syncServerStatus.lastErrorMessage, + syncServerStatus.localDBDocCount, + syncServerStatus.remoteDBDocCount, isSetupNotDone, navigation, - scrollViewContentContainerPaddingTop, - syncServerStatus.lastErrorMessage, ], ); const dbSyncWorkingMarkCurrentProfileAsSetupDoneEffectRef = useRef(false); @@ -1719,7 +1689,7 @@ function OnboardingScreen({ })(); } else if ( dbSyncHasBeenSetupStatus === 'WORKING' && - initialSyncDoneProgress > 100 + (syncServerStatus.localDBDocCount || 0) > 100 ) { if (dbSyncWorkingMarkCurrentProfileAsSetupDoneEffectRef.current) return; const getConfig = getGetConfig({ db }); @@ -1744,8 +1714,8 @@ function OnboardingScreen({ dbSyncHasBeenSetupStatus, dispatch, navigation, - initialSyncDoneProgress, logger, + syncServerStatus.localDBDocCount, ]); // Load form values from config