diff --git a/angular.json b/angular.json index 2ed16994b..907d1a9f5 100644 --- a/angular.json +++ b/angular.json @@ -72,6 +72,11 @@ "production": { "baseHref": "", "budgets": [ + { + "type": "initial", + "maximumWarning": "5.5mb", + "maximumError": "6mb" + }, { "type": "anyComponentStyle", "maximumWarning": "20kb" @@ -104,6 +109,11 @@ "browser": "browser" }, "budgets": [ + { + "type": "initial", + "maximumWarning": "5.5mb", + "maximumError": "6mb" + }, { "type": "anyComponentStyle", "maximumWarning": "20kb" @@ -133,6 +143,11 @@ "stage": { "baseHref": "", "budgets": [ + { + "type": "initial", + "maximumWarning": "5.5mb", + "maximumError": "6mb" + }, { "type": "anyComponentStyle", "maximumWarning": "20kb" diff --git a/src/app/app.component.ts b/src/app/app.component.ts index efcc623f4..77c37b91a 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -230,9 +230,14 @@ export class AppComponent implements OnDestroy, AfterViewInit { }); // ! For keyboard shortcuts to work correctly with any layouts (QWERTZ/AZERTY/etc) - user's keyboard layout must be presaved - // Connect the service to the utility functions and save the layout + // Connect the service to the utility functions setKeyboardLayoutService(this._keyboardLayoutService); - this._keyboardLayoutService.saveUserLayout(); + // Defer keyboard layout detection to idle time for better initial load performance + if (typeof requestIdleCallback === 'function') { + requestIdleCallback(() => this._keyboardLayoutService.saveUserLayout()); + } else { + setTimeout(() => this._keyboardLayoutService.saveUserLayout(), 0); + } } skipInitialSync(): void { diff --git a/src/app/core/startup/startup.service.ts b/src/app/core/startup/startup.service.ts index 305f99079..a3278d6c7 100644 --- a/src/app/core/startup/startup.service.ts +++ b/src/app/core/startup/startup.service.ts @@ -162,11 +162,13 @@ export class StartupService { private async _checkIsSingleInstance(): Promise { const channel = new BroadcastChannel('superProductivityTab'); let isAnotherInstanceActive = false; + let resolved = false; // 1. Listen for other instances saying "I'm here!" const checkListener = (msg: MessageEvent): void => { if (msg.data === 'alreadyOpenElsewhere') { isAnotherInstanceActive = true; + resolved = true; } }; channel.addEventListener('message', checkListener); @@ -174,8 +176,20 @@ export class StartupService { // 2. Ask "Is anyone here?" channel.postMessage('newTabOpened'); - // 3. Wait a bit for a response - await new Promise((resolve) => setTimeout(resolve, 150)); + // 3. Wait for response with early exit - reduced from 150ms to 50ms + // BroadcastChannel is synchronous within the same origin, so 50ms is sufficient + await new Promise((resolve) => { + const checkInterval = setInterval(() => { + if (resolved) { + clearInterval(checkInterval); + resolve(); + } + }, 10); + setTimeout(() => { + clearInterval(checkInterval); + resolve(); + }, 50); + }); channel.removeEventListener('message', checkListener); diff --git a/src/app/features/metric/metric.component.html b/src/app/features/metric/metric.component.html index ab563fd4d..2992e942a 100644 --- a/src/app/features/metric/metric.component.html +++ b/src/app/features/metric/metric.component.html @@ -68,7 +68,15 @@ }
- + @defer (on viewport) { + + } @placeholder { +
+ Loading activity heatmap... +
+ }
@if (!metricService.hasData()) { diff --git a/src/main.ts b/src/main.ts index 8a9392896..36f0e0b5c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -197,10 +197,28 @@ bootstrapApplication(AppComponent, { // Initialize touch fix for Material menus initializeMatMenuTouchFix(); - // Register all supported locales - Object.keys(LocalesImports).forEach((locale) => { - registerLocaleData(LocalesImports[locale], locale); - }); + // Register default locale immediately for fast startup + registerLocaleData(LocalesImports[DEFAULT_LANGUAGE], DEFAULT_LANGUAGE); + + // Defer other locales to idle time for better initial load performance + if (typeof requestIdleCallback === 'function') { + requestIdleCallback(() => { + Object.keys(LocalesImports).forEach((locale) => { + if (locale !== DEFAULT_LANGUAGE) { + registerLocaleData(LocalesImports[locale], locale); + } + }); + }); + } else { + // Fallback for browsers without requestIdleCallback + setTimeout(() => { + Object.keys(LocalesImports).forEach((locale) => { + if (locale !== DEFAULT_LANGUAGE) { + registerLocaleData(LocalesImports[locale], locale); + } + }); + }, 0); + } // TODO make asset caching work for electron