Keep the Radio tuning dial silent unless a stream is playing

Releasing the tuning dial always started the chosen station, plus tuning
static, even when nothing was playing. It now follows the Previous/Next
rule: the release plays only if a stream was loading, buffering or playing
when the drag began; otherwise it just selects the station.

The privacy note no longer lists finishing tuning as a way to connect and
now names voice instead, in all locales. Docs, changelog and comments are
tightened to match.
This commit is contained in:
German Khokhlov committed 2026-11-02 13:27:55 +04:00
1 parent 0ec6735440
commit 857b007fb1
21 files changed
+70 -34

No files matched your search

+4 -1
View File
@@ -902,12 +902,15 @@ test('failed exact tuner release cannot consume a stale playback fallback', asyn
stations: stationRows,
failingUrls: [stationRows[0].streamUrl],
}, async ({ playUrls }) => {
assert.equal(radioLayer.selectStation(stationRows[0].id, { autoplay: false }), true);
// A playing stream, so the release plays.
assert.equal(radioLayer.selectStation(stationRows[1].id, { autoplay: true }), true);
await new Promise((resolve) => setTimeout(resolve));
assert.equal(radioLayer.cycleStation(1, {
stationIds: stationRows.map(({ id }) => id),
autoplay: false,
fallback: true,
}), true, 'a voice-style prepared cycle creates the stale-fallback precondition');
playUrls.length = 0;
assert.equal(radioLayer.beginTuning(), true);
assert.equal(radioLayer.previewTuningStation(stationRows[0].id, { rotate: false }), true);
assert.equal(radioLayer.commitTuningStation(stationRows[0].id, { origin: 'user' }).ok, true);
+26 -2
View File
@@ -1,5 +1,4 @@
// Radio Next/Previous that only select (autoplay: false), against the real layer
// and the panel that renders it.
// Radio selection, fallbacks and voice handoffs on the real layer and its published view.
import assert from 'node:assert/strict';
import test from 'node:test';
import { radioLayer } from './radio.js';
@@ -247,3 +246,28 @@ test('while a stream connects the transport buttons offer Pause, because a click
assert.equal(shown.button, 'PAUSE');
assert.equal(shown.cockpit, 'Ⅱ');
});
test('a dial release plays only if a stream was active when the drag began', async () => {
await withLayer([], async ({ playUrls }) => {
radioLayer.selectStation(ids[0], { autoplay: false });
assert.equal(radioLayer.beginTuning(), true);
radioLayer.previewTuningStation(ids[1], { rotate: false });
assert.equal(radioLayer.commitTuningStation(ids[1], { origin: 'user' }).ok, true);
await settle();
let state = radioLayer.getUIState();
assert.deepEqual(playUrls, []);
assert.equal(state.selected?.id, ids[1]);
assert.equal(state.audioState, 'stopped');
assert.equal(state.tuningStatic, false);
await radioLayer.togglePlayback({ origin: 'user' });
assert.equal(radioLayer.beginTuning(), true);
radioLayer.previewTuningStation(ids[2], { rotate: false });
radioLayer.commitTuningStation(ids[2], { origin: 'user' });
await settle();
state = radioLayer.getUIState();
assert.deepEqual(playUrls, [stations[1].streamUrl, stations[2].streamUrl]);
assert.equal(state.audioState, 'playing');
assert.equal(state.playingStationId, ids[2]);
});
});