mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-18 17:05:48 +00:00
fix(sync): scope interval timer to file-based providers and drop unneeded migration
Guard the periodic sync interval timer behind a useIntervalTimer flag so it only fires for file-based providers (WebDAV, LocalFile, Nextcloud). SuperSync uses WebSocket push notifications and does not need polling. Remove the syncFolderPath backwards-compatibility migration: master already routes localFileSync settings to the credential store via PROP_MAP_TO_FORM, so no real users have the path in global config.
This commit is contained in:
parent
08fdea1772
commit
d6880d47da
4 changed files with 15 additions and 75 deletions
|
|
@ -3,7 +3,7 @@ import { SyncProviderManager } from '../../op-log/sync-providers/provider-manage
|
|||
import { GlobalConfigService } from '../../features/config/global-config.service';
|
||||
import { combineLatest, from, Observable, of } from 'rxjs';
|
||||
import { SyncConfig } from '../../features/config/global-config.model';
|
||||
import { first, switchMap, tap } from 'rxjs/operators';
|
||||
import { switchMap, tap } from 'rxjs/operators';
|
||||
import {
|
||||
CurrentProviderPrivateCfg,
|
||||
PrivateCfgByProviderId,
|
||||
|
|
@ -14,7 +14,6 @@ import { SyncLog } from '../../core/log';
|
|||
import { clearSessionKeyCache } from '../../op-log/encryption/encryption';
|
||||
import { SuperSyncPrivateCfg } from '../../op-log/sync-providers/super-sync/super-sync.model';
|
||||
import { SyncWrapperService } from './sync-wrapper.service';
|
||||
import { LocalFileSyncPrivateCfg } from '../../op-log/core/types/sync.types';
|
||||
|
||||
// Maps sync providers to their corresponding form field in SyncConfig
|
||||
// Dropbox is null because it doesn't store settings in the form (uses OAuth)
|
||||
|
|
@ -103,19 +102,6 @@ export class SyncConfigService {
|
|||
|
||||
private _lastSettings: SyncConfig | null = null;
|
||||
|
||||
constructor() {
|
||||
// One-time startup migration: move syncFolderPath from legacy global config to credential
|
||||
// store. Must run at startup (not only when sync settings UI is open) so that the app
|
||||
// correctly finds the configured folder after a restart. Fixes: #5455 (Mac restart).
|
||||
this._globalConfigService.sync$
|
||||
.pipe(first((cfg) => !!cfg?.localFileSync?.syncFolderPath))
|
||||
.subscribe((cfg) => {
|
||||
this._migrateLegacyLocalFileSyncPath(cfg.localFileSync!.syncFolderPath!).catch(
|
||||
(e) => SyncLog.err('SyncConfigService: LocalFile path migration failed', e),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private _deriveEncryptionState(
|
||||
baseConfig: SyncConfig,
|
||||
currentProviderCfg: CurrentProviderPrivateCfg | null,
|
||||
|
|
@ -423,47 +409,4 @@ export class SyncConfigService {
|
|||
clearSessionKeyCache();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates syncFolderPath from the legacy global config storage to SyncCredentialStore.
|
||||
*
|
||||
* Older versions stored the LocalFile sync folder path in globalConfig.sync.localFileSync.
|
||||
* The current architecture stores it in SyncCredentialStore (IndexedDB 'sup-sync').
|
||||
* This one-time migration preserves the user's configured folder across upgrades.
|
||||
* Fixes: Mac forgets sync folder on restart (#5455).
|
||||
*/
|
||||
private async _migrateLegacyLocalFileSyncPath(legacyPath: string): Promise<void> {
|
||||
SyncLog.normal(
|
||||
'SyncConfigService: Checking for legacy LocalFile syncFolderPath migration',
|
||||
);
|
||||
const provider = await this._providerManager.getProviderById(
|
||||
SyncProviderId.LocalFile,
|
||||
);
|
||||
if (!provider) {
|
||||
// Provider not registered (e.g. web build). Clear the stale field anyway so
|
||||
// it never triggers migration again and doesn't silently persist.
|
||||
SyncLog.warn(
|
||||
'SyncConfigService: LocalFile provider not found during migration; clearing legacy field',
|
||||
);
|
||||
} else {
|
||||
const existingCfg =
|
||||
(await provider.privateCfg.load()) as LocalFileSyncPrivateCfg | null;
|
||||
if (!existingCfg?.syncFolderPath) {
|
||||
SyncLog.normal(
|
||||
'SyncConfigService: Migrating LocalFile syncFolderPath from global config to credential store',
|
||||
);
|
||||
await provider.privateCfg.upsertPartial({
|
||||
syncFolderPath: legacyPath,
|
||||
} as LocalFileSyncPrivateCfg);
|
||||
}
|
||||
}
|
||||
// Always clear the legacy path from global config so this migration never re-runs.
|
||||
// Spread DEFAULT to avoid shallow-merge data loss on any other localFileSync fields.
|
||||
this._globalConfigService.updateSection('sync', {
|
||||
localFileSync: {
|
||||
...DEFAULT_GLOBAL_CONFIG.sync.localFileSync,
|
||||
syncFolderPath: null,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,7 +206,10 @@ export class SyncTriggerService {
|
|||
shareReplay(1),
|
||||
);
|
||||
|
||||
getSyncTrigger$(syncInterval: number = SYNC_DEFAULT_AUDIT_TIME): Observable<unknown> {
|
||||
getSyncTrigger$(
|
||||
syncInterval: number = SYNC_DEFAULT_AUDIT_TIME,
|
||||
useIntervalTimer = false,
|
||||
): Observable<unknown> {
|
||||
const _immediateSyncTrigger$: Observable<string> = IS_ANDROID_WEB_VIEW
|
||||
? // ANDROID ONLY
|
||||
merge(
|
||||
|
|
@ -233,9 +236,11 @@ export class SyncTriggerService {
|
|||
this._onElectronResumeTrigger$,
|
||||
// Periodic interval timer: fires every syncInterval ms to detect external file
|
||||
// changes (e.g. Syncthing on Linux) even without user activity.
|
||||
// Applies to Electron desktop and web/PWA (not Android, which has its own timer).
|
||||
// Only for file-based providers — SuperSync uses WebSocket push and doesn't need polling.
|
||||
// Fixes: Linux auto-sync ignoring interval (#4783)
|
||||
timer(syncInterval, syncInterval).pipe(mapTo('I_INTERVAL_TIMER')),
|
||||
...(useIntervalTimer
|
||||
? [timer(syncInterval, syncInterval).pipe(mapTo('I_INTERVAL_TIMER'))]
|
||||
: []),
|
||||
);
|
||||
return merge(
|
||||
// once immediately
|
||||
|
|
|
|||
|
|
@ -135,10 +135,14 @@ export class SyncEffects {
|
|||
combineLatest([
|
||||
this._syncWrapperService.isEnabledAndReady$,
|
||||
this._syncWrapperService.syncInterval$,
|
||||
this._syncWrapperService.syncProviderId$,
|
||||
]).pipe(
|
||||
switchMap(([isEnabledAndReady, syncInterval]) =>
|
||||
switchMap(([isEnabledAndReady, syncInterval, providerId]) =>
|
||||
isEnabledAndReady && syncInterval
|
||||
? this._syncTriggerService.getSyncTrigger$(syncInterval)
|
||||
? this._syncTriggerService.getSyncTrigger$(
|
||||
syncInterval,
|
||||
providerId !== SyncProviderId.SuperSync,
|
||||
)
|
||||
: EMPTY,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
12
src/main.ts
12
src/main.ts
|
|
@ -81,7 +81,6 @@ import { Log } from './app/core/log';
|
|||
import { OperationWriteFlushService } from './app/op-log/sync/operation-write-flush.service';
|
||||
import { PluginOAuthRedirectHandler } from './app/plugins/oauth/plugin-oauth-redirect.handler';
|
||||
import { OAuthCallbackHandlerService } from './app/imex/sync/oauth-callback-handler.service';
|
||||
import { SyncConfigService } from './app/imex/sync/sync-config.service';
|
||||
import { GlobalConfigService } from './app/features/config/global-config.service';
|
||||
import { LocaleDatePipe } from './app/ui/pipes/locale-date.pipe';
|
||||
import { DateTimeFormatService } from './app/core/date-time-format/date-time-format.service';
|
||||
|
|
@ -281,17 +280,6 @@ bootstrapApplication(AppComponent, {
|
|||
deps: [OAuthCallbackHandlerService],
|
||||
multi: true,
|
||||
},
|
||||
// Ensure SyncConfigService is instantiated at bootstrap so the startup migration
|
||||
// (syncFolderPath from global config → credential store) runs on every app launch,
|
||||
// not only when the sync settings UI is opened. Fixes: #5455 (Mac restart).
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: (_svc: SyncConfigService) => {
|
||||
return () => {};
|
||||
},
|
||||
deps: [SyncConfigService],
|
||||
multi: true,
|
||||
},
|
||||
// Note: ImmediateUploadService now initializes itself in constructor
|
||||
// after DataInitStateService.isAllDataLoadedInitially$ fires to avoid
|
||||
// race condition where upload attempts happen before sync config is loaded
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue