diff --git a/public/components/faq.html b/public/components/faq.html index 1b322bd5..334d5834 100644 --- a/public/components/faq.html +++ b/public/components/faq.html @@ -211,17 +211,10 @@ -
Go to Settings > Active Sessions to see all devices where you are logged in. You can revoke any session individually or click Revoke All Other Sessions to log out every other device. Your current session is highlighted and cannot be revoked from this screen — use the Logout button instead.
-Settings > Active Sessions > Revoke All Other Sessions signs out every other device from this app. Your PedsHub sign-in itself can be ended from the PedsHub sign-in page's account menu, which signs you out of every PedsHub app at once.
+Press Sign out. It ends your session in this app and your PedsHub sign-in itself, so the quiz app is signed out too and the next person at the computer cannot get back in with one click. Signing in again is one press of Sign in with PedsHub.
No active sessions found.
'; - return; - } - var currentSid = data.currentSessionId || (window.SecureStorage ? window.SecureStorage.getSync(SESSION_KEY) : localStorage.getItem(SESSION_KEY)); - list.innerHTML = data.sessions.map(function(s) { - var isCurrent = s.id === currentSid; - var created = new Date(s.created_at).toLocaleDateString(); - var lastAct = timeAgo(new Date(s.last_activity)); - return 'Could not load sessions.
'; - }); - }; - - // Revoke all other sessions button - document.addEventListener('click', function(e) { - if (e.target.id === 'btn-revoke-all-sessions' || e.target.closest('#btn-revoke-all-sessions')) { - e.preventDefault(); - showConfirm('Revoke all other sessions? All other devices will be logged out.', function() { - fetch('/api/sessions', { method: 'DELETE', headers: getAuthHeaders() }) - .then(function(r) { return r.json(); }) - .then(function(d) { - if (d.success) { showToast('All other sessions revoked (' + (d.revoked || 0) + ' removed)', 'info'); loadSessions(); } - }); - }, { danger: true, confirmText: 'Revoke All' }); - } - }); - // ---- CHANGE PASSWORD ---- document.addEventListener('click', function(e) { if (e.target.id === 'btn-change-password' || e.target.closest('#btn-change-password')) { @@ -1157,7 +1100,6 @@ document.addEventListener('DOMContentLoaded', function() { if (data.passwordWarning) setTimeout(function() { showToast(data.passwordWarning, 'warning'); }, 1500); current.value = ''; newPw.value = ''; confirmPw.value = ''; if (status) { status.textContent = ''; } - if (typeof loadSessions === 'function') loadSessions(); } else { showToast(data.error || 'Failed', 'error'); if (status) { status.textContent = ''; } @@ -1190,13 +1132,11 @@ document.addEventListener('DOMContentLoaded', function() { // legit local user's sections due to a transient fetch hiccup. var pwSection = document.getElementById('change-password-section'); var twofaSection = document.getElementById('2fa-section'); - var sessionsSection = document.getElementById('sessions-section'); if (data.user.canLocalAuth === false) { return; // SSO-only — keep sections hidden } if (pwSection) pwSection.style.display = ''; if (twofaSection) twofaSection.style.display = ''; - if (sessionsSection) sessionsSection.style.display = ''; var status = document.getElementById('2fa-status'); var setupBtn = document.getElementById('btn-setup-2fa'); var disableBtn = document.getElementById('btn-disable-2fa'); diff --git a/test/backend-hardening.test.js b/test/backend-hardening.test.js index 64a604b1..78200fc2 100644 --- a/test/backend-hardening.test.js +++ b/test/backend-hardening.test.js @@ -314,3 +314,13 @@ test('sign-out ends the provider session: the server hands back the end-session assert.match(js, /if \(!leaving\) boundary\.reload\(\)/); assert.match(read('docs/authentication.md'), /## Signing out signs you out of PedsHub/); }); + +test('a failed transcription keeps its tab, the retry puts the words after the last word, and Settings has no session list', () => { + const app = read('public/js/app.js'); + assert.equal((app.match(/saveAudioBackup\(blob, moduleName \|\| 'failed-transcription'\)/g) || []).length, 2); + const backup = read('public/js/audioBackup.js'); + assert.match(backup, /existing \+ ' ' \+ text\.trim\(\)/); + assert.match(backup, /' Transcribe into ' \+ retryTarget\.label/); + assert.doesNotMatch(read('public/components/settings.html'), /sessions-section|btn-revoke-all-sessions/); + assert.doesNotMatch(read('public/js/auth.js'), /loadSessions/); +}); diff --git a/test/transcription-memory-policy.test.js b/test/transcription-memory-policy.test.js index ae5458e2..9e7fc163 100644 --- a/test/transcription-memory-policy.test.js +++ b/test/transcription-memory-policy.test.js @@ -281,8 +281,8 @@ test('a retried transcript goes back to the tab the audio came from', () => { assert.match(backup, /if \(!window\.activateTab\(entry\.tab\)\) return false;/, 'it opens that tab'); // A retry usually recovers text ON TOP of a live transcript, so replacing the // box would lose the words the browser did hear. - assert.match(backup, /box\.textContent = existing \? existing \+ '\\n\\n' \+ text : text;/, - 'existing text is appended to, never replaced'); + assert.match(backup, /box\.textContent = existing \? existing \+ ' ' \+ text\.trim\(\) : text\.trim\(\);/, + 'existing text is appended to after its last word, never replaced'); // The box only exists once the tab's markup has been fetched. assert.match(backup, /if \(attempts\+\+ > 40\)/, 'it waits for the component rather than guessing a delay'); assert.match(backup, /showToast\('Transcript copied — could not open '/, 'and falls back to the clipboard');