From 0bdce359c0bf19fa98f1b7f9975be32704abe1f0 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Fri, 25 Sep 2026 10:47:37 +0200 Subject: feat(client): fetch the media apps, the player, settings and search on first use apps.js registers Videos, Music, Photos and every settings pane through lazy.js; the group page does the same for the video player and the settings panel, and the shell for the search page. The first download goes from 49 modules / 386 KB gzip to 37 / 289 KB; opening Music now fetches music-app.js and media-tiles.js and nothing of Videos. test_first_load_is_lean holds the eager graph; test_spa_imports checks that every on-demand load names an export that exists. Co-Authored-By: Claude Opus 5.5 --- .../meshbay-hub/src/meshbay_hub/static/lazy.js | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 packages/meshbay-hub/src/meshbay_hub/static/lazy.js (limited to 'packages/meshbay-hub/src/meshbay_hub/static/lazy.js') diff --git a/packages/meshbay-hub/src/meshbay_hub/static/lazy.js b/packages/meshbay-hub/src/meshbay_hub/static/lazy.js new file mode 100644 index 0000000..30a564c --- /dev/null +++ b/packages/meshbay-hub/src/meshbay_hub/static/lazy.js @@ -0,0 +1,41 @@ +// A component whose code is fetched the first time it is shown. +// +// Every static `import` reachable from app.js is downloaded before anything +// renders, whether or not the visitor ever opens it: the Videos app, the video +// player and the search page used to cost a member who only chats. Wrapping a +// component here moves its module out of that first download and into the +// moment it is needed; once loaded it is the component itself, rendered with +// the same props, and every later mount is immediate. +// +// A failed fetch (a network drop on a phone) is not remembered: the next time +// the component is shown it asks again, rather than failing for the session. + +import { html, useState, useEffect } from './vendor/htm-preact.js'; + +const SPINNER = html`

`; + +function lazy(load, name, placeholder = SPINNER) { + let Loaded = null; + let pending = null; + + function Lazy(props) { + const [, setReady] = useState(Loaded !== null); + useEffect(() => { + if (Loaded) return undefined; + let alive = true; + if (!pending) { + pending = load().then( + (m) => { Loaded = m[name]; }, + (e) => { pending = null; throw e; }); + } + pending.then(() => { if (alive) setReady(true); }, () => {}); + return () => { alive = false; }; + }, []); + if (!Loaded) return placeholder; + return html`<${Loaded} ...${props} />`; + } + Lazy.displayName = `Lazy(${name})`; + return Lazy; +} + +export { lazy }; -- cgit v1.2.3