feat: PWA restores last project + playhead/stack on reopen

A homescreen launch opens the manifest start_url ("/") with no route, so the
deep state was lost. Persist the in-app URL (already kept current with stack +
playhead) to localStorage, and on a standalone cold launch with a bare hash,
re-enter it before the router reads the URL. Browser tabs are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-06-30 10:45:34 -04:00
parent b031a167a4
commit 7e32dc70dd
2 changed files with 20 additions and 1 deletions

View file

@ -29,6 +29,7 @@
(defn init []
(re-frame/dispatch-sync [::events/initialize-db])
(re-frame/dispatch [::events/fetch-me])
(routes/restore-last!) ; PWA: re-enter the last URL before the router reads it
(routes/start! route!)
(dev-setup)
(mount-root))

View file

@ -66,10 +66,28 @@
(str (href :project/show {:id id}) query)))
(defonce ^:private last-replaced (atom nil))
(def ^:private last-url-key "tl/last-url")
(defn replace-project-state! [{:keys [id stack playhead]}]
(when id
(let [url (project-url id stack playhead)]
(when (not= url @last-replaced)
(reset! last-replaced url)
(.replaceState js/history nil "" url)))))
(.replaceState js/history nil "" url)
;; remember where we were so an installed PWA can come back here
(.setItem js/localStorage last-url-key url)))))
(defn- standalone?
"Running as an installed PWA (added to homescreen), not a browser tab."
[]
(or (some-> (.matchMedia js/window "(display-mode: standalone)") .-matches)
(true? (.-standalone js/navigator))))
(defn restore-last!
"A cold PWA launch opens the manifest start_url ('/') with no route, dropping
the last project + playhead/stack. If we booted bare in standalone mode and
have a saved location, put it in the hash before the router reads it."
[]
(when (and (standalone?) (contains? #{"" "#"} (.. js/window -location -hash)))
(when-let [saved (.getItem js/localStorage last-url-key)]
(set! (.. js/window -location -hash) saved))))