🐛 Fix parse-long crash when index query param is duplicated in URL

lambdaisland/uri's query-string->map uses :multikeys :duplicates by
default: a key that appears once yields a plain string, but the same
key repeated yields a vector. cljs.core/parse-long only accepts
strings and therefore threw "Expected string, got: object" whenever
a URL contained a duplicate 'index' parameter.

Add rt/get-query-param to app.main.router. The helper returns the
scalar value of a query param key, taking the last element when the
value is a sequential (i.e. the key was repeated). Use it at every
call site that feeds a query-param value into parse-long, in both
app.main.ui (page*) and app.main.data.viewer.
This commit is contained in:
Andrey Antukh 2026-04-13 13:24:54 +00:00 committed by Alonso Torres
parent 62f3454607
commit 18f0ad246f
3 changed files with 18 additions and 8 deletions

View File

@ -204,7 +204,7 @@
(watch [_ state _]
(let [route (:route state)
qparams (:query-params route)
index (some-> (:index qparams) parse-long)
index (some-> (rt/get-query-param qparams :index) parse-long)
frame-id (some-> (:frame-id qparams) uuid/parse)]
(rx/merge
(rx/of (case (:zoom qparams)
@ -301,7 +301,7 @@
(update [_ state]
(let [params (rt/get-params state)
page-id (some-> (:page-id params) uuid/parse)
index (some-> (:index params) parse-long)
index (some-> (rt/get-query-param params :index) parse-long)
frames (dm/get-in state [:viewer :pages page-id :frames])
index (min (or index 0) (max 0 (dec (count frames))))
@ -325,7 +325,7 @@
(let [params (rt/get-params state)
page-id (some-> (:page-id params) uuid/parse)
index (some-> (:index params) parse-long)
index (some-> (rt/get-query-param params :index) parse-long)
frames (dm/get-in state [:viewer :pages page-id :frames])
index (min (or index 0) (max 0 (dec (count frames))))
@ -399,7 +399,7 @@
ptk/WatchEvent
(watch [_ state _]
(let [params (rt/get-params state)
index (some-> params :index parse-long)]
index (some-> (rt/get-query-param params :index) parse-long)]
(when (pos? index)
(rx/of
(dcmt/close-thread)
@ -415,7 +415,7 @@
ptk/WatchEvent
(watch [_ state _]
(let [params (rt/get-params state)
index (some-> params :index parse-long)
index (some-> (rt/get-query-param params :index) parse-long)
page-id (some-> params :page-id uuid/parse)
total (count (get-in state [:viewer :pages page-id :frames]))]
@ -530,7 +530,7 @@
(let [route (:route state)
qparams (:query-params route)
page-id (some-> (:page-id qparams) uuid/parse)
index (some-> (:index qparams) parse-long)
index (some-> (rt/get-query-param qparams :index) parse-long)
frames (get-in state [:viewer :pages page-id :frames])
frame (get frames index)]
(cond-> state
@ -744,7 +744,7 @@
(let [route (:route state)
qparams (:query-params route)
page-id (some-> (:page-id qparams) uuid/parse)
index (some-> (:index qparams) parse-long)
index (some-> (rt/get-query-param qparams :index) parse-long)
objects (get-in state [:viewer :pages page-id :objects])
frame-id (get-in state [:viewer :pages page-id :frames index :id])

View File

@ -136,6 +136,16 @@
[state]
(dm/get-in state [:route :params :query]))
(defn get-query-param
"Safely extracts a scalar value for a query param key from a params
map. When the same key appears multiple times in a URL,
query-string->map returns a vector for that key; this function
always returns a single (last) element in that case, so downstream
consumers such as parse-long always receive a plain string or nil."
[params k]
(let [v (get params k)]
(if (sequential? v) (peek v) v)))
(defn nav-back
[]
(ptk/reify ::nav-back

View File

@ -277,7 +277,7 @@
:viewer
(let [params (get params :query)
index (some-> (:index params) parse-long)
index (some-> (rt/get-query-param params :index) parse-long)
share-id (some-> (:share-id params) uuid/parse*)
section (or (some-> (:section params) keyword)
:interactions)