diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-10 17:49:58 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-10 17:49:58 +0200 |
| commit | 07ff8b4f6143039fcc74b8cf7c423282bce093c1 (patch) | |
| tree | 89e095aeef9f5ad0bcbe7b3e6cfdc67d36bcbba8 /packages/meshbay-hub/src/meshbay_hub/static | |
| parent | 1e6f3861a1570029897a30b42121836fd03565c1 (diff) | |
| download | meshbay-07ff8b4f6143039fcc74b8cf7c423282bce093c1.tar.gz | |
refactor(mnp)!: one operation for an app's folders, not one per app
`video_root`, `audio_root` and `photo_roots` are gone — the messages, the
signed operations, the handlers, the `ops` wrappers, the three scalars on the
handshake ack, and the client's handlers for their acks. `app_directories`
does the same thing for every application, keyed by the app's own registry
name, and it is what the SPA has been sending.
The three were the same instruction three times, differing only in the key they
wrote and whether they carried a string or a list. That shape is what made
adding an application mean adding a message type, an op, a handler and a widget;
it also meant three validation paths, and the older ones validated nothing —
a typo was stored and then quietly matched no entry, an app showing an empty tab
with no way to tell "misconfigured" from "no files yet".
**What stays, and why.** `Roster.LEGACY_DIR_KEYS` still reads `video_root` and
friends out of `group_settings`: that is a key on an operator's disk, not on the
wire, and a node upgraded into this must find its own configuration. The Search
page still reads its own older cache keys, for the same reason — the cache
outlives a deploy. `CTX_ALIASES` keeps only `chat`, which is the one app whose
second name something still reads.
The two per-app policy test files go with the messages. What only they held —
the real challenge/response path from message to database, which no other test
exercises — is retargeted at `app_directories` in
`test_app_directories_signed.py`, and the handler's own refusals (unknown app,
malformed `directories`, nobody to authorize it) join `test_app_directories.py`.
Node and common suites 1368 passed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AsoWC3GmhNdwVFomW3QjH3
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static')
3 files changed, 10 insertions, 40 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js index 341d37e..fec685b 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js @@ -355,11 +355,9 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, // missing `video_directories` as "nothing configured" would empty a // working Videos tab. setAppDirectories({ - video: ack.video_directories - || (ack.video_root ? [ack.video_root] : []), - music: ack.music_directories - || (ack.audio_root ? [ack.audio_root] : []), - photo: ack.photo_directories || ack.photo_roots || [], + video: ack.video_directories || [], + music: ack.music_directories || [], + photo: ack.photo_directories || [], }); setChatDirectory(ack.chat_directory || ''); setChatLinkPreview(ack.chat_link_preview !== false); @@ -374,16 +372,9 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, // recent value. transport.onTmdbConfig = (cfg) => setTmdbConfig((prev) => ({ ...(prev || {}), ...cfg })); transport.onTmdbEnabled = (enabled) => setTmdbConfig((prev) => ({ ...(prev || {}), enabled })); - // One handler for every app's directories, plus the three older - // per-app messages a node that predates the generic op still sends. + // One handler for every app's directories, keyed by the app's name. transport.onAppDirectories = (app, dirs) => setAppDirectories((prev) => ({ ...prev, [app]: dirs })); - transport.onVideoRoot = (path) => setAppDirectories( - (prev) => ({ ...prev, video: path ? [path] : [] })); - transport.onAudioRoot = (path) => setAppDirectories( - (prev) => ({ ...prev, music: path ? [path] : [] })); - transport.onPhotoRoots = (roots) => setAppDirectories( - (prev) => ({ ...prev, photo: roots || [] })); transport.onChatDirectory = (path) => setChatDirectory(path); transport.onChatLinkPreview = (on) => setChatLinkPreview(on); transport.onMusicbrainzEnabled = (enabled) => diff --git a/packages/meshbay-hub/src/meshbay_hub/static/search-page.js b/packages/meshbay-hub/src/meshbay_hub/static/search-page.js index 81d4ac2..ee99ccc 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/search-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/search-page.js @@ -151,9 +151,9 @@ async function fetchGroupIndex(groupId, token, bundleKey, username, userId) { // Plural, with the old scalars as the fallback for a node still speaking // MNP 1.0 — the same reading group-page.js does on its own handshake. const roots = { - video: ack.video_directories || (ack.video_root ? [ack.video_root] : []), - music: ack.music_directories || (ack.audio_root ? [ack.audio_root] : []), - photo: ack.photo_directories || ack.photo_roots || [], + video: ack.video_directories || [], + music: ack.music_directories || [], + photo: ack.photo_directories || [], }; // Which of the reader's groups sit on their own node — the tie-breaker // when the same file is announced by several of them diff --git a/packages/meshbay-hub/src/meshbay_hub/static/transport.js b/packages/meshbay-hub/src/meshbay_hub/static/transport.js index 2e3712c..32005b7 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/transport.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/transport.js @@ -75,8 +75,8 @@ function _aborted() { // back-to-back sequence) fired two of these within milliseconds of each // other. Both admin_challenge replies, and both domain acks afterward, // were routed by nothing more than "whichever request happens to be -// oldest pending" — apps_enabled's challenge stole audio_root's slot, then -// audio_root's own request just sat there until its 30s timeout, having +// oldest pending" — apps_enabled's challenge stole app_directories' slot, +// then that request just sat there until its 30s timeout, having // never received a challenge to answer at all. Keying both hops by op name // (below, in _key and in _dispatch) fixes this without needing the node to // change anything — `op` is already on every admin_challenge, and this @@ -532,9 +532,6 @@ class MeshBayTransport { set onChatEpoch(fn) { this._onChatEpoch = fn; } set onTmdbConfig(fn) { this._onTmdbConfig = fn; } set onTmdbEnabled(fn) { this._onTmdbEnabled = fn; } - set onVideoRoot(fn) { this._onVideoRoot = fn; } - set onAudioRoot(fn) { this._onAudioRoot = fn; } - set onPhotoRoots(fn) { this._onPhotoRoots = fn; } set onMusicbrainzEnabled(fn) { this._onMusicbrainzEnabled = fn; } set onIndexProgress(fn) { this._onIndexProgress = fn; } // Fired when a message that must open under the group key does not — @@ -1961,7 +1958,7 @@ class MeshBayTransport { // signature) — carried so _sendAndWait can key this reply by op, the // same way the admin_challenge that preceded it was keyed. Without // it, two admin_response replies in flight together (e.g. one op's - // audio_root_ack arriving while another's apps_enabled_ack is still + // app_directories_ack arriving while another's apps_enabled_ack is still // pending) are matched by nothing more than arrival order. op: challenge.op, }); @@ -3133,24 +3130,6 @@ class MeshBayTransport { this._onTmdbOverride({ fileId: msg.file_id || '', tmdbId: '', mediaType: '' }); } - // Same shape: the operator changed which folder is the Videos app's - // entry point for this group. - if (msg.type === 'video_root_ack' && this._onVideoRoot) { - this._onVideoRoot(msg.path || ''); - } - - // Same shape: the operator changed which folder is the Music app's - // entry point for this group. - if (msg.type === 'audio_root_ack' && this._onAudioRoot) { - this._onAudioRoot(msg.path || ''); - } - - // Same shape: the operator replaced the Photos app's whole root set - // for this group (docs/photos.md §2.1). - if (msg.type === 'photo_roots_ack' && this._onPhotoRoots) { - this._onPhotoRoots(msg.roots || []); - } - // Per-group, like tmdb_enabled_ack above. if (msg.type === 'musicbrainz_enabled_ack' && this._onMusicbrainzEnabled) { this._onMusicbrainzEnabled(Boolean(msg.enabled)); |