diff --git a/.yarn/cache/@standardnotes-domain-events-npm-2.122.0-e9214530a6-9dd2f02f1c.zip b/.yarn/cache/@standardnotes-domain-events-npm-2.122.0-e9214530a6-9dd2f02f1c.zip deleted file mode 100644 index afb796a96..000000000 Binary files a/.yarn/cache/@standardnotes-domain-events-npm-2.122.0-e9214530a6-9dd2f02f1c.zip and /dev/null differ diff --git a/.yarn/cache/@standardnotes-predicates-npm-1.6.9-7b42d49f9d-3b931aaccd.zip b/.yarn/cache/@standardnotes-predicates-npm-1.6.9-7b42d49f9d-3b931aaccd.zip deleted file mode 100644 index 8da3424d5..000000000 Binary files a/.yarn/cache/@standardnotes-predicates-npm-1.6.9-7b42d49f9d-3b931aaccd.zip and /dev/null differ diff --git a/.yarn/cache/@standardnotes-security-npm-1.12.0-b20ed17598-96d42255e7.zip b/.yarn/cache/@standardnotes-security-npm-1.12.0-b20ed17598-96d42255e7.zip deleted file mode 100644 index 2c8912e3d..000000000 Binary files a/.yarn/cache/@standardnotes-security-npm-1.12.0-b20ed17598-96d42255e7.zip and /dev/null differ diff --git a/packages/services/src/Domain/Api/WebSocketsServiceEvent.ts b/packages/services/src/Domain/Api/WebSocketsServiceEvent.ts index e8c6ce61e..e9cdf367a 100644 --- a/packages/services/src/Domain/Api/WebSocketsServiceEvent.ts +++ b/packages/services/src/Domain/Api/WebSocketsServiceEvent.ts @@ -3,4 +3,5 @@ export enum WebSocketsServiceEvent { NotificationAddedForUser = 'NotificationAddedForUser', MessageSentToUser = 'MessageSentToUser', UserInvitedToSharedVault = 'UserInvitedToSharedVault', + ItemsChangedOnServer = 'ItemsChangedOnServer', } diff --git a/packages/services/src/Domain/Api/WebsocketsService.ts b/packages/services/src/Domain/Api/WebsocketsService.ts index e47ce937b..301da5f4a 100644 --- a/packages/services/src/Domain/Api/WebsocketsService.ts +++ b/packages/services/src/Domain/Api/WebsocketsService.ts @@ -55,6 +55,10 @@ export class WebSocketsService extends AbstractService { isDatabaseLoaded(): boolean onNewDatabaseCreated(): Promise loadDatabasePayloads(): Promise - + beginAutoSyncTimer(): void resetSyncState(): void markAllItemsAsNeedingSyncAndPersist(): Promise downloadFirstSync(waitTimeOnFailureMs: number, otherSyncOptions?: Partial): Promise diff --git a/packages/snjs/lib/Application/Application.ts b/packages/snjs/lib/Application/Application.ts index c69d5343b..019241bfa 100644 --- a/packages/snjs/lib/Application/Application.ts +++ b/packages/snjs/lib/Application/Application.ts @@ -137,9 +137,6 @@ import { TYPES } from './Dependencies/Types' import { RegisterApplicationServicesEvents } from './Dependencies/DependencyEvents' import { Result } from '@standardnotes/domain-core' -/** How often to automatically sync, in milliseconds */ -const DEFAULT_AUTO_SYNC_INTERVAL = 30_000 - type LaunchCallback = { receiveChallenge: (challenge: Challenge) => void } @@ -165,7 +162,6 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli private serviceObservers: ObserverRemover[] = [] private managedSubscribers: ObserverRemover[] = [] - private autoSyncInterval!: ReturnType /** True if the result of deviceInterface.openDatabase yields a new database being created */ private createdNewDatabase = false @@ -463,7 +459,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli throw 'Application has been destroyed.' } await this.handleStage(ApplicationStage.LoadedDatabase_12) - this.beginAutoSyncTimer() + this.sync.beginAutoSyncTimer() await this.sync.sync({ mode: SyncMode.DownloadFirst, source: SyncSource.External, @@ -503,14 +499,6 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli } } - private beginAutoSyncTimer() { - this.autoSyncInterval = setInterval(() => { - const logger = this.dependencies.get(TYPES.Logger) - logger.info('Syncing from autosync') - void this.sync.sync({ sourceDescription: 'Auto Sync' }) - }, DEFAULT_AUTO_SYNC_INTERVAL) - } - private async handleStage(stage: ApplicationStage) { await this.events.publishSync( { @@ -743,9 +731,6 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli public deinit(mode: DeinitMode, source: DeinitSource): void { this.dealloced = true - clearInterval(this.autoSyncInterval) - ;(this.autoSyncInterval as unknown) = undefined - for (const uninstallObserver of this.serviceObservers) { uninstallObserver() } diff --git a/packages/snjs/lib/Application/Dependencies/Dependencies.ts b/packages/snjs/lib/Application/Dependencies/Dependencies.ts index 4a1536de2..a5e89f472 100644 --- a/packages/snjs/lib/Application/Dependencies/Dependencies.ts +++ b/packages/snjs/lib/Application/Dependencies/Dependencies.ts @@ -1331,6 +1331,15 @@ export class Dependencies { ) }) + this.factory.set(TYPES.WebSocketsService, () => { + return new WebSocketsService( + this.get(TYPES.DiskStorageService), + this.options.webSocketUrl, + this.get(TYPES.WebSocketApiService), + this.get(TYPES.InternalEventBus), + ) + }) + this.factory.set(TYPES.SyncService, () => { return new SyncService( this.get(TYPES.ItemManager), @@ -1347,6 +1356,7 @@ export class Dependencies { sleepBetweenBatches: this.options.sleepBetweenBatches, }, this.get(TYPES.Logger), + this.get(TYPES.WebSocketsService), this.get(TYPES.InternalEventBus), ) }) @@ -1390,15 +1400,6 @@ export class Dependencies { ) }) - this.factory.set(TYPES.WebSocketsService, () => { - return new WebSocketsService( - this.get(TYPES.DiskStorageService), - this.options.webSocketUrl, - this.get(TYPES.WebSocketApiService), - this.get(TYPES.InternalEventBus), - ) - }) - this.factory.set(TYPES.WebSocketApiService, () => { return new WebSocketApiService(this.get(TYPES.WebSocketServer)) }) diff --git a/packages/snjs/lib/Application/Dependencies/DependencyEvents.ts b/packages/snjs/lib/Application/Dependencies/DependencyEvents.ts index 5a3896bc9..49e3e3a07 100644 --- a/packages/snjs/lib/Application/Dependencies/DependencyEvents.ts +++ b/packages/snjs/lib/Application/Dependencies/DependencyEvents.ts @@ -40,6 +40,7 @@ export function RegisterApplicationServicesEvents(container: Dependencies, event events.addEventHandler(container.get(TYPES.SubscriptionManager), ApplicationEvent.UserRolesChanged) events.addEventHandler(container.get(TYPES.SubscriptionManager), SessionEvent.Restored) events.addEventHandler(container.get(TYPES.SyncService), IntegrityEvent.IntegrityCheckCompleted) + events.addEventHandler(container.get(TYPES.SyncService), WebSocketsServiceEvent.ItemsChangedOnServer) events.addEventHandler(container.get(TYPES.UserService), AccountEvent.SignedInOrRegistered) events.addEventHandler(container.get(TYPES.VaultInviteService), ApplicationEvent.Launched) events.addEventHandler(container.get(TYPES.VaultInviteService), SyncEvent.ReceivedSharedVaultInvites) diff --git a/packages/snjs/lib/Services/Sync/SyncService.ts b/packages/snjs/lib/Services/Sync/SyncService.ts index fe896389f..b256d8a1f 100644 --- a/packages/snjs/lib/Services/Sync/SyncService.ts +++ b/packages/snjs/lib/Services/Sync/SyncService.ts @@ -84,6 +84,8 @@ import { SyncEventReceivedAsymmetricMessagesData, SyncOpStatus, ApplicationSyncOptions, + WebSocketsServiceEvent, + WebSocketsService, } from '@standardnotes/services' import { OfflineSyncResponse } from './Offline/Response' import { @@ -98,6 +100,7 @@ import { ContentType } from '@standardnotes/domain-core' const DEFAULT_MAJOR_CHANGE_THRESHOLD = 15 const INVALID_SESSION_RESPONSE_STATUS = 401 +const DEFAULT_AUTO_SYNC_INTERVAL = 30_000 /** Content types appearing first are always mapped first */ const ContentTypeLocalLoadPriorty = [ @@ -149,6 +152,9 @@ export class SyncService public lastSyncInvokationPromise?: Promise public currentSyncRequestPromise?: Promise + private autoSyncInterval?: NodeJS.Timer + private wasNotifiedOfItemsChangeOnServer = false + constructor( private itemManager: ItemManager, private sessionManager: SessionManager, @@ -161,6 +167,7 @@ export class SyncService private identifier: string, private readonly options: ApplicationSyncOptions, private logger: LoggerInterface, + private sockets: WebSocketsService, protected override internalEventBus: InternalEventBusInterface, ) { super(internalEventBus) @@ -187,6 +194,10 @@ export class SyncService public override deinit(): void { this.dealloced = true + if (this.autoSyncInterval) { + clearInterval(this.autoSyncInterval) + } + ;(this.autoSyncInterval as unknown) = undefined ;(this.sessionManager as unknown) = undefined ;(this.itemManager as unknown) = undefined ;(this.encryptionService as unknown) = undefined @@ -349,6 +360,28 @@ export class SyncService this.opStatus.setDatabaseLoadStatus(0, 0, true) } + beginAutoSyncTimer(): void { + this.autoSyncInterval = setInterval(this.autoSync.bind(this), DEFAULT_AUTO_SYNC_INTERVAL) + } + + private autoSync(): void { + if (!this.sockets.isWebSocketConnectionOpen()) { + this.logger.debug('WebSocket connection is closed, doing autosync') + + void this.sync({ sourceDescription: 'Auto Sync' }) + + return + } + + if (this.wasNotifiedOfItemsChangeOnServer) { + this.logger.debug('Was notified of items changed on server, doing autosync') + + this.wasNotifiedOfItemsChangeOnServer = false + + void this.sync({ sourceDescription: 'WebSockets Event - Items Changed On Server' }) + } + } + private async processPayloadBatch( batch: FullyFormedPayloadInterface[], currentPosition?: number, @@ -1400,8 +1433,15 @@ export class SyncService } async handleEvent(event: InternalEventInterface): Promise { - if (event.type === IntegrityEvent.IntegrityCheckCompleted) { - await this.handleIntegrityCheckEventResponse(event.payload as IntegrityEventPayload) + switch (event.type) { + case IntegrityEvent.IntegrityCheckCompleted: + await this.handleIntegrityCheckEventResponse(event.payload as IntegrityEventPayload) + break + case WebSocketsServiceEvent.ItemsChangedOnServer: + this.wasNotifiedOfItemsChangeOnServer = true + break + default: + break } } diff --git a/packages/snjs/package.json b/packages/snjs/package.json index 9ac0bc364..b997dfe7c 100644 --- a/packages/snjs/package.json +++ b/packages/snjs/package.json @@ -38,7 +38,7 @@ "@standardnotes/api": "workspace:*", "@standardnotes/common": "^1.50.0", "@standardnotes/domain-core": "^1.40.0", - "@standardnotes/domain-events": "^2.122.0", + "@standardnotes/domain-events": "^2.138.1", "@standardnotes/encryption": "workspace:*", "@standardnotes/features": "workspace:*", "@standardnotes/files": "workspace:*", diff --git a/yarn.lock b/yarn.lock index 7dd7a8b71..03b7e592d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6610,7 +6610,7 @@ __metadata: languageName: node linkType: hard -"@standardnotes/domain-events@npm:2.138.1": +"@standardnotes/domain-events@npm:2.138.1, @standardnotes/domain-events@npm:^2.138.1": version: 2.138.1 resolution: "@standardnotes/domain-events@npm:2.138.1" dependencies: @@ -6620,16 +6620,6 @@ __metadata: languageName: node linkType: hard -"@standardnotes/domain-events@npm:^2.122.0": - version: 2.122.0 - resolution: "@standardnotes/domain-events@npm:2.122.0" - dependencies: - "@standardnotes/predicates": 1.6.9 - "@standardnotes/security": 1.12.0 - checksum: 9dd2f02f1c9c91b3380d0db88dba3e504aaa14b7e58dcb519611dd8b15edb27c111805eb0e34f1612751fb59428296a216e39b5711892f6c8f1fc1f057e61c50 - languageName: node - linkType: hard - "@standardnotes/dynamic-theme@npm:^1.2.8": version: 1.2.8 resolution: "@standardnotes/dynamic-theme@npm:1.2.8" @@ -6992,13 +6982,6 @@ __metadata: languageName: unknown linkType: soft -"@standardnotes/predicates@npm:1.6.9": - version: 1.6.9 - resolution: "@standardnotes/predicates@npm:1.6.9" - checksum: 3b931aaccd763c98d043487e90624507265a350d5656ef55a3355b69fbdae2f718ecd2bde1e402d8fc49355404f91220df99b0a80c081e8da0d515eea1def9e7 - languageName: node - linkType: hard - "@standardnotes/predicates@npm:1.8.1": version: 1.8.1 resolution: "@standardnotes/predicates@npm:1.8.1" @@ -7081,16 +7064,6 @@ __metadata: languageName: node linkType: hard -"@standardnotes/security@npm:1.12.0": - version: 1.12.0 - resolution: "@standardnotes/security@npm:1.12.0" - dependencies: - jsonwebtoken: ^9.0.0 - reflect-metadata: ^0.1.13 - checksum: 96d42255e79fc2cf4c52f3d370c04ae00296673993e1c0cea2356ca4a6c934d22d6100ba95f9ad602ffb72ee686367ef4a263dc6a6c51795d9b61dc835e635cd - languageName: node - linkType: hard - "@standardnotes/security@npm:1.17.2, @standardnotes/security@npm:^1.17.2": version: 1.17.2 resolution: "@standardnotes/security@npm:1.17.2" @@ -7239,7 +7212,7 @@ __metadata: "@standardnotes/api": "workspace:*" "@standardnotes/common": ^1.50.0 "@standardnotes/domain-core": ^1.40.0 - "@standardnotes/domain-events": ^2.122.0 + "@standardnotes/domain-events": ^2.138.1 "@standardnotes/encryption": "workspace:*" "@standardnotes/features": "workspace:*" "@standardnotes/files": "workspace:*"