From 7e32dc70ddce3de220a65a493d389da4ddeb69e2 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 30 Jun 2026 10:45:34 -0400 Subject: [PATCH] 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 --- tl/src/tl/core.cljs | 1 + tl/src/tl/routes.cljs | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/tl/src/tl/core.cljs b/tl/src/tl/core.cljs index b7ae11d..e722287 100644 --- a/tl/src/tl/core.cljs +++ b/tl/src/tl/core.cljs @@ -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)) diff --git a/tl/src/tl/routes.cljs b/tl/src/tl/routes.cljs index 4bda8aa..ad62a90 100644 --- a/tl/src/tl/routes.cljs +++ b/tl/src/tl/routes.cljs @@ -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))))