mirror of
https://github.com/penpot/penpot.git
synced 2026-04-25 11:18:36 +00:00
🐛 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>
This commit is contained in:
parent
77560b9305
commit
6eccffb8bb
@ -96,6 +96,7 @@
|
||||
(fn [{:keys [params path-params method] :as request}]
|
||||
(let [handler-name (:method-name path-params)
|
||||
etag (yreq/get-header request "if-none-match")
|
||||
session-id (yreq/get-header request "x-session-id")
|
||||
|
||||
key-id (get request ::http/auth-key-id)
|
||||
profile-id (or (::session/profile-id request)
|
||||
@ -108,6 +109,7 @@
|
||||
(assoc ::handler-name handler-name)
|
||||
(assoc ::ip-addr ip-addr)
|
||||
(assoc ::request-at (ct/now))
|
||||
(assoc ::session-id (some-> session-id uuid/parse*))
|
||||
(assoc ::cond/key etag)
|
||||
(cond-> (uuid? profile-id)
|
||||
(assoc ::profile-id profile-id)))
|
||||
|
||||
@ -71,7 +71,7 @@
|
||||
{::doc/added "1.20"
|
||||
::sm/params schema:restore-file-snapshot
|
||||
::db/transaction true}
|
||||
[{:keys [::db/conn ::mbus/msgbus] :as cfg} {:keys [::rpc/profile-id file-id id] :as params}]
|
||||
[{:keys [::db/conn ::mbus/msgbus] :as cfg} {:keys [::rpc/profile-id ::rpc/session-id file-id id] :as params}]
|
||||
(files/check-edition-permissions! conn profile-id file-id)
|
||||
(let [file (bfc/get-file cfg file-id)
|
||||
team (teams/get-team conn
|
||||
@ -88,7 +88,8 @@
|
||||
;; Send to the clients a notification to reload the file
|
||||
(mbus/pub! msgbus
|
||||
:topic (:id file)
|
||||
:message {:type :file-restore
|
||||
:message {:type :file-restored
|
||||
:session-id session-id
|
||||
:file-id (:id file)
|
||||
:vern vern})
|
||||
nil)))
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
[app.common.logging :as log]
|
||||
[app.common.time :as ct]
|
||||
[app.common.uri :as u]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.common.version :as v]
|
||||
[app.util.avatars :as avatars]
|
||||
[app.util.extends]
|
||||
@ -112,10 +113,12 @@
|
||||
(def target (parse-target global))
|
||||
(def browser (parse-browser))
|
||||
(def platform (parse-platform))
|
||||
(def session-id (uuid/next))
|
||||
|
||||
(def version (parse-version global))
|
||||
(def version-tag (obj/get global "penpotVersionTag"))
|
||||
|
||||
|
||||
(defn stale-build?
|
||||
"Returns true when the compiled JS was built with a different version
|
||||
tag than the one present in the current index.html. This indicates
|
||||
|
||||
@ -11,7 +11,6 @@
|
||||
[app.common.time :as ct]
|
||||
[app.common.transit :as t]
|
||||
[app.common.types.objects-map]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.auth :as da]
|
||||
[app.main.data.event :as ev]
|
||||
@ -45,7 +44,8 @@
|
||||
(log/inf :version (:full cf/version)
|
||||
:asserts *assert*
|
||||
:build-date cf/build-date
|
||||
:public-uri (dm/str cf/public-uri))
|
||||
: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)
|
||||
@ -71,7 +71,7 @@
|
||||
(ptk/reify ::initialize
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc state :session-id (uuid/next)))
|
||||
(assoc state :session-id cf/session-id))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ stream]
|
||||
|
||||
@ -431,6 +431,7 @@
|
||||
context (-> @context
|
||||
(merge (:context event))
|
||||
(assoc :session session*)
|
||||
(assoc :session-id cf/session-id)
|
||||
(assoc :external-session-id (cf/external-session-id))
|
||||
(add-external-context-info)
|
||||
(d/without-nils))]
|
||||
|
||||
@ -224,18 +224,12 @@
|
||||
IDeref
|
||||
(-deref [_] bundle)
|
||||
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [pending-version-id (:workspace-pending-file-version-id state)
|
||||
state (-> state
|
||||
(assoc :thumbnails thumbnails)
|
||||
(update :files assoc file-id file)
|
||||
(dissoc :workspace-pending-file-version-id))]
|
||||
(cond-> state
|
||||
(some? pending-version-id)
|
||||
(assoc :workspace-file-version-id pending-version-id)
|
||||
(nil? pending-version-id)
|
||||
(dissoc :workspace-file-version-id))))))
|
||||
(-> state
|
||||
(assoc :thumbnails thumbnails)
|
||||
(update :files assoc file-id file)))))
|
||||
|
||||
(defn zoom-to-frame
|
||||
[]
|
||||
@ -253,6 +247,7 @@
|
||||
(rx/of (dws/select-shapes frames-id)
|
||||
dwz/zoom-to-selected-shape)))))
|
||||
|
||||
;; FIXME: rename to `fetch-file`
|
||||
(defn- fetch-bundle
|
||||
"Multi-stage file bundle fetch coordinator"
|
||||
[file-id features]
|
||||
@ -289,204 +284,215 @@
|
||||
;; This prevents errors when processing changes from other pages
|
||||
(when shape
|
||||
(wasm.api/process-object shape))))))
|
||||
|
||||
(defn initialize-file
|
||||
[team-id file-id]
|
||||
(assert (uuid? team-id) "expected valud uuid for `team-id`")
|
||||
(assert (uuid? file-id) "expected valud uuid for `file-id`")
|
||||
|
||||
(ptk/reify ::initialize-file
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [features (features/get-enabled-features state team-id)]
|
||||
(log/dbg :hint "initialize-file"
|
||||
:team-id (dm/str team-id)
|
||||
:file-id (dm/str file-id))
|
||||
(rx/of (fetch-bundle file-id features))))))
|
||||
|
||||
(defn initialize-workspace
|
||||
([team-id file-id]
|
||||
(initialize-workspace team-id file-id nil))
|
||||
([team-id file-id version-id]
|
||||
(assert (uuid? team-id) "expected valud uuid for `team-id`")
|
||||
(assert (uuid? file-id) "expected valud uuid for `file-id`")
|
||||
[team-id file-id]
|
||||
(assert (uuid? team-id) "expected valud uuid for `team-id`")
|
||||
(assert (uuid? file-id) "expected valud uuid for `file-id`")
|
||||
|
||||
(ptk/reify ::initialize-workspace
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc :recent-colors (:recent-colors storage/user))
|
||||
(assoc :recent-fonts (:recent-fonts storage/user))
|
||||
(assoc :current-file-id file-id)
|
||||
(assoc :workspace-presence {})
|
||||
;; Store pending version-id; bundle-fetched will set workspace-file-version-id
|
||||
;; when the new bundle is applied so the viewport re-inits with new data
|
||||
(assoc :workspace-pending-file-version-id version-id)))
|
||||
(ptk/reify ::initialize-workspace
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc :recent-colors (:recent-colors storage/user))
|
||||
(assoc :recent-fonts (:recent-fonts storage/user))
|
||||
(assoc :current-file-id file-id)
|
||||
(assoc :workspace-presence {})))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [stoper-s (rx/filter (ptk/type? ::finalize-workspace) stream)
|
||||
rparams (rt/get-params state)
|
||||
features (features/get-enabled-features state team-id)
|
||||
render-wasm? (contains? features "render-wasm/v1")]
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [stoper-s (rx/filter (ptk/type? ::finalize-workspace) stream)
|
||||
rparams (rt/get-params state)
|
||||
features (features/get-enabled-features state team-id)
|
||||
render-wasm? (contains? features "render-wasm/v1")]
|
||||
|
||||
(log/debug :hint "initialize-workspace"
|
||||
:team-id (dm/str team-id)
|
||||
:file-id (dm/str file-id))
|
||||
(log/debug :hint "initialize-workspace"
|
||||
:team-id (dm/str team-id)
|
||||
:file-id (dm/str file-id))
|
||||
|
||||
(rx/concat
|
||||
(->> (rx/merge
|
||||
(rx/concat
|
||||
;; Fetch all essential data that should be loaded before the file
|
||||
(rx/merge
|
||||
(if ^boolean render-wasm?
|
||||
(->> (rx/from @wasm/module)
|
||||
(rx/filter true?)
|
||||
(rx/tap (fn [_]
|
||||
(let [event (ug/event "penpot:wasm:loaded")]
|
||||
(ug/dispatch! event))))
|
||||
(rx/ignore))
|
||||
(rx/empty))
|
||||
(rx/concat
|
||||
(->> (rx/merge
|
||||
(rx/concat
|
||||
;; Fetch all essential data that should be loaded before the file
|
||||
(rx/merge
|
||||
(if ^boolean render-wasm?
|
||||
(->> (rx/from @wasm/module)
|
||||
(rx/filter true?)
|
||||
(rx/tap (fn [_]
|
||||
(let [event (ug/event "penpot:wasm:loaded")]
|
||||
(ug/dispatch! event))))
|
||||
(rx/ignore))
|
||||
(rx/empty))
|
||||
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::df/fonts-loaded))
|
||||
(rx/take 1)
|
||||
(rx/ignore))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::df/fonts-loaded))
|
||||
(rx/take 1)
|
||||
(rx/ignore))
|
||||
|
||||
(rx/of (ntf/hide)
|
||||
(dcmt/retrieve-comment-threads file-id)
|
||||
(dcmt/fetch-profiles)
|
||||
(df/fetch-fonts team-id))
|
||||
(rx/of (ntf/hide)
|
||||
(dcmt/retrieve-comment-threads file-id)
|
||||
(dcmt/fetch-profiles)
|
||||
(df/fetch-fonts team-id))
|
||||
|
||||
(when (contains? cf/flags :mcp)
|
||||
(rx/of (du/fetch-access-tokens))))
|
||||
(when (contains? cf/flags :mcp)
|
||||
(rx/of (du/fetch-access-tokens))))
|
||||
|
||||
;; Once the essential data is fetched, lets proceed to
|
||||
;; fetch teh file bunldle
|
||||
(rx/of (fetch-bundle file-id features)))
|
||||
;; Once the essential data is fetched, lets proceed to
|
||||
;; fetch teh file bunldle
|
||||
(rx/of (initialize-file team-id file-id)))
|
||||
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::bundle-fetched))
|
||||
(rx/take 1)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [file]}]
|
||||
(log/debug :hint "bundle fetched"
|
||||
:team-id (dm/str team-id)
|
||||
:file-id (dm/str file-id))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::bundle-fetched))
|
||||
(rx/take 1)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [file]}]
|
||||
(log/debug :hint "bundle fetched"
|
||||
:team-id (dm/str team-id)
|
||||
:file-id (dm/str file-id))
|
||||
|
||||
(rx/of (dpj/initialize-project (:project-id file))
|
||||
(dwn/initialize team-id file-id)
|
||||
(dwsl/initialize-shape-layout)
|
||||
(fetch-libraries file-id features)
|
||||
(-> (workspace-initialized file-id)
|
||||
(with-meta {:team-id team-id
|
||||
:file-id file-id}))))))
|
||||
(rx/of (dpj/initialize-project (:project-id file))
|
||||
(dwn/initialize team-id file-id)
|
||||
(dwsl/initialize-shape-layout)
|
||||
(fetch-libraries file-id features)
|
||||
(-> (workspace-initialized file-id)
|
||||
(with-meta {:team-id team-id
|
||||
:file-id file-id}))))))
|
||||
|
||||
;; Install dev perf observers once the workspace is ready
|
||||
(when (contains? cf/flags :perf-logs)
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/take 1)
|
||||
(rx/tap (fn [_] (perf/setup)))))
|
||||
;; Install dev perf observers once the workspace is ready
|
||||
(when (contains? cf/flags :perf-logs)
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/take 1)
|
||||
(rx/tap (fn [_] (perf/setup)))))
|
||||
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dps/persistence-notification))
|
||||
(rx/take 1)
|
||||
(rx/map dwc/set-workspace-visited))
|
||||
|
||||
(when-let [component-id (some-> rparams :component-id uuid/parse)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/observe-on :async)
|
||||
(rx/take 1)
|
||||
(rx/map #(dwl/go-to-local-component :id component-id :update-layout? (:update-layout rparams)))))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dps/persistence-notification))
|
||||
(rx/take 1)
|
||||
(rx/map dwc/set-workspace-visited))
|
||||
|
||||
(when (:board-id rparams)
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dwv/initialize-viewport))
|
||||
(rx/take 1)
|
||||
(rx/map zoom-to-frame)))
|
||||
(when-let [component-id (some-> rparams :component-id uuid/parse)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/observe-on :async)
|
||||
(rx/take 1)
|
||||
(rx/map #(dwl/go-to-local-component :id component-id :update-layout? (:update-layout rparams)))))
|
||||
|
||||
(when-let [comment-id (some-> rparams :comment-id uuid/parse)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/observe-on :async)
|
||||
(rx/take 1)
|
||||
(rx/map #(dwcm/navigate-to-comment-id comment-id))))
|
||||
(when (:board-id rparams)
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dwv/initialize-viewport))
|
||||
(rx/take 1)
|
||||
(rx/map zoom-to-frame)))
|
||||
|
||||
(when render-wasm?
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [redo-changes]}]
|
||||
(let [added (->> redo-changes
|
||||
(filter #(= (:type %) :add-obj))
|
||||
(map :id))]
|
||||
(->> (rx/from added)
|
||||
(rx/map process-wasm-object)))))))
|
||||
(when-let [comment-id (some-> rparams :comment-id uuid/parse)]
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::workspace-initialized))
|
||||
(rx/observe-on :async)
|
||||
(rx/take 1)
|
||||
(rx/map #(dwcm/navigate-to-comment-id comment-id))))
|
||||
|
||||
(when render-wasm?
|
||||
(let [local-commits-s
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/filter #(and (= :local (:source %))
|
||||
(not (contains? (:tags %) :position-data))))
|
||||
(rx/filter (complement empty?)))
|
||||
(when render-wasm?
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [redo-changes]}]
|
||||
(let [added (->> redo-changes
|
||||
(filter #(= (:type %) :add-obj))
|
||||
(map :id))]
|
||||
(->> (rx/from added)
|
||||
(rx/map process-wasm-object)))))))
|
||||
|
||||
notifier-s
|
||||
(rx/merge
|
||||
(->> local-commits-s (rx/debounce 1000))
|
||||
(->> stream (rx/filter dps/force-persist?)))
|
||||
(when render-wasm?
|
||||
(let [local-commits-s
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/filter #(and (= :local (:source %))
|
||||
(not (contains? (:tags %) :position-data))))
|
||||
(rx/filter (complement empty?)))
|
||||
|
||||
objects-s
|
||||
(rx/from-atom refs/workspace-page-objects {:emit-current-value? true})
|
||||
notifier-s
|
||||
(rx/merge
|
||||
(->> local-commits-s (rx/debounce 1000))
|
||||
(->> stream (rx/filter dps/force-persist?)))
|
||||
|
||||
current-page-id-s
|
||||
(rx/from-atom refs/current-page-id {:emit-current-value? true})]
|
||||
objects-s
|
||||
(rx/from-atom refs/workspace-page-objects {:emit-current-value? true})
|
||||
|
||||
(->> local-commits-s
|
||||
(rx/buffer-until notifier-s)
|
||||
(rx/with-latest-from objects-s)
|
||||
(rx/map
|
||||
(fn [[commits objects]]
|
||||
(->> commits
|
||||
(mapcat :redo-changes)
|
||||
(filter #(contains? #{:mod-obj :add-obj} (:type %)))
|
||||
(filter #(cfh/text-shape? objects (:id %)))
|
||||
(map #(vector
|
||||
(:id %)
|
||||
(wasm.api/calculate-position-data (get objects (:id %))))))))
|
||||
current-page-id-s
|
||||
(rx/from-atom refs/current-page-id {:emit-current-value? true})]
|
||||
|
||||
(rx/with-latest-from current-page-id-s)
|
||||
(rx/map
|
||||
(fn [[text-position-data page-id]]
|
||||
(let [changes
|
||||
(->> text-position-data
|
||||
(mapv (fn [[id position-data]]
|
||||
{:type :mod-obj
|
||||
:id id
|
||||
:page-id page-id
|
||||
:operations
|
||||
[{:type :set
|
||||
:attr :position-data
|
||||
:val position-data
|
||||
:ignore-touched true
|
||||
:ignore-geometry true}]})))]
|
||||
(when (d/not-empty? changes)
|
||||
(dch/commit-changes
|
||||
{:redo-changes changes :undo-changes []
|
||||
:save-undo? false
|
||||
:tags #{:position-data}})))))
|
||||
(rx/take-until stoper-s))))
|
||||
(->> local-commits-s
|
||||
(rx/buffer-until notifier-s)
|
||||
(rx/with-latest-from objects-s)
|
||||
(rx/map
|
||||
(fn [[commits objects]]
|
||||
(->> commits
|
||||
(mapcat :redo-changes)
|
||||
(filter #(contains? #{:mod-obj :add-obj} (:type %)))
|
||||
(filter #(cfh/text-shape? objects (:id %)))
|
||||
(map #(vector
|
||||
(:id %)
|
||||
(wasm.api/calculate-position-data (get objects (:id %))))))))
|
||||
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [save-undo? undo-changes redo-changes undo-group tags stack-undo?]}]
|
||||
(if (and save-undo? (seq undo-changes))
|
||||
(let [entry {:undo-changes undo-changes
|
||||
:redo-changes redo-changes
|
||||
:undo-group undo-group
|
||||
:tags tags}]
|
||||
(rx/of (dwu/append-undo entry stack-undo?)))
|
||||
(rx/empty))))))
|
||||
(rx/take-until stoper-s))
|
||||
(rx/with-latest-from current-page-id-s)
|
||||
(rx/map
|
||||
(fn [[text-position-data page-id]]
|
||||
(let [changes
|
||||
(->> text-position-data
|
||||
(mapv (fn [[id position-data]]
|
||||
{:type :mod-obj
|
||||
:id id
|
||||
:page-id page-id
|
||||
:operations
|
||||
[{:type :set
|
||||
:attr :position-data
|
||||
:val position-data
|
||||
:ignore-touched true
|
||||
:ignore-geometry true}]})))]
|
||||
(when (d/not-empty? changes)
|
||||
(dch/commit-changes
|
||||
{:redo-changes changes :undo-changes []
|
||||
:save-undo? false
|
||||
:tags #{:position-data}})))))
|
||||
(rx/take-until stoper-s))))
|
||||
|
||||
(rx/of (mcp/notify-other-tabs-disconnect)))))
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [save-undo? undo-changes redo-changes undo-group tags stack-undo?]}]
|
||||
(if (and save-undo? (seq undo-changes))
|
||||
(let [entry {:undo-changes undo-changes
|
||||
:redo-changes redo-changes
|
||||
:undo-group undo-group
|
||||
:tags tags}]
|
||||
(rx/of (dwu/append-undo entry stack-undo?)))
|
||||
(rx/empty))))))
|
||||
(rx/take-until stoper-s))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ _ _]
|
||||
(let [name (dm/str "workspace-" file-id)]
|
||||
(unchecked-set ug/global "name" name))))))
|
||||
(rx/of (mcp/notify-other-tabs-disconnect)))))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ _ _]
|
||||
(let [name (dm/str "workspace-" file-id)]
|
||||
(unchecked-set ug/global "name" name)))))
|
||||
|
||||
(defn finalize-workspace
|
||||
[_team-id file-id]
|
||||
|
||||
@ -1350,9 +1350,10 @@
|
||||
(watch [_ _ stream]
|
||||
(let [stopper-s
|
||||
(->> stream
|
||||
(rx/filter #(or (= ::dwpg/finalize-page (ptk/type %))
|
||||
(= ::watch-component-changes (ptk/type %)))))
|
||||
|
||||
(rx/map ptk/type)
|
||||
(rx/filter (fn [event-type]
|
||||
(or (= ::dwpg/finalize-page event-type)
|
||||
(= ::watch-component-changes event-type)))))
|
||||
workspace-data-s
|
||||
(->> (rx/from-atom refs/workspace-data {:emit-current-value? true})
|
||||
(rx/share))
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
(declare handle-pointer-update)
|
||||
(declare handle-file-change)
|
||||
(declare handle-file-deleted)
|
||||
(declare handle-file-restore)
|
||||
(declare handle-file-restored)
|
||||
(declare handle-library-change)
|
||||
(declare handle-pointer-send)
|
||||
(declare handle-export-update)
|
||||
@ -132,7 +132,7 @@
|
||||
:pointer-update (handle-pointer-update msg)
|
||||
:file-change (handle-file-change msg)
|
||||
:file-deleted (handle-file-deleted msg)
|
||||
:file-restore (handle-file-restore msg)
|
||||
:file-restored (handle-file-restored msg)
|
||||
:library-change (handle-library-change msg)
|
||||
:notification (dc/handle-notification msg)
|
||||
:team-role-change (handle-change-team-role msg)
|
||||
@ -283,22 +283,22 @@
|
||||
(rt/nav :dashboard-recent {:team-id team-id})))))))
|
||||
|
||||
(def ^:private
|
||||
schema:handle-file-restore
|
||||
[:map {:title "handle-file-restore"}
|
||||
schema:handle-file-restored
|
||||
[:map {:title "handle-file-restored"}
|
||||
[:type :keyword]
|
||||
[:file-id ::sm/uuid]
|
||||
[:vern :int]])
|
||||
|
||||
(def ^:private check-file-restore-params
|
||||
(sm/check-fn schema:handle-file-restore))
|
||||
(def ^:private check-file-restored-params
|
||||
(sm/check-fn schema:handle-file-restored))
|
||||
|
||||
(defn handle-file-restore
|
||||
(defn handle-file-restored
|
||||
[{:keys [file-id vern] :as msg}]
|
||||
|
||||
(assert (check-file-restore-params msg)
|
||||
(assert (check-file-restored-params msg)
|
||||
"expected valid parameters")
|
||||
|
||||
(ptk/reify ::handle-file-restore
|
||||
(ptk/reify ::handle-file-restored
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [curr-file-id (:current-file-id state)
|
||||
|
||||
@ -11,9 +11,10 @@
|
||||
[app.common.schema :as sm]
|
||||
[app.common.time :as ct]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.helpers :as dsh]
|
||||
[app.main.data.notifications :as ntf]
|
||||
[app.main.data.persistence :as dwp]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.pages :as dwpg]
|
||||
[app.main.data.workspace.thumbnails :as th]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.repo :as rp]
|
||||
@ -92,33 +93,59 @@
|
||||
(->> (rp/cmd! :update-file-snapshot {:id id :label label})
|
||||
(rx/map fetch-versions)))))))
|
||||
|
||||
(defn- initialize-version
|
||||
[]
|
||||
(ptk/reify ::initialize-version
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (:current-page-id state)
|
||||
file-id (:current-file-id state)
|
||||
team-id (:current-team-id state)]
|
||||
|
||||
(rx/merge
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dw/bundle-fetched))
|
||||
(rx/take 1)
|
||||
(rx/map #(dwpg/initialize-page file-id page-id)))
|
||||
|
||||
(rx/of (ntf/hide :tag :restore-dialog)
|
||||
(dw/initialize-file team-id file-id)))))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ _ _]
|
||||
(th/clear-queue!))))
|
||||
|
||||
(defn- wait-for-persistence
|
||||
[file-id snapshot-id]
|
||||
(->> (rx/from-atom refs/persistence-state {:emit-current-value? true})
|
||||
(rx/filter #(or (nil? %) (= :saved %)))
|
||||
(rx/take 1)
|
||||
(rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id snapshot-id}))))
|
||||
|
||||
(defn restore-version
|
||||
[id origin]
|
||||
(assert (uuid? id) "expected valid uuid for `id`")
|
||||
(ptk/reify ::restore-version
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [file-id (:current-file-id state)
|
||||
team-id (:current-team-id state)]
|
||||
(let [file-id (:current-file-id state)
|
||||
team-id (:current-team-id state)
|
||||
event-name (case origin
|
||||
:version "restore-pin-version"
|
||||
:snapshot "restore-autosave"
|
||||
:plugin "restore-version-plugin")]
|
||||
|
||||
(rx/concat
|
||||
(rx/of ::dwp/force-persist
|
||||
(dw/remove-layout-flag :document-history))
|
||||
(->> (rx/from-atom refs/persistence-state {:emit-current-value? true})
|
||||
(rx/filter #(or (nil? %) (= :saved %)))
|
||||
(rx/take 1)
|
||||
(rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id}))
|
||||
(rx/tap #(th/clear-queue!))
|
||||
(rx/map #(dw/initialize-workspace team-id file-id id)))
|
||||
(case origin
|
||||
:version
|
||||
(rx/of (ptk/event ::ev/event {::ev/name "restore-pin-version"}))
|
||||
|
||||
:snapshot
|
||||
(rx/of (ptk/event ::ev/event {::ev/name "restore-autosave"}))
|
||||
|
||||
:plugin
|
||||
(rx/of (ptk/event ::ev/event {::ev/name "restore-version-plugin"}))
|
||||
(->> (wait-for-persistence file-id id)
|
||||
(rx/map #(initialize-version)))
|
||||
|
||||
(if event-name
|
||||
(rx/of (ev/event {::ev/name event-name
|
||||
:file-id file-id
|
||||
:team-id team-id}))
|
||||
(rx/empty)))))))
|
||||
|
||||
(defn delete-version
|
||||
@ -220,18 +247,15 @@
|
||||
(ptk/reify ::restore-version-from-plugins
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [file (dsh/lookup-file state file-id)
|
||||
team-id (or (:team-id file) (:current-file-id state))]
|
||||
(let [team-id (:current-team-id state)]
|
||||
(rx/concat
|
||||
(rx/of (ptk/event ::ev/event {::ev/name "restore-version-plugin"})
|
||||
(rx/of (ev/event {::ev/name "restore-version-plugin"
|
||||
:file-id file-id
|
||||
:team-id team-id})
|
||||
::dwp/force-persist)
|
||||
|
||||
;; FIXME: we should abstract this
|
||||
(->> (rx/from-atom refs/persistence-state {:emit-current-value? true})
|
||||
(rx/filter #(or (nil? %) (= :saved %)))
|
||||
(rx/take 1)
|
||||
(rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id}))
|
||||
(rx/map #(dw/initialize-workspace team-id file-id id)))
|
||||
(->> (wait-for-persistence file-id id)
|
||||
(rx/map #(initialize-version)))
|
||||
|
||||
(->> (rx/of 1)
|
||||
(rx/tap resolve)
|
||||
|
||||
@ -259,9 +259,6 @@
|
||||
(def workspace-layout
|
||||
(l/derived :workspace-layout st/state))
|
||||
|
||||
(def workspace-file-version-id
|
||||
(l/derived :workspace-file-version-id st/state))
|
||||
|
||||
(def snap-pixel?
|
||||
(l/derived #(contains? % :snap-pixel-grid) workspace-layout))
|
||||
|
||||
|
||||
@ -182,6 +182,7 @@
|
||||
:credentials "include"
|
||||
:headers {"accept" "application/transit+json,text/event-stream,*/*"
|
||||
"x-external-session-id" (cf/external-session-id)
|
||||
"x-session-id" (str cf/session-id)
|
||||
"x-event-origin" (::ev/origin (meta params))}
|
||||
:body (when (= method :post)
|
||||
(if form-data?
|
||||
|
||||
@ -51,7 +51,7 @@
|
||||
|
||||
(mf/defc workspace-content*
|
||||
{::mf/private true}
|
||||
[{:keys [file layout page wglobal file-version-id]}]
|
||||
[{:keys [file layout page wglobal]}]
|
||||
|
||||
(let [palete-size (mf/use-state nil)
|
||||
selected (mf/deref refs/selected-shapes)
|
||||
@ -109,7 +109,6 @@
|
||||
:wglobal wglobal
|
||||
:selected selected
|
||||
:layout layout
|
||||
:file-version-id file-version-id
|
||||
:palete-size
|
||||
(when (and (or colorpalette? textpalette?) (not hide-ui?))
|
||||
@palete-size)}]]]
|
||||
@ -169,7 +168,7 @@
|
||||
|
||||
(mf/defc workspace-inner*
|
||||
{::mf/private true}
|
||||
[{:keys [page-id file-id file layout wglobal file-version-id]}]
|
||||
[{:keys [page-id file-id file layout wglobal]}]
|
||||
(let [page-ref (mf/with-memo [file-id page-id]
|
||||
(make-page-ref file-id page-id))
|
||||
page (mf/deref page-ref)]
|
||||
@ -188,8 +187,7 @@
|
||||
[:> workspace-content* {:file file
|
||||
:page page
|
||||
:wglobal wglobal
|
||||
:layout layout
|
||||
:file-version-id file-version-id}]
|
||||
:layout layout}]
|
||||
[:> workspace-loader*])))
|
||||
|
||||
(mf/defc workspace*
|
||||
@ -201,7 +199,6 @@
|
||||
|
||||
layout (mf/deref refs/workspace-layout)
|
||||
wglobal (mf/deref refs/workspace-global)
|
||||
file-version-id (mf/deref refs/workspace-file-version-id)
|
||||
|
||||
team-ref (mf/with-memo [team-id]
|
||||
(make-team-ref team-id))
|
||||
@ -277,8 +274,7 @@
|
||||
:file-id file-id
|
||||
:file file
|
||||
:wglobal wglobal
|
||||
:layout layout
|
||||
:file-version-id file-version-id}])
|
||||
:layout layout}])
|
||||
(when (or (not (and file-loaded? page-id))
|
||||
;; in wasm renderer, extend the pixel loader until the first frame is rendered
|
||||
;; but do not apply it when switching pages
|
||||
|
||||
@ -539,16 +539,18 @@
|
||||
[:map
|
||||
[:values schema:layout-item-props-schema]
|
||||
[:applied-tokens [:maybe [:map-of :keyword :string]]]
|
||||
[:ids [::sm/vec ::sm/uuid]]
|
||||
[:v-sizing {:optional true} [:maybe [:enum :fill :fix :auto]]]])
|
||||
[:ids [::sm/vec ::sm/uuid]]])
|
||||
|
||||
(mf/defc layout-size-constraints*
|
||||
{::mf/private true
|
||||
::mf/schema (sm/schema schema:layout-size-constraints)}
|
||||
[{:keys [values v-sizing ids applied-tokens] :as props}]
|
||||
[{:keys [values ids applied-tokens] :as props}]
|
||||
(let [token-numeric-inputs
|
||||
(features/use-feature "tokens/numeric-input")
|
||||
|
||||
v-sizing
|
||||
(:layout-item-v-sizing values)
|
||||
|
||||
min-w (get values :layout-item-min-w)
|
||||
|
||||
max-w (get values :layout-item-max-w)
|
||||
@ -914,5 +916,4 @@
|
||||
(= v-sizing :fill))
|
||||
[:> layout-size-constraints* {:ids ids
|
||||
:values values
|
||||
:applied-tokens applied-tokens
|
||||
:v-sizing v-sizing}])])]))
|
||||
:applied-tokens applied-tokens}])])]))
|
||||
|
||||
@ -81,6 +81,7 @@
|
||||
selected))
|
||||
|
||||
(mf/defc viewport-classic*
|
||||
{::mf/private true}
|
||||
[{:keys [selected wglobal layout file page palete-size]}]
|
||||
(let [{:keys [edit-path
|
||||
panning
|
||||
@ -108,8 +109,8 @@
|
||||
;; DEREFS
|
||||
drawing (mf/deref refs/workspace-drawing)
|
||||
focus (mf/deref refs/workspace-focus-selected)
|
||||
|
||||
file-id (get file :id)
|
||||
vern (get file :vern)
|
||||
page-id (get page :id)
|
||||
objects (get page :objects)
|
||||
background (get page :background clr/canvas)
|
||||
@ -340,7 +341,7 @@
|
||||
:opacity 0.6}}
|
||||
(when (and (:can-edit permissions) (not read-only?))
|
||||
[:& stvh/viewport-texts
|
||||
{:key (dm/str "texts-" page-id)
|
||||
{:key (dm/str "viewport-texts-" page-id "-" vern)
|
||||
:page-id page-id
|
||||
:objects objects
|
||||
:modifiers modifiers
|
||||
@ -366,7 +367,7 @@
|
||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||
:xmlns:penpot "https://penpot.app/xmlns"
|
||||
:preserveAspectRatio "xMidYMid meet"
|
||||
:key (str "render" page-id)
|
||||
:key (dm/str "viewport-svg-" page-id "-" vern)
|
||||
:width (:width vport 0)
|
||||
:height (:height vport 0)
|
||||
:view-box (utils/format-viewbox vbox)
|
||||
@ -400,7 +401,7 @@
|
||||
[:& (mf/provider ctx/current-vbox) {:value vbox'}
|
||||
[:& (mf/provider use/include-metadata-ctx) {:value (dbg/enabled? :show-export-metadata)}
|
||||
;; Render root shape
|
||||
[:& shapes/root-shape {:key page-id
|
||||
[:& shapes/root-shape {:key (str page-id)
|
||||
:objects base-objects
|
||||
:active-frames @active-frames}]]]]
|
||||
|
||||
@ -408,7 +409,7 @@
|
||||
{:xmlns "http://www.w3.org/2000/svg"
|
||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||
:preserveAspectRatio "xMidYMid meet"
|
||||
:key (str "viewport" page-id)
|
||||
:key (dm/str "viewport-controls-" page-id "-" vern)
|
||||
:view-box (utils/format-viewbox vbox)
|
||||
:ref on-viewport-ref
|
||||
:class (dm/str @cursor (when drawing-tool " drawing") " " (stl/css :viewport-controls))
|
||||
@ -719,7 +720,7 @@
|
||||
(not= @hover-top-frame-id (:id frame)))
|
||||
[:& grid-layout/editor
|
||||
{:zoom zoom
|
||||
:key (dm/str (:id frame))
|
||||
:key (dm/str "viewport-frame-" (:id frame))
|
||||
:objects base-objects
|
||||
:modifiers modifiers
|
||||
:shape frame
|
||||
@ -733,8 +734,11 @@
|
||||
:bottom-padding (when palete-size (+ palete-size 8))}]]]]]))
|
||||
|
||||
(mf/defc viewport*
|
||||
[props]
|
||||
(let [wasm-renderer-enabled? (features/use-feature "render-wasm/v1")]
|
||||
(if ^boolean wasm-renderer-enabled?
|
||||
[:> viewport.wasm/viewport* props]
|
||||
[:> viewport-classic* props])))
|
||||
[{:keys [file page] :as props}]
|
||||
(let [vern (get file :vern)
|
||||
page-id (get page :id)
|
||||
render-wasm? (features/use-feature "render-wasm/v1")]
|
||||
[:* {:key (dm/str "viewport-" page-id "-" vern)}
|
||||
(if ^boolean render-wasm?
|
||||
[:> viewport.wasm/viewport* props]
|
||||
[:> viewport-classic* props])]))
|
||||
|
||||
@ -79,7 +79,7 @@
|
||||
(apply-modifiers-to-objects objects (select-keys (into {} modifiers) selected)))
|
||||
|
||||
(mf/defc viewport*
|
||||
[{:keys [selected wglobal layout file page palete-size file-version-id]}]
|
||||
[{:keys [selected wglobal layout file page palete-size]}]
|
||||
(let [;; When adding data from workspace-local revisit `app.main.ui.workspace` to check
|
||||
;; that the new parameter is sent
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
workspace-editor-state (mf/deref refs/workspace-editor-state)
|
||||
|
||||
file-id (get file :id)
|
||||
vern (get file :vern)
|
||||
objects (get page :objects)
|
||||
page-id (get page :id)
|
||||
background (get page :background clr/canvas)
|
||||
@ -154,7 +155,7 @@
|
||||
|
||||
canvas-ref (mf/use-ref nil)
|
||||
text-editor-ref (mf/use-ref nil)
|
||||
last-file-version-id-ref (mf/use-ref nil)
|
||||
last-vern-ref (mf/use-ref nil)
|
||||
|
||||
;; STATE REFS
|
||||
disable-paste-ref (mf/use-ref false)
|
||||
@ -391,10 +392,11 @@
|
||||
(when (and @canvas-init? preview-blend)
|
||||
(wasm.api/request-render "with-effect")))
|
||||
|
||||
(mf/with-effect [@canvas-init? file-version-id zoom vbox background]
|
||||
(mf/with-effect [@canvas-init? vern zoom vbox background]
|
||||
(when @canvas-init?
|
||||
(if (not @initialized?)
|
||||
(do
|
||||
(mf/set-ref-val! last-vern-ref vern)
|
||||
;; Initial file open uses the same transition workflow as page switches,
|
||||
;; but with a solid background-color blurred placeholder.
|
||||
(wasm.api/start-initial-load-transition! background)
|
||||
@ -402,14 +404,12 @@
|
||||
;; blank canvas (first load) visible while shapes load.
|
||||
;; The loading overlay is suppressed because on-shapes-ready
|
||||
;; is set.
|
||||
(wasm.api/initialize-viewport
|
||||
base-objects zoom vbox :background background)
|
||||
(reset! initialized? true)
|
||||
(mf/set-ref-val! last-file-version-id-ref file-version-id))
|
||||
(when (and (some? file-version-id)
|
||||
(not= file-version-id (mf/ref-val last-file-version-id-ref)))
|
||||
(wasm.api/initialize-viewport base-objects zoom vbox :background background)
|
||||
(mf/set-ref-val! last-file-version-id-ref file-version-id)))))
|
||||
(reset! initialized? true))
|
||||
|
||||
(when (and (some? vern) (not= vern (mf/ref-val last-vern-ref)))
|
||||
(wasm.api/initialize-viewport base-objects zoom vbox :background background)
|
||||
(mf/set-ref-val! last-vern-ref vern)))))
|
||||
|
||||
(mf/with-effect [focus]
|
||||
(when (and @canvas-init? @initialized?)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user