mirror of
https://github.com/penpot/penpot.git
synced 2026-11-01 07:20:32 +00:00
* 🐛 Stop thumbnail subscribers re-rendering on every store change A thumbnail loaded from the server keeps a media id as :uri, and workspace-thumbnail-by-id resolves it into a new map on each read. The ref compared values with identical?, so every root frame, every component in the assets panel and the interactions layer re-rendered on every store change, mouse moves included. Compare by value instead. This does not loop by itself, but it adds work to every commit and brings other update loops closer to React's limit. Relates to #12580 AI-assisted-by: claude-opus-5-5 * ✨ Report update loops throttled by the update scheduler When the scheduler throttles an update loop, it only logs a console warning, so a loop that no longer crashes leaves no trace. Send an error report too, with the events in flight and the last events, at most once per report window. Relates to #12580 AI-assisted-by: claude-opus-5-5 * 🐛 Report throttled update loops as unhandled exceptions The throttled update loop report used the handled-exception event, which only lands in the audit log: error reports and alerts read unhandled-exception and exception-page. A throttled loop is an application defect (its cause has :type :internal), and the point of the report is to find those loops, so it is now an unhandled exception. AI-assisted-by: claude-opus-5-5
52 lines
2.2 KiB
Clojure
52 lines
2.2 KiB
Clojure
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
;;
|
|
;; Copyright (c) KALEIDOS SUBSIDIARY SL
|
|
|
|
(ns frontend-tests.main.refs-test
|
|
(:require
|
|
[app.common.uuid :as uuid]
|
|
[app.main.refs :as refs]
|
|
[app.main.store :as st]
|
|
[cljs.test :as t :include-macros true]))
|
|
|
|
(def ^:private resolved-uri? @#'refs/resolved-uri?)
|
|
|
|
(t/deftest resolved-uri-detects-blob-and-data-uris
|
|
(t/is (true? (resolved-uri? "blob:http://localhost/thumb")))
|
|
(t/is (true? (resolved-uri? "data:image/png;base64,iVBOR"))))
|
|
|
|
(t/deftest resolved-uri-rejects-plain-media-ids
|
|
(t/is (false? (resolved-uri? "275c9c60-7f5e-4a2b-9f1c-2d3e4f5a6b7c")))
|
|
(t/is (false? (resolved-uri? ""))))
|
|
|
|
(t/deftest resolved-uri-tolerates-non-string-uris
|
|
;; Thumbnail :uri values arrive from the server over transit, so
|
|
;; media-ids decode to UUID objects instead of strings. These must
|
|
;; fall through to resolve-media instead of raising a TypeError
|
|
;; (str.lastIndexOf is not a function) that crashes the workspace.
|
|
(t/is (false? (resolved-uri? (uuid/next))))
|
|
(t/is (false? (resolved-uri? nil)))
|
|
(t/is (false? (resolved-uri? 42))))
|
|
|
|
(t/deftest thumbnail-ref-ignores-unrelated-state-changes
|
|
;; A thumbnail loaded from the server keeps a media id as :uri, and
|
|
;; the ref resolves it to a new map on each read. Subscribers (root
|
|
;; frames, assets) must not re-render on every unrelated store change.
|
|
(let [object-id (str (uuid/next))
|
|
ref (refs/workspace-thumbnail-by-id object-id)
|
|
calls (atom 0)]
|
|
(swap! st/state assoc-in [:thumbnails object-id] {:uri (uuid/next) :rendered-at nil})
|
|
(add-watch ref ::probe (fn [_ _ _ _] (swap! calls inc)))
|
|
(try
|
|
(swap! st/state assoc ::probe (uuid/next))
|
|
(t/is (zero? @calls) "unrelated change does not notify")
|
|
(swap! st/state assoc-in [:thumbnails object-id :uri] (uuid/next))
|
|
(t/is (= 1 @calls) "a new thumbnail uri notifies")
|
|
(finally
|
|
(remove-watch ref ::probe)
|
|
(swap! st/state #(-> %
|
|
(dissoc ::probe)
|
|
(update :thumbnails dissoc object-id)))))))
|