penpot/frontend/src/app/main.cljs
Andrey Antukh 6eccffb8bb
🐛 Fix incorrect handlig of version restore operation (#9041)
- Add session ID tracking to RPC layer (backend and frontend)
- Send session ID header with RPC requests for request correlation
- Rename file-restore to file-restored for consistency
- Extract initialize-file function from initialize-workspace flow
- Improve file restoration initialization with wait-for-persistence
- Extract initialize-version event handler for version restoration
- Fix viewport key generation with file version numbers for proper re-renders
- Update layout item schema and constraints to use internal sizing state
- Add v-sizing state retrieval in layout-size-constraints component
- Refactor file-change notifications stream handling with rx/map
- Fix team-id lookup in restore-version-from-plugins

Improves request traceability across frontend/backend sessions and streamlines
the workspace initialization flow for file restoration scenarios.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-21 19:19:51 +02:00

156 lines
4.4 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 INC
(ns app.main
(:require
[app.common.data.macros :as dm]
[app.common.logging :as log]
[app.common.time :as ct]
[app.common.transit :as t]
[app.common.types.objects-map]
[app.config :as cf]
[app.main.data.auth :as da]
[app.main.data.event :as ev]
[app.main.data.profile :as dp]
[app.main.data.websocket :as ws]
[app.main.errors]
[app.main.features :as feat]
[app.main.rasterizer :as thr]
[app.main.store :as st]
[app.main.ui :as ui]
[app.main.ui.alert]
[app.main.ui.confirm]
[app.main.ui.css-cursors :as cur]
[app.main.ui.delete-shared]
[app.main.ui.routes :as rt]
[app.main.worker :as mw]
[app.plugins :as plugins]
[app.util.dom :as dom]
[app.util.i18n :as i18n]
[beicon.v2.core :as rx]
[cuerdas.core :as str]
[debug]
[features]
[potok.v2.core :as ptk]
[rumext.v2 :as mf]))
(log/setup! {:app :info})
(log/set-level! :debug)
(when (= :browser cf/target)
(log/inf :version (:full cf/version)
:asserts *assert*
:build-date cf/build-date
:public-uri (dm/str cf/public-uri)
:session-id (str cf/session-id))
(log/inf :hint "enabled flags" :flags (str/join " " (map name cf/flags))))
(declare reinit)
(defonce app-root
(let [el (dom/get-element "app")]
(mf/create-root el)))
(defn init-ui
[]
(mf/render! app-root (mf/element ui/app)))
(defn- initialize-rasterizer
[]
(ptk/reify ::initialize-rasterizer
ptk/EffectEvent
(effect [_ _ _]
;; The rasterizer is used for the dashboard thumbnails
(thr/init!))))
(defn initialize
[]
(ptk/reify ::initialize
ptk/UpdateEvent
(update [_ state]
(assoc state :session-id cf/session-id))
ptk/WatchEvent
(watch [_ _ stream]
(rx/merge
(if (contains? cf/flags :audit-log)
(rx/of (ev/initialize))
(rx/empty))
(rx/of (dp/refresh-profile))
;; Watch for profile deletion events
(->> stream
(rx/filter dp/profile-deleted-event?)
(rx/map da/logged-out))
;; Once profile is fetched, initialize all penpot application
;; routes
(->> stream
(rx/filter dp/profile-fetched?)
(rx/take 1)
(rx/map #(rt/init-routes)))
;; Once profile fetched and the current user is authenticated,
;; proceed to initialize the websockets connection.
(->> stream
(rx/filter dp/profile-fetched?)
(rx/map deref)
(rx/filter dp/is-authenticated?)
(rx/take 1)
(rx/map #(ws/initialize)))
(->> stream
(rx/filter (ptk/type? ::feat/initialize))
(rx/take 1)
(rx/map #(initialize-rasterizer)))))))
(defn ^:export init
[options]
;; WORKAROUND: we set this really not usefull property for signal a
;; sideffect and prevent GCC remove it. We need it because we need
;; to populate the Date prototype with transit related properties
;; before SES hardning is applied on loading MCP plugin
(unchecked-set js/globalThis "penpotStartDate"
(-> (ct/now)
(t/encode-str)
(t/decode-str)))
;; Before initializing anything, check if the browser has loaded
;; stale JS from a previous deployment. If so, do a hard reload so
;; the browser fetches fresh assets matching the current index.html.
(if (cf/stale-build?)
(cf/throttled-reload
:reason (dm/str "stale JS: compiled=" cf/compiled-version-tag
" expected=" cf/version-tag))
(do
(some-> (unchecked-get options "defaultTranslations")
(i18n/set-default-translations))
(mw/init!)
(i18n/init)
(cur/init-styles)
(init-ui)
(st/emit! (plugins/initialize)
(initialize)))))
(defn ^:export reinit
([]
(reinit false))
([hard?]
;; The hard flag will force to unmount the whole UI and will redraw every component
(when hard?
(mf/unmount! app-root)
(set! app-root (mf/create-root (dom/get-element "app"))))
(st/emit! (ev/initialize))
(init-ui)))
(defn ^:dev/after-load after-load
[]
(reinit))
(set! (.-stackTraceLimit js/Error) 50)