diff --git a/photomap/frontend/static/javascript/slideshow.js b/photomap/frontend/static/javascript/slideshow.js index 0d81b15c..9fe700f6 100644 --- a/photomap/frontend/static/javascript/slideshow.js +++ b/photomap/frontend/static/javascript/slideshow.js @@ -116,8 +116,19 @@ export async function toggleSlideshowWithIndicator(e) { if (slideShowRunning()) { // pause + const wasShuffling = state.mode === "random"; try { state.single_swiper.pauseSlideshow(); + // A shuffle run fills the swiper buffer with slides in random order. Once + // stopped, the forward/back buttons just walk that buffer, so the user + // would see the shuffled neighborhood instead of the current image's real + // sequential neighbors. Rebuild the buffer in album order around the + // current slide so manual navigation behaves sequentially again. + // (Sequential runs already leave an in-order buffer, so only shuffle needs + // this.) + if (wasShuffling) { + await state.single_swiper.resetAllSlides(); + } } catch (err) { console.warn("pauseSlideshow failed:", err); } diff --git a/tests/frontend/slideshow.test.js b/tests/frontend/slideshow.test.js index 38215c06..2632e2b8 100644 --- a/tests/frontend/slideshow.test.js +++ b/tests/frontend/slideshow.test.js @@ -38,8 +38,13 @@ jest.unstable_mockModule("../../photomap/frontend/static/javascript/umap.js", () })); // Now import the module we want to test -const { slideShowRunning, updateSlideshowButtonIcon, showPlayPauseIndicator, removeExistingIndicator } = - await import("../../photomap/frontend/static/javascript/slideshow.js"); +const { + slideShowRunning, + updateSlideshowButtonIcon, + showPlayPauseIndicator, + removeExistingIndicator, + toggleSlideshowWithIndicator, +} = await import("../../photomap/frontend/static/javascript/slideshow.js"); const { state } = await import("../../photomap/frontend/static/javascript/state.js"); @@ -165,6 +170,43 @@ describe("slideshow.js", () => { }); }); + describe("toggleSlideshowWithIndicator (pause path)", () => { + beforeEach(() => { + document.body.innerHTML = ` +
+ + `; + }); + + it("rebuilds the buffer sequentially when pausing a shuffle run", async () => { + // Regression: after stopping a shuffle slideshow, the swiper buffer is + // still in random order, so forward/back navigation would walk the + // leftover shuffle instead of the current image's sequential neighbors. + const resetAllSlides = jest.fn(() => Promise.resolve()); + const pauseSlideshow = jest.fn(); + state.single_swiper = { swiper: { autoplay: { running: true } }, pauseSlideshow, resetAllSlides }; + state.mode = "random"; + + await toggleSlideshowWithIndicator(); + + expect(pauseSlideshow).toHaveBeenCalled(); + expect(resetAllSlides).toHaveBeenCalled(); + }); + + it("does not rebuild the buffer when pausing a sequential run", async () => { + // Sequential runs already leave an in-order buffer, so no rebuild needed. + const resetAllSlides = jest.fn(() => Promise.resolve()); + const pauseSlideshow = jest.fn(); + state.single_swiper = { swiper: { autoplay: { running: true } }, pauseSlideshow, resetAllSlides }; + state.mode = "chronological"; + + await toggleSlideshowWithIndicator(); + + expect(pauseSlideshow).toHaveBeenCalled(); + expect(resetAllSlides).not.toHaveBeenCalled(); + }); + }); + describe("showPlayPauseIndicator", () => { it("should create indicator element when showing play", () => { state.mode = "chronological";