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 ? : '▶'} )}