Add pause/stop buttons to SOAP note recording
- Add Pause and Stop buttons (hidden until recording starts) - Record button hides during recording (same pattern as encounter) - Pause: suspends MediaRecorder + speech recognition, shows Resume - Resume: handles MediaRecorder state recovery if browser killed it - Stop: triggers the record button's stop flow - Native mobile: haptic feedback + keep-awake + foreground service - Recognition respects pause state (doesn't restart during pause)
This commit is contained in:
parent
6ed2778a12
commit
5bf55499a4
2 changed files with 65 additions and 2 deletions
|
|
@ -42,6 +42,8 @@
|
|||
<div class="card">
|
||||
<div class="record-controls">
|
||||
<button id="soap-record-btn" class="record-btn btn-teal"><i class="fas fa-microphone"></i><span>Dictate</span></button>
|
||||
<button id="soap-pause-btn" class="btn-sm btn-ghost hidden" style="margin-left:8px;"><i class="fas fa-pause"></i> Pause</button>
|
||||
<button id="soap-stop-btn" class="btn-sm hidden" style="margin-left:8px;background:var(--red);color:white;border:none;border-radius:8px;padding:7px 16px;cursor:pointer;font-size:13px;"><i class="fas fa-stop"></i> Stop</button>
|
||||
<div id="soap-recording-indicator" class="recording-indicator hidden"><div class="pulse-dot pulse-teal"></div><span>Dictating... <span id="soap-timer">00:00</span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -13,10 +13,14 @@
|
|||
var soapText = document.getElementById('soap-text');
|
||||
var modelTag = document.getElementById('soap-model-tag');
|
||||
|
||||
var pauseBtn = document.getElementById('soap-pause-btn');
|
||||
var stopBtn = document.getElementById('soap-stop-btn');
|
||||
|
||||
var recorder = new AudioRecorder();
|
||||
recorder._module = 'soap';
|
||||
var timer = createTimer(timerEl);
|
||||
var isRecording = false;
|
||||
var isPaused = false;
|
||||
var recognition = createSpeechRecognition();
|
||||
var finalText = '';
|
||||
var sessionFinals = '';
|
||||
|
|
@ -41,7 +45,7 @@
|
|||
recognition.onend = function() {
|
||||
finalText += sessionFinals;
|
||||
sessionFinals = '';
|
||||
if (isRecording) try { recognition.start(); } catch(e) {}
|
||||
if (isRecording && !isPaused) try { recognition.start(); } catch(e) {}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -51,19 +55,33 @@
|
|||
sessionFinals = '';
|
||||
recorder.start().then(function() {
|
||||
isRecording = true;
|
||||
isPaused = false;
|
||||
recordBtn.classList.add('recording');
|
||||
recordBtn.querySelector('span').textContent = 'Stop';
|
||||
recordBtn.style.display = 'none';
|
||||
if (pauseBtn) pauseBtn.classList.remove('hidden');
|
||||
if (stopBtn) stopBtn.classList.remove('hidden');
|
||||
indicator.classList.remove('hidden');
|
||||
timer.start();
|
||||
if (recognition) try { recognition.start(); } catch(e) {}
|
||||
if (typeof nativeHaptic === 'function') nativeHaptic('heavy');
|
||||
if (typeof nativeKeepAwake === 'function') nativeKeepAwake(true);
|
||||
if (typeof nativeStartRecordingService === 'function') nativeStartRecordingService();
|
||||
showToast('Recording started', 'info');
|
||||
}).catch(function() { showToast('Microphone denied', 'error'); });
|
||||
} else {
|
||||
isRecording = false;
|
||||
isPaused = false;
|
||||
var dur = timer.stop();
|
||||
recordBtn.classList.remove('recording');
|
||||
recordBtn.style.display = '';
|
||||
recordBtn.querySelector('span').textContent = 'Dictate';
|
||||
if (pauseBtn) { pauseBtn.classList.add('hidden'); pauseBtn.innerHTML = '<i class="fas fa-pause"></i> Pause'; pauseBtn.classList.remove('btn-primary'); pauseBtn.classList.add('btn-ghost'); }
|
||||
if (stopBtn) stopBtn.classList.add('hidden');
|
||||
indicator.classList.add('hidden');
|
||||
if (recognition) try { recognition.stop(); } catch(e) {}
|
||||
if (typeof nativeHaptic === 'function') nativeHaptic('medium');
|
||||
if (typeof nativeKeepAwake === 'function') nativeKeepAwake(false);
|
||||
if (typeof nativeStopRecordingService === 'function') nativeStopRecordingService();
|
||||
|
||||
var liveText = (finalText + sessionFinals).trim();
|
||||
if (window._transcribeAvailable === false) {
|
||||
|
|
@ -91,6 +109,49 @@
|
|||
}
|
||||
});
|
||||
|
||||
// Dedicated stop button
|
||||
if (stopBtn) {
|
||||
stopBtn.addEventListener('click', function() {
|
||||
if (!isRecording) return;
|
||||
recordBtn.style.display = '';
|
||||
recordBtn.click();
|
||||
});
|
||||
}
|
||||
|
||||
// Pause / Resume
|
||||
if (pauseBtn) {
|
||||
pauseBtn.addEventListener('click', function() {
|
||||
if (!isRecording) return;
|
||||
if (!isPaused) {
|
||||
try { if (recorder.mediaRecorder && recorder.mediaRecorder.state === 'recording') recorder.mediaRecorder.pause(); } catch(e) {}
|
||||
timer.stop();
|
||||
isPaused = true;
|
||||
pauseBtn.innerHTML = '<i class="fas fa-play"></i> Resume';
|
||||
pauseBtn.classList.add('btn-primary');
|
||||
pauseBtn.classList.remove('btn-ghost');
|
||||
if (recognition) try { recognition.stop(); } catch(e) {}
|
||||
showToast('Paused', 'info');
|
||||
} else {
|
||||
try {
|
||||
if (recorder.mediaRecorder && recorder.mediaRecorder.state === 'paused') { recorder.mediaRecorder.resume(); }
|
||||
else if (recorder.mediaRecorder && recorder.mediaRecorder.state === 'inactive' && recorder.stream && recorder.stream.active) {
|
||||
var mime = MediaRecorder.isTypeSupported('audio/webm;codecs=opus') ? 'audio/webm;codecs=opus' : 'audio/webm';
|
||||
recorder.mediaRecorder = new MediaRecorder(recorder.stream, { mimeType: mime, audioBitsPerSecond: 32000 });
|
||||
recorder.mediaRecorder.ondataavailable = function(e) { if (e.data.size > 0) recorder.chunks.push(e.data); };
|
||||
recorder.mediaRecorder.start(1000);
|
||||
}
|
||||
} catch(e) {}
|
||||
timer.resume();
|
||||
isPaused = false;
|
||||
pauseBtn.innerHTML = '<i class="fas fa-pause"></i> Pause';
|
||||
pauseBtn.classList.remove('btn-primary');
|
||||
pauseBtn.classList.add('btn-ghost');
|
||||
if (recognition) try { recognition.start(); } catch(e) {}
|
||||
showToast('Resumed', 'info');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
clearBtn.addEventListener('click', function() {
|
||||
transcript.textContent = ''; finalText = ''; outputCard.classList.add('hidden');
|
||||
var instrEl = document.getElementById('soap-instructions');
|
||||
|
|
|
|||
Loading…
Reference in a new issue