diff --git a/.github/workflows/pr-preview-deploy.yml b/.github/workflows/pr-preview-deploy.yml index 6858a7cb5..ba8a74b76 100644 --- a/.github/workflows/pr-preview-deploy.yml +++ b/.github/workflows/pr-preview-deploy.yml @@ -33,7 +33,7 @@ jobs: ssh://git@github.com/ - name: Install npm Packages - run: npm ci + run: npm i - name: Generate environment file run: npm run env diff --git a/src/app/op-log/sync/immediate-upload.service.ts b/src/app/op-log/sync/immediate-upload.service.ts index ee5309215..92ae4ba09 100644 --- a/src/app/op-log/sync/immediate-upload.service.ts +++ b/src/app/op-log/sync/immediate-upload.service.ts @@ -1,11 +1,12 @@ import { inject, Injectable, OnDestroy } from '@angular/core'; import { Subject, Subscription } from 'rxjs'; -import { debounceTime, exhaustMap, filter } from 'rxjs/operators'; +import { debounceTime, exhaustMap, filter, take } from 'rxjs/operators'; import { isOnline } from '../../util/is-online'; import { SyncProviderManager } from '../sync-providers/provider-manager.service'; import { OperationLogSyncService } from './operation-log-sync.service'; import { isFileBasedProvider, isOperationSyncCapable } from './operation-sync.util'; import { OpLog } from '../../core/log'; +import { DataInitStateService } from '../../core/data-init/data-init-state.service'; const IMMEDIATE_UPLOAD_DEBOUNCE_MS = 2000; @@ -46,11 +47,23 @@ const IMMEDIATE_UPLOAD_DEBOUNCE_MS = 2000; export class ImmediateUploadService implements OnDestroy { private _providerManager = inject(SyncProviderManager); private _syncService = inject(OperationLogSyncService); + private _dataInitStateService = inject(DataInitStateService); private _uploadTrigger$ = new Subject(); private _subscription: Subscription | null = null; private _isInitialized = false; + constructor() { + // Initialize only after data is loaded to avoid race condition where + // upload attempts happen before sync config is loaded from IndexedDB. + // This prevents 404 errors to default baseUrl during app startup. + this._dataInitStateService.isAllDataLoadedInitially$ + .pipe(filter(Boolean), take(1)) + .subscribe(() => { + this.initialize(); + }); + } + /** * Initializes the immediate upload pipeline. * Call once after app initialization. diff --git a/src/main.ts b/src/main.ts index b4b67fec0..2aa57addd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -41,7 +41,6 @@ import { StoreModule } from '@ngrx/store'; import { META_REDUCERS } from './app/root-store/meta/meta-reducer-registry'; import { setOperationCaptureService } from './app/root-store/meta/task-shared-meta-reducers'; import { OperationCaptureService } from './app/op-log/capture/operation-capture.service'; -import { ImmediateUploadService } from './app/op-log/sync/immediate-upload.service'; import { EffectsModule } from '@ngrx/effects'; import { StoreDevtoolsModule } from '@ngrx/store-devtools'; import { ReactiveFormsModule } from '@angular/forms'; @@ -182,17 +181,9 @@ bootstrapApplication(AppComponent, { deps: [OperationCaptureService], multi: true, }, - // Initialize immediate upload service for real-time sync to SuperSync - { - provide: APP_INITIALIZER, - useFactory: (immediateUploadService: ImmediateUploadService) => { - return () => { - immediateUploadService.initialize(); - }; - }, - deps: [ImmediateUploadService], - 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 ], }).then(() => { // Initialize touch fix for Material menus