diff --git a/packages/app-mobile/root.tsx b/packages/app-mobile/root.tsx index 194fd1b2eb..85e689e9c4 100644 --- a/packages/app-mobile/root.tsx +++ b/packages/app-mobile/root.tsx @@ -144,7 +144,7 @@ const generalMiddleware = (store: any) => (next: any) => async (action: any) => if (action.type === 'NAV_GO') Keyboard.dismiss(); if (['NOTE_UPDATE_ONE', 'NOTE_DELETE', 'FOLDER_UPDATE_ONE', 'FOLDER_DELETE'].indexOf(action.type) >= 0) { - if (!await reg.syncTarget().syncStarted()) void reg.scheduleSync(1000, { syncSteps: ['update_remote', 'delete_remote'] }, true); + if (!await reg.syncTarget().syncStarted()) void reg.scheduleSync(reg.syncAsYouTypeInterval(), { syncSteps: ['update_remote', 'delete_remote'] }, true); SearchEngine.instance().scheduleSyncTables(); } diff --git a/packages/lib/BaseApplication.ts b/packages/lib/BaseApplication.ts index bde7bf5830..694e2fe396 100644 --- a/packages/lib/BaseApplication.ts +++ b/packages/lib/BaseApplication.ts @@ -451,7 +451,7 @@ export default class BaseApplication { const newState = store.getState() as State; if (this.hasGui() && ['NOTE_UPDATE_ONE', 'NOTE_DELETE', 'FOLDER_UPDATE_ONE', 'FOLDER_DELETE'].indexOf(action.type) >= 0) { - if (!(await reg.syncTarget().syncStarted())) void reg.scheduleSync(15 * 1000, { syncSteps: ['update_remote', 'delete_remote'] }); + if (!(await reg.syncTarget().syncStarted())) void reg.scheduleSync(reg.syncAsYouTypeInterval(), { syncSteps: ['update_remote', 'delete_remote'] }); SearchEngine.instance().scheduleSyncTables(); } diff --git a/packages/lib/Synchronizer.ts b/packages/lib/Synchronizer.ts index d5f27f5f90..7bd017e7bf 100644 --- a/packages/lib/Synchronizer.ts +++ b/packages/lib/Synchronizer.ts @@ -32,6 +32,7 @@ import syncDeleteStep from './services/synchronizer/utils/syncDeleteStep'; import { ErrorCode } from './errors'; import { SyncAction } from './services/synchronizer/utils/types'; import checkDisabledSyncItemsNotification from './services/synchronizer/utils/checkDisabledSyncItemsNotification'; +import { reg } from './registry'; import SyncTargetRegistry from './SyncTargetRegistry'; const { sprintf } = require('sprintf-js'); const { Dirnames } = require('./services/synchronizer/utils/types'); @@ -388,7 +389,7 @@ export default class Synchronizer { // 2. DELETE_REMOTE: Delete on the sync target, the items that have been deleted locally. // 3. DELTA: Find on the sync target the items that have been modified or deleted and apply the changes locally. // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - public async start(options: any = null): Promise { + public async start(options: any = null) { if (!options) options = {}; if (this.state() !== 'idle') { @@ -629,7 +630,7 @@ export default class Synchronizer { } else if (syncItem.force_sync) { throw new JoplinError(sprintf('Processing a path that has already been done: %s. Item was marked for sync using force_sync', path), 'processingPathTwice'); } else { - logger.info(sprintf('Processing a path that has already been done: %s. The user is making changes while the sync is in progress', path)); + throw new JoplinError(sprintf('Processing a path that has already been done: %s. The user is making changes while the sync is in progress', path), 'changedDuringSync'); } } @@ -1161,6 +1162,10 @@ export default class Synchronizer { } else if (error.code === 'unknownItemType') { this.progressReport_.errors.push(_('Unknown item type downloaded - please upgrade Joplin to the latest version')); logger.error(error); + } else if (error.code === 'changedDuringSync') { + // We want to re-trigger the sync in this scenario + hasCaughtError = false; + logger.info(error.message); } else { logger.error(error); if (error.details) logger.error('Details:', error.details); @@ -1222,11 +1227,10 @@ export default class Synchronizer { // IMPORTANT: This must be the very last step in the sync, to avoid any window to allow an un-synced change to get missed if (!hasErrors && !hasCaughtError && !cancelledBeforeClearedState && !this.cancelling()) { const result = await BaseItem.itemsThatNeedSync(syncTargetId); - options.context = outputContext; if (result.items.length > 0) { - logger.info('There are more outgoing changes to sync, trigger the sync again'); - return await this.start(options); + logger.info('There are more outgoing changes to sync, schedule the sync again'); + void reg.scheduleSync(reg.syncAsYouTypeInterval(), { syncSteps }, true); } } diff --git a/packages/lib/registry.ts b/packages/lib/registry.ts index 12b09ccf20..2506950b46 100644 --- a/packages/lib/registry.ts +++ b/packages/lib/registry.ts @@ -70,6 +70,16 @@ class Registry { return this.syncTarget(SyncTargetRegistry.nameToId('nextcloud')); } + // There is a delay of at least 1 second to avoid using excessive data usage, as the full note contents are uploaded every time a change is made. + // On desktop this delay is longer, to avoid distraction to the user due to the sync button continually stopping and starting spinning + public syncAsYouTypeInterval() { + if (shim.isElectron()) { + return 15 * 1000; + } else { + return 1000; + } + } + public syncTarget = (syncTargetId: number = null) => { if (syncTargetId === null) syncTargetId = Setting.value('sync.target'); if (this.syncTargets_[syncTargetId]) return this.syncTargets_[syncTargetId];