From 4e1eb15b90b6c9e09ec0b0be0e87e96ca7e434d5 Mon Sep 17 00:00:00 2001 From: 4gray Date: Tue, 13 Oct 2026 19:01:56 +0200 Subject: [PATCH] fix(sources): show a new search's first match; keep inconclusive check causes Local review round 6 (Codex; Greptile was clean at 5/5): - Typing a search or picking a type in the switcher highlighted the first match but kept the list scrolled where the previous result left it, so the highlighted row could sit above the view. The list returns to the top whenever the search or type changes. - A source with no kept history whose check was inconclusive (connection checks paused by the connectivity guard, a cancelled check) lost the live cause and time, and its dot said only "Not verified". The status now keeps them; a running check still reads as checking. Co-Authored-By: Claude Opus 5.5 --- .../playlist-switcher-panel.component.spec.ts | 13 +++++++++++++ .../playlist-switcher-panel.component.ts | 10 ++++++++++ .../src/lib/source-status.service.spec.ts | 14 ++++++++++++++ .../data-access/src/lib/source-status.service.ts | 9 ++++++++- 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/libs/playlist/shared/ui/src/lib/playlist-switcher/playlist-switcher-panel.component.spec.ts b/libs/playlist/shared/ui/src/lib/playlist-switcher/playlist-switcher-panel.component.spec.ts index d001c9838..736c7775a 100644 --- a/libs/playlist/shared/ui/src/lib/playlist-switcher/playlist-switcher-panel.component.spec.ts +++ b/libs/playlist/shared/ui/src/lib/playlist-switcher/playlist-switcher-panel.component.spec.ts @@ -194,6 +194,19 @@ describe('PlaylistSwitcherPanelComponent', () => { expect(closed).toHaveBeenCalled(); }); + it('scrolls a new search back to its first match at the top', () => { + const list = (fixture.nativeElement as HTMLElement).querySelector( + '.switcher__list' + ) as HTMLElement; + list.scrollTop = 120; + type('a'); + expect(list.scrollTop).toBe(0); + + list.scrollTop = 120; + component.setType('xtream'); + expect(list.scrollTop).toBe(0); + }); + it('moves through options whose IDs a selector would misread', () => { const restored = { ...ALPHA, _id: 'odd:id.one', title: 'Aardvark' }; playlists.set([restored, BRAVO]); diff --git a/libs/playlist/shared/ui/src/lib/playlist-switcher/playlist-switcher-panel.component.ts b/libs/playlist/shared/ui/src/lib/playlist-switcher/playlist-switcher-panel.component.ts index f67f620de..4e41812c4 100644 --- a/libs/playlist/shared/ui/src/lib/playlist-switcher/playlist-switcher-panel.component.ts +++ b/libs/playlist/shared/ui/src/lib/playlist-switcher/playlist-switcher-panel.component.ts @@ -205,11 +205,21 @@ export class PlaylistSwitcherPanelComponent { setQuery(value: string): void { this.query.set(value); + this.scrollToTop(); } setType(type: SourcesTypeFilter): void { this.type.set(type); writeSwitcherType(type); + this.scrollToTop(); + } + + /** + * A new search or type highlights the first match, at the top of the + * list: a list scrolled down for the previous one would hide it. + */ + private scrollToTop(): void { + this.list().nativeElement.scrollTop = 0; } onSearchKeydown(event: KeyboardEvent): void { diff --git a/libs/portal/shared/data-access/src/lib/source-status.service.spec.ts b/libs/portal/shared/data-access/src/lib/source-status.service.spec.ts index 085319db3..213295db4 100644 --- a/libs/portal/shared/data-access/src/lib/source-status.service.spec.ts +++ b/libs/portal/shared/data-access/src/lib/source-status.service.spec.ts @@ -178,6 +178,20 @@ describe('SourceStatusService', () => { expect(service.statusOf(m3u)?.pending).toBe(true); }); + it('says why an inconclusive check is inconclusive', () => { + snapshots.set('m', { + state: 'unavailable', + reason: 'paused', + confirmedInactive: false, + checkedAt: 700, + }); + expect(create(true).statusOf(m3u)).toEqual({ + reachability: 'unknown', + reason: 'paused', + checkedAt: 700, + }); + }); + it('reports an unchecked source as unknown', () => { expect(create(true).statusOf(m3u)).toEqual({ reachability: 'unknown', diff --git a/libs/portal/shared/data-access/src/lib/source-status.service.ts b/libs/portal/shared/data-access/src/lib/source-status.service.ts index b6039757d..a71cfd38d 100644 --- a/libs/portal/shared/data-access/src/lib/source-status.service.ts +++ b/libs/portal/shared/data-access/src/lib/source-status.service.ts @@ -116,7 +116,14 @@ export class SourceStatusService { reason: kept.reason, }; } - return { reachability }; + // An inconclusive check still says why ("Connection checks paused"). + return reachability === 'checking' || !live + ? { reachability } + : { + reachability, + reason: live.reason, + checkedAt: live.checkedAt || undefined, + }; } /** A passive check: cached results count; the signal cancels it. */