Make browser back exit a loaded course instead of the whole app

Every course-loading path (Library browser, Continue Watching, Next
Up, ...) goes through loadCoursePath(), which reloads the page after
POSTing to /load_course - a plain reload never touches history, so
once you were on the course view, back had nothing of ours to land on
and fell straight through to exiting the app.

Push a history entry before that reload, and teach the back handler
that popping back to the course view means "unload the course and
reload" (there's no client-side way to patch back to the library
view's markup - it's a distinct server render tied to current_course).
Also restore the exact library folder trail on that reload instead of
resetting to the root, so back genuinely returns to where you were
browsing, not just the top level.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-22 21:06:41 -04:00
co-authored by Claude Sonnet 5
parent 255361ca6e
commit 393f78206a
+26 -2
View File
@@ -1131,10 +1131,18 @@
function loadLibrary() { function loadLibrary() {
const libraryCard = document.getElementById('library-card'); const libraryCard = document.getElementById('library-card');
if (!libraryCard) return; if (!libraryCard) return;
libraryTrail = []; // If we landed here via popstate/reload from a course view
fetchLibraryLevel(null); // that got unloaded (see the popstate listener below), the
// current history entry may already carry the folder trail
// we were browsing before the course was loaded - restore it
// instead of always resetting to the library root.
const restored = history.state && history.state.libraryTrail;
libraryTrail = restored ? history.state.libraryTrail : [];
fetchLibraryLevel(libraryTrail.length ? libraryTrail[libraryTrail.length - 1].path : null);
if (!restored) {
history.replaceState({ libraryTrail: [], path: null }, ''); history.replaceState({ libraryTrail: [], path: null }, '');
} }
}
// Drilling into a folder only ever swapped #library-groups's // Drilling into a folder only ever swapped #library-groups's
// contents - no history entry, no URL change - so the back // contents - no history entry, no URL change - so the back
@@ -1148,6 +1156,17 @@
} }
window.addEventListener('popstate', function(event) { window.addEventListener('popstate', function(event) {
{% if course %}
// The course view is a distinct server-side render tied to
// current_course, not something this page can patch back to
// client-side - unload the course and reload so back actually
// returns to the library/dashboard instead of doing nothing
// (which would otherwise leave a stale course view on screen
// while quietly burning through history entries until there's
// nowhere left to go but out of the app).
fetch('/reset_course').then(function() { location.reload(); });
return;
{% endif %}
const state = event.state; const state = event.state;
if (!state || !document.getElementById('library-card')) return; if (!state || !document.getElementById('library-card')) return;
libraryTrail = state.libraryTrail || []; libraryTrail = state.libraryTrail || [];
@@ -1385,6 +1404,11 @@
.then(r => r.json()) .then(r => r.json())
.then(data => { .then(data => {
if (data.success) { if (data.success) {
// A plain reload doesn't touch history, so without this
// push there'd be nothing for back to land on once the
// course view replaces this page - see the popstate
// listener above for how landing back here unloads it.
history.pushState({ courseLoaded: true }, '');
location.reload(); location.reload();
} else { } else {
alert('Error: ' + data.error); alert('Error: ' + data.error);