fix(switcher): fall back to All when the remembered type has no sources left

Local review round 7 (Greptile 5/5 with one P2; Codex was clean): when the
last source of the remembered switcher type was removed while two other
types remained, the list kept filtering by the missing type. Its segment
was gone, no segment was selected, and the list said "Nothing found". The
list now filters by All until a source of that type returns; the remembered
choice is kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5.5 committed 2026-10-13 20:13:55 +02:00
1 parent 4e1eb15b90
commit 89d64472c0
3 files changed
+35 -3

No files matched your search

@@ -60,7 +60,7 @@
<mat-button-toggle-group
class="switcher__types"
hideSingleSelectionIndicator
[value]="type()"
[value]="activeType()"
(change)="setType($event.value)"
[attr.aria-label]="'WORKSPACE.SOURCES.TYPE' | translate"
>
@@ -233,6 +233,25 @@ describe('PlaylistSwitcherPanelComponent', () => {
).toContain('WORKSPACE.SWITCHER.NO_MATCHES');
});
it('falls back to All when the remembered type has no sources left', () => {
component.setType('xtream');
fixture.detectChanges();
expect(optionIds()).toEqual(['playlist-switcher-all-bravo']);
// The last Xtream source is removed; two other types remain.
playlists.set([ALPHA, CHARLIE]);
fixture.detectChanges();
expect(component.activeType()).toBe('all');
expect(optionIds()).toEqual([
'playlist-switcher-all-alpha',
'playlist-switcher-all-charlie',
]);
// The choice is kept for when an Xtream source returns.
expect(localStorage.getItem('playlist-switcher:type:v2')).toBe(
'xtream'
);
});
it('filters by type with one segment and remembers it', () => {
component.setType('xtream');
fixture.detectChanges();
@@ -135,6 +135,19 @@ export class PlaylistSwitcherPanelComponent {
readonly showTypeFilter = computed(
() => KINDS.filter((kind) => this.typeCounts()[kind] > 0).length > 1
);
/**
* The type the list filters by: the remembered one while it has sources
* and its segment shows, else All. Removing the last source of the
* remembered type would otherwise leave an empty list with no segment
* selected. The remembered choice itself is kept for when it returns.
*/
readonly activeType = computed<SourcesTypeFilter>(() => {
const type = this.type();
return this.showTypeFilter() &&
(type === 'all' || this.typeCounts()[type] > 0)
? type
: 'all';
});
/** The Sources page order, so both lists agree. */
private readonly ordered = computed(() => {
const options = this.sortOptions();
@@ -151,7 +164,7 @@ export class PlaylistSwitcherPanelComponent {
buildSwitcherGroups(
this.ordered(),
this.recentHistory.ids(),
this.showTypeFilter() ? this.type() : 'all',
this.activeType(),
this.query()
)
);
@@ -174,7 +187,7 @@ export class PlaylistSwitcherPanelComponent {
>({
source: () => ({
query: this.query(),
type: this.type(),
type: this.activeType(),
ids: this.options().map((option) => option.id),
}),
computation: (source, previous) => {