diff --git a/apps/web/src/app/app-update-notification-panel.component.ts b/apps/web/src/app/app-update-notification-panel.component.ts index a99e814ad..91afea93b 100644 --- a/apps/web/src/app/app-update-notification-panel.component.ts +++ b/apps/web/src/app/app-update-notification-panel.component.ts @@ -101,8 +101,7 @@ import { AppUpdateInstallService } from './services/app-update-install.service'; } `, - // 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 { diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 15c3df9c3..804899aa4 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -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, diff --git a/apps/web/src/app/settings/app-update-release-notes-dialog.component.ts b/apps/web/src/app/settings/app-update-release-notes-dialog.component.ts index 870e1c142..afacf4eff 100644 --- a/apps/web/src/app/settings/app-update-release-notes-dialog.component.ts +++ b/apps/web/src/app/settings/app-update-release-notes-dialog.component.ts @@ -178,8 +178,7 @@ function decorateReleaseNotesHtml(html: string): string { `, - // 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 { diff --git a/apps/web/src/app/settings/settings-about-section.component.ts b/apps/web/src/app/settings/settings-about-section.component.ts index 9d6f1f6a0..30c8979f2 100644 --- a/apps/web/src/app/settings/settings-about-section.component.ts +++ b/apps/web/src/app/settings/settings-about-section.component.ts @@ -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; }', diff --git a/apps/web/src/app/settings/settings-backup-section.component.ts b/apps/web/src/app/settings/settings-backup-section.component.ts index f383c6163..0af8aefba 100644 --- a/apps/web/src/app/settings/settings-backup-section.component.ts +++ b/apps/web/src/app/settings/settings-backup-section.component.ts @@ -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 { diff --git a/apps/web/src/app/settings/settings-dashboard-section.component.ts b/apps/web/src/app/settings/settings-dashboard-section.component.ts index 3cb1739b6..142838da2 100644 --- a/apps/web/src/app/settings/settings-dashboard-section.component.ts +++ b/apps/web/src/app/settings/settings-dashboard-section.component.ts @@ -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 { diff --git a/apps/web/src/app/settings/settings-delete-all-playlists-dialog.component.ts b/apps/web/src/app/settings/settings-delete-all-playlists-dialog.component.ts index 87dd57aa2..9f4a0fea2 100644 --- a/apps/web/src/app/settings/settings-delete-all-playlists-dialog.component.ts +++ b/apps/web/src/app/settings/settings-delete-all-playlists-dialog.component.ts @@ -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, diff --git a/apps/web/src/app/settings/settings-epg-section.component.ts b/apps/web/src/app/settings/settings-epg-section.component.ts index 8f07d4124..b7d5bc1c2 100644 --- a/apps/web/src/app/settings/settings-epg-section.component.ts +++ b/apps/web/src/app/settings/settings-epg-section.component.ts @@ -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 { diff --git a/apps/web/src/app/settings/settings-general-section.component.ts b/apps/web/src/app/settings/settings-general-section.component.ts index b24d2afbc..e4bdff31a 100644 --- a/apps/web/src/app/settings/settings-general-section.component.ts +++ b/apps/web/src/app/settings/settings-general-section.component.ts @@ -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 { diff --git a/apps/web/src/app/settings/settings-playback-section.component.ts b/apps/web/src/app/settings/settings-playback-section.component.ts index 6b0afd01f..4d38424c3 100644 --- a/apps/web/src/app/settings/settings-playback-section.component.ts +++ b/apps/web/src/app/settings/settings-playback-section.component.ts @@ -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 { diff --git a/apps/web/src/app/settings/settings-remote-control-section.component.ts b/apps/web/src/app/settings/settings-remote-control-section.component.ts index 37b6408ef..9383aa348 100644 --- a/apps/web/src/app/settings/settings-remote-control-section.component.ts +++ b/apps/web/src/app/settings/settings-remote-control-section.component.ts @@ -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 { diff --git a/apps/web/src/app/settings/settings-reset-section.component.ts b/apps/web/src/app/settings/settings-reset-section.component.ts index ae6d5226f..8396ee5c4 100644 --- a/apps/web/src/app/settings/settings-reset-section.component.ts +++ b/apps/web/src/app/settings/settings-reset-section.component.ts @@ -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 { diff --git a/apps/web/src/app/settings/settings-tmdb-section.component.ts b/apps/web/src/app/settings/settings-tmdb-section.component.ts index 4b57acfba..b72c46de8 100644 --- a/apps/web/src/app/settings/settings-tmdb-section.component.ts +++ b/apps/web/src/app/settings/settings-tmdb-section.component.ts @@ -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 { diff --git a/apps/web/src/app/settings/settings-unsaved-changes-dialog.component.ts b/apps/web/src/app/settings/settings-unsaved-changes-dialog.component.ts index c556f7d1b..c709a9e06 100644 --- a/apps/web/src/app/settings/settings-unsaved-changes-dialog.component.ts +++ b/apps/web/src/app/settings/settings-unsaved-changes-dialog.component.ts @@ -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: `

{{ 'SETTINGS.UNSAVED_DIALOG_TITLE' | translate }} diff --git a/apps/web/src/app/settings/settings.component.form.spec.ts b/apps/web/src/app/settings/settings.component.form.spec.ts index 5cd4b2d3d..41484cce2 100644 --- a/apps/web/src/app/settings/settings.component.form.spec.ts +++ b/apps/web/src/app/settings/settings.component.form.spec.ts @@ -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, diff --git a/apps/web/src/app/settings/settings.component.ts b/apps/web/src/app/settings/settings.component.ts index 5f95e8b07..f43b3eb30 100644 --- a/apps/web/src/app/settings/settings.component.ts +++ b/apps/web/src/app/settings/settings.component.ts @@ -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, diff --git a/docs/architecture/zoneless-migration.md b/docs/architecture/zoneless-migration.md index 3873f3389..2dc07968c 100644 --- a/docs/architecture/zoneless-migration.md +++ b/docs/architecture/zoneless-migration.md @@ -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) @@ -175,7 +175,7 @@ the field a signal (or a `computed`), or writes it through one. | [x] | `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 | | [x] | `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 | | [x] | 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