fix(lightbox): clean up on PhotoSwipe init failure

attach! sets every Radix popper to inert=true and installs 5 window-
capture listeners (pointerdown/mousedown/click/contextmenu/keydown).
Cleanup was wired only to PhotoSwipe's "destroy" event, but if .init
or .loadAndOpen threw, that event never fired — leaving the listeners
attached and every popper permanently inert (soft-bricked app).

Wrap init+loadAndOpen in try/catch: on failure synchronously call
detach! to roll back inert + remove listeners, clear the window-global
so mobile/navigation's later .destroy call doesn't act on a broken
instance, and rethrow so the underlying error surfaces.

Verified by stubbing PhotoSwipeLightbox to throw inside .init and
calling preview-images!: caught exception bubbles up, inert restored
to false on a probe popper, and window.photoLightbox is null.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
scheinriese
2026-05-19 13:20:49 +02:00
parent b1685ea6bc
commit 69f0ef55b2

View File

@@ -1,5 +1,6 @@
(ns frontend.extensions.lightbox
(:require [cljs-bean.core :as bean]))
(:require [cljs-bean.core :as bean]
[lambdaisland.glogi :as log]))
(defn- swallow-outside-pswp!
"Capture-phase listener that absorbs any pointer/mouse/click event whose
@@ -67,7 +68,19 @@
(doseq [^js el roots] (set! (.-inert el) false)))]
(attach!)
(set! (.-photoLightbox js/window) lightbox)
(doto lightbox
(.on "destroy" detach!)
(.init)
(.loadAndOpen 0))))
(.on lightbox "destroy" detach!)
;; If PhotoSwipe `init`/`loadAndOpen` throws, the "destroy" event never
;; fires, so `detach!` would never run — leaving the window listeners
;; attached and every Radix popper permanently `inert=true` (soft-bricks
;; the app). Synchronously roll back the attach! side effects, clear the
;; window-global so mobile/navigation doesn't act on a broken instance,
;; then rethrow so the failure surfaces.
(try
(doto lightbox
(.init)
(.loadAndOpen 0))
(catch :default e
(detach!)
(set! (.-photoLightbox js/window) nil)
(log/error :lightbox/init-failed {:error e})
(throw e)))))