Mirror of https://github.com/improsocial/impro An extensible Bluesky client for web impro.social
6

Configure Feed

Select the types of activity you want to include in your feed.

Update settings back button behavior

Grace Kind (May 16, 2026, 7:03 PM -0500) 25b57abf 49bdf418

+199 -4
+1 -1
package.json
··· 1 1 { 2 2 "name": "impro", 3 - "version": "0.14.39", 3 + "version": "0.14.40", 4 4 "type": "module", 5 5 "scripts": { 6 6 "start": "rm -rf build && NODE_ENV=development eleventy --serve",
+10 -2
src/js/router.js
··· 137 137 138 138 async go(path) { 139 139 this.scrollStates.set(window.location.pathname, window.scrollY); 140 - window.history.pushState({ canGoBack: true }, "", path); 140 + window.history.pushState( 141 + { previousRoute: window.location.pathname }, 142 + "", 143 + path, 144 + ); 141 145 this.emit("navigate"); 142 146 await this.load(path); 143 147 } 144 148 145 149 async back() { 146 150 this.scrollStates.set(window.location.pathname, window.scrollY); 147 - if (window.history.state?.canGoBack) { 151 + if (!!window.history.state?.previousRoute) { 148 152 window.history.back(); 149 153 } else { 150 154 this.go("/"); 151 155 } 156 + } 157 + 158 + get previousRoute() { 159 + return window.history.state?.previousRoute ?? null; 152 160 } 153 161 }
+11
src/js/views/settings.view.js
··· 94 94 pluginService, 95 95 children: html`${headerTemplate({ 96 96 title: "Settings", 97 + onClickBackButton: () => { 98 + // If navigating from settings detail page, go home instead of navigating back 99 + if ( 100 + window.router.previousRoute && 101 + window.router.previousRoute.startsWith("/settings/") 102 + ) { 103 + window.router.go("/"); 104 + } else { 105 + window.router.back(); 106 + } 107 + }, 97 108 })} 98 109 <main> 99 110 <nav class="vertical-nav">
+60
tests/e2e/specs/flows/settingsBackButton.test.js
··· 1 + import { test, expect } from "../../base.js"; 2 + import { login } from "../../helpers.js"; 3 + import { MockServer } from "../../mockServer.js"; 4 + 5 + test.describe("Settings back button", () => { 6 + test("should navigate home when coming from a settings detail page", async ({ 7 + page, 8 + }) => { 9 + const mockServer = new MockServer(); 10 + await mockServer.setup(page); 11 + 12 + await login(page); 13 + await page.goto("/settings"); 14 + 15 + const settingsView = page.locator("#settings-view"); 16 + await expect( 17 + settingsView.locator('[data-testid="settings-nav-appearance"]'), 18 + ).toBeVisible({ timeout: 10000 }); 19 + 20 + await settingsView 21 + .locator('[data-testid="settings-nav-appearance"]') 22 + .click(); 23 + await expect(page).toHaveURL("/settings/appearance", { timeout: 10000 }); 24 + 25 + const appearanceView = page.locator("#settings-appearance-view"); 26 + await appearanceView.locator('[data-testid="back-button"]').click(); 27 + 28 + await expect(page).toHaveURL("/settings", { timeout: 10000 }); 29 + 30 + // Now the back button on the settings index page should go home rather 31 + // than navigating back into the settings detail page that brought us here. 32 + await settingsView.locator('[data-testid="back-button"]').click(); 33 + 34 + await expect(page).toHaveURL("/", { timeout: 10000 }); 35 + }); 36 + 37 + test("should navigate back to the previous non-settings page", async ({ 38 + page, 39 + }) => { 40 + const mockServer = new MockServer(); 41 + await mockServer.setup(page); 42 + 43 + await login(page); 44 + await page.goto("/search"); 45 + await expect(page.locator("#search-view")).toBeVisible({ timeout: 10000 }); 46 + 47 + // Navigate via the in-app router so that previousRoute is populated. 48 + await page.waitForFunction(() => !!window.router); 49 + await page.evaluate(() => window.router.go("/settings")); 50 + 51 + const settingsView = page.locator("#settings-view"); 52 + await expect( 53 + settingsView.locator('[data-testid="header-title"]'), 54 + ).toBeVisible({ timeout: 10000 }); 55 + 56 + await settingsView.locator('[data-testid="back-button"]').click(); 57 + 58 + await expect(page).toHaveURL("/search", { timeout: 10000 }); 59 + }); 60 + });
+117 -1
tests/unit/specs/router.test.js
··· 261 261 t.describe("go", (it) => { 262 262 const originalPath = 263 263 window.location.pathname + window.location.search + window.location.hash; 264 + const originalState = window.history.state; 264 265 265 266 it("should emit navigate event before loading the new page", async () => { 266 267 const router = new Router(); ··· 275 276 try { 276 277 await router.go("/go-test"); 277 278 } finally { 278 - window.history.replaceState(null, "", originalPath); 279 + window.history.replaceState(originalState, "", originalPath); 279 280 } 280 281 281 282 assertEquals(order[0], "navigate"); 282 283 assertEquals(order[1], "page-shown"); 284 + }); 285 + 286 + it("should store the previous route in history state", async () => { 287 + const router = new Router(); 288 + const container = document.createElement("div"); 289 + router.mount(container); 290 + router.addRoute("/go-prev-test", () => Promise.resolve({})); 291 + 292 + window.history.replaceState(null, "", "/starting-path"); 293 + 294 + try { 295 + await router.go("/go-prev-test"); 296 + assertEquals(window.history.state?.previousRoute, "/starting-path"); 297 + assertEquals(window.location.pathname, "/go-prev-test"); 298 + } finally { 299 + window.history.replaceState(originalState, "", originalPath); 300 + } 301 + }); 302 + }); 303 + 304 + t.describe("previousRoute", (it) => { 305 + const originalPath = 306 + window.location.pathname + window.location.search + window.location.hash; 307 + const originalState = window.history.state; 308 + 309 + it("should return null when history state has no previousRoute", () => { 310 + const router = new Router(); 311 + window.history.replaceState(null, "", originalPath); 312 + try { 313 + assertEquals(router.previousRoute, null); 314 + } finally { 315 + window.history.replaceState(originalState, "", originalPath); 316 + } 317 + }); 318 + 319 + it("should return the previousRoute stored in history state", () => { 320 + const router = new Router(); 321 + window.history.replaceState( 322 + { previousRoute: "/some/prior/path" }, 323 + "", 324 + originalPath, 325 + ); 326 + try { 327 + assertEquals(router.previousRoute, "/some/prior/path"); 328 + } finally { 329 + window.history.replaceState(originalState, "", originalPath); 330 + } 331 + }); 332 + 333 + it("should reflect the previousRoute after a go() call", async () => { 334 + const router = new Router(); 335 + const container = document.createElement("div"); 336 + router.mount(container); 337 + router.addRoute("/prev-getter-test", () => Promise.resolve({})); 338 + 339 + window.history.replaceState(null, "", "/origin-path"); 340 + 341 + try { 342 + await router.go("/prev-getter-test"); 343 + assertEquals(router.previousRoute, "/origin-path"); 344 + } finally { 345 + window.history.replaceState(originalState, "", originalPath); 346 + } 347 + }); 348 + }); 349 + 350 + t.describe("back", (it) => { 351 + const originalPath = 352 + window.location.pathname + window.location.search + window.location.hash; 353 + const originalState = window.history.state; 354 + 355 + it("should call window.history.back when a previousRoute exists", async () => { 356 + const router = new Router(); 357 + const container = document.createElement("div"); 358 + router.mount(container); 359 + 360 + window.history.replaceState({ previousRoute: "/prior" }, "", originalPath); 361 + 362 + const originalBack = window.history.back.bind(window.history); 363 + let backCalled = false; 364 + window.history.back = () => { 365 + backCalled = true; 366 + }; 367 + 368 + try { 369 + await router.back(); 370 + assert(backCalled); 371 + } finally { 372 + window.history.back = originalBack; 373 + window.history.replaceState(originalState, "", originalPath); 374 + } 375 + }); 376 + 377 + it("should navigate to / when no previousRoute exists", async () => { 378 + const router = new Router(); 379 + const container = document.createElement("div"); 380 + router.mount(container); 381 + router.addRoute("/", () => Promise.resolve({})); 382 + 383 + window.history.replaceState(null, "", originalPath); 384 + 385 + const originalBack = window.history.back.bind(window.history); 386 + let backCalled = false; 387 + window.history.back = () => { 388 + backCalled = true; 389 + }; 390 + 391 + try { 392 + await router.back(); 393 + assert(!backCalled); 394 + assertEquals(window.location.pathname, "/"); 395 + } finally { 396 + window.history.back = originalBack; 397 + window.history.replaceState(originalState, "", originalPath); 398 + } 283 399 }); 284 400 }); 285 401