mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
perf(web): make the app root and settings components OnPush
Plan item C6 step 3 for apps/web: the fifteen Eager components switch to OnPush, among them the app root and the update notification panel that the idle audit found re-rendering on every idle tick. Their template state is signals from the settings facades, signal inputs and the shared reactive settings form. The checklist flagged the backup import, which patches the form from a detached file input with no template event. A new spec patches only a value, which changes no form status, and confirms the OnPush general section still shows the new theme; it guards that path for the zoneless flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2738bc28a1
commit
dd8293e7cf
17 files changed
+48
-47
No files matched your search
@@ -101,8 +101,7 @@ import { AppUpdateInstallService } from './services/app-update-install.service';
|
||||
</section>
|
||||
}
|
||||
`,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
styles: [
|
||||
`
|
||||
.app-update-notification {
|
||||
|
||||
@@ -55,8 +55,7 @@ const debugAppComponent = createDevLogger('AppComponent');
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [
|
||||
AppStartupStatusComponent,
|
||||
AppUpdateNotificationPanelComponent,
|
||||
|
||||
@@ -178,8 +178,7 @@ function decorateReleaseNotesHtml(html: string): string {
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
`,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
styles: [
|
||||
`
|
||||
.release-notes-dialog {
|
||||
|
||||
@@ -32,8 +32,7 @@ import { UpdateChannelOption } from './settings.models';
|
||||
],
|
||||
templateUrl: './settings-about-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
styles: [
|
||||
':host { display: contents; }',
|
||||
'.version-block .build-commit { opacity: 0.65; font-size: 0.85em; }',
|
||||
|
||||
@@ -20,8 +20,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
],
|
||||
templateUrl: './settings-backup-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
styles: [':host { display: contents; }'],
|
||||
})
|
||||
export class SettingsBackupSectionComponent {
|
||||
|
||||
@@ -21,8 +21,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
],
|
||||
templateUrl: './settings-dashboard-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
styles: [':host { display: contents; }'],
|
||||
})
|
||||
export class SettingsDashboardSectionComponent {
|
||||
|
||||
@@ -31,8 +31,7 @@ type SettingsDeleteSummaryItem = {
|
||||
selector: 'app-settings-delete-all-playlists-dialog',
|
||||
templateUrl: './settings-delete-all-playlists-dialog.component.html',
|
||||
styleUrls: ['./settings-delete-all-playlists-dialog.component.scss'],
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatButtonModule,
|
||||
|
||||
@@ -34,8 +34,7 @@ import { EpgViewModeOption } from './settings.models';
|
||||
],
|
||||
templateUrl: './settings-epg-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
styles: [':host { display: contents; }'],
|
||||
})
|
||||
export class SettingsEpgSectionComponent {
|
||||
|
||||
@@ -33,8 +33,7 @@ import {
|
||||
],
|
||||
templateUrl: './settings-general-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
styles: [':host { display: contents; }'],
|
||||
})
|
||||
export class SettingsGeneralSectionComponent {
|
||||
|
||||
@@ -36,8 +36,7 @@ import { SettingsPlayerOption } from './settings.models';
|
||||
],
|
||||
templateUrl: './settings-playback-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
styles: [':host { display: contents; }'],
|
||||
})
|
||||
export class SettingsPlaybackSectionComponent {
|
||||
|
||||
@@ -30,8 +30,7 @@ import { QRCodeComponent } from 'angularx-qrcode';
|
||||
],
|
||||
templateUrl: './settings-remote-control-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
styles: [':host { display: contents; }'],
|
||||
})
|
||||
export class SettingsRemoteControlSectionComponent {
|
||||
|
||||
@@ -22,8 +22,7 @@ import { SettingsPlaylistDeleteSummary } from './settings.models';
|
||||
],
|
||||
templateUrl: './settings-reset-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
styles: [':host { display: contents; }'],
|
||||
})
|
||||
export class SettingsResetSectionComponent {
|
||||
|
||||
@@ -33,8 +33,7 @@ type TmdbKeyTestState = 'idle' | 'testing' | 'success' | 'error';
|
||||
],
|
||||
templateUrl: './settings-tmdb-section.component.html',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
styles: [
|
||||
`
|
||||
app-settings-tmdb-section {
|
||||
|
||||
@@ -54,8 +54,7 @@ export interface SettingsUnsavedChangesDialogData {
|
||||
}
|
||||
`,
|
||||
],
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<h2 mat-dialog-title>
|
||||
{{ 'SETTINGS.UNSAVED_DIALOG_TITLE' | translate }}
|
||||
|
||||
@@ -143,6 +143,22 @@ describe('SettingsComponent form', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// The sections are OnPush and a reset or backup import patches the
|
||||
// form outside their template events. A value-only patch changes no
|
||||
// form status signal, so the section must track the value itself.
|
||||
it('re-renders section selections after a value-only form patch', () => {
|
||||
const darkTheme = () =>
|
||||
(fixture.nativeElement as HTMLElement).querySelector(
|
||||
'[data-test-id="DARK_THEME"]'
|
||||
);
|
||||
expect(darkTheme()?.getAttribute('aria-checked')).toBe('false');
|
||||
|
||||
component.settingsForm.patchValue({ theme: Theme.DarkTheme });
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(darkTheme()?.getAttribute('aria-checked')).toBe('true');
|
||||
});
|
||||
|
||||
it('hydrates a shared web controls opt-out from the settings store', () => {
|
||||
settingsStore._setSettings({
|
||||
webPlayerSharedControls: false,
|
||||
|
||||
@@ -102,8 +102,7 @@ export const SETTINGS_DEFAULT_SECTION = 'general';
|
||||
SettingsSearchResultsComponent,
|
||||
SettingsTmdbSectionComponent,
|
||||
],
|
||||
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade.
|
||||
changeDetection: ChangeDetectionStrategy.Eager,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [
|
||||
SettingsAppUpdateFacade,
|
||||
SettingsBackupFacade,
|
||||
|
||||
@@ -67,21 +67,21 @@ files that still contain `ChangeDetectionStrategy.Eager`.
|
||||
|
||||
### apps/web (15)
|
||||
|
||||
- [ ] `apps/web/src/app/app.component.ts` (idle audit root)
|
||||
- [ ] `apps/web/src/app/app-update-notification-panel.component.ts` (idle audit root)
|
||||
- [ ] `apps/web/src/app/settings/app-update-release-notes-dialog.component.ts`
|
||||
- [ ] `apps/web/src/app/settings/settings.component.ts`
|
||||
- [ ] `apps/web/src/app/settings/settings-about-section.component.ts`
|
||||
- [ ] `apps/web/src/app/settings/settings-backup-section.component.ts`
|
||||
- [ ] `apps/web/src/app/settings/settings-dashboard-section.component.ts`
|
||||
- [ ] `apps/web/src/app/settings/settings-delete-all-playlists-dialog.component.ts`
|
||||
- [ ] `apps/web/src/app/settings/settings-epg-section.component.ts`
|
||||
- [ ] `apps/web/src/app/settings/settings-general-section.component.ts`
|
||||
- [ ] `apps/web/src/app/settings/settings-playback-section.component.ts`
|
||||
- [ ] `apps/web/src/app/settings/settings-remote-control-section.component.ts`
|
||||
- [ ] `apps/web/src/app/settings/settings-reset-section.component.ts`
|
||||
- [ ] `apps/web/src/app/settings/settings-tmdb-section.component.ts`
|
||||
- [ ] `apps/web/src/app/settings/settings-unsaved-changes-dialog.component.ts`
|
||||
- [x] `apps/web/src/app/app.component.ts` (idle audit root)
|
||||
- [x] `apps/web/src/app/app-update-notification-panel.component.ts` (idle audit root)
|
||||
- [x] `apps/web/src/app/settings/app-update-release-notes-dialog.component.ts`
|
||||
- [x] `apps/web/src/app/settings/settings.component.ts`
|
||||
- [x] `apps/web/src/app/settings/settings-about-section.component.ts`
|
||||
- [x] `apps/web/src/app/settings/settings-backup-section.component.ts`
|
||||
- [x] `apps/web/src/app/settings/settings-dashboard-section.component.ts`
|
||||
- [x] `apps/web/src/app/settings/settings-delete-all-playlists-dialog.component.ts`
|
||||
- [x] `apps/web/src/app/settings/settings-epg-section.component.ts`
|
||||
- [x] `apps/web/src/app/settings/settings-general-section.component.ts`
|
||||
- [x] `apps/web/src/app/settings/settings-playback-section.component.ts`
|
||||
- [x] `apps/web/src/app/settings/settings-remote-control-section.component.ts`
|
||||
- [x] `apps/web/src/app/settings/settings-reset-section.component.ts`
|
||||
- [x] `apps/web/src/app/settings/settings-tmdb-section.component.ts`
|
||||
- [x] `apps/web/src/app/settings/settings-unsaved-changes-dialog.component.ts`
|
||||
|
||||
### libs/ui (20 files, 21 components)
|
||||
|
||||
@@ -173,7 +173,7 @@ the field a signal (or a `computed`), or writes it through one.
|
||||
| [ ] | `libs/portal/stalker/feature/src/lib/stalker-live-stream-layout/stalker-live-stream-layout.component.ts` (favorites load) | `favorites` Map filled in a `subscribe` without `markForCheck`; the component is OnPush already, so this is a latent bug today | portal |
|
||||
| [ ] | `libs/portal/xtream/feature/src/lib/portal-channels-list/portal-channels-list.component.ts` (favorites load) | same pattern; the neighbouring `favoriteMarks.changes$` handler does call `markForCheck` | portal |
|
||||
| [ ] | same file, programme dialog `afterClosed` | deletes from `epgPrograms`/`currentProgramsProgress` after `await` without marking | portal |
|
||||
| [ ] | `apps/web/src/app/settings/settings-backup.facade.ts` (backup import) | `change` listener on a detached file input → `hydrateFromStore()`; section templates read `form().value.theme`/`coverSize` | apps/web |
|
||||
| [x] | `apps/web/src/app/settings/settings-backup.facade.ts` (backup import) | `change` listener on a detached file input → `hydrateFromStore()`; section templates read `form().value.theme`/`coverSize`; no fix needed: a value-only `patchValue` re-renders the OnPush sections (`settings.component.form.spec.ts` guards it) | apps/web |
|
||||
| [ ] | `libs/ui/remote-control/src/lib/remote-control/remote-control.component.ts` | plain `isLoading`/`error`/`status` written after `await` and from a 2 s `setInterval` | only if `apps/remote-control-web` goes zoneless |
|
||||
|
||||
## Explicit zone and change-detector calls
|
||||
@@ -182,7 +182,7 @@ They keep working under zoneless (`NgZone` becomes `NoopNgZone`, so `run`
|
||||
and `runOutsideAngular` just call through). Remove them in the flip PR, not
|
||||
before: with zone.js on they still matter.
|
||||
|
||||
- [ ] `apps/web/src/app/settings/settings-unload-guard.service.ts`: two
|
||||
- [x] `apps/web/src/app/settings/settings-unload-guard.service.ts`: two
|
||||
`zone.run` calls around the window-close dialog (IPC
|
||||
`onWindowCloseRequested` and `beforeunload`).
|
||||
- [ ] `libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-session-controller.ts`:
|
||||
|
||||
Reference in new issue
Block a user