From df1960b7f9d237646d562f7718d0f8ab8a873902 Mon Sep 17 00:00:00 2001 From: Brian Krabach Date: Thu, 9 Apr 2026 02:37:18 -0700 Subject: [PATCH] fix: apply hidden sessions filter to all sessions including remote/federated --- muxplex/frontend/app.js | 6 +++--- muxplex/frontend/tests/test_app.mjs | 30 ++++++++++++++--------------- muxplex/tests/test_frontend_js.py | 18 ++++++++++------- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/muxplex/frontend/app.js b/muxplex/frontend/app.js index f3451dd..4d112dd 100644 --- a/muxplex/frontend/app.js +++ b/muxplex/frontend/app.js @@ -521,8 +521,8 @@ function buildStatusTileHTML(deviceName, statusText, statusClass) { /** * Returns sessions with hidden session names removed. - * Only hides LOCAL sessions (those with no remoteId) matching the - * hidden_sessions list. Remote sessions with the same name remain visible. + * Hides any session whose name appears in the hidden_sessions list, + * regardless of whether it is local or remote (federated). * Consolidates the hidden-session filter used by all render paths. * @param {object[]} sessions * @returns {object[]} @@ -530,7 +530,7 @@ function buildStatusTileHTML(deviceName, statusText, statusClass) { function getVisibleSessions(sessions) { var hidden = (_serverSettings && _serverSettings.hidden_sessions) || []; return (sessions || []).filter(function(s) { - if (hidden.length > 0 && s.remoteId == null && hidden.includes(s.name)) { + if (hidden.length > 0 && hidden.includes(s.name)) { return false; } return true; diff --git a/muxplex/frontend/tests/test_app.mjs b/muxplex/frontend/tests/test_app.mjs index d108d03..257fc9e 100644 --- a/muxplex/frontend/tests/test_app.mjs +++ b/muxplex/frontend/tests/test_app.mjs @@ -2442,26 +2442,21 @@ test('getVisibleSessions exported and filters hidden sessions', () => { app._setServerSettings(null); }); -test('getVisibleSessions hides local sessions by name but not remote sessions with same name', () => { - // Set server settings with a session name that exists both locally and remotely +test('getVisibleSessions hides remote sessions with matching name (federation-aware)', () => { + // hidden_sessions syncs across federation nodes, so the filter must apply to + // ALL sessions regardless of remoteId — both local and federated. app._setServerSettings({ hidden_sessions: ['shared-name'] }); const sessions = [ { name: 'shared-name' }, // local (no remoteId) — should be hidden - { name: 'shared-name', remoteId: 1 }, // remote — should remain visible + { name: 'shared-name', remoteId: 1 }, // remote — should ALSO be hidden { name: 'another' }, // local, not in hidden list — should remain ]; const result = app.getVisibleSessions(sessions); - assert.strictEqual(result.length, 2, 'should show 2 sessions (remote + another)'); - // The remote one should survive - const remote = result.find((s) => s.remoteId === 1); - assert.ok(remote, 'remote session with same name should not be hidden'); - assert.strictEqual(remote.name, 'shared-name', 'remote session name should be shared-name'); - // The local one should be hidden - assert.ok(!result.some((s) => !s.remoteId && s.name === 'shared-name'), 'local session with hidden name should be removed'); - // another should remain + assert.strictEqual(result.length, 1, 'should show only 1 session (another); both local and remote shared-name are hidden'); assert.ok(result.some((s) => s.name === 'another'), 'another should remain visible'); + assert.ok(!result.some((s) => s.name === 'shared-name'), 'all sessions named shared-name (local or remote) should be hidden'); // Clean up app._setServerSettings(null); @@ -4622,23 +4617,26 @@ test('getDisplaySettings reads display keys from _serverSettings with DISPLAY_DE // --- getVisibleSessions: remoteId=0 falsy-zero bug fix --- -test('getVisibleSessions does NOT hide sessions with remoteId 0 whose name is in hidden_sessions', () => { +test('getVisibleSessions hides remote sessions with remoteId=0 when name is in hidden_sessions', () => { + // The filter no longer guards on remoteId — hidden_sessions applies to all sessions, + // local and federated alike (settings now sync across nodes). app._setServerSettings({ hidden_sessions: ['work'] }); const sessions = [{ name: 'work', remoteId: 0 }]; const visible = app.getVisibleSessions(sessions); - assert.strictEqual(visible.length, 1, 'session with remoteId=0 must NOT be hidden even if name is in hidden_sessions'); + assert.strictEqual(visible.length, 0, 'session with remoteId=0 must be hidden when its name is in hidden_sessions'); app._setServerSettings(null); }); -test('getVisibleSessions hides local session (remoteId null) but keeps remote session (remoteId 0) with same name', () => { +test('getVisibleSessions hides all sessions (local and remoteId=0) with names in hidden_sessions', () => { + // Both the local session (remoteId=null) and the remote session (remoteId=0) + // should be hidden when hidden_sessions syncs across federation nodes. app._setServerSettings({ hidden_sessions: ['work'] }); const sessions = [ { name: 'work', remoteId: null }, { name: 'work', remoteId: 0 }, ]; const visible = app.getVisibleSessions(sessions); - assert.strictEqual(visible.length, 1, 'only the remote session (remoteId=0) should be visible'); - assert.strictEqual(visible[0].remoteId, 0, 'the visible session must be the remote one with remoteId=0'); + assert.strictEqual(visible.length, 0, 'both local and remote sessions named "work" should be hidden'); app._setServerSettings(null); }); diff --git a/muxplex/tests/test_frontend_js.py b/muxplex/tests/test_frontend_js.py index f2d4ada..4bb427c 100644 --- a/muxplex/tests/test_frontend_js.py +++ b/muxplex/tests/test_frontend_js.py @@ -2715,9 +2715,13 @@ def test_bind_sidebar_click_away_calls_patch_server_setting() -> None: # ─── task-1: getVisibleSessions falsy-0 bug fix ──────────────────────────── -def test_get_visible_sessions_uses_null_check_not_falsy() -> None: - """getVisibleSessions must use s.remoteId == null (not !s.remoteId) to avoid - treating remoteId=0 (first remote instance) as falsy and incorrectly hiding it.""" +def test_get_visible_sessions_has_no_remote_id_filter() -> None: + """getVisibleSessions must not check s.remoteId at all. + + After federation settings sync, hidden_sessions applies to both local and + remote sessions. The filter must check session name only, with no remoteId + guard. Must also not use !s.remoteId which would treat remoteId=0 as falsy. + """ match = re.search( r"function getVisibleSessions\s*\(\w+\)\s*\{(.*?)(?=\n(?:function|//|window\.))", _JS, @@ -2727,11 +2731,11 @@ def test_get_visible_sessions_uses_null_check_not_falsy() -> None: body = match.group(1) assert "!s.remoteId" not in body, ( "getVisibleSessions must NOT use '!s.remoteId' — this treats remoteId=0 as falsy, " - "hiding the first remote instance. Use 's.remoteId == null' instead." + "hiding the first remote instance." ) - assert "s.remoteId == null" in body or "s.remoteId === null" in body, ( - "getVisibleSessions must use 's.remoteId == null' (or '=== null') to correctly treat " - "remoteId=0 as a real remote while matching null/undefined as local sessions." + assert "s.remoteId" not in body, ( + "getVisibleSessions must NOT reference s.remoteId at all — the filter applies to " + "all sessions regardless of origin (local or federated)." )