refactor(frontend): adopt the lifecycle and the notification surface

The remaining views, brought onto the two mechanisms added earlier in
this series.

The lifecycle: every cached primary view moves its document listeners,
intervals and event subscriptions off connect/disconnect and onto
`viewActivated`/`viewDeactivated`, so `autotag-view` stops fielding
keystrokes from Settings, `downloads-view`'s 30 s clock stops ticking
for the session, and an off-screen view stops rendering on every
search keystroke. `autotag-view` keeps a document listener only for
Escape, whose dialogs Phase 5 migrates to wa-dialog anyway.

The voice: the silent failures now speak — scan and full rescan (with
a guard against the double-click the coalescing window allowed), job
pause/resume/cancel, playlist delete, download request pause/remove/
clear, add and rename library, add-to-playlist, playlist track
removal, autotag's dialogs and its apply, and favourite reverts. Both
private toasts are gone, along with their CSS and keyframes. Playlist
delete (single and the multi-select loop), download-request removal,
download-client removal and a queue clear over 20 tracks ask first.

Loading, empty and failed become three states rather than one, in
`track-list` and `genre-details` — the first is on the first screen a
new user ever sees — and the Settings index panel seeds itself with
`GetIndexStatus()` instead of waiting forever for a change event.
`smart-playlist-editor` and `download-picker` take the request-version
guard `explore-view` already had.

`track-details` loads through one memoised dynamic import in all ten
openers, which is what takes its 42 kB out of the startup chunk: an
un-upgraded custom element is a real HTMLElement on which `?.show()`
throws, so each opener awaits it before touching the element its
template already rendered.
This commit is contained in:
2026-08-12 01:19:47 -04:00
parent c8bc6db9fa
commit 2518385330
18 changed files with 930 additions and 375 deletions
@@ -15,6 +15,7 @@ type MBTrack = explore.MBTrack;
import { exploreCache } from '../../store/explore-cache';
import { libraryStore } from '../../store/library-store';
import { artistLink, exploreLinkStyles } from '../../utils/explore-link';
import { describeError } from '../../utils/describe-error';
import { EventsOn } from '@runtime/runtime';
import { Events } from '../../events';
import '@awesome.me/webawesome/dist/components/icon/icon.js';
@@ -664,22 +665,13 @@ export class ExploreAlbumDetails extends LitElement {
// Local-only album (no MBID) — populate entirely from library.
if (!mbid && this.localAlbumId) {
console.log(
`[explore-album] loading local-only: "${this.albumName}" (id=${this.localAlbumId})`,
);
await this.hydrateLocalOnly();
console.log(
`[explore-album] loaded (local-only): "${this.albumName}"`,
);
return;
}
console.log(
`[explore-album] loading: "${this.albumName}" (${mbid})`,
);
// Phase 0: hydrate from explore cache (instant).
const cached = exploreCache.getAlbum(mbid);
@@ -692,7 +684,6 @@ export class ExploreAlbumDetails extends LitElement {
primaryType: 'Album',
} as MBReleaseGroup;
this.loadingInfo = false;
console.log(`[explore-album] hydrated from cache: "${cached.title}"`);
}
// Phase 1: hydrate tracklist from local library if available.
@@ -712,9 +703,6 @@ export class ExploreAlbumDetails extends LitElement {
// Fire cover art resolution immediately — doesn't wait for release group.
this.resolveCoverArt();
console.log(
`[explore-album] data requests fired: "${this.albumName}" (${mbid})`,
);
}
/**
@@ -874,9 +862,6 @@ export class ExploreAlbumDetails extends LitElement {
this.buildClusters();
this.loadingReleases = false;
console.log(
`[explore-album] hydrated ${localTracks.length} tracks from library`,
);
return true;
}
@@ -908,9 +893,11 @@ export class ExploreAlbumDetails extends LitElement {
this.releaseGroup = await LookupReleaseGroup(mbid);
this.resolveCoverArt();
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
this.errorInfo = msg;
console.error(`[explore-album] LookupReleaseGroup error: ${msg}`);
console.error('[explore-album] LookupReleaseGroup error', err);
this.errorInfo = describeError(
err,
'The catalog did not answer for this album.',
);
} finally {
this.loadingInfo = false;
}
@@ -965,9 +952,11 @@ export class ExploreAlbumDetails extends LitElement {
this.catalogPending = false;
}
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
this.errorReleases = msg;
console.error(`[explore-album] BrowseReleases error: ${msg}`);
console.error('[explore-album] BrowseReleases error', err);
this.errorReleases = describeError(
err,
'The catalog did not answer for this album\u2019s versions.',
);
this.loadingReleases = false;
this.catalogPending = false;
}