From 862ddbb87daeb2174aa7eb891789e1a1af71e005 Mon Sep 17 00:00:00 2001 From: Tobias Gesellchen Date: Sun, 30 Aug 2026 22:15:21 +0200 Subject: [PATCH] fix(player): follow Library's selected device to its pair master If the device selected in the Library tab disappeared from the devices map (e.g. it just became a hidden stereo-pair member per device_projection.go), the sync effect fell back to entries[0][0] -- whichever key happens to sort first in the map -- silently redirecting the user's Library browsing session to an unrelated speaker. Now checks first whether the vanished device reappears as a member of some other device's stereoPair (the pair's master, which now represents the same physical speaker for control purposes) and follows it there. Only falls back to an arbitrary device when the selection is gone for a genuinely unrelated reason (removed, discovery gap), matching the prior behavior for that case. No JS unit-test framework exists in this repo for component-level logic (consistent with the rest of the client-side code), so this is verified by manual trace rather than an automated regression test. Co-Authored-By: Claude Sonnet 5 --- .../static/js/components/Library.js | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/pkg/service/soundtouchweb/static/js/components/Library.js b/pkg/service/soundtouchweb/static/js/components/Library.js index 239e491f..3c7a281a 100644 --- a/pkg/service/soundtouchweb/static/js/components/Library.js +++ b/pkg/service/soundtouchweb/static/js/components/Library.js @@ -23,11 +23,26 @@ export function Library({ devices }) { // invalidate the current selection. useEffect(() => { const entries = Object.entries(devices); - if ((!deviceId || !devices[deviceId]) && entries.length > 0) { - setDeviceId(entries[0][0]); - } else if (deviceId && entries.length === 0) { - setDeviceId(null); + if (deviceId && devices[deviceId]) return; + + if (entries.length === 0) { + if (deviceId) setDeviceId(null); + return; } + + if (deviceId) { + // The selected device vanished from the list -- if that's because + // it just became a hidden stereo-pair member (see + // device_projection.go), follow it to its pair's master instead + // of silently jumping to an unrelated device. + const master = entries.find(([, d]) => d.stereoPair?.members?.some(m => m.ipAddress === deviceId)); + if (master) { + setDeviceId(master[0]); + return; + } + } + + setDeviceId(entries[0][0]); }, [devices, deviceId]); // Reload registered servers whenever deviceId changes.