[feat](trx-frontend-http): auto-subscribe new sessions to primary channel

When a new tab connects and receives the initial channels event,
automatically subscribe to channel 0 (primary) so the session joins
the same tuned channel as other users on that rig. Uses a lightweight
auto-join that skips scheduler control takeover since audio isn't
started yet at this point.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-03-22 06:15:47 +01:00
parent 0e195bd3c6
commit 6fb7b61c1c
@@ -130,9 +130,15 @@ function vchanHandleChannels(data) {
const d = JSON.parse(data);
vchanRigId = d.rig_id || null;
vchanChannels = d.channels || [];
// If the active channel was evicted, fall back to channel 0 and reconnect audio.
const ids = new Set(vchanChannels.map(c => c.id));
if (vchanActiveId && !ids.has(vchanActiveId)) {
if (!vchanActiveId && vchanChannels.length > 0 && vchanSessionId) {
// First channels event for this session — auto-subscribe to channel 0
// so we join the same tuned channel as other users on this rig.
// Use a direct subscribe (no scheduler control takeover) to avoid
// side-effects on initial connect.
vchanAutoJoinPrimary(vchanChannels[0].id);
} else if (vchanActiveId && !ids.has(vchanActiveId)) {
// Active channel was evicted — fall back to channel 0 and reconnect audio.
vchanActiveId = vchanChannels.length > 0 ? vchanChannels[0].id : null;
vchanReconnectAudio();
}
@@ -243,6 +249,31 @@ async function vchanDelete(channelId) {
}
}
// Lightweight auto-join for initial connect: registers the session on
// channel 0 without taking scheduler control or reconnecting audio
// (audio isn't started yet at this point).
async function vchanAutoJoinPrimary(channelId) {
if (!vchanSessionId || !vchanRigId) return;
try {
const resp = await fetch(
`/channels/${encodeURIComponent(vchanRigId)}/${encodeURIComponent(channelId)}/subscribe`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ session_id: vchanSessionId }),
}
);
if (!resp.ok) {
console.warn("vchan: auto-join primary failed", resp.status);
return;
}
vchanActiveId = channelId;
vchanRender();
} catch (e) {
console.error("vchan: auto-join error", e);
}
}
async function vchanSubscribe(channelId) {
if (!vchanSessionId || !vchanRigId) return;
try {