mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-18 00:46:45 +00:00
perf: reduce initial bundle size via lazy-loading
- Lazy-load locale data: only en-GB statically imported, all 26 others use dynamic import() loaded during idle time - Remove core-js/es/object polyfill (Object.fromEntries supported since Chrome 73+, Android 9 is EOL) - Defer right-panel content with @defer (prefetch on idle) blocks, moving markdown, schedule, and task-detail to lazy chunks - Lazy-load bottom panel via dynamic import() - Lazy-load Formly: remove FormlyConfigModule from bootstrap, each dialog component imports it directly; dialog openers use dynamic import() so Formly is only loaded when a dialog opens - Extract _openSyncInitialCfgDialog helper to DRY up duplicate import - Guard bottom sheet and shortcut dialog opens against race conditions after async import
This commit is contained in:
parent
14ed231a1e
commit
d989c064f3
21 changed files with 175 additions and 157 deletions
|
|
@ -10,7 +10,6 @@ import { ShepherdService } from '../../features/shepherd/shepherd.service';
|
|||
import { TourId } from '../../features/shepherd/shepherd-steps.const';
|
||||
import { T } from '../../t.const';
|
||||
import { LS } from '../../core/persistence/storage-keys.const';
|
||||
import { DialogCreateProjectComponent } from '../../features/project/dialogs/create-project/dialog-create-project.component';
|
||||
import { getGithubErrorUrl } from '../../core/error-handler/global-error-handler.util';
|
||||
import { DialogPromptComponent } from '../../ui/dialog-prompt/dialog-prompt.component';
|
||||
import {
|
||||
|
|
@ -498,7 +497,9 @@ export class MagicNavConfigService {
|
|||
}
|
||||
|
||||
// Simple action handlers
|
||||
private _openCreateProject(): void {
|
||||
private async _openCreateProject(): Promise<void> {
|
||||
const { DialogCreateProjectComponent } =
|
||||
await import('../../features/project/dialogs/create-project/dialog-create-project.component');
|
||||
this._matDialog
|
||||
.open(DialogCreateProjectComponent, { restoreFocus: true })
|
||||
.afterClosed()
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import { LayoutService } from '../layout/layout.service';
|
|||
import { TaskService } from '../../features/tasks/task.service';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { DialogAddNoteComponent } from '../../features/note/dialog-add-note/dialog-add-note.component';
|
||||
import { DialogCreateProjectComponent } from '../../features/project/dialogs/create-project/dialog-create-project.component';
|
||||
import { IPC } from '../../../../electron/shared-with-frontend/ipc-events.const';
|
||||
import { UiHelperService } from '../../features/ui-helper/ui-helper.service';
|
||||
import { WorkContextService } from '../../features/work-context/work-context.service';
|
||||
|
|
@ -156,15 +155,19 @@ export class ShortcutService {
|
|||
ev.preventDefault();
|
||||
} else if (checkKeyCombo(ev, keys.addNewProject)) {
|
||||
if (this._matDialog.openDialogs.length === 0) {
|
||||
this._matDialog
|
||||
.open(DialogCreateProjectComponent, { restoreFocus: true })
|
||||
.afterClosed()
|
||||
.subscribe((newProjectId: string | undefined) => {
|
||||
if (newProjectId) {
|
||||
this._router.navigate([`project/${newProjectId}/tasks`]);
|
||||
}
|
||||
});
|
||||
ev.preventDefault();
|
||||
const { DialogCreateProjectComponent } =
|
||||
await import('../../features/project/dialogs/create-project/dialog-create-project.component');
|
||||
if (this._matDialog.openDialogs.length === 0) {
|
||||
this._matDialog
|
||||
.open(DialogCreateProjectComponent, { restoreFocus: true })
|
||||
.afterClosed()
|
||||
.subscribe((newProjectId: string | undefined) => {
|
||||
if (newProjectId) {
|
||||
this._router.navigate([`project/${newProjectId}/tasks`]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (checkKeyCombo(ev, keys.addNewNote)) {
|
||||
if (this._matDialog.openDialogs.length === 0) {
|
||||
|
|
|
|||
|
|
@ -28,10 +28,7 @@ import { Store } from '@ngrx/store';
|
|||
import { TaskSharedActions } from '../../root-store/meta/task-shared.actions';
|
||||
import { TaskWithSubTasks } from '../../features/tasks/task.model';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import {
|
||||
DialogWorkContextSettingsComponent,
|
||||
WorkContextSettingsDialogData,
|
||||
} from '../../features/work-context/dialog-work-context-settings/dialog-work-context-settings.component';
|
||||
import type { WorkContextSettingsDialogData } from '../../features/work-context/dialog-work-context-settings/dialog-work-context-settings.component';
|
||||
|
||||
@Component({
|
||||
selector: 'work-context-menu',
|
||||
|
|
@ -238,6 +235,8 @@ export class WorkContextMenuComponent implements OnInit {
|
|||
this._tagService.getTagById$(this.contextId).pipe(first()),
|
||||
);
|
||||
|
||||
const { DialogWorkContextSettingsComponent } =
|
||||
await import('../../features/work-context/dialog-work-context-settings/dialog-work-context-settings.component');
|
||||
this._matDialog.open(DialogWorkContextSettingsComponent, {
|
||||
restoreFocus: true,
|
||||
backdropClass: 'cdk-overlay-transparent-backdrop',
|
||||
|
|
|
|||
|
|
@ -1,29 +1,4 @@
|
|||
import localeEnUS from '@angular/common/locales/en';
|
||||
import localeEnGB from '@angular/common/locales/en-GB';
|
||||
import localeEs from '@angular/common/locales/es';
|
||||
import localeDe from '@angular/common/locales/de';
|
||||
import localeAr from '@angular/common/locales/ar';
|
||||
import localeCs from '@angular/common/locales/cs';
|
||||
import localeFa from '@angular/common/locales/fa';
|
||||
import localeFr from '@angular/common/locales/fr';
|
||||
import localeJa from '@angular/common/locales/ja';
|
||||
import localeKo from '@angular/common/locales/ko';
|
||||
import localeRu from '@angular/common/locales/ru';
|
||||
import localeSk from '@angular/common/locales/sk';
|
||||
import localeTr from '@angular/common/locales/tr';
|
||||
import localeZh from '@angular/common/locales/zh';
|
||||
import localeIt from '@angular/common/locales/it';
|
||||
import localePl from '@angular/common/locales/pl';
|
||||
import localePt from '@angular/common/locales/pt';
|
||||
import localeNl from '@angular/common/locales/nl';
|
||||
import localeNb from '@angular/common/locales/nb';
|
||||
import localeHr from '@angular/common/locales/hr';
|
||||
import localeUk from '@angular/common/locales/uk';
|
||||
import localeId from '@angular/common/locales/id';
|
||||
import localeFi from '@angular/common/locales/fi';
|
||||
import localeSv from '@angular/common/locales/sv';
|
||||
import localeRo from '@angular/common/locales/ro';
|
||||
import localeRoMD from '@angular/common/locales/ro-MD';
|
||||
|
||||
/**
|
||||
* All of available app languages
|
||||
|
|
@ -102,51 +77,62 @@ export const DateTimeLocales = {
|
|||
|
||||
export type DateTimeLocale = (typeof DateTimeLocales)[keyof typeof DateTimeLocales];
|
||||
|
||||
export const LocalesImports: Record<keyof typeof DateTimeLocales, unknown> = {
|
||||
en: localeEnGB,
|
||||
en_gb: localeEnGB,
|
||||
en_us: localeEnUS,
|
||||
tr_tr: localeTr,
|
||||
de_de: localeDe,
|
||||
de: localeDe,
|
||||
fr_fr: localeFr,
|
||||
es_es: localeEs,
|
||||
es: localeEs,
|
||||
it_it: localeIt,
|
||||
pt_br: localePt,
|
||||
ru_ru: localeRu,
|
||||
ru: localeRu,
|
||||
zh_cn: localeZh,
|
||||
ja_jp: localeJa,
|
||||
ja: localeJa,
|
||||
ko_kr: localeKo,
|
||||
ko: localeKo,
|
||||
zh_tw: localeZh,
|
||||
ar: localeAr,
|
||||
cs_cz: localeCs,
|
||||
cs: localeCs,
|
||||
fa: localeFa,
|
||||
fi: localeFi,
|
||||
fr: localeFr,
|
||||
id: localeId,
|
||||
it: localeIt,
|
||||
pl: localePl,
|
||||
pt: localePt,
|
||||
nl: localeNl,
|
||||
nb: localeNb,
|
||||
hr: localeHr,
|
||||
uk_ua: localeUk,
|
||||
uk: localeUk,
|
||||
sk_sk: localeSk,
|
||||
sk: localeSk,
|
||||
sv: localeSv,
|
||||
tr: localeTr,
|
||||
zh: localeZh,
|
||||
ro: localeRo,
|
||||
ro_ro: localeRo,
|
||||
ro_md: localeRoMD,
|
||||
/**
|
||||
* Maps locale keys to dynamic import functions for lazy loading.
|
||||
* Only the default locale (en-GB) is statically imported; all others
|
||||
* are loaded on demand to reduce the initial bundle size.
|
||||
*/
|
||||
export const LocaleImportFns: Record<
|
||||
keyof typeof DateTimeLocales,
|
||||
() => Promise<{ default: unknown }>
|
||||
> = {
|
||||
en: () => Promise.resolve({ default: localeEnGB }),
|
||||
en_gb: () => Promise.resolve({ default: localeEnGB }),
|
||||
en_us: () => import('@angular/common/locales/en'),
|
||||
tr_tr: () => import('@angular/common/locales/tr'),
|
||||
de_de: () => import('@angular/common/locales/de'),
|
||||
de: () => import('@angular/common/locales/de'),
|
||||
fr_fr: () => import('@angular/common/locales/fr'),
|
||||
es_es: () => import('@angular/common/locales/es'),
|
||||
es: () => import('@angular/common/locales/es'),
|
||||
it_it: () => import('@angular/common/locales/it'),
|
||||
pt_br: () => import('@angular/common/locales/pt'),
|
||||
ru_ru: () => import('@angular/common/locales/ru'),
|
||||
ru: () => import('@angular/common/locales/ru'),
|
||||
zh_cn: () => import('@angular/common/locales/zh'),
|
||||
ja_jp: () => import('@angular/common/locales/ja'),
|
||||
ja: () => import('@angular/common/locales/ja'),
|
||||
ko_kr: () => import('@angular/common/locales/ko'),
|
||||
ko: () => import('@angular/common/locales/ko'),
|
||||
zh_tw: () => import('@angular/common/locales/zh'),
|
||||
ar: () => import('@angular/common/locales/ar'),
|
||||
cs_cz: () => import('@angular/common/locales/cs'),
|
||||
cs: () => import('@angular/common/locales/cs'),
|
||||
fa: () => import('@angular/common/locales/fa'),
|
||||
fi: () => import('@angular/common/locales/fi'),
|
||||
fr: () => import('@angular/common/locales/fr'),
|
||||
id: () => import('@angular/common/locales/id'),
|
||||
it: () => import('@angular/common/locales/it'),
|
||||
pl: () => import('@angular/common/locales/pl'),
|
||||
pt: () => import('@angular/common/locales/pt'),
|
||||
nl: () => import('@angular/common/locales/nl'),
|
||||
nb: () => import('@angular/common/locales/nb'),
|
||||
hr: () => import('@angular/common/locales/hr'),
|
||||
uk_ua: () => import('@angular/common/locales/uk'),
|
||||
uk: () => import('@angular/common/locales/uk'),
|
||||
sk_sk: () => import('@angular/common/locales/sk'),
|
||||
sk: () => import('@angular/common/locales/sk'),
|
||||
sv: () => import('@angular/common/locales/sv'),
|
||||
tr: () => import('@angular/common/locales/tr'),
|
||||
zh: () => import('@angular/common/locales/zh'),
|
||||
ro: () => import('@angular/common/locales/ro'),
|
||||
ro_ro: () => import('@angular/common/locales/ro'),
|
||||
ro_md: () => import('@angular/common/locales/ro-MD'),
|
||||
};
|
||||
|
||||
/** Default locale data, statically imported for instant availability */
|
||||
export const DEFAULT_LOCALE_DATA = localeEnGB;
|
||||
|
||||
export const DEFAULT_LANGUAGE = LanguageCode.en;
|
||||
export const DEFAULT_LOCALE = DateTimeLocales.en_gb;
|
||||
export const DEFAULT_FIRST_DAY_OF_WEEK = 1; // monday
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import {
|
|||
output,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import { FormlyModule } from '@ngx-formly/core';
|
||||
import { FormlyConfigModule } from '../../../ui/formly-config.module';
|
||||
import { BoardCfg } from '../boards.model';
|
||||
import { BOARDS_FORM } from '../boards-form.const';
|
||||
import { UntypedFormGroup } from '@angular/forms';
|
||||
|
|
@ -24,7 +24,7 @@ import { DEFAULT_BOARD_CFG, DEFAULT_PANEL_CFG } from '../boards.const';
|
|||
@Component({
|
||||
selector: 'board-edit',
|
||||
standalone: true,
|
||||
imports: [FormlyModule, MatButton, MatIcon, TranslatePipe],
|
||||
imports: [FormlyConfigModule, MatButton, MatIcon, TranslatePipe],
|
||||
templateUrl: './board-edit.component.html',
|
||||
styleUrl: './board-edit.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ import {
|
|||
input,
|
||||
output,
|
||||
} from '@angular/core';
|
||||
import { FormlyFieldConfig, FormlyFormOptions, FormlyModule } from '@ngx-formly/core';
|
||||
import { FormlyFieldConfig, FormlyFormOptions } from '@ngx-formly/core';
|
||||
import { FormlyConfigModule } from '../../../ui/formly-config.module';
|
||||
import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms';
|
||||
import { GlobalConfigSectionKey } from '../global-config.model';
|
||||
import { ProjectCfgFormKey } from '../../project/project.model';
|
||||
|
|
@ -19,7 +20,7 @@ import { Log } from '../../../core/log';
|
|||
templateUrl: './config-form.component.html',
|
||||
styleUrls: ['./config-form.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [FormsModule, ReactiveFormsModule, FormlyModule],
|
||||
imports: [FormsModule, ReactiveFormsModule, FormlyConfigModule],
|
||||
})
|
||||
export class ConfigFormComponent {
|
||||
T: typeof T = T;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
||||
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatDialogRef, MatDialogTitle, MatDialogContent } from '@angular/material/dialog';
|
||||
import { FormlyFieldConfig, FormlyModule } from '@ngx-formly/core';
|
||||
import { FormlyFieldConfig } from '@ngx-formly/core';
|
||||
import { FormlyConfigModule } from '../../../ui/formly-config.module';
|
||||
import { GlobalConfigService } from '../../config/global-config.service';
|
||||
import { T } from '../../../t.const';
|
||||
import { PomodoroConfig } from '../../config/global-config.model';
|
||||
|
|
@ -52,7 +53,7 @@ const POMODORO_DURATION_FIELDS: FormlyFieldConfig[] = [
|
|||
standalone: true,
|
||||
imports: [
|
||||
ReactiveFormsModule,
|
||||
FormlyModule,
|
||||
FormlyConfigModule,
|
||||
MatDialogTitle,
|
||||
MatDialogContent,
|
||||
TranslatePipe,
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ import { JiraAdditionalCfgComponent } from '../providers/jira/jira-view-componen
|
|||
import { HelpSectionComponent } from '../../../ui/help-section/help-section.component';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
||||
import { FormlyModule } from '@ngx-formly/core';
|
||||
import { FormlyConfigModule } from '../../../ui/formly-config.module';
|
||||
import { MatButton } from '@angular/material/button';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { IS_ANDROID_WEB_VIEW } from '../../../util/is-android-web-view';
|
||||
|
|
@ -72,7 +72,7 @@ import { ISSUE_PROVIDER_COMMON_FORM_FIELDS } from '../common-issue-form-stuff.co
|
|||
HelpSectionComponent,
|
||||
TranslatePipe,
|
||||
MatSlideToggle,
|
||||
FormlyModule,
|
||||
FormlyConfigModule,
|
||||
MatDialogActions,
|
||||
MatButton,
|
||||
MatIcon,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ import {
|
|||
} from '@angular/material/dialog';
|
||||
import { Project, ProjectCopy } from '../../project.model';
|
||||
import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms';
|
||||
import { FormlyFieldConfig, FormlyFormOptions, FormlyModule } from '@ngx-formly/core';
|
||||
import { FormlyFieldConfig, FormlyFormOptions } from '@ngx-formly/core';
|
||||
import { FormlyConfigModule } from '../../../../ui/formly-config.module';
|
||||
import { ProjectService } from '../../project.service';
|
||||
import { DEFAULT_PROJECT } from '../../project.const';
|
||||
import { JiraCfg } from '../../../issue/providers/jira/jira.model';
|
||||
|
|
@ -48,7 +49,7 @@ import { Log } from '../../../../core/log';
|
|||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatDialogContent,
|
||||
FormlyModule,
|
||||
FormlyConfigModule,
|
||||
MatDialogActions,
|
||||
MatButton,
|
||||
TranslatePipe,
|
||||
|
|
|
|||
|
|
@ -1,20 +1,30 @@
|
|||
@if (panelContent() === 'ISSUE_PANEL') {
|
||||
<issue-panel [@slideInFromRight]></issue-panel>
|
||||
@defer (prefetch on idle) {
|
||||
<issue-panel [@slideInFromRight]></issue-panel>
|
||||
}
|
||||
} @else if (panelContent() === 'NOTES') {
|
||||
<notes [@slideInFromRight]></notes>
|
||||
@defer (prefetch on idle) {
|
||||
<notes [@slideInFromRight]></notes>
|
||||
}
|
||||
} @else if (panelContent() === 'PLUGIN') {
|
||||
@for (key of pluginPanelKeys(); track key) {
|
||||
<plugin-panel-container [@slideInFromRight]></plugin-panel-container>
|
||||
@defer (prefetch on idle) {
|
||||
<plugin-panel-container [@slideInFromRight]></plugin-panel-container>
|
||||
}
|
||||
}
|
||||
<!-- Task content comes last so we can avoid an extra effect to unset selected task -->
|
||||
} @else if (panelContent() === 'SCHEDULE_DAY_PANEL') {
|
||||
<schedule-day-panel [@slideInFromRight]></schedule-day-panel>
|
||||
@defer (prefetch on idle) {
|
||||
<schedule-day-panel [@slideInFromRight]></schedule-day-panel>
|
||||
}
|
||||
} @else {
|
||||
@if (selectedTaskWithDelayForNone(); as task) {
|
||||
<task-detail-panel
|
||||
[@taskDetailPanelTaskChange]="task.id"
|
||||
[@.disabled]="isDisableTaskPanelAni()"
|
||||
[task]="task"
|
||||
></task-detail-panel>
|
||||
@defer (prefetch on idle) {
|
||||
<task-detail-panel
|
||||
[@taskDetailPanelTaskChange]="task.id"
|
||||
[@.disabled]="isDisableTaskPanelAni()"
|
||||
[task]="task"
|
||||
></task-detail-panel>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ import {
|
|||
import { isInputElement } from '../../util/dom-element';
|
||||
import { BottomPanelStateService } from '../../core-ui/bottom-panel-state.service';
|
||||
import { slideRightPanelAni } from './slide-right-panel-out.ani';
|
||||
import { BottomPanelContainerComponent } from '../bottom-panel/bottom-panel-container.component';
|
||||
import { MatBottomSheet, MatBottomSheetRef } from '@angular/material/bottom-sheet';
|
||||
import { PanelContentService } from '../panels/panel-content.service';
|
||||
// Right panel resize constants
|
||||
|
|
@ -155,7 +154,7 @@ export class RightPanelComponent implements AfterViewInit, OnDestroy {
|
|||
private readonly _boundOnPointerUp = this._handlePointerUp.bind(this);
|
||||
private readonly _boundOnWindowResize = this._throttledWindowResize.bind(this);
|
||||
private _bottomSheet = inject(MatBottomSheet);
|
||||
private _bottomSheetRef: MatBottomSheetRef<BottomPanelContainerComponent> | null = null;
|
||||
private _bottomSheetRef: MatBottomSheetRef | null = null;
|
||||
private _bottomSheetSubscription: Subscription | null = null;
|
||||
|
||||
// Track listener state to prevent double attachment/removal
|
||||
|
|
@ -218,21 +217,29 @@ export class RightPanelComponent implements AfterViewInit, OnDestroy {
|
|||
if (hasContent && !this._bottomSheetRef) {
|
||||
// Open bottom sheet
|
||||
|
||||
this._bottomSheetRef = this._bottomSheet.open(BottomPanelContainerComponent, {
|
||||
hasBackdrop: true,
|
||||
closeOnNavigation: false,
|
||||
panelClass: 'bottom-panel-sheet',
|
||||
// Let CSS handle positioning and height
|
||||
});
|
||||
import('../bottom-panel/bottom-panel-container.component').then((m) => {
|
||||
// Re-check: conditions may have changed while chunk was loading
|
||||
if (this._bottomSheetRef) {
|
||||
return;
|
||||
}
|
||||
this._bottomSheetRef = this._bottomSheet.open(
|
||||
m.BottomPanelContainerComponent,
|
||||
{
|
||||
hasBackdrop: true,
|
||||
closeOnNavigation: false,
|
||||
panelClass: 'bottom-panel-sheet',
|
||||
},
|
||||
);
|
||||
|
||||
// Handle bottom sheet dismissal
|
||||
this._bottomSheetSubscription = this._bottomSheetRef
|
||||
.afterDismissed()
|
||||
.subscribe(() => {
|
||||
this._bottomSheetRef = null;
|
||||
this._bottomSheetSubscription = null;
|
||||
this.close();
|
||||
});
|
||||
// Handle bottom sheet dismissal
|
||||
this._bottomSheetSubscription = this._bottomSheetRef
|
||||
.afterDismissed()
|
||||
.subscribe(() => {
|
||||
this._bottomSheetRef = null;
|
||||
this._bottomSheetSubscription = null;
|
||||
this.close();
|
||||
});
|
||||
});
|
||||
} else if (!hasContent && this._bottomSheetRef) {
|
||||
// Close bottom sheet
|
||||
this._bottomSheetRef.dismiss();
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ import {
|
|||
SimpleCounterType,
|
||||
} from '../simple-counter.model';
|
||||
import { T } from '../../../t.const';
|
||||
import { FormlyFieldConfig, FormlyFormOptions, FormlyModule } from '@ngx-formly/core';
|
||||
import { FormlyFieldConfig, FormlyFormOptions } from '@ngx-formly/core';
|
||||
import { FormlyConfigModule } from '../../../ui/formly-config.module';
|
||||
import { FormsModule, ReactiveFormsModule, UntypedFormGroup } from '@angular/forms';
|
||||
import { adjustToDialogFormlyForm } from '../../../util/adjust-to-dialog-formly-form';
|
||||
import { SIMPLE_COUNTER_FORM } from '../../config/form-cfgs/simple-counter-form.const';
|
||||
|
|
@ -38,7 +39,7 @@ import { DialogConfirmComponent } from '../../../ui/dialog-confirm/dialog-confir
|
|||
TranslatePipe,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
FormlyModule,
|
||||
FormlyConfigModule,
|
||||
],
|
||||
})
|
||||
export class DialogSimpleCounterEditSettingsComponent {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ import {
|
|||
TaskRepeatCfg,
|
||||
TaskRepeatCfgCopy,
|
||||
} from '../task-repeat-cfg.model';
|
||||
import { FormlyFieldConfig, FormlyModule } from '@ngx-formly/core';
|
||||
import { FormlyFieldConfig } from '@ngx-formly/core';
|
||||
import { FormlyConfigModule } from '../../../ui/formly-config.module';
|
||||
import { UntypedFormGroup } from '@angular/forms';
|
||||
import {
|
||||
TASK_REPEAT_CFG_ADVANCED_FORM_CFG,
|
||||
|
|
@ -63,7 +64,7 @@ import { CollapsibleComponent } from '../../../ui/collapsible/collapsible.compon
|
|||
TranslatePipe,
|
||||
MatDialogContent,
|
||||
HelpSectionComponent,
|
||||
FormlyModule,
|
||||
FormlyConfigModule,
|
||||
ChipListInputComponent,
|
||||
MatDialogActions,
|
||||
MatButton,
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@ import { CHRONO_SUGGESTIONS } from './add-task-bar.const';
|
|||
import { TaskRepeatCfgService } from '../../task-repeat-cfg/task-repeat-cfg.service';
|
||||
import { DEFAULT_TASK_REPEAT_CFG } from '../../task-repeat-cfg/task-repeat-cfg.model';
|
||||
import { getQuickSettingUpdates } from '../../task-repeat-cfg/dialog-edit-task-repeat-cfg/get-quick-setting-updates';
|
||||
import { DialogEditTaskRepeatCfgComponent } from '../../task-repeat-cfg/dialog-edit-task-repeat-cfg/dialog-edit-task-repeat-cfg.component';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { ShortSyntaxTag, shortSyntaxToTags } from './short-syntax-to-tags';
|
||||
import { DEFAULT_PROJECT_COLOR } from '../../work-context/work-context.const';
|
||||
|
|
@ -791,7 +790,9 @@ export class AddTaskBarComponent implements AfterViewInit, OnInit, OnDestroy {
|
|||
.getByIdOnce$(taskId)
|
||||
.pipe(timeout(1000), takeUntilDestroyed(this._destroyRef))
|
||||
.subscribe({
|
||||
next: (task) => {
|
||||
next: async (task) => {
|
||||
const { DialogEditTaskRepeatCfgComponent } =
|
||||
await import('../../task-repeat-cfg/dialog-edit-task-repeat-cfg/dialog-edit-task-repeat-cfg.component');
|
||||
this._matDialog.open(DialogEditTaskRepeatCfgComponent, {
|
||||
data: { task, defaultRemindOption: remindOption },
|
||||
});
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import { PanDirective, PanEvent } from '../../../ui/swipe-gesture/pan.directive'
|
|||
import { TaskAttachmentService } from '../task-attachment/task-attachment.service';
|
||||
import { DialogEditTaskAttachmentComponent } from '../task-attachment/dialog-edit-attachment/dialog-edit-task-attachment.component';
|
||||
import { swirlAnimation } from '../../../ui/animations/swirl-in-out.ani';
|
||||
import { DialogEditTaskRepeatCfgComponent } from '../../task-repeat-cfg/dialog-edit-task-repeat-cfg/dialog-edit-task-repeat-cfg.component';
|
||||
import { ProjectService } from '../../project/project.service';
|
||||
import { Project } from '../../project/project.model';
|
||||
import { _MISSING_PROJECT_ } from '../../project/project.const';
|
||||
|
|
@ -389,7 +388,9 @@ export class TaskComponent implements OnDestroy, AfterViewInit {
|
|||
});
|
||||
}
|
||||
|
||||
editTaskRepeatCfg(): void {
|
||||
async editTaskRepeatCfg(): Promise<void> {
|
||||
const { DialogEditTaskRepeatCfgComponent } =
|
||||
await import('../../task-repeat-cfg/dialog-edit-task-repeat-cfg/dialog-edit-task-repeat-cfg.component');
|
||||
this._matDialog
|
||||
.open(DialogEditTaskRepeatCfgComponent, {
|
||||
data: {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import {
|
|||
MatDialogTitle,
|
||||
} from '@angular/material/dialog';
|
||||
import { ReactiveFormsModule, UntypedFormGroup } from '@angular/forms';
|
||||
import { FormlyFieldConfig, FormlyModule } from '@ngx-formly/core';
|
||||
import { FormlyFieldConfig } from '@ngx-formly/core';
|
||||
import { FormlyConfigModule } from '../../../ui/formly-config.module';
|
||||
import { T } from '../../../t.const';
|
||||
import { Project } from '../../project/project.model';
|
||||
import { Tag } from '../../tag/tag.model';
|
||||
|
|
@ -34,7 +35,7 @@ export interface WorkContextSettingsDialogData {
|
|||
MatDialogTitle,
|
||||
ReactiveFormsModule,
|
||||
MatDialogContent,
|
||||
FormlyModule,
|
||||
FormlyConfigModule,
|
||||
MatDialogActions,
|
||||
MatButton,
|
||||
TranslatePipe,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import { T } from '../../../t.const';
|
|||
import { SYNC_FORM } from '../../../features/config/form-cfgs/sync-form.const';
|
||||
import { FormGroup } from '@angular/forms';
|
||||
import { FormlyConfigModule } from '../../../ui/formly-config.module';
|
||||
import { FormlyModule } from '@ngx-formly/core';
|
||||
import { FormlyFieldConfig } from '@ngx-formly/core';
|
||||
import { SyncConfig } from '../../../features/config/global-config.model';
|
||||
import { SyncProviderId } from '../../../op-log/sync-providers/provider.const';
|
||||
|
|
@ -46,7 +45,6 @@ import { isOnline } from '../../../util/is-online';
|
|||
MatIcon,
|
||||
TranslatePipe,
|
||||
FormlyConfigModule,
|
||||
FormlyModule,
|
||||
],
|
||||
})
|
||||
export class DialogSyncInitialCfgComponent implements AfterViewInit {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ import { DialogConflictResolutionResult } from './sync.model';
|
|||
import { DialogSyncConflictComponent } from './dialog-sync-conflict/dialog-sync-conflict.component';
|
||||
import { ReminderService } from '../../features/reminder/reminder.service';
|
||||
|
||||
import { DialogSyncInitialCfgComponent } from './dialog-sync-initial-cfg/dialog-sync-initial-cfg.component';
|
||||
import { DialogHandleDecryptErrorComponent } from './dialog-handle-decrypt-error/dialog-handle-decrypt-error.component';
|
||||
import { DialogEnterEncryptionPasswordComponent } from './dialog-enter-encryption-password/dialog-enter-encryption-password.component';
|
||||
import {
|
||||
|
|
@ -449,14 +448,14 @@ export class SyncWrapperService {
|
|||
msg: T.F.SYNC.S.AUTH_TOKEN_REJECTED,
|
||||
translateParams: { reason: error.message },
|
||||
type: 'ERROR',
|
||||
actionFn: async () => this._matDialog.open(DialogSyncInitialCfgComponent),
|
||||
actionFn: () => this._openSyncInitialCfgDialog(),
|
||||
actionStr: T.F.SYNC.S.BTN_CONFIGURE,
|
||||
});
|
||||
} else {
|
||||
this._snackService.open({
|
||||
msg: T.F.SYNC.S.INCOMPLETE_CFG,
|
||||
type: 'ERROR',
|
||||
actionFn: async () => this._matDialog.open(DialogSyncInitialCfgComponent),
|
||||
actionFn: () => this._openSyncInitialCfgDialog(),
|
||||
actionStr: T.F.SYNC.S.BTN_CONFIGURE,
|
||||
});
|
||||
}
|
||||
|
|
@ -693,6 +692,12 @@ export class SyncWrapperService {
|
|||
* Handle incoherent timestamps dialog with proper async error handling.
|
||||
* Uses fire-and-forget pattern but logs errors instead of swallowing them.
|
||||
*/
|
||||
private async _openSyncInitialCfgDialog(): Promise<void> {
|
||||
const { DialogSyncInitialCfgComponent } =
|
||||
await import('./dialog-sync-initial-cfg/dialog-sync-initial-cfg.component');
|
||||
this._matDialog.open(DialogSyncInitialCfgComponent);
|
||||
}
|
||||
|
||||
private _handleIncoherentTimestampsDialog(): void {
|
||||
this._openSyncErrorDialog({ type: 'incoherent-timestamps' });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ import {
|
|||
import { MatButton } from '@angular/material/button';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { FormlyFieldConfig, FormlyModule } from '@ngx-formly/core';
|
||||
import { FormlyFieldConfig } from '@ngx-formly/core';
|
||||
import { FormlyConfigModule } from '../../../ui/formly-config.module';
|
||||
import { FormlyJsonschema } from '@ngx-formly/core/json-schema';
|
||||
import { JSONSchema7 } from 'json-schema';
|
||||
import { PluginManifest } from '../../plugin-api.model';
|
||||
|
|
@ -123,7 +124,7 @@ interface PluginConfigData {
|
|||
MatProgressSpinner,
|
||||
MatError,
|
||||
ReactiveFormsModule,
|
||||
FormlyModule,
|
||||
FormlyConfigModule,
|
||||
],
|
||||
})
|
||||
export class PluginConfigDialogComponent {
|
||||
|
|
|
|||
41
src/main.ts
41
src/main.ts
|
|
@ -11,7 +11,11 @@ import { registerLocaleData } from '@angular/common';
|
|||
|
||||
import { environment } from './environments/environment';
|
||||
import { IS_ELECTRON } from './app/app.constants';
|
||||
import { DEFAULT_LANGUAGE, LocalesImports } from './app/core/locale.constants';
|
||||
import {
|
||||
DEFAULT_LANGUAGE,
|
||||
DEFAULT_LOCALE_DATA,
|
||||
LocaleImportFns,
|
||||
} from './app/core/locale.constants';
|
||||
import { IS_ANDROID_WEB_VIEW } from './app/util/is-android-web-view';
|
||||
import { androidInterface } from './app/features/android/android-interface';
|
||||
import { IS_IOS_NATIVE, IS_NATIVE_PLATFORM } from './app/util/is-native-platform';
|
||||
|
|
@ -30,7 +34,6 @@ import {
|
|||
MatDateFormats,
|
||||
DateAdapter,
|
||||
} from '@angular/material/core';
|
||||
import { FormlyConfigModule } from './app/ui/formly-config.module';
|
||||
import { markedOptionsFactory } from './app/ui/marked-options-factory';
|
||||
import { MaterialCssVarsModule } from 'angular-material-css-vars';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
|
|
@ -96,7 +99,6 @@ bootstrapApplication(AppComponent, {
|
|||
importProvidersFrom(
|
||||
FeatureStoresModule,
|
||||
MatNativeDateModule,
|
||||
FormlyConfigModule,
|
||||
MarkdownModule.forRoot({
|
||||
markedOptions: {
|
||||
provide: MARKED_OPTIONS,
|
||||
|
|
@ -251,27 +253,24 @@ bootstrapApplication(AppComponent, {
|
|||
// Initialize touch fix for Material menus
|
||||
initializeMatMenuTouchFix();
|
||||
|
||||
// Register default locale immediately for fast startup
|
||||
registerLocaleData(LocalesImports[DEFAULT_LANGUAGE], DEFAULT_LANGUAGE);
|
||||
// Register default locale immediately (statically imported, no network fetch)
|
||||
registerLocaleData(DEFAULT_LOCALE_DATA, 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);
|
||||
}
|
||||
});
|
||||
// Lazily load and register remaining locales during idle time
|
||||
const registerRemainingLocales = (): void => {
|
||||
Object.keys(LocaleImportFns).forEach((locale) => {
|
||||
if (locale !== DEFAULT_LANGUAGE) {
|
||||
LocaleImportFns[locale as keyof typeof LocaleImportFns]().then((m) => {
|
||||
registerLocaleData(m.default, locale);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (typeof requestIdleCallback === 'function') {
|
||||
requestIdleCallback(() => registerRemainingLocales());
|
||||
} else {
|
||||
// Fallback for browsers without requestIdleCallback
|
||||
setTimeout(() => {
|
||||
Object.keys(LocalesImports).forEach((locale) => {
|
||||
if (locale !== DEFAULT_LANGUAGE) {
|
||||
registerLocaleData(LocalesImports[locale], locale);
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
setTimeout(() => registerRemainingLocales(), 0);
|
||||
}
|
||||
|
||||
// TODO make asset caching work for electron
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@
|
|||
|
||||
/* IE9, IE10 and IE11 requires all of the following polyfills. **/
|
||||
//import 'core-js/es/symbol';
|
||||
import 'core-js/es/object'; // Support for Chrome mobile <= 69, which is the default browser for Android <= 9, especially the following error: TypeError: Object.fromEntries is not a function
|
||||
// core-js/es/object removed: Object.fromEntries is supported since Chrome 73+ (2019).
|
||||
// Android 9 (Chrome 69) is EOL and no longer targeted.
|
||||
/*import 'core-js/es/function';
|
||||
import 'core-js/es/parse-int';
|
||||
import 'core-js/es/parse-float';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue