mirror of
https://github.com/penpot/penpot.git
synced 2026-04-25 11:18:36 +00:00
* ✨ Add read-only preview mode for saved versions (#7622) * 🔧 Address review feedback on version preview (#7622) * 🐛 Fix version preview for WASM renderer (#7622) * 🐛 Fix stylelint color-named and color-function-notation in preview banner (#7622) * 🐛 Fix invalid-arity call to initialize-workspace in exit-preview (#7622) * 🐛 Fix unclosed defn paren in exit-preview (#7622) * ♻️ Refactor version preview/restore flow Separate enter-preview and enter-restore flows with dedicated dialogs instead of a persistent banner. Removes preview-banner component in favor of inline actions dialog. Uses backup/restore pattern for exit-preview instead of full workspace reinitialization. Adds analytics events for preview/restore actions. Signed-off-by: Andrey Antukh <niwi@niwi.nz> * ⚡ Extract on-name-input-focus as namespace-level private function The callback had no dependencies on component-local state or props, making it a pure function that can be hoisted to a defn-. This avoids recreating the same callback identity on every render of version-entry*. * ⚡ Extract extract-id-from-event helper to deduplicate snapshot callbacks Three callbacks in snapshot-entry* shared the same DOM extraction logic (get current target, read data-id, parse UUID). Extracted into a private defn- to remove the duplication and simplify each callback. * ⚡ Extract pure state-update callbacks from versions-toolbox* to namespace level Eight callbacks that only emit fixed Potok events with no meaningful deps were hoisted out of the component as defn- functions: - on-create-version - on-edit-version - on-cancel-version-edition - on-rename-version - on-delete-version - on-pin-version - on-lock-version - on-unlock-version These no longer need mf/use-fn wrappers since namespace-level functions have stable identity across renders, avoiding unnecessary callback recreation on each render cycle. * ✨ Rename filter parameter to filter-value in on-change-filter to avoid core shadowing The parameter name 'filter' shadowed clojure.core/filter within the function scope. Renamed to 'filter-value' for clarity and to prevent potential bugs if core/filter were needed in future changes. * 🔧 Fix linter warnings and errors across version-related namespaces frontend/src/app/main/ui/workspace.cljs: - Remove unused requires: app.common.data, app.main.data.notifications, app.main.data.workspace.versions frontend/src/app/main/data/workspace/versions.cljs: - Remove unused require: app.common.uuid - Fix duplicate reify type: enter-restore used ::restore-version (same as the private restore-version fn), renamed to ::enter-restore - Remove unused bindings: state in enter-restore, team-id in exit-preview and restore-version-from-plugin --------- Signed-off-by: Andrey Antukh <niwi@niwi.nz> Signed-off-by: wdeveloper16 <wdeveloer16@protonmail.com> Co-authored-by: wdeveloper16 <wdeveloer16@protonmail.com> Co-authored-by: Andrey Antukh <niwi@niwi.nz>
This commit is contained in:
parent
7c1a29ccf7
commit
e280168de9
@ -112,8 +112,9 @@
|
||||
THEN (c.deleted_at IS NULL OR c.deleted_at >= ?::timestamptz)
|
||||
END"))
|
||||
|
||||
(defn- get-snapshot
|
||||
"Get snapshot with decoded data"
|
||||
(defn get-snapshot-data
|
||||
"Get a fully decoded snapshot for read-only preview or restoration.
|
||||
Returns the snapshot map with decoded :data field."
|
||||
[cfg file-id snapshot-id]
|
||||
(let [now (ct/now)]
|
||||
(->> (db/get-with-sql cfg [sql:get-snapshot file-id snapshot-id now]
|
||||
@ -326,7 +327,7 @@
|
||||
(sto/resolve cfg {::db/reuse-conn true})
|
||||
|
||||
snapshot
|
||||
(get-snapshot cfg file-id snapshot-id)]
|
||||
(get-snapshot-data cfg file-id snapshot-id)]
|
||||
|
||||
(when-not snapshot
|
||||
(ex/raise :type :not-found
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
(:require
|
||||
[app.binfile.common :as bfc]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.features :as-alias cfeat]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.time :as ct]
|
||||
[app.db :as db]
|
||||
@ -35,6 +36,43 @@
|
||||
(files/check-read-permissions! conn profile-id file-id)
|
||||
(fsnap/get-visible-snapshots conn file-id))))
|
||||
|
||||
;; --- COMMAND QUERY: get-file-snapshot
|
||||
|
||||
(def ^:private schema:get-file-snapshot
|
||||
[:map {:title "get-file-snapshot"}
|
||||
[:file-id ::sm/uuid]
|
||||
[:id ::sm/uuid]
|
||||
[:features {:optional true} ::cfeat/features]])
|
||||
|
||||
(sv/defmethod ::get-file-snapshot
|
||||
"Retrieve a file bundle with data from a specific snapshot for
|
||||
read-only preview. Does not modify any database state."
|
||||
{::doc/added "2.16"
|
||||
::sm/params schema:get-file-snapshot
|
||||
::sm/result files/schema:file-with-permissions
|
||||
::db/transaction true}
|
||||
[{:keys [::db/conn] :as cfg} {:keys [::rpc/profile-id file-id id] :as params}]
|
||||
(let [perms (bfc/get-file-permissions conn profile-id file-id)]
|
||||
(files/check-read-permissions! perms)
|
||||
(let [snapshot (fsnap/get-snapshot-data cfg file-id id)]
|
||||
(when-not snapshot
|
||||
(ex/raise :type :not-found
|
||||
:code :snapshot-not-found
|
||||
:hint "unable to find snapshot with the provided id"
|
||||
:snapshot-id id
|
||||
:file-id file-id))
|
||||
;; Load current file metadata only (no data decoding) then overlay
|
||||
;; the snapshot data so the client receives the same shape as a
|
||||
;; normal get-file response but with historical page/object content.
|
||||
(let [base-file (bfc/get-file cfg file-id :load-data? false)]
|
||||
(-> base-file
|
||||
(assoc :data (:data snapshot))
|
||||
(assoc :version (:version snapshot))
|
||||
(assoc :features (:features snapshot))
|
||||
(assoc :revn (:revn snapshot))
|
||||
(assoc :vern (rand-int 100000))
|
||||
(assoc :permissions perms))))))
|
||||
|
||||
(def ^:private schema:create-file-snapshot
|
||||
[:map
|
||||
[:file-id ::sm/uuid]
|
||||
|
||||
@ -121,8 +121,10 @@
|
||||
:features features}
|
||||
permissions (:permissions state)]
|
||||
|
||||
;; Prevent commit changes by a team member without edition permission
|
||||
(when (:can-edit permissions)
|
||||
;; Prevent saving changes when in version preview (read-only) mode
|
||||
;; or when the user does not have edition permission.
|
||||
(when (and (:can-edit permissions)
|
||||
(not (get-in state [:workspace-global :read-only?])))
|
||||
(->> (rp/cmd! :update-file params)
|
||||
(rx/mapcat (fn [{:keys [revn lagged] :as response}]
|
||||
(log/debug :hint "changes persisted" :commit-id (dm/str commit-id) :lagged (count lagged))
|
||||
|
||||
@ -511,7 +511,8 @@
|
||||
:workspace-persistence
|
||||
:workspace-presence
|
||||
:workspace-tokens
|
||||
:workspace-undo)
|
||||
:workspace-undo
|
||||
:workspace-versions)
|
||||
(update :workspace-global dissoc :read-only?)
|
||||
(assoc-in [:workspace-global :options-mode] :design)
|
||||
(update :files d/update-vals #(dissoc % :data))))
|
||||
|
||||
@ -8,23 +8,28 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.logging :as log]
|
||||
[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.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.repo :as rp]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(defonce default-state
|
||||
{:status :loading
|
||||
:data nil
|
||||
:editing nil})
|
||||
:editing nil
|
||||
:preview-id nil})
|
||||
|
||||
(declare fetch-versions)
|
||||
|
||||
@ -122,32 +127,6 @@
|
||||
(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)
|
||||
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))
|
||||
|
||||
(->> (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
|
||||
[id]
|
||||
(assert (uuid? id) "expected valid uuid for `id`")
|
||||
@ -193,6 +172,145 @@
|
||||
(->> (rp/cmd! :unlock-file-snapshot {:id id})
|
||||
(rx/map fetch-versions)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; RESTORE VERSION EVENTS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn- restore-version
|
||||
[id]
|
||||
(assert (uuid? id) "expected valid uuid for `id`")
|
||||
(ptk/reify ::restore-version
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [file-id (:current-file-id state)]
|
||||
(rx/concat
|
||||
(rx/of ::dwp/force-persist
|
||||
(dw/remove-layout-flag :document-history))
|
||||
|
||||
(->> (wait-for-persistence file-id id)
|
||||
(rx/map #(initialize-version))))))))
|
||||
|
||||
(defn enter-restore
|
||||
[id]
|
||||
(assert (uuid? id) "expected valid uuid for `id`")
|
||||
(ptk/reify ::enter-restore
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(let [output-s (rx/subject)]
|
||||
(rx/merge
|
||||
output-s
|
||||
(rx/of (ntf/dialog
|
||||
:content (tr "workspace.versions.restore-warning")
|
||||
:controls :inline-actions
|
||||
:cancel {:label (tr "workspace.updates.dismiss")
|
||||
:callback #(do
|
||||
(rx/push! output-s (ntf/hide :tag :restore-dialog))
|
||||
(rx/end! output-s))}
|
||||
:accept {:label (tr "labels.restore")
|
||||
:callback #(do
|
||||
(rx/push! output-s (restore-version id))
|
||||
(rx/end! output-s))}
|
||||
:tag :restore-dialog)))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; PREVIEW VERSION EVENTS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn- apply-snapshot
|
||||
"Swap the file data in app state with the provided snapshot-file
|
||||
response. Used by the version preview feature to show historical
|
||||
file content without modifying the database"
|
||||
[{:keys [id] :as snapshot}]
|
||||
(ptk/reify ::apply-snapshot-data
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :files assoc id snapshot))))
|
||||
|
||||
(defn exit-preview
|
||||
"Exit from preview mode and reload the live file data"
|
||||
[]
|
||||
(ptk/reify ::exit-preview
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [backup (dm/get-in state [:workspace-versions :backup])]
|
||||
(-> state
|
||||
(update :workspace-versions dissoc :backup)
|
||||
(update :workspace-global dissoc :read-only? :preview-id)
|
||||
(update :files assoc (:id backup) backup))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [file-id (:current-file-id state)
|
||||
page-id (:current-page-id state)]
|
||||
|
||||
(rx/of (dwpg/initialize-page file-id page-id))))))
|
||||
|
||||
(defn enter-preview
|
||||
"Load a snapshot into the workspace for read-only preview without
|
||||
modifying any database state. Sets a read-only flag so no changes
|
||||
are persisted while previewing and enter on the preview mode"
|
||||
[id]
|
||||
(assert (uuid? id) "expected valid uuid for `id`")
|
||||
|
||||
(ptk/reify ::enter-preview
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [file (dsh/lookup-file state)]
|
||||
(-> state
|
||||
(update :workspace-versions assoc :backup file)
|
||||
(update :workspace-global assoc :read-only? true :preview-id id))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [file-id (:current-file-id state)
|
||||
page-id (:current-page-id state)
|
||||
team-id (:current-team-id state)
|
||||
features (features/get-enabled-features state team-id)
|
||||
snapshot (->> (dm/get-in state [:workspace-versions :data])
|
||||
(d/seek #(= id (:id %))))
|
||||
label (or (:label snapshot)
|
||||
(tr "workspace.versions.preview.unnamed"))
|
||||
output-s (rx/subject)]
|
||||
(rx/merge
|
||||
output-s
|
||||
|
||||
(rx/of (ntf/dialog
|
||||
:content (tr "workspace.versions.preview-banner-title" label)
|
||||
:controls :inline-actions
|
||||
:cancel {:label (tr "labels.exit")
|
||||
:callback #(do
|
||||
(rx/push! output-s (ntf/hide))
|
||||
(rx/push! output-s (exit-preview))
|
||||
(rx/end! output-s))}
|
||||
:accept {:label (tr "labels.restore")
|
||||
:callback #(do
|
||||
(rx/push! output-s (ntf/hide))
|
||||
(rx/push! output-s (restore-version id))
|
||||
(rx/end! output-s))}
|
||||
:tag :preview-dialog))
|
||||
|
||||
(->> (rp/cmd! :get-file-snapshot
|
||||
{:file-id file-id
|
||||
:id id
|
||||
:features features})
|
||||
(rx/mapcat
|
||||
(fn [snapshot]
|
||||
(rx/of
|
||||
;; Swap the file data in state with snapshot content.
|
||||
;; Passing id sets workspace-file-version-id, which
|
||||
;; causes the WASM viewport to reload its shape buffer.
|
||||
(apply-snapshot snapshot)
|
||||
;; Re-initialize the page to rebuild its search index
|
||||
;; and page-local state with the new snapshot
|
||||
;; objects.
|
||||
(dwpg/initialize-page file-id page-id))))
|
||||
|
||||
(rx/catch (fn [err]
|
||||
;; On error roll back the read-only flag so the
|
||||
;; user is not stuck in a broken preview state.
|
||||
(log/error :hint "failed to load snapshot" :cause err :file-id file-id :snapshot-id id)
|
||||
(rx/of (exit-preview))))))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; PLUGINS SPECIFIC EVENTS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@ -246,20 +364,18 @@
|
||||
|
||||
(ptk/reify ::restore-version-from-plugins
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [team-id (:current-team-id state)]
|
||||
(rx/concat
|
||||
(rx/of (ev/event {::ev/name "restore-version-plugin"
|
||||
:file-id file-id
|
||||
:team-id team-id})
|
||||
::dwp/force-persist)
|
||||
(watch [_ _ _]
|
||||
(rx/concat
|
||||
(rx/of (ev/event {::ev/name "restore-version"
|
||||
::ev/origin "plugins"})
|
||||
::dwp/force-persist)
|
||||
|
||||
(->> (wait-for-persistence file-id id)
|
||||
(rx/map #(initialize-version)))
|
||||
(->> (wait-for-persistence file-id id)
|
||||
(rx/map #(initialize-version)))
|
||||
|
||||
(->> (rx/of 1)
|
||||
(rx/tap resolve)
|
||||
(rx/ignore)))))))
|
||||
(->> (rx/of 1)
|
||||
(rx/tap resolve)
|
||||
(rx/ignore))))))
|
||||
|
||||
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
[app.common.time :as ct]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cfg]
|
||||
[app.main.data.notifications :as ntf]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.workspace.versions :as dwv]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
@ -77,20 +77,49 @@
|
||||
(assoc item :index index)))
|
||||
(reverse)))
|
||||
|
||||
(defn- open-restore-version-dialog
|
||||
[origin id]
|
||||
(st/emit! (ntf/dialog
|
||||
:content (tr "workspace.versions.restore-warning")
|
||||
:controls :inline-actions
|
||||
:cancel {:label (tr "workspace.updates.dismiss")
|
||||
:callback #(st/emit! (ntf/hide))}
|
||||
:accept {:label (tr "labels.restore")
|
||||
:callback #(st/emit! (dwv/restore-version id origin))}
|
||||
:tag :restore-dialog)))
|
||||
(defn- on-name-input-focus
|
||||
[event]
|
||||
(dom/select-text! (dom/get-target event)))
|
||||
|
||||
(defn- extract-id-from-event
|
||||
[event]
|
||||
(-> event dom/get-current-target (dom/get-data "id") uuid/parse))
|
||||
|
||||
(defn- on-create-version
|
||||
[]
|
||||
(st/emit! (dwv/create-version)))
|
||||
|
||||
(defn- on-edit-version
|
||||
[id _event]
|
||||
(st/emit! (dwv/update-versions-state {:editing id})))
|
||||
|
||||
(defn- on-cancel-version-edition
|
||||
[_id _event]
|
||||
(st/emit! (dwv/update-versions-state {:editing nil})))
|
||||
|
||||
(defn- on-rename-version
|
||||
[id label]
|
||||
(st/emit! (dwv/rename-version id label)))
|
||||
|
||||
(defn- on-delete-version
|
||||
[id]
|
||||
(st/emit! (dwv/delete-version id)))
|
||||
|
||||
(defn- on-pin-version
|
||||
[id]
|
||||
(st/emit! (dwv/pin-version id)))
|
||||
|
||||
(defn- on-lock-version
|
||||
[id]
|
||||
(st/emit! (dwv/lock-version id)))
|
||||
|
||||
(defn- on-unlock-version
|
||||
[id]
|
||||
(st/emit! (dwv/unlock-version id)))
|
||||
|
||||
(mf/defc version-entry*
|
||||
{::mf/private true}
|
||||
[{:keys [entry current-profile on-restore on-delete on-rename on-lock on-unlock on-edit on-cancel-edit is-editing]}]
|
||||
[{:keys [entry current-profile on-preview on-restore on-delete on-rename on-lock on-unlock on-edit on-cancel-edit is-editing]}]
|
||||
(let [show-menu? (mf/use-state false)
|
||||
profiles (mf/deref refs/profiles)
|
||||
|
||||
@ -108,6 +137,13 @@
|
||||
(fn [event]
|
||||
(on-edit (:id entry) event)))
|
||||
|
||||
on-preview
|
||||
(mf/use-fn
|
||||
(mf/deps entry on-preview)
|
||||
(fn []
|
||||
(when (fn? on-preview)
|
||||
(on-preview (:id entry)))))
|
||||
|
||||
on-restore
|
||||
(mf/use-fn
|
||||
(mf/deps entry on-restore)
|
||||
@ -136,11 +172,6 @@
|
||||
(when on-unlock
|
||||
(on-unlock (:id entry)))))
|
||||
|
||||
on-name-input-focus
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(dom/select-text! (dom/get-target event))))
|
||||
|
||||
on-name-input-blur
|
||||
(mf/use-fn
|
||||
(mf/deps entry on-rename on-cancel-edit)
|
||||
@ -191,6 +222,11 @@
|
||||
:on-click on-edit}
|
||||
(tr "labels.rename")])
|
||||
|
||||
[:li {:class (stl/css :menu-option)
|
||||
:role "button"
|
||||
:on-click on-preview}
|
||||
(tr "workspace.versions.button.preview")]
|
||||
|
||||
[:li {:class (stl/css :menu-option)
|
||||
:role "button"
|
||||
:on-click on-restore}
|
||||
@ -216,7 +252,7 @@
|
||||
(tr "labels.delete")])])]]))
|
||||
|
||||
(mf/defc snapshot-entry*
|
||||
[{:keys [entry on-pin-snapshot on-restore-snapshot]}]
|
||||
[{:keys [entry on-pin-snapshot on-restore-snapshot on-preview-snapshot]}]
|
||||
|
||||
(let [open-menu* (mf/use-state nil)
|
||||
entry-ref (mf/use-ref nil)
|
||||
@ -225,23 +261,22 @@
|
||||
(mf/use-fn
|
||||
(mf/deps on-pin-snapshot)
|
||||
(fn [event]
|
||||
(let [node (dom/get-current-target event)
|
||||
id (-> node
|
||||
(dom/get-data "id")
|
||||
(uuid/parse))]
|
||||
(when (fn? on-pin-snapshot)
|
||||
(on-pin-snapshot id event)))))
|
||||
(when (fn? on-pin-snapshot)
|
||||
(on-pin-snapshot (extract-id-from-event event) event))))
|
||||
|
||||
on-restore-snapshot
|
||||
(mf/use-fn
|
||||
(mf/deps on-restore-snapshot)
|
||||
(fn [event]
|
||||
(let [node (dom/get-current-target event)
|
||||
id (-> node
|
||||
(dom/get-data "id")
|
||||
(uuid/parse))]
|
||||
(when (fn? on-restore-snapshot)
|
||||
(on-restore-snapshot id event)))))
|
||||
(when (fn? on-restore-snapshot)
|
||||
(on-restore-snapshot (extract-id-from-event event) event))))
|
||||
|
||||
on-preview-snapshot
|
||||
(mf/use-fn
|
||||
(mf/deps on-preview-snapshot)
|
||||
(fn [event]
|
||||
(when (fn? on-preview-snapshot)
|
||||
(on-preview-snapshot (extract-id-from-event event) event))))
|
||||
|
||||
on-open-snapshot-menu
|
||||
(mf/use-fn
|
||||
@ -266,6 +301,11 @@
|
||||
:on-close #(reset! open-menu* nil)}
|
||||
[:ul {:class (stl/css :version-options-dropdown)
|
||||
:style {"--offset" (dm/str (:offset @open-menu*) "px")}}
|
||||
[:li {:class (stl/css :menu-option)
|
||||
:role "button"
|
||||
:data-id (dm/str (:snapshot @open-menu*))
|
||||
:on-click on-preview-snapshot}
|
||||
(tr "workspace.versions.button.preview")]
|
||||
[:li {:class (stl/css :menu-option)
|
||||
:role "button"
|
||||
:data-id (dm/str (:snapshot @open-menu*))
|
||||
@ -302,66 +342,50 @@
|
||||
(= (:filter state) (:profile-id %)))))
|
||||
(group-snapshots)))
|
||||
|
||||
on-create-version
|
||||
on-preview-version
|
||||
(mf/use-fn
|
||||
(fn [] (st/emit! (dwv/create-version))))
|
||||
(fn [id]
|
||||
(st/emit! (dwv/enter-preview id)
|
||||
(ev/event {::ev/name "preview-version"
|
||||
::ev/origin "workspace:sidebar"
|
||||
:type "pinned-version"}))))
|
||||
|
||||
on-edit-version
|
||||
on-preview-snapshot
|
||||
(mf/use-fn
|
||||
(fn [id _event]
|
||||
(st/emit! (dwv/update-versions-state {:editing id}))))
|
||||
|
||||
on-cancel-version-edition
|
||||
(mf/use-fn
|
||||
(fn [_id _event]
|
||||
(st/emit! (dwv/update-versions-state {:editing nil}))))
|
||||
|
||||
on-rename-version
|
||||
(mf/use-fn
|
||||
(fn [id label]
|
||||
(st/emit! (dwv/rename-version id label))))
|
||||
(st/emit! (dwv/enter-preview id)
|
||||
(ev/event {::ev/name "preview-version"
|
||||
::ev/origin "workspace:sidebar"
|
||||
:type "autosaved-version"}))))
|
||||
|
||||
on-restore-version
|
||||
(mf/use-fn
|
||||
(fn [id _event]
|
||||
(open-restore-version-dialog :version id)))
|
||||
(st/emit! (dwv/enter-restore id)
|
||||
(ev/event {::ev/name "restore-version"
|
||||
::ev/origin "workspace:sidebar"
|
||||
:type "pinned-version"}))))
|
||||
|
||||
on-restore-snapshot
|
||||
(mf/use-fn
|
||||
(fn [id _event]
|
||||
(open-restore-version-dialog :snapshot id)))
|
||||
|
||||
on-delete-version
|
||||
(mf/use-fn
|
||||
(fn [id]
|
||||
(st/emit! (dwv/delete-version id))))
|
||||
|
||||
on-pin-version
|
||||
(mf/use-fn
|
||||
(fn [id] (st/emit! (dwv/pin-version id))))
|
||||
|
||||
on-lock-version
|
||||
(mf/use-fn
|
||||
(fn [id]
|
||||
(st/emit! (dwv/lock-version id))))
|
||||
|
||||
on-unlock-version
|
||||
(mf/use-fn
|
||||
(fn [id]
|
||||
(st/emit! (dwv/unlock-version id))))
|
||||
(st/emit! (dwv/enter-restore id)
|
||||
(ev/event {::ev/name "restore-version"
|
||||
::ev/origin "workspace:sidebar"
|
||||
:type "autosaved-version"}))))
|
||||
|
||||
on-change-filter
|
||||
(mf/use-fn
|
||||
(fn [filter]
|
||||
(fn [filter-value]
|
||||
(cond
|
||||
(= :all filter)
|
||||
(= :all filter-value)
|
||||
(st/emit! (dwv/update-versions-state {:filter nil}))
|
||||
|
||||
(= :own filter)
|
||||
(= :own filter-value)
|
||||
(st/emit! (dwv/update-versions-state {:filter (:id profile)}))
|
||||
|
||||
:else
|
||||
(st/emit! (dwv/update-versions-state {:filter filter})))))
|
||||
(st/emit! (dwv/update-versions-state {:filter filter-value})))))
|
||||
|
||||
options
|
||||
(mf/with-memo [users profile]
|
||||
@ -415,6 +439,7 @@
|
||||
:on-edit on-edit-version
|
||||
:on-cancel-edit on-cancel-version-edition
|
||||
:on-rename on-rename-version
|
||||
:on-preview on-preview-version
|
||||
:on-restore on-restore-version
|
||||
:on-delete on-delete-version
|
||||
:on-lock on-lock-version
|
||||
@ -423,6 +448,7 @@
|
||||
:snapshot
|
||||
[:> snapshot-entry* {:key (:index entry)
|
||||
:entry entry
|
||||
:on-preview-snapshot on-preview-snapshot
|
||||
:on-restore-snapshot on-restore-snapshot
|
||||
:on-pin-snapshot on-pin-version}]
|
||||
|
||||
|
||||
@ -97,6 +97,7 @@
|
||||
|
||||
{:keys [options-mode
|
||||
tooltip
|
||||
preview-id
|
||||
show-distances?
|
||||
picking-color?]}
|
||||
wglobal
|
||||
@ -314,23 +315,28 @@
|
||||
(hooks/setup-shortcuts path-editing? path-drawing? text-editing? grid-editing?)
|
||||
(hooks/setup-active-frames base-objects hover-ids selected active-frames zoom transform vbox)
|
||||
|
||||
[:div {:class (stl/css :viewport) :style #js {"--zoom" zoom} :data-testid "viewport"}
|
||||
(when (:can-edit permissions)
|
||||
(if read-only?
|
||||
[:> view-only-bar* {}]
|
||||
[:*
|
||||
(when-not hide-ui?
|
||||
[:> top-toolbar* {:layout layout}])
|
||||
[:div {:class (stl/css :viewport) :style {"--zoom" zoom} :data-testid "viewport"}
|
||||
(cond
|
||||
(some? preview-id)
|
||||
nil
|
||||
|
||||
(when (and ^boolean path-editing?
|
||||
^boolean single-select?)
|
||||
[:> path-edition-bar* {:shape editing-shape
|
||||
:edit-path-state edit-path-state
|
||||
:layout layout}])
|
||||
(and read-only? (:can-edit permissions))
|
||||
[:> view-only-bar* {}]
|
||||
|
||||
(when (and ^boolean grid-editing?
|
||||
^boolean single-select?)
|
||||
[:> grid-edition-bar* {:shape editing-shape}])]))
|
||||
:else
|
||||
[:*
|
||||
(when-not hide-ui?
|
||||
[:> top-toolbar* {:layout layout}])
|
||||
|
||||
(when (and ^boolean path-editing?
|
||||
^boolean single-select?)
|
||||
[:> path-edition-bar* {:shape editing-shape
|
||||
:edit-path-state edit-path-state
|
||||
:layout layout}])
|
||||
|
||||
(when (and ^boolean grid-editing?
|
||||
^boolean single-select?)
|
||||
[:> grid-edition-bar* {:shape editing-shape}])])
|
||||
|
||||
[:div {:class (stl/css :viewport-overlays)}
|
||||
;; The behaviour inside a foreign object is a bit different that in plain HTML so we wrap
|
||||
|
||||
@ -20,14 +20,12 @@
|
||||
;; branch.
|
||||
|
||||
(mf/defc view-only-bar*
|
||||
{::mf/private true}
|
||||
[]
|
||||
(let [handle-close-view-mode
|
||||
(let [on-close
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(st/emit! :interrupt
|
||||
(dw/set-options-mode :design)
|
||||
(dwc/set-workspace-read-only false))))]
|
||||
#(st/emit! :interrupt
|
||||
(dw/set-options-mode :design)
|
||||
(dwc/set-workspace-read-only false)))]
|
||||
[:div {:class (stl/css :viewport-actions)}
|
||||
[:div {:class (stl/css :viewport-actions-container)}
|
||||
[:div {:class (stl/css :viewport-actions-title)}
|
||||
@ -35,7 +33,7 @@
|
||||
{:tag-name "span"
|
||||
:content (tr "workspace.top-bar.view-only")}]]
|
||||
[:button {:class (stl/css :done-btn)
|
||||
:on-click handle-close-view-mode}
|
||||
:on-click on-close}
|
||||
(tr "workspace.top-bar.read-only.done")]]]))
|
||||
|
||||
(mf/defc path-edition-bar*
|
||||
|
||||
@ -98,6 +98,7 @@
|
||||
{:keys [options-mode
|
||||
tooltip
|
||||
show-distances?
|
||||
preview-id
|
||||
picking-color?]}
|
||||
wglobal
|
||||
|
||||
@ -456,22 +457,28 @@
|
||||
(hooks/setup-active-frames base-objects hover-ids selected active-frames zoom transform vbox)
|
||||
|
||||
[:div {:class (stl/css :viewport) :style #js {"--zoom" zoom} :data-testid "viewport"}
|
||||
(when (:can-edit permissions)
|
||||
(if read-only?
|
||||
[:> view-only-bar* {}]
|
||||
[:*
|
||||
(when-not hide-ui?
|
||||
[:> top-toolbar* {:layout layout}])
|
||||
|
||||
(when (and ^boolean path-editing?
|
||||
^boolean single-select?)
|
||||
[:> path-edition-bar* {:shape editing-shape
|
||||
:edit-path-state edit-path-state
|
||||
:layout layout}])
|
||||
(cond
|
||||
(some? preview-id)
|
||||
nil
|
||||
|
||||
(when (and ^boolean grid-editing?
|
||||
^boolean single-select?)
|
||||
[:> grid-edition-bar* {:shape editing-shape}])]))
|
||||
(and read-only? (:can-edit permissions))
|
||||
[:> view-only-bar* {}]
|
||||
|
||||
:else
|
||||
[:*
|
||||
(when-not hide-ui?
|
||||
[:> top-toolbar* {:layout layout}])
|
||||
|
||||
(when (and ^boolean path-editing?
|
||||
^boolean single-select?)
|
||||
[:> path-edition-bar* {:shape editing-shape
|
||||
:edit-path-state edit-path-state
|
||||
:layout layout}])
|
||||
|
||||
(when (and ^boolean grid-editing?
|
||||
^boolean single-select?)
|
||||
[:> grid-edition-bar* {:shape editing-shape}])])
|
||||
|
||||
[:div {:class (stl/css :viewport-overlays)}
|
||||
(when show-comments?
|
||||
|
||||
@ -3049,6 +3049,9 @@ msgstr "Resend invitation"
|
||||
msgid "labels.restore"
|
||||
msgstr "Restore"
|
||||
|
||||
msgid "labels.exit"
|
||||
msgstr "Exit"
|
||||
|
||||
#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:299, src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:419
|
||||
msgid "labels.retry"
|
||||
msgstr "Retry"
|
||||
@ -9064,6 +9067,9 @@ msgstr "Autosaved %s"
|
||||
msgid "workspace.versions.button.pin"
|
||||
msgstr "Pin version"
|
||||
|
||||
msgid "workspace.versions.button.preview"
|
||||
msgstr "Preview version"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/versions.cljs:273
|
||||
msgid "workspace.versions.button.restore"
|
||||
msgstr "Restore version"
|
||||
@ -9108,6 +9114,12 @@ msgstr "This version is locked by %s and cannot be modified"
|
||||
msgid "workspace.versions.locked-by-you"
|
||||
msgstr "This version is locked by you"
|
||||
|
||||
msgid "workspace.versions.preview-banner-title"
|
||||
msgstr "Previewing version: %s"
|
||||
|
||||
msgid "workspace.versions.preview.unnamed"
|
||||
msgstr "Unnamed version"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/versions.cljs:83
|
||||
msgid "workspace.versions.restore-warning"
|
||||
msgstr "Do you want to restore this version?"
|
||||
|
||||
@ -2976,6 +2976,9 @@ msgstr "Reenviar invitacion"
|
||||
msgid "labels.restore"
|
||||
msgstr "Restaurar"
|
||||
|
||||
msgid "labels.exit"
|
||||
msgstr "Salir"
|
||||
|
||||
#: src/app/main/ui/components/progress.cljs:80, src/app/main/ui/static.cljs:299, src/app/main/ui/static.cljs:308, src/app/main/ui/static.cljs:419
|
||||
msgid "labels.retry"
|
||||
msgstr "Reintentar"
|
||||
@ -8869,6 +8872,9 @@ msgstr "Versiones de %s"
|
||||
msgid "workspace.versions.loading"
|
||||
msgstr "Cargando..."
|
||||
|
||||
msgid "workspace.versions.preview-banner-title"
|
||||
msgstr "Previsualizando version: %s"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/versions.cljs:83
|
||||
msgid "workspace.versions.restore-warning"
|
||||
msgstr "¿Quieres restaurar esta versión?"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user