perf(ui): lazy-load content-card images + OnPush on sources filters panel

Two small focused changes from an Angular perf audit.

content-card lazy loading
ContentCard is the building block for catalog grids and rails — Xtream
recently-added (3 rails × 20 = 60 cards on first paint), Xtream/Stalker
search results, unified favorites/recent grids. The poster <img> had
no loading attribute, so every off-screen card eagerly fetched its
poster from the Xtream/Stalker server on initial render. Same fix
already in place on the dashboard rail's separate <img>; mirror it on
the shared ContentCard so every consumer benefits.

Add loading="lazy" decoding="async" to both <img> tags (real poster
and the default-poster fallback). Browser defers off-screen requests;
no JS changes.

OnPush on workspace-sources-filters-panel
The sources filters panel renders one row per filter option and
displays {{ getTypeCount(option.id) }} per row — that method is a
plain Map lookup but fires every CD cycle under default change
detection. The component is signal-only (computed + selectSignal, no
subscribes) so the OnPush conversion is purely additive.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f957cd9849e0
This commit is contained in:
4grayandClaude Opus 4.7 committed 2026-05-02 17:58:59 +02:00
1 parent 6b1e4a3ba7
commit cf30c129f1
2 files changed
+11 -1

No files matched your search

@@ -17,6 +17,8 @@
[alt]="title()"
(error)="onImageError($event)"
class="poster"
loading="lazy"
decoding="async"
/>
} @else if (showPlaceholder()) {
<div class="poster-placeholder">
@@ -27,6 +29,8 @@
src="./assets/images/default-poster.png"
[alt]="title()"
class="poster"
loading="lazy"
decoding="async"
/>
}
@@ -1,4 +1,9 @@
import { computed, Component, inject } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
computed,
inject,
} from '@angular/core';
import { MatIcon } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list';
import { Store } from '@ngrx/store';
@@ -25,6 +30,7 @@ const ALL_FILTERS = ['m3u', 'xtream', 'stalker'];
imports: [MatIcon, MatListModule, TranslatePipe],
templateUrl: './workspace-sources-filters-panel.component.html',
styleUrl: './workspace-sources-filters-panel.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class WorkspaceSourcesFiltersPanelComponent {
private readonly store = inject(Store);