feat(shell): draw only the destinations the user kept

The navigation reads the resolved map from the backend rather than
holding a copy of the defaults, which would be the copy that shipped in
the binary rather than the one being edited.

Hiding takes away the nav item and nothing else: `navigate` still
resolves a hidden view, which detail views and the launch page depend
on. No special case was needed for the highlight, because #72 moved
that onto `active-view-store` -- the sidebar asks `isActive(id)` per
*rendered* item, so a hidden view lights nothing exactly as a detail
view does.

Downloads is gated at the nav on `downloadStore.available` rather than
in the config, so switching it on in Settings still means what it says
once a client exists, and the tab appears without a restart. `available`
is false until the providers have loaded, which makes the item appear on
a fresh launch rather than appearing and then vanishing.

The tab bar honours the toggles too, and the reason is local rather than
a general rule about phones: "More" opens the *same* `<app-sidebar>`,
which filters, so an unfiltered bar would contradict its own drawer one
tap away. Which four tabs is still plan 016's subset; this only removes
from it, and "More" is never filtered.

`services/view-meta.ts` is the destination list, on `shortcut-meta.ts`'s
pattern, because Settings is now a second reader of the same labels in
the same order.

Two existing sidebar tests had to say which world they describe: eleven
destinations now assumes a configured download client.
This commit is contained in:
2026-08-19 19:33:44 -04:00
parent a3926704cc
commit 43d78a731a
10 changed files with 622 additions and 29 deletions
+21
View File
@@ -189,6 +189,8 @@ class DownloadStore {
private initialized = false;
private providersLoaded = false;
constructor() {
EventsOn(Events.DownloadProvidersChanged, () => {
void this.refreshProviders();
@@ -285,6 +287,25 @@ class DownloadStore {
}
}
/**
* Loads the providers, and only those, once.
*
* `init()` additionally fetches the descriptors, the downloads and
* the request list, which is right for a page about downloading and
* wrong for the sidebar: it only needs `available`, to decide
* whether the Downloads destination exists at all (#25), and that
* is one query. `DownloadProvidersChanged` keeps it current
* afterwards, so configuring a client makes the tab appear without
* a restart.
*/
async ensureProviders(): Promise<void> {
if (this.providersLoaded) return;
this.providersLoaded = true;
await this.refreshProviders();
}
async refreshProviders(): Promise<void> {
try {
this.providersValue = (await ListProviders()) ?? [];