diff --git a/changelog.json b/changelog.json
index 0788ff7..a6be974 100644
--- a/changelog.json
+++ b/changelog.json
@@ -5,12 +5,14 @@
"en": [
"Cluster filters gain NEW POTA and NEW COUNTY, alongside the existing status chips.",
"A playback that fails to start now says so in the log instead of ending silently after a tenth of a second.",
- "Replaying a recording before the previous one has finished now works: the new playback waits for the audio device to be free instead of keying the radio and releasing at once."
+ "Replaying a recording before the previous one has finished now works: the new playback waits for the audio device to be free instead of keying the radio and releasing at once.",
+ "The play button becomes a stop button while the recording is going out: it cuts the transmission and releases the PTT, and you can send it again straight away."
],
"fr": [
"Les filtres du cluster gagnent NEW POTA et NEW COUNTY, à côté des pastilles de statut existantes.",
"Une lecture qui ne démarre pas le signale désormais dans le journal, au lieu de s'arrêter en silence au bout d'un dixième de seconde.",
- "Relancer un enregistrement avant la fin du précédent fonctionne désormais : la nouvelle lecture attend que le périphérique audio soit libre, au lieu de passer en émission et de relâcher aussitôt."
+ "Relancer un enregistrement avant la fin du précédent fonctionne désormais : la nouvelle lecture attend que le périphérique audio soit libre, au lieu de passer en émission et de relâcher aussitôt.",
+ "Le bouton de lecture devient un bouton d'arrêt pendant l'émission de l'enregistrement : il coupe l'émission et relâche le PTT, et vous pouvez le renvoyer aussitôt."
]
},
{
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 8e7ea5f..3e7057a 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -794,7 +794,15 @@ export default function App() {
const resumeRecording = () => {
QSOAudioResume().then((ok) => { if (ok) { setRecStopped(false); setRecTick((t) => t + 1); } }).catch(() => {});
};
+ // One button, two jobs: play, and stop what is playing.
+ //
+ // A second press used to RESTART the message from the beginning. What an
+ // operator wants there is to cut it short — they have heard enough, or they
+ // pressed it by mistake with the transmitter keyed — and then to be able to
+ // send it again straight away. Stopping releases the PTT through the same
+ // path the natural end uses.
const playRecordingOnAir = () => {
+ if (dvkStat.playing) { DVKStop(); return; }
QSOAudioPlayOnAir().catch((e: any) => setError(String(e?.message ?? e)));
};
const refreshManualRecReady = () => { QSOAudioManualReady().then(setManualRecReady).catch(() => {}); };
@@ -3775,10 +3783,11 @@ export default function App() {
type="button"
onMouseDown={(e) => e.preventDefault()}
onClick={playRecordingOnAir}
- title={t('rec.playOnAir')}
- className="inline-flex items-center justify-center size-4 rounded text-success hover:bg-success/15"
+ title={dvkStat.playing ? t('rec.stopPlaying') : t('rec.playOnAir')}
+ className={cn('inline-flex items-center justify-center size-4 rounded',
+ dvkStat.playing ? 'text-destructive hover:bg-destructive-muted' : 'text-success hover:bg-success/15')}
>
- ▶
+ {dvkStat.playing ? : '▶'}
)}