fuzzyworld

tower / commit

fix(app): a shared #view= link was erased before it was read

dc85f1924d99b9b864d3a5bcd03f9141e40e79db · Pavel Safronov · 2026-09-06 23:28:18 -0700 · parents cf37225c2c8b

fix(app): a shared #view= link was erased before it was read

The constructor rendered before it decided which view to open, and render()
rewrites the hash from current state — so `#view=exterior` was replaced by an
empty hash a few lines before initialView() went looking for it. Every shared
link naming a view had been landing on the map, masked by the fact that the
last-used view is also persisted in localStorage and restored the right one for
anyone who had visited before.

Also honour the view on hashchange. Editing a hash in place fires hashchange
rather than reloading, so `#view=` only ever worked on a cold load and following
one link from another ignored it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
 src/ui/app.ts | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/ui/app.ts b/src/ui/app.ts
index 0bb85e2..4d74a49 100644
--- a/src/ui/app.ts
+++ b/src/ui/app.ts
@@ -90,8 +90,15 @@ export class App {
     this.store.subscribe(() => this.render())
     this.map.fit()
     this.readHash()
+    /**
+     * Decide the view BEFORE the first render. render() rewrites the hash from the
+     * current state, so reading `#view=` afterwards reads a hash this very
+     * constructor has already erased — which quietly broke every shared link that
+     * named a view.
+     */
+    this.view = this.initialView()
     this.render()
-    void this.setView(this.initialView())
+    void this.setView(this.view)
   }
 
   // ----------------------------------------------------------------- views
@@ -219,6 +226,11 @@ export class App {
 
   private readHash(): void {
     const params = new URLSearchParams(location.hash.slice(1))
+    // A hash edited in place fires hashchange rather than reloading, so the view
+    // has to be honoured here too — otherwise `#view=` only ever works on a cold
+    // load, and following one link from another quietly ignores it.
+    const view = params.get('view')
+    if (isView(view) && view !== this.view) void this.setView(view)
     const id = params.get('place')
     if (id && this.store.get(id)) {
       this.selectedId = id