mirror of
https://github.com/penpot/penpot.git
synced 2025-03-25 06:01:46 -05:00
♻️ Refactor persistence layer
This commit is contained in:
parent
d679001955
commit
6436ef334b
62 changed files with 1030 additions and 1070 deletions
|
@ -12,6 +12,7 @@
|
|||
|
||||
(def registry (atom {}))
|
||||
|
||||
|
||||
(defn potok-reify
|
||||
[{:keys [:node :filename] :as params}]
|
||||
(let [[rnode rtype & other] (:children node)
|
||||
|
|
|
@ -9,9 +9,11 @@
|
|||
### :heart: Community contributions (Thank you!)
|
||||
|
||||
### :sparkles: New features
|
||||
- Improve auth process [Taiga #Change Auth Process](https://tree.taiga.io/project/penpot/us/7094)
|
||||
- Add locking degrees increment (hold shift) on path edition [Taiga #7761](https://tree.taiga.io/project/penpot/issue/7761)
|
||||
- Allow library colors as recent colors [Taiga issue #7640](https://tree.taiga.io/project/penpot/issue/7640)
|
||||
|
||||
- Improve auth process [Taiga #7094](https://tree.taiga.io/project/penpot/us/7094)
|
||||
- Add locking degrees increment (hold shift) on path edition [Taiga Issue #7761](https://tree.taiga.io/project/penpot/issue/7761)
|
||||
- Persistence & Concurrent Edition Enhancements [Taiga #5657](https://tree.taiga.io/project/penpot/us/5657)
|
||||
- Allow library colors as recent colors [Taiga Issue #7640](https://tree.taiga.io/project/penpot/issue/7640)
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
|
|
|
@ -262,8 +262,8 @@
|
|||
;; Send asynchronous notifications
|
||||
(send-notifications! cfg params)
|
||||
|
||||
;; Retrieve and return lagged data
|
||||
(get-lagged-changes conn params))))
|
||||
{:revn (:revn file)
|
||||
:lagged (get-lagged-changes conn params)})))
|
||||
|
||||
(defn- soft-validate-file-schema!
|
||||
[file]
|
||||
|
|
166
frontend/src/app/main/data/changes.cljs
Normal file
166
frontend/src/app/main/data/changes.cljs
Normal file
|
@ -0,0 +1,166 @@
|
|||
;; 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.data.changes
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.files.changes :as cpc]
|
||||
[app.common.logging :as log]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.features :as features]
|
||||
[app.main.worker :as uw]
|
||||
[app.util.time :as dt]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;; Change this to :info :debug or :trace to debug this module
|
||||
(log/set-level! :debug)
|
||||
|
||||
(def page-change?
|
||||
#{:add-page :mod-page :del-page :mov-page})
|
||||
(def update-layout-attr?
|
||||
#{:hidden})
|
||||
|
||||
(def commit?
|
||||
(ptk/type? ::commit))
|
||||
|
||||
(defn update-indexes
|
||||
"Given a commit, send the changes to the worker for updating the
|
||||
indexes."
|
||||
[{:keys [changes] :as commit}]
|
||||
(ptk/reify ::update-indexes
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(let [changes (->> changes
|
||||
(map (fn [{:keys [id type page] :as change}]
|
||||
(cond-> change
|
||||
(and (page-change? type) (nil? (:page-id change)))
|
||||
(assoc :page-id (or id (:id page))))))
|
||||
(filter :page-id)
|
||||
(group-by :page-id))]
|
||||
|
||||
(->> (rx/from changes)
|
||||
(rx/merge-map (fn [[page-id changes]]
|
||||
(log/debug :hint "update-indexes" :page-id page-id :changes (count changes))
|
||||
(uw/ask! {:cmd :update-page-index
|
||||
:page-id page-id
|
||||
:changes changes})))
|
||||
(rx/ignore))))))
|
||||
|
||||
(defn- get-pending-commits
|
||||
[{:keys [persistence]}]
|
||||
(->> (:queue persistence)
|
||||
(map (d/getf (:index persistence)))
|
||||
(not-empty)))
|
||||
|
||||
(defn commit
|
||||
"Create a commit event instance"
|
||||
[{:keys [commit-id redo-changes undo-changes origin save-undo? features
|
||||
file-id file-revn undo-group tags stack-undo? source]}]
|
||||
|
||||
(dm/assert!
|
||||
"expect valid vector of changes"
|
||||
(and (cpc/check-changes! redo-changes)
|
||||
(cpc/check-changes! undo-changes)))
|
||||
|
||||
(let [commit-id (or commit-id (uuid/next))
|
||||
commit {:id commit-id
|
||||
:created-at (dt/now)
|
||||
:source (d/nilv source :local)
|
||||
:origin (ptk/type origin)
|
||||
:features features
|
||||
:file-id file-id
|
||||
:file-revn file-revn
|
||||
:changes redo-changes
|
||||
:redo-changes redo-changes
|
||||
:undo-changes undo-changes
|
||||
:save-undo? save-undo?
|
||||
:undo-group undo-group
|
||||
:tags tags
|
||||
:stack-undo? stack-undo?}]
|
||||
|
||||
(ptk/reify ::commit
|
||||
cljs.core/IDeref
|
||||
(-deref [_] commit)
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [current-file-id (get state :current-file-id)
|
||||
path (if (= file-id current-file-id)
|
||||
[:workspace-data]
|
||||
[:workspace-libraries file-id :data])
|
||||
|
||||
not-local? (not= source :local)
|
||||
pending (if not-local?
|
||||
(get-pending-commits state)
|
||||
nil)
|
||||
|
||||
undo-changes (if pending
|
||||
(->> pending
|
||||
(map :undo-changes)
|
||||
(reverse)
|
||||
(mapcat identity)
|
||||
(vec))
|
||||
nil)
|
||||
|
||||
redo-changes (if pending
|
||||
(into redo-changes
|
||||
(comp
|
||||
(map :redo-changes)
|
||||
(mapcat identity))
|
||||
pending)
|
||||
redo-changes)]
|
||||
|
||||
(d/update-in-when state path
|
||||
(fn [file]
|
||||
(let [file (cpc/process-changes file undo-changes false)
|
||||
file (cpc/process-changes file redo-changes false)
|
||||
pids (into #{} (map :page-id) redo-changes)]
|
||||
(reduce #(ctst/update-object-indices %1 %2) file pids)))))))))
|
||||
|
||||
(defn- resolve-file-revn
|
||||
[state file-id]
|
||||
(let [file (:workspace-file state)]
|
||||
(if (= (:id file) file-id)
|
||||
(:revn file)
|
||||
(dm/get-in state [:workspace-libraries file-id :revn]))))
|
||||
|
||||
(defn commit-changes
|
||||
"Schedules a list of changes to execute now, and add the corresponding undo changes to
|
||||
the undo stack.
|
||||
|
||||
Options:
|
||||
- save-undo?: if set to false, do not add undo changes.
|
||||
- undo-group: if some consecutive changes (or even transactions) share the same
|
||||
undo-group, they will be undone or redone in a single step
|
||||
"
|
||||
[{:keys [redo-changes undo-changes save-undo? undo-group tags stack-undo? file-id]
|
||||
:or {save-undo? true
|
||||
stack-undo? false
|
||||
undo-group (uuid/next)
|
||||
tags #{}}
|
||||
:as params}]
|
||||
(ptk/reify ::commit-changes
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [file-id (or file-id (:current-file-id state))
|
||||
uchg (vec undo-changes)
|
||||
rchg (vec redo-changes)
|
||||
features (features/get-team-enabled-features state)]
|
||||
|
||||
(rx/of (-> params
|
||||
(assoc :undo-group undo-group)
|
||||
(assoc :features features)
|
||||
(assoc :tags tags)
|
||||
(assoc :stack-undo? stack-undo?)
|
||||
(assoc :save-undo? save-undo?)
|
||||
(assoc :file-id file-id)
|
||||
(assoc :file-revn (resolve-file-revn state file-id))
|
||||
(assoc :undo-changes uchg)
|
||||
(assoc :redo-changes rchg)
|
||||
(commit)))))))
|
|
@ -8,7 +8,7 @@
|
|||
(:require
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.workspace.persistence :as dwp]
|
||||
[app.main.data.persistence :as dwp]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.repo :as rp]
|
||||
|
|
230
frontend/src/app/main/data/persistence.cljs
Normal file
230
frontend/src/app/main/data/persistence.cljs
Normal file
|
@ -0,0 +1,230 @@
|
|||
;; 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.data.persistence
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.logging :as log]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.repo :as rp]
|
||||
[app.util.router :as rt]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(declare ^:private run-persistence-task)
|
||||
|
||||
(log/set-level! :warn)
|
||||
|
||||
(def running (atom false))
|
||||
(def revn-data (atom {}))
|
||||
(def queue-conj (fnil conj #queue []))
|
||||
|
||||
(defn- update-status
|
||||
[status]
|
||||
(ptk/reify ::update-status
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :persistence (fn [pstate]
|
||||
(log/trc :hint "update-status"
|
||||
:from (:status pstate)
|
||||
:to status)
|
||||
(let [status (if (and (= status :pending)
|
||||
(= (:status pstate) :saving))
|
||||
(:status pstate)
|
||||
status)]
|
||||
|
||||
(-> (assoc pstate :status status)
|
||||
(cond-> (= status :error)
|
||||
(dissoc :run-id))
|
||||
(cond-> (= status :saved)
|
||||
(dissoc :run-id)))))))))
|
||||
|
||||
(defn- update-file-revn
|
||||
[file-id revn]
|
||||
(ptk/reify ::update-file-revn
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(log/dbg :hint "update-file-revn" :file-id (dm/str file-id) :revn revn)
|
||||
(if-let [current-file-id (:current-file-id state)]
|
||||
(if (= file-id current-file-id)
|
||||
(update-in state [:workspace-file :revn] max revn)
|
||||
(d/update-in-when state [:workspace-libraries file-id :revn] max revn))
|
||||
state))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ _ _]
|
||||
(swap! revn-data update file-id (fnil max 0) revn))))
|
||||
|
||||
(defn- discard-commit
|
||||
[commit-id]
|
||||
(ptk/reify ::discard-commit
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :persistence (fn [pstate]
|
||||
(-> pstate
|
||||
(update :queue (fn [queue]
|
||||
(if (= commit-id (peek queue))
|
||||
(pop queue)
|
||||
(throw (ex-info "invalid state" {})))))
|
||||
(update :index dissoc commit-id)))))))
|
||||
|
||||
(defn- append-commit
|
||||
"Event used internally to append the current change to the
|
||||
persistence queue."
|
||||
[{:keys [id] :as commit}]
|
||||
(let [run-id (uuid/next)]
|
||||
(ptk/reify ::append-commit
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(log/trc :hint "append-commit" :method "update" :commit-id (dm/str id))
|
||||
(update state :persistence
|
||||
(fn [pstate]
|
||||
(-> pstate
|
||||
(update :run-id d/nilv run-id)
|
||||
(update :queue queue-conj id)
|
||||
(update :index assoc id commit)))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [pstate (:persistence state)]
|
||||
(when (= run-id (:run-id pstate))
|
||||
(rx/of (run-persistence-task)
|
||||
(update-status :saving))))))))
|
||||
|
||||
(defn- persist-commit
|
||||
[commit-id]
|
||||
(ptk/reify ::persist-commit
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(log/dbg :hint "persist-commit" :commit-id (dm/str commit-id))
|
||||
(when-let [{:keys [file-id file-revn changes features] :as commit} (dm/get-in state [:persistence :index commit-id])]
|
||||
(let [sid (:session-id state)
|
||||
revn (max file-revn (get @revn-data file-id 0))
|
||||
params {:id file-id
|
||||
:revn revn
|
||||
:session-id sid
|
||||
:origin (:origin commit)
|
||||
:created-at (:created-at commit)
|
||||
:commit-id commit-id
|
||||
:changes (vec changes)
|
||||
:features features}]
|
||||
|
||||
;; FIXME: handle lagged here !!!!
|
||||
(->> (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))
|
||||
(rx/of (ptk/data-event ::commit-persisted commit)
|
||||
(update-file-revn file-id revn))))
|
||||
|
||||
(rx/catch (fn [cause]
|
||||
(rx/concat
|
||||
(if (= :authentication (:type cause))
|
||||
(rx/empty)
|
||||
(rx/of (rt/assign-exception cause)
|
||||
(ptk/data-event ::error cause)
|
||||
(update-status :error)))
|
||||
(rx/throw cause))))))))))
|
||||
|
||||
|
||||
(defn- run-persistence-task
|
||||
[]
|
||||
(ptk/reify ::run-persistence-task
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [queue (-> state :persistence :queue)]
|
||||
(if-let [commit-id (peek queue)]
|
||||
(let [stoper-s (rx/merge
|
||||
(rx/filter (ptk/type? ::run-persistence-task) stream)
|
||||
(rx/filter (ptk/type? ::error) stream))]
|
||||
|
||||
(log/dbg :hint "run-persistence-task" :commit-id (dm/str commit-id))
|
||||
(->> (rx/merge
|
||||
(rx/of (persist-commit commit-id))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::commit-persisted))
|
||||
(rx/map deref)
|
||||
(rx/filter #(= commit-id (:id %)))
|
||||
(rx/take 1)
|
||||
(rx/mapcat (fn [_]
|
||||
(rx/of (discard-commit commit-id)
|
||||
(run-persistence-task))))))
|
||||
(rx/take-until stoper-s)))
|
||||
(rx/of (update-status :saved)))))))
|
||||
|
||||
(def ^:private xf-mapcat-undo
|
||||
(mapcat :undo-changes))
|
||||
|
||||
(def ^:private xf-mapcat-redo
|
||||
(mapcat :redo-changes))
|
||||
|
||||
(defn- merge-commit
|
||||
[buffer]
|
||||
(->> (rx/from (group-by :file-id buffer))
|
||||
(rx/map (fn [[_ [item :as commits]]]
|
||||
(let [uchg (into [] xf-mapcat-undo commits)
|
||||
rchg (into [] xf-mapcat-redo commits)]
|
||||
(-> item
|
||||
(assoc :undo-changes uchg)
|
||||
(assoc :redo-changes rchg)
|
||||
(assoc :changes rchg)))))))
|
||||
|
||||
|
||||
(defn initialize-persistence
|
||||
[]
|
||||
(ptk/reify ::initialize-persistence
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ stream]
|
||||
(log/debug :hint "initialize persistence")
|
||||
(let [stoper-s (rx/filter (ptk/type? ::initialize-persistence) stream)
|
||||
|
||||
commits-s
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/filter #(= :local (:source %)))
|
||||
(rx/filter (complement empty?))
|
||||
(rx/share))
|
||||
|
||||
notifier-s
|
||||
(rx/merge
|
||||
(->> commits-s
|
||||
(rx/debounce 3000)
|
||||
(rx/tap #(log/trc :hint "persistence beat")))
|
||||
(->> stream
|
||||
(rx/filter #(= % ::force-persist))))]
|
||||
|
||||
(rx/merge
|
||||
(->> commits-s
|
||||
(rx/debounce 200)
|
||||
(rx/map (fn [_]
|
||||
(update-status :pending)))
|
||||
(rx/take-until stoper-s))
|
||||
|
||||
;; Here we watch for local commits, buffer them in a small
|
||||
;; chunks (very near in time commits) and append them to the
|
||||
;; persistence queue
|
||||
(->> commits-s
|
||||
(rx/buffer-until notifier-s)
|
||||
(rx/mapcat merge-commit)
|
||||
(rx/mapcat (fn [commit]
|
||||
(rx/of (append-commit commit)
|
||||
(dch/update-indexes commit))))
|
||||
(rx/take-until (rx/delay 100 stoper-s))
|
||||
(rx/finalize (fn []
|
||||
(log/debug :hint "finalize persistence: changes watcher"))))
|
||||
|
||||
;; Here we track all incoming remote commits for maintain
|
||||
;; updated the local state with the file revn
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/filter #(= :remote (:source %)))
|
||||
(rx/mapcat (fn [{:keys [file-id file-revn] :as commit}]
|
||||
(rx/of (update-file-revn file-id file-revn)
|
||||
(dch/update-indexes commit))))
|
||||
(rx/take-until stoper-s)))))))
|
|
@ -19,6 +19,7 @@
|
|||
[app.common.geom.rect :as grc]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.shapes.grid-layout :as gslg]
|
||||
[app.common.logging :as log]
|
||||
[app.common.logic.libraries :as cll]
|
||||
[app.common.logic.shapes :as cls]
|
||||
[app.common.schema :as sm]
|
||||
|
@ -34,14 +35,15 @@
|
|||
[app.common.types.typography :as ctt]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.comments :as dcm]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.fonts :as df]
|
||||
[app.main.data.messages :as msg]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.persistence :as dps]
|
||||
[app.main.data.users :as du]
|
||||
[app.main.data.workspace.bool :as dwb]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.collapse :as dwco]
|
||||
[app.main.data.workspace.drawing :as dwd]
|
||||
[app.main.data.workspace.edition :as dwe]
|
||||
|
@ -59,7 +61,6 @@
|
|||
[app.main.data.workspace.notifications :as dwn]
|
||||
[app.main.data.workspace.path :as dwdp]
|
||||
[app.main.data.workspace.path.shapes-to-path :as dwps]
|
||||
[app.main.data.workspace.persistence :as dwp]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
|
@ -87,6 +88,7 @@
|
|||
[potok.v2.core :as ptk]))
|
||||
|
||||
(def default-workspace-local {:zoom 1})
|
||||
(log/set-level! :debug)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Workspace Initialization
|
||||
|
@ -341,15 +343,32 @@
|
|||
:workspace-presence {}))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of msg/hide
|
||||
(dcm/retrieve-comment-threads file-id)
|
||||
(dwp/initialize-file-persistence file-id)
|
||||
(fetch-bundle project-id file-id)))
|
||||
(watch [_ _ stream]
|
||||
(log/debug :hint "initialize-file" :file-id file-id)
|
||||
(let [stoper-s (rx/filter (ptk/type? ::finalize-file) stream)]
|
||||
(rx/merge
|
||||
(rx/of msg/hide
|
||||
(features/initialize)
|
||||
(dcm/retrieve-comment-threads file-id)
|
||||
(fetch-bundle project-id file-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)))))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ _ _]
|
||||
(let [name (str "workspace-" file-id)]
|
||||
(let [name (dm/str "workspace-" file-id)]
|
||||
(unchecked-set ug/global "name" name)))))
|
||||
|
||||
(defn finalize-file
|
||||
|
@ -671,7 +690,7 @@
|
|||
(ptk/reify ::update-shape
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (dch/update-shapes [id] #(merge % attrs))))))
|
||||
(rx/of (dwsh/update-shapes [id] #(merge % attrs))))))
|
||||
|
||||
(defn start-rename-shape
|
||||
"Start shape renaming process"
|
||||
|
@ -982,7 +1001,7 @@
|
|||
(assoc shape :proportion-lock false)
|
||||
(-> (assoc shape :proportion-lock true)
|
||||
(gpp/assign-proportions))))]
|
||||
(rx/of (dch/update-shapes [id] assign-proportions))))))
|
||||
(rx/of (dwsh/update-shapes [id] assign-proportions))))))
|
||||
|
||||
(defn toggle-proportion-lock
|
||||
[]
|
||||
|
@ -996,8 +1015,8 @@
|
|||
multi (attrs/get-attrs-multi selected-obj [:proportion-lock])
|
||||
multi? (= :multiple (:proportion-lock multi))]
|
||||
(if multi?
|
||||
(rx/of (dch/update-shapes selected #(assoc % :proportion-lock true)))
|
||||
(rx/of (dch/update-shapes selected #(update % :proportion-lock not))))))))
|
||||
(rx/of (dwsh/update-shapes selected #(assoc % :proportion-lock true)))
|
||||
(rx/of (dwsh/update-shapes selected #(update % :proportion-lock not))))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Navigation
|
||||
|
@ -1258,7 +1277,7 @@
|
|||
(assoc :section section)
|
||||
(some? frame-id)
|
||||
(assoc :frame-id frame-id))]
|
||||
(rx/of ::dwp/force-persist
|
||||
(rx/of ::dps/force-persist
|
||||
(rt/nav-new-window* {:rname :viewer
|
||||
:path-params pparams
|
||||
:query-params qparams
|
||||
|
@ -1271,7 +1290,7 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(when-let [team-id (or team-id (:current-team-id state))]
|
||||
(rx/of ::dwp/force-persist
|
||||
(rx/of ::dps/force-persist
|
||||
(rt/nav :dashboard-projects {:team-id team-id})))))))
|
||||
|
||||
(defn go-to-dashboard-fonts
|
||||
|
@ -1280,7 +1299,7 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [team-id (:current-team-id state)]
|
||||
(rx/of ::dwp/force-persist
|
||||
(rx/of ::dps/force-persist
|
||||
(rt/nav :dashboard-fonts {:team-id team-id}))))))
|
||||
|
||||
|
||||
|
|
|
@ -15,8 +15,9 @@
|
|||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -117,7 +118,7 @@
|
|||
change-to-bool
|
||||
(fn [shape] (group->bool shape bool-type objects))]
|
||||
(when-not (ctn/has-any-copy-parent? objects (get objects shape-id))
|
||||
(rx/of (dch/update-shapes [shape-id] change-to-bool {:reg-objects? true})))))))
|
||||
(rx/of (dwsh/update-shapes [shape-id] change-to-bool {:reg-objects? true})))))))
|
||||
|
||||
(defn bool-to-group
|
||||
[shape-id]
|
||||
|
@ -128,7 +129,7 @@
|
|||
change-to-group
|
||||
(fn [shape] (bool->group shape objects))]
|
||||
(when-not (ctn/has-any-copy-parent? objects (get objects shape-id))
|
||||
(rx/of (dch/update-shapes [shape-id] change-to-group {:reg-objects? true})))))))
|
||||
(rx/of (dwsh/update-shapes [shape-id] change-to-group {:reg-objects? true})))))))
|
||||
|
||||
|
||||
(defn change-bool-type
|
||||
|
@ -140,4 +141,4 @@
|
|||
change-type
|
||||
(fn [shape] (assoc shape :bool-type bool-type))]
|
||||
(when-not (ctn/has-any-copy-parent? objects (get objects shape-id))
|
||||
(rx/of (dch/update-shapes [shape-id] change-type {:reg-objects? true})))))))
|
||||
(rx/of (dwsh/update-shapes [shape-id] change-type {:reg-objects? true})))))))
|
||||
|
|
|
@ -1,264 +0,0 @@
|
|||
;; 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.data.workspace.changes
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.files.changes :as cpc]
|
||||
[app.common.files.changes-builder :as pcb]
|
||||
[app.common.files.helpers :as cph]
|
||||
[app.common.logging :as log]
|
||||
[app.common.logic.shapes :as cls]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.store :as st]
|
||||
[app.main.worker :as uw]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;; Change this to :info :debug or :trace to debug this module
|
||||
(log/set-level! :warn)
|
||||
|
||||
(defonce page-change? #{:add-page :mod-page :del-page :mov-page})
|
||||
(defonce update-layout-attr? #{:hidden})
|
||||
|
||||
(declare commit-changes)
|
||||
|
||||
(defn- add-undo-group
|
||||
[changes state]
|
||||
(let [undo (:workspace-undo state)
|
||||
items (:items undo)
|
||||
index (or (:index undo) (dec (count items)))
|
||||
prev-item (when-not (or (empty? items) (= index -1))
|
||||
(get items index))
|
||||
undo-group (:undo-group prev-item)
|
||||
add-undo-group? (and
|
||||
(not (nil? undo-group))
|
||||
(= (get-in changes [:redo-changes 0 :type]) :mod-obj)
|
||||
(= (get-in prev-item [:redo-changes 0 :type]) :add-obj)
|
||||
(contains? (:tags prev-item) :alt-duplication))] ;; This is a copy-and-move with mouse+alt
|
||||
|
||||
(cond-> changes add-undo-group? (assoc :undo-group undo-group))))
|
||||
|
||||
(def commit-changes? (ptk/type? ::commit-changes))
|
||||
|
||||
(defn update-shapes
|
||||
([ids update-fn] (update-shapes ids update-fn nil))
|
||||
([ids update-fn {:keys [reg-objects? save-undo? stack-undo? attrs ignore-tree page-id ignore-remote? ignore-touched undo-group with-objects?]
|
||||
:or {reg-objects? false save-undo? true stack-undo? false ignore-remote? false ignore-touched false with-objects? false}}]
|
||||
|
||||
(dm/assert!
|
||||
"expected a valid coll of uuid's"
|
||||
(sm/check-coll-of-uuid! ids))
|
||||
|
||||
(dm/assert! (fn? update-fn))
|
||||
|
||||
(ptk/reify ::update-shapes
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [page-id (or page-id (:current-page-id state))
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
ids (into [] (filter some?) ids)
|
||||
|
||||
update-layout-ids
|
||||
(->> ids
|
||||
(map (d/getf objects))
|
||||
(filter #(some update-layout-attr? (pcb/changed-attrs % objects update-fn {:attrs attrs :with-objects? with-objects?})))
|
||||
(map :id))
|
||||
|
||||
changes (-> (pcb/empty-changes it page-id)
|
||||
(pcb/set-save-undo? save-undo?)
|
||||
(pcb/set-stack-undo? stack-undo?)
|
||||
(cls/generate-update-shapes ids
|
||||
update-fn
|
||||
objects
|
||||
{:attrs attrs
|
||||
:ignore-tree ignore-tree
|
||||
:ignore-touched ignore-touched
|
||||
:with-objects? with-objects?})
|
||||
(cond-> undo-group
|
||||
(pcb/set-undo-group undo-group)))
|
||||
|
||||
changes (add-undo-group changes state)]
|
||||
(rx/concat
|
||||
(if (seq (:redo-changes changes))
|
||||
(let [changes (cond-> changes reg-objects? (pcb/resize-parents ids))
|
||||
changes (cond-> changes ignore-remote? (pcb/ignore-remote))]
|
||||
(rx/of (commit-changes changes)))
|
||||
(rx/empty))
|
||||
|
||||
;; Update layouts for properties marked
|
||||
(if (d/not-empty? update-layout-ids)
|
||||
(rx/of (ptk/data-event :layout/update {:ids update-layout-ids}))
|
||||
(rx/empty))))))))
|
||||
|
||||
(defn send-update-indices
|
||||
[]
|
||||
(ptk/reify ::send-update-indices
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(->> (rx/of
|
||||
(fn [state]
|
||||
(-> state
|
||||
(dissoc ::update-indices-debounce)
|
||||
(dissoc ::update-changes))))
|
||||
(rx/observe-on :async)))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ state _]
|
||||
(doseq [[page-id changes] (::update-changes state)]
|
||||
(uw/ask! {:cmd :update-page-index
|
||||
:page-id page-id
|
||||
:changes changes})))))
|
||||
|
||||
;; Update indices will debounce operations so we don't have to update
|
||||
;; the index several times (which is an expensive operation)
|
||||
(defn update-indices
|
||||
[page-id changes]
|
||||
|
||||
(let [start (uuid/next)]
|
||||
(ptk/reify ::update-indices
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(if (nil? (::update-indices-debounce state))
|
||||
(assoc state ::update-indices-debounce start)
|
||||
(update-in state [::update-changes page-id] (fnil d/concat-vec []) changes)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(if (= (::update-indices-debounce state) start)
|
||||
(let [stopper (->> stream (rx/filter (ptk/type? :app.main.data.workspace/finalize)))]
|
||||
(rx/merge
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::update-indices))
|
||||
(rx/debounce 50)
|
||||
(rx/take 1)
|
||||
(rx/map #(send-update-indices))
|
||||
(rx/take-until stopper))
|
||||
(rx/of (update-indices page-id changes))))
|
||||
(rx/empty))))))
|
||||
|
||||
(defn changed-frames
|
||||
"Extracts the frame-ids changed in the given changes"
|
||||
[changes objects]
|
||||
|
||||
(let [change->ids
|
||||
(fn [change]
|
||||
(case (:type change)
|
||||
:add-obj
|
||||
[(:parent-id change)]
|
||||
|
||||
(:mod-obj :del-obj)
|
||||
[(:id change)]
|
||||
|
||||
:mov-objects
|
||||
(d/concat-vec (:shapes change) [(:parent-id change)])
|
||||
|
||||
[]))]
|
||||
(into #{}
|
||||
(comp (mapcat change->ids)
|
||||
(keep #(cph/get-shape-id-root-frame objects %))
|
||||
(remove #(= uuid/zero %)))
|
||||
changes)))
|
||||
|
||||
(defn commit-changes
|
||||
"Schedules a list of changes to execute now, and add the corresponding undo changes to
|
||||
the undo stack.
|
||||
|
||||
Options:
|
||||
- save-undo?: if set to false, do not add undo changes.
|
||||
- undo-group: if some consecutive changes (or even transactions) share the same
|
||||
undo-group, they will be undone or redone in a single step
|
||||
"
|
||||
[{:keys [redo-changes undo-changes
|
||||
origin save-undo? file-id undo-group tags stack-undo?]
|
||||
:or {save-undo? true stack-undo? false tags #{} undo-group (uuid/next)}}]
|
||||
(let [error (volatile! nil)
|
||||
page-id (:current-page-id @st/state)
|
||||
frames (changed-frames redo-changes (wsh/lookup-page-objects @st/state))
|
||||
undo-changes (vec undo-changes)
|
||||
redo-changes (vec redo-changes)]
|
||||
(ptk/reify ::commit-changes
|
||||
cljs.core/IDeref
|
||||
(-deref [_]
|
||||
{:file-id file-id
|
||||
:hint-events @st/last-events
|
||||
:hint-origin (ptk/type origin)
|
||||
:changes redo-changes
|
||||
:page-id page-id
|
||||
:frames frames
|
||||
:save-undo? save-undo?
|
||||
:undo-group undo-group
|
||||
:tags tags
|
||||
:stack-undo? stack-undo?})
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(log/info :msg "commit-changes"
|
||||
:js/undo-group (str undo-group)
|
||||
:js/file-id (str (or file-id "nil"))
|
||||
:js/redo-changes redo-changes
|
||||
:js/undo-changes undo-changes)
|
||||
(let [current-file-id (get state :current-file-id)
|
||||
file-id (or file-id current-file-id)
|
||||
path (if (= file-id current-file-id)
|
||||
[:workspace-data]
|
||||
[:workspace-libraries file-id :data])]
|
||||
|
||||
(try
|
||||
(dm/assert!
|
||||
"expect valid vector of changes"
|
||||
(and (cpc/check-changes! redo-changes)
|
||||
(cpc/check-changes! undo-changes)))
|
||||
|
||||
(update-in state path (fn [file]
|
||||
(-> file
|
||||
(cpc/process-changes redo-changes false)
|
||||
(ctst/update-object-indices page-id))))
|
||||
|
||||
(catch :default err
|
||||
(when-let [data (ex-data err)]
|
||||
(js/console.log (ex/explain data)))
|
||||
|
||||
(when (ex/error? err)
|
||||
(js/console.log (.-stack ^js err)))
|
||||
(vreset! error err)
|
||||
state))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(when-not @error
|
||||
(let [;; adds page-id to page changes (that have the `id` field instead)
|
||||
add-page-id
|
||||
(fn [{:keys [id type page] :as change}]
|
||||
(cond-> change
|
||||
(and (page-change? type) (nil? (:page-id change)))
|
||||
(assoc :page-id (or id (:id page)))))
|
||||
|
||||
changes-by-pages
|
||||
(->> redo-changes
|
||||
(map add-page-id)
|
||||
(remove #(nil? (:page-id %)))
|
||||
(group-by :page-id))
|
||||
|
||||
process-page-changes
|
||||
(fn [[page-id _changes]]
|
||||
(update-indices page-id redo-changes))]
|
||||
|
||||
(rx/concat
|
||||
(rx/from (map process-page-changes changes-by-pages))
|
||||
|
||||
(when (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?)))))))))))
|
|
@ -15,9 +15,9 @@
|
|||
[app.main.broadcast :as mbc]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.modal :as md]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.layout :as layout]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
|
@ -116,7 +116,7 @@
|
|||
(rx/concat
|
||||
(rx/of (dwu/start-undo-transaction undo-id))
|
||||
(rx/from (map #(dwt/update-text-with-function % transform-attrs) text-ids))
|
||||
(rx/of (dch/update-shapes shape-ids transform-attrs))
|
||||
(rx/of (dwsh/update-shapes shape-ids transform-attrs))
|
||||
(rx/of (dwu/commit-undo-transaction undo-id)))))
|
||||
|
||||
(defn swap-attrs [shape attr index new-index]
|
||||
|
@ -140,7 +140,7 @@
|
|||
|
||||
(rx/concat
|
||||
(rx/from (map #(dwt/update-text-with-function % transform-attrs) text-ids))
|
||||
(rx/of (dch/update-shapes shape-ids transform-attrs)))))))
|
||||
(rx/of (dwsh/update-shapes shape-ids transform-attrs)))))))
|
||||
|
||||
(defn change-fill
|
||||
[ids color position]
|
||||
|
@ -203,10 +203,10 @@
|
|||
is-text? #(= :text (:type (get objects %)))
|
||||
shape-ids (filter (complement is-text?) ids)
|
||||
attrs {:hide-fill-on-export hide-fill-on-export}]
|
||||
(rx/of (dch/update-shapes shape-ids (fn [shape]
|
||||
(if (= (:type shape) :frame)
|
||||
(d/merge shape attrs)
|
||||
shape))))))))
|
||||
(rx/of (dwsh/update-shapes shape-ids (fn [shape]
|
||||
(if (= (:type shape) :frame)
|
||||
(d/merge shape attrs)
|
||||
shape))))))))
|
||||
(defn change-stroke
|
||||
[ids attrs index]
|
||||
(ptk/reify ::change-stroke
|
||||
|
@ -236,7 +236,7 @@
|
|||
(dissoc :image)
|
||||
(dissoc :gradient))]
|
||||
|
||||
(rx/of (dch/update-shapes
|
||||
(rx/of (dwsh/update-shapes
|
||||
ids
|
||||
(fn [shape]
|
||||
(let [new-attrs (merge (get-in shape [:strokes index]) attrs)
|
||||
|
@ -264,7 +264,7 @@
|
|||
(ptk/reify ::change-shadow
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (dch/update-shapes
|
||||
(rx/of (dwsh/update-shapes
|
||||
ids
|
||||
(fn [shape]
|
||||
(let [;; If we try to set a gradient to a shadow (for
|
||||
|
@ -288,7 +288,7 @@
|
|||
(watch [_ _ _]
|
||||
(let [add-shadow (fn [shape]
|
||||
(update shape :shadow #(into [shadow] %)))]
|
||||
(rx/of (dch/update-shapes ids add-shadow))))))
|
||||
(rx/of (dwsh/update-shapes ids add-shadow))))))
|
||||
|
||||
(defn add-stroke
|
||||
[ids stroke]
|
||||
|
@ -296,7 +296,7 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(let [add-stroke (fn [shape] (update shape :strokes #(into [stroke] %)))]
|
||||
(rx/of (dch/update-shapes ids add-stroke))))))
|
||||
(rx/of (dwsh/update-shapes ids add-stroke))))))
|
||||
|
||||
(defn remove-stroke
|
||||
[ids position]
|
||||
|
@ -309,7 +309,7 @@
|
|||
(mapv second)))
|
||||
(remove-stroke [shape]
|
||||
(update shape :strokes remove-fill-by-index position))]
|
||||
(rx/of (dch/update-shapes ids remove-stroke))))))
|
||||
(rx/of (dwsh/update-shapes ids remove-stroke))))))
|
||||
|
||||
(defn remove-all-strokes
|
||||
[ids]
|
||||
|
@ -317,14 +317,14 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(let [remove-all #(assoc % :strokes [])]
|
||||
(rx/of (dch/update-shapes ids remove-all))))))
|
||||
(rx/of (dwsh/update-shapes ids remove-all))))))
|
||||
|
||||
(defn reorder-shadows
|
||||
[ids index new-index]
|
||||
(ptk/reify ::reorder-shadow
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (dch/update-shapes
|
||||
(rx/of (dwsh/update-shapes
|
||||
ids
|
||||
#(swap-attrs % :shadow index new-index))))))
|
||||
|
||||
|
@ -333,7 +333,7 @@
|
|||
(ptk/reify ::reorder-strokes
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (dch/update-shapes
|
||||
(rx/of (dwsh/update-shapes
|
||||
ids
|
||||
#(swap-attrs % :strokes index new-index))))))
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.comments :as dcm]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.common :as dwco]
|
||||
[app.main.data.workspace.drawing :as dwd]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
|
|
|
@ -6,14 +6,7 @@
|
|||
|
||||
(ns app.main.data.workspace.common
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.logging :as log]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.util.router :as rt]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
;; Change this to :info :debug or :trace to debug this module
|
||||
|
@ -34,136 +27,11 @@
|
|||
[e]
|
||||
(= e :interrupt))
|
||||
|
||||
(defn- assure-valid-current-page
|
||||
[]
|
||||
(ptk/reify ::assure-valid-current-page
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [current_page (:current-page-id state)
|
||||
pages (get-in state [:workspace-data :pages])
|
||||
exists? (some #(= current_page %) pages)
|
||||
|
||||
project-id (:current-project-id state)
|
||||
file-id (:current-file-id state)
|
||||
pparams {:file-id file-id :project-id project-id}
|
||||
qparams {:page-id (first pages)}]
|
||||
(if exists?
|
||||
(rx/empty)
|
||||
(rx/of (rt/nav :workspace pparams qparams)))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; UNDO
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(declare undo-to-index)
|
||||
|
||||
;; These functions should've been in
|
||||
;; `src/app/main/data/workspace/undo.cljs` but doing that causes a
|
||||
;; circular dependency with `src/app/main/data/workspace/changes.cljs`
|
||||
|
||||
(def undo
|
||||
(ptk/reify ::undo
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
edition (get-in state [:workspace-local :edition])
|
||||
drawing (get state :workspace-drawing)]
|
||||
|
||||
;; Editors handle their own undo's
|
||||
(when (or (and (nil? edition) (nil? (:object drawing)))
|
||||
(ctl/grid-layout? objects edition))
|
||||
(let [undo (:workspace-undo state)
|
||||
items (:items undo)
|
||||
index (or (:index undo) (dec (count items)))]
|
||||
(when-not (or (empty? items) (= index -1))
|
||||
(let [item (get items index)
|
||||
changes (:undo-changes item)
|
||||
undo-group (:undo-group item)
|
||||
|
||||
find-first-group-idx
|
||||
(fn [index]
|
||||
(if (= (dm/get-in items [index :undo-group]) undo-group)
|
||||
(recur (dec index))
|
||||
(inc index)))
|
||||
|
||||
undo-group-index
|
||||
(when undo-group
|
||||
(find-first-group-idx index))]
|
||||
|
||||
(if undo-group
|
||||
(rx/of (undo-to-index (dec undo-group-index)))
|
||||
(rx/of (dwu/materialize-undo changes (dec index))
|
||||
(dch/commit-changes {:redo-changes changes
|
||||
:undo-changes []
|
||||
:save-undo? false
|
||||
:origin it})
|
||||
(assure-valid-current-page)))))))))))
|
||||
|
||||
(def redo
|
||||
(ptk/reify ::redo
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
edition (get-in state [:workspace-local :edition])
|
||||
drawing (get state :workspace-drawing)]
|
||||
(when (and (or (nil? edition) (ctl/grid-layout? objects edition))
|
||||
(or (empty? drawing) (= :curve (:tool drawing))))
|
||||
(let [undo (:workspace-undo state)
|
||||
items (:items undo)
|
||||
index (or (:index undo) (dec (count items)))]
|
||||
(when-not (or (empty? items) (= index (dec (count items))))
|
||||
(let [item (get items (inc index))
|
||||
changes (:redo-changes item)
|
||||
undo-group (:undo-group item)
|
||||
find-last-group-idx (fn flgidx [index]
|
||||
(let [item (get items index)]
|
||||
(if (= (:undo-group item) undo-group)
|
||||
(flgidx (inc index))
|
||||
(dec index))))
|
||||
|
||||
redo-group-index (when undo-group
|
||||
(find-last-group-idx (inc index)))]
|
||||
(if undo-group
|
||||
(rx/of (undo-to-index redo-group-index))
|
||||
(rx/of (dwu/materialize-undo changes (inc index))
|
||||
(dch/commit-changes {:redo-changes changes
|
||||
:undo-changes []
|
||||
:origin it
|
||||
:save-undo? false})))))))))))
|
||||
|
||||
(defn undo-to-index
|
||||
"Repeat undoing or redoing until dest-index is reached."
|
||||
[dest-index]
|
||||
(ptk/reify ::undo-to-index
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
edition (get-in state [:workspace-local :edition])
|
||||
drawing (get state :workspace-drawing)]
|
||||
(when-not (and (or (some? edition) (some? (:object drawing)))
|
||||
(not (ctl/grid-layout? objects edition)))
|
||||
(let [undo (:workspace-undo state)
|
||||
items (:items undo)
|
||||
index (or (:index undo) (dec (count items)))]
|
||||
(when (and (some? items)
|
||||
(<= -1 dest-index (dec (count items))))
|
||||
(let [changes (vec (apply concat
|
||||
(cond
|
||||
(< dest-index index)
|
||||
(->> (subvec items (inc dest-index) (inc index))
|
||||
(reverse)
|
||||
(map :undo-changes))
|
||||
(> dest-index index)
|
||||
(->> (subvec items (inc index) (inc dest-index))
|
||||
(map :redo-changes))
|
||||
:else [])))]
|
||||
(when (seq changes)
|
||||
(rx/of (dwu/materialize-undo changes dest-index)
|
||||
(dch/commit-changes {:redo-changes changes
|
||||
:undo-changes []
|
||||
:origin it
|
||||
:save-undo? false})))))))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Toolbar
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
@ -82,9 +83,9 @@
|
|||
:objects (-> component migrate-component :objects)}))
|
||||
components)]
|
||||
|
||||
(rx/of (dch/update-shapes ids #(update-shape % objects) {:reg-objects? false
|
||||
:save-undo? false
|
||||
:ignore-tree true}))
|
||||
(rx/of (dwsh/update-shapes ids #(update-shape % objects) {:reg-objects? false
|
||||
:save-undo? false
|
||||
:ignore-tree true}))
|
||||
|
||||
(if (empty? component-changes)
|
||||
(rx/empty)
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
(ns app.main.data.workspace.fix-broken-shapes
|
||||
(:require
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.changes :as dch]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.text :as txt]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.changes :as dwc]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.fonts :as fonts]
|
||||
[beicon.v2.core :as rx]
|
||||
|
@ -111,19 +112,19 @@
|
|||
typographies)]
|
||||
|
||||
(rx/concat
|
||||
(rx/of (dch/update-shapes ids #(fix-deleted-font-shape %) {:reg-objects? false
|
||||
:save-undo? false
|
||||
:ignore-tree true}))
|
||||
(rx/of (dwsh/update-shapes ids #(fix-deleted-font-shape %) {:reg-objects? false
|
||||
:save-undo? false
|
||||
:ignore-tree true}))
|
||||
(if (empty? component-changes)
|
||||
(rx/empty)
|
||||
(rx/of (dch/commit-changes {:origin it
|
||||
(rx/of (dwc/commit-changes {:origin it
|
||||
:redo-changes component-changes
|
||||
:undo-changes []
|
||||
:save-undo? false})))
|
||||
|
||||
(if (empty? typography-changes)
|
||||
(rx/empty)
|
||||
(rx/of (dch/commit-changes {:origin it
|
||||
(rx/of (dwc/commit-changes {:origin it
|
||||
:redo-changes typography-changes
|
||||
:undo-changes []
|
||||
:save-undo? false}))))))))
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.files.changes-builder :as pcb]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
@ -51,8 +52,8 @@
|
|||
grid {:type :square
|
||||
:params params
|
||||
:display true}]
|
||||
(rx/of (dch/update-shapes [frame-id]
|
||||
(fn [obj] (update obj :grids (fnil #(conj % grid) [])))))))))
|
||||
(rx/of (dwsh/update-shapes [frame-id]
|
||||
(fn [obj] (update obj :grids (fnil #(conj % grid) [])))))))))
|
||||
|
||||
|
||||
(defn remove-frame-grid
|
||||
|
@ -60,14 +61,14 @@
|
|||
(ptk/reify ::remove-frame-grid
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (dch/update-shapes [frame-id] (fn [o] (update o :grids (fnil #(d/remove-at-index % index) []))))))))
|
||||
(rx/of (dwsh/update-shapes [frame-id] (fn [o] (update o :grids (fnil #(d/remove-at-index % index) []))))))))
|
||||
|
||||
(defn set-frame-grid
|
||||
[frame-id index data]
|
||||
(ptk/reify ::set-frame-grid
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (dch/update-shapes [frame-id] #(assoc-in % [:grids index] data))))))
|
||||
(rx/of (dwsh/update-shapes [frame-id] #(assoc-in % [:grids index] data))))))
|
||||
|
||||
(defn set-default-grid
|
||||
[type params]
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
(:require
|
||||
[app.main.data.shortcuts :as ds]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.store :as st]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
@ -38,11 +38,11 @@
|
|||
|
||||
:undo {:tooltip (ds/meta "Z")
|
||||
:command (ds/c-mod "z")
|
||||
:fn #(st/emit! dwc/undo)}
|
||||
:fn #(st/emit! dwu/undo)}
|
||||
|
||||
:redo {:tooltip (ds/meta "Y")
|
||||
:command [(ds/c-mod "shift+z") (ds/c-mod "y")]
|
||||
:fn #(st/emit! dwc/redo)}
|
||||
:fn #(st/emit! dwu/redo)}
|
||||
|
||||
;; ZOOM
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.types.page :as ctp]
|
||||
[app.main.data.changes :as dwc]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
@ -42,7 +42,7 @@
|
|||
(-> (pcb/empty-changes it)
|
||||
(pcb/with-page page)
|
||||
(pcb/update-page-option :guides assoc (:id guide) guide))]
|
||||
(rx/of (dch/commit-changes changes))))))
|
||||
(rx/of (dwc/commit-changes changes))))))
|
||||
|
||||
(defn remove-guide
|
||||
[guide]
|
||||
|
@ -66,7 +66,7 @@
|
|||
(-> (pcb/empty-changes it)
|
||||
(pcb/with-page page)
|
||||
(pcb/update-page-option :guides dissoc (:id guide)))]
|
||||
(rx/of (dch/commit-changes changes))))))
|
||||
(rx/of (dwc/commit-changes changes))))))
|
||||
|
||||
(defn remove-guides
|
||||
[ids]
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.shape.interactions :as ctsi]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.streams :as ms]
|
||||
|
@ -125,13 +126,13 @@
|
|||
:flows] [])
|
||||
flow (ctp/get-frame-flow flows (:id frame))]
|
||||
(rx/concat
|
||||
(rx/of (dch/update-shapes [(:id shape)]
|
||||
(fn [shape]
|
||||
(let [new-interaction (-> ctsi/default-interaction
|
||||
(ctsi/set-destination destination)
|
||||
(assoc :position-relative-to (:id shape)))]
|
||||
(update shape :interactions
|
||||
ctsi/add-interaction new-interaction)))))
|
||||
(rx/of (dwsh/update-shapes [(:id shape)]
|
||||
(fn [shape]
|
||||
(let [new-interaction (-> ctsi/default-interaction
|
||||
(ctsi/set-destination destination)
|
||||
(assoc :position-relative-to (:id shape)))]
|
||||
(update shape :interactions
|
||||
ctsi/add-interaction new-interaction)))))
|
||||
(when (and (not (connected-frame? objects (:id frame)))
|
||||
(nil? flow))
|
||||
(rx/of (add-flow (:id frame))))))))))
|
||||
|
@ -141,20 +142,19 @@
|
|||
(ptk/reify ::remove-interaction
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (dch/update-shapes [(:id shape)]
|
||||
(fn [shape]
|
||||
(update shape :interactions
|
||||
ctsi/remove-interaction index)))))))
|
||||
|
||||
(rx/of (dwsh/update-shapes [(:id shape)]
|
||||
(fn [shape]
|
||||
(update shape :interactions
|
||||
ctsi/remove-interaction index)))))))
|
||||
(defn update-interaction
|
||||
[shape index update-fn]
|
||||
(ptk/reify ::update-interaction
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (dch/update-shapes [(:id shape)]
|
||||
(fn [shape]
|
||||
(update shape :interactions
|
||||
ctsi/update-interaction index update-fn)))))))
|
||||
(rx/of (dwsh/update-shapes [(:id shape)]
|
||||
(fn [shape]
|
||||
(update shape :interactions
|
||||
ctsi/update-interaction index update-fn)))))))
|
||||
|
||||
(defn remove-all-interactions-nav-to
|
||||
"Remove all interactions that navigate to the given frame."
|
||||
|
@ -171,9 +171,9 @@
|
|||
new-interactions (ctsi/remove-interactions #(ctsi/navs-to? % frame-id)
|
||||
interactions)]
|
||||
(when (not= (count interactions) (count new-interactions))
|
||||
(dch/update-shapes [(:id shape)]
|
||||
(fn [shape]
|
||||
(assoc shape :interactions new-interactions))))))]
|
||||
(dwsh/update-shapes [(:id shape)]
|
||||
(fn [shape]
|
||||
(assoc shape :interactions new-interactions))))))]
|
||||
|
||||
(rx/from (->> (vals objects)
|
||||
(map remove-interactions-shape)
|
||||
|
@ -260,20 +260,20 @@
|
|||
(dwu/start-undo-transaction undo-id)
|
||||
|
||||
(when (:hide-in-viewer target-frame)
|
||||
; If the target frame is hidden, we need to unhide it so
|
||||
; users can navigate to it.
|
||||
(dch/update-shapes [(:id target-frame)]
|
||||
#(dissoc % :hide-in-viewer)))
|
||||
;; If the target frame is hidden, we need to unhide it so
|
||||
;; users can navigate to it.
|
||||
(dwsh/update-shapes [(:id target-frame)]
|
||||
#(dissoc % :hide-in-viewer)))
|
||||
|
||||
(cond
|
||||
(or (nil? shape)
|
||||
;; Didn't changed the position for the interaction
|
||||
;; Didn't changed the position for the interaction
|
||||
(= position initial-pos)
|
||||
;; New interaction but invalid target
|
||||
;; New interaction but invalid target
|
||||
(and (nil? index) (nil? target-frame)))
|
||||
nil
|
||||
|
||||
;; Dropped interaction in an invalid target. We remove it
|
||||
;; Dropped interaction in an invalid target. We remove it
|
||||
(and (some? index) (nil? target-frame))
|
||||
(remove-interaction shape index)
|
||||
|
||||
|
@ -364,5 +364,5 @@
|
|||
(update interactions index
|
||||
#(ctsi/set-overlay-position % overlay-pos))]
|
||||
|
||||
(rx/of (dch/update-shapes [(:id shape)] #(merge % {:interactions new-interactions})))))))
|
||||
(rx/of (dwsh/update-shapes [(:id shape)] #(merge % {:interactions new-interactions})))))))
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.math :as mth]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -48,7 +48,7 @@
|
|||
shapes (map #(get objects %) selected)
|
||||
shapes-ids (->> shapes
|
||||
(map :id))]
|
||||
(rx/of (dch/update-shapes shapes-ids #(assoc % :opacity opacity)))))))
|
||||
(rx/of (dwsh/update-shapes shapes-ids #(assoc % :opacity opacity)))))))
|
||||
|
||||
(defn pressed-opacity
|
||||
[opacity]
|
||||
|
|
|
@ -24,15 +24,16 @@
|
|||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.types.typography :as ctt]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.comments :as dc]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.messages :as msg]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.workspace :as-alias dw]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.groups :as dwg]
|
||||
[app.main.data.workspace.notifications :as-alias dwn]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.specialized-panel :as dwsp]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.thumbnails :as dwt]
|
||||
|
@ -54,7 +55,6 @@
|
|||
;; Change this to :info :debug or :trace to debug this module, or :warn to reset to default
|
||||
(log/set-level! :warn)
|
||||
|
||||
|
||||
(defn- pretty-file
|
||||
[file-id state]
|
||||
(if (= file-id (:current-file-id state))
|
||||
|
@ -441,7 +441,7 @@
|
|||
|
||||
;; NOTE: only when components-v2 is enabled
|
||||
(when (and shape-id page-id)
|
||||
(rx/of (dch/update-shapes [shape-id] #(assoc % :name clean-name) {:page-id page-id :stack-undo? true}))))))))))
|
||||
(rx/of (dwsh/update-shapes [shape-id] #(assoc % :name clean-name) {:page-id page-id :stack-undo? true}))))))))))
|
||||
|
||||
(defn duplicate-component
|
||||
"Create a new component copied from the one with the given id."
|
||||
|
@ -1148,7 +1148,7 @@
|
|||
|
||||
changes-s
|
||||
(->> stream
|
||||
(rx/filter #(or (dch/commit-changes? %)
|
||||
(rx/filter #(or (dch/commit? %)
|
||||
(ptk/type? % ::dwn/handle-file-change)))
|
||||
(rx/observe-on :async))
|
||||
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
[app.common.types.shape :as cts]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.media :as dmm]
|
||||
[app.main.data.messages :as msg]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
|
|
|
@ -23,9 +23,9 @@
|
|||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.constants :refer [zoom-half-pixel-precision]]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.comments :as-alias dwcm]
|
||||
[app.main.data.workspace.guides :as-alias dwg]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[beicon.v2.core :as rx]
|
||||
|
@ -499,7 +499,7 @@
|
|||
(rx/empty))
|
||||
(rx/of (ptk/event ::dwg/move-frame-guides ids-with-children)
|
||||
(ptk/event ::dwcm/move-frame-comment-threads ids-with-children)
|
||||
(dch/update-shapes
|
||||
(dwsh/update-shapes
|
||||
ids
|
||||
(fn [shape]
|
||||
(let [modif (get-in object-modifiers [(:id shape) :modifiers])
|
||||
|
|
|
@ -11,11 +11,10 @@
|
|||
[app.common.files.changes :as cpc]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.common :refer [handle-notification]]
|
||||
[app.main.data.websocket :as dws]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.persistence :as dwp]
|
||||
[app.util.globals :refer [global]]
|
||||
[app.util.mouse :as mse]
|
||||
[app.util.object :as obj]
|
||||
|
@ -84,7 +83,7 @@
|
|||
(->> stream
|
||||
(rx/filter mse/pointer-event?)
|
||||
(rx/filter #(= :viewport (mse/get-pointer-source %)))
|
||||
(rx/pipe (rxs/throttle 100))
|
||||
(rx/pipe (rxs/throttle 50))
|
||||
(rx/map #(handle-pointer-send file-id (:pt %)))))
|
||||
|
||||
(rx/take-until stopper))]
|
||||
|
@ -197,9 +196,10 @@
|
|||
[:changes ::cpc/changes]]))
|
||||
|
||||
(defn handle-file-change
|
||||
[{:keys [file-id changes] :as msg}]
|
||||
[{:keys [file-id changes revn] :as msg}]
|
||||
|
||||
(dm/assert!
|
||||
"expected valid arguments"
|
||||
"expected valid parameters"
|
||||
(sm/check! schema:handle-file-change msg))
|
||||
|
||||
(ptk/reify ::handle-file-change
|
||||
|
@ -209,15 +209,11 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [page-id (:current-page-id state)
|
||||
|
||||
position-data-operation?
|
||||
(fn [{:keys [type attr]}]
|
||||
(and (= :set type) (= attr :position-data)))
|
||||
|
||||
;;add-origin-session-id
|
||||
;;(fn [{:keys [] :as op}]
|
||||
;; (cond-> op
|
||||
;; (position-data-operation? op)
|
||||
;; (update :val with-meta {:session-id (:session-id msg)})))
|
||||
(and (= :set type)
|
||||
(= attr :position-data)))
|
||||
|
||||
update-position-data
|
||||
(fn [change]
|
||||
|
@ -228,24 +224,23 @@
|
|||
(= :mod-obj (:type change)))
|
||||
(update :operations #(d/removev position-data-operation? %))))
|
||||
|
||||
process-page-changes
|
||||
(fn [[page-id changes]]
|
||||
(dch/update-indices page-id changes))
|
||||
|
||||
;; We update `position-data` from the incoming message
|
||||
changes (->> changes
|
||||
(mapv update-position-data)
|
||||
(d/removev (fn [change]
|
||||
(and (= page-id (:page-id change))
|
||||
(:ignore-remote? change)))))
|
||||
(map update-position-data)
|
||||
(remove (fn [change]
|
||||
(and (= page-id (:page-id change))
|
||||
(:ignore-remote? change))))
|
||||
(vec))]
|
||||
|
||||
changes-by-pages (group-by :page-id changes)]
|
||||
|
||||
(rx/merge
|
||||
(rx/of (dwp/shapes-changes-persisted file-id (assoc msg :changes changes)))
|
||||
|
||||
(when-not (empty? changes-by-pages)
|
||||
(rx/from (map process-page-changes changes-by-pages))))))))
|
||||
;; The commit event is responsible to apply the data localy
|
||||
;; and update the persistence internal state with the updated
|
||||
;; file-revn
|
||||
(rx/of (dch/commit {:file-id file-id
|
||||
:file-revn revn
|
||||
:save-undo? false
|
||||
:source :remote
|
||||
:redo-changes changes
|
||||
:undo-changes []}))))))
|
||||
|
||||
(def ^:private
|
||||
schema:handle-library-change
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.files.changes-builder :as pcb]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.workspace.path.common :refer [check-path-content!]]
|
||||
[app.main.data.workspace.path.helpers :as helpers]
|
||||
[app.main.data.workspace.path.state :as st]
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.drawing.common :as dwdc]
|
||||
[app.main.data.workspace.edition :as dwe]
|
||||
[app.main.data.workspace.path.changes :as changes]
|
||||
|
@ -24,6 +23,7 @@
|
|||
[app.main.data.workspace.path.state :as st]
|
||||
[app.main.data.workspace.path.streams :as streams]
|
||||
[app.main.data.workspace.path.undo :as undo]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.util.mouse :as mse]
|
||||
[beicon.v2.core :as rx]
|
||||
|
@ -333,7 +333,7 @@
|
|||
edit-mode (get-in state [:workspace-local :edit-path id :edit-mode])]
|
||||
(if (= :draw edit-mode)
|
||||
(rx/concat
|
||||
(rx/of (dch/update-shapes [id] upsp/convert-to-path))
|
||||
(rx/of (dwsh/update-shapes [id] upsp/convert-to-path))
|
||||
(rx/of (handle-drawing id))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::common/finish-path))
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
[app.common.svg.path.command :as upc]
|
||||
[app.common.svg.path.shapes-to-path :as upsp]
|
||||
[app.common.svg.path.subpath :as ups]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.workspace.edition :as dwe]
|
||||
[app.main.data.workspace.path.changes :as changes]
|
||||
[app.main.data.workspace.path.drawing :as drawing]
|
||||
|
@ -23,6 +23,7 @@
|
|||
[app.main.data.workspace.path.state :as st]
|
||||
[app.main.data.workspace.path.streams :as streams]
|
||||
[app.main.data.workspace.path.undo :as undo]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.streams :as ms]
|
||||
[app.util.mouse :as mse]
|
||||
|
@ -143,7 +144,7 @@
|
|||
selected? (contains? selected-points position)]
|
||||
(streams/drag-stream
|
||||
(rx/of
|
||||
(dch/update-shapes [id] upsp/convert-to-path)
|
||||
(dwsh/update-shapes [id] upsp/convert-to-path)
|
||||
(when-not selected? (selection/select-node position shift?))
|
||||
(drag-selected-points @ms/mouse-position))
|
||||
(rx/of (selection/select-node position shift?)))))))
|
||||
|
@ -227,7 +228,7 @@
|
|||
mov-vec (gpt/multiply (get-displacement direction) scale)]
|
||||
|
||||
(rx/concat
|
||||
(rx/of (dch/update-shapes [id] upsp/convert-to-path))
|
||||
(rx/of (dwsh/update-shapes [id] upsp/convert-to-path))
|
||||
(rx/merge
|
||||
(->> move-events
|
||||
(rx/take-until stopper)
|
||||
|
@ -265,7 +266,7 @@
|
|||
|
||||
(streams/drag-stream
|
||||
(rx/concat
|
||||
(rx/of (dch/update-shapes [id] upsp/convert-to-path))
|
||||
(rx/of (dwsh/update-shapes [id] upsp/convert-to-path))
|
||||
(->> (streams/move-handler-stream handler point handler opposite points)
|
||||
(rx/map
|
||||
(fn [{:keys [x y alt? shift?]}]
|
||||
|
@ -354,5 +355,5 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [id (st/get-path-id state)]
|
||||
(rx/of (dch/update-shapes [id] upsp/convert-to-path)
|
||||
(rx/of (dwsh/update-shapes [id] upsp/convert-to-path)
|
||||
(split-segments event))))))
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
[app.common.files.helpers :as cph]
|
||||
[app.common.svg.path.shapes-to-path :as upsp]
|
||||
[app.common.types.container :as ctn]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
|
|
@ -8,10 +8,11 @@
|
|||
(:require
|
||||
[app.common.svg.path.shapes-to-path :as upsp]
|
||||
[app.common.svg.path.subpath :as ups]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.workspace.edition :as dwe]
|
||||
[app.main.data.workspace.path.changes :as changes]
|
||||
[app.main.data.workspace.path.state :as st]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.util.path.tools :as upt]
|
||||
[beicon.v2.core :as rx]
|
||||
|
@ -37,7 +38,7 @@
|
|||
changes (changes/generate-path-changes it objects page-id shape (:content shape) new-content)]
|
||||
|
||||
(rx/concat
|
||||
(rx/of (dch/update-shapes [id] upsp/convert-to-path))
|
||||
(rx/of (dwsh/update-shapes [id] upsp/convert-to-path))
|
||||
(rx/of (dch/commit-changes changes)
|
||||
(when (empty? new-content)
|
||||
(dwe/clear-edition-mode)))))))))))
|
||||
|
|
|
@ -1,263 +0,0 @@
|
|||
;; 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.data.workspace.persistence
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.files.changes :as cpc]
|
||||
[app.common.logging :as log]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.thumbnails :as dwt]
|
||||
[app.main.features :as features]
|
||||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
[app.util.time :as dt]
|
||||
[beicon.v2.core :as rx]
|
||||
[okulary.core :as l]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(log/set-level! :info)
|
||||
|
||||
(declare persist-changes)
|
||||
(declare persist-synchronous-changes)
|
||||
(declare shapes-changes-persisted)
|
||||
(declare shapes-changes-persisted-finished)
|
||||
(declare update-persistence-status)
|
||||
|
||||
;; --- Persistence
|
||||
|
||||
(defn initialize-file-persistence
|
||||
[file-id]
|
||||
(ptk/reify ::initialize-persistence
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ stream]
|
||||
(log/debug :hint "initialize persistence")
|
||||
(let [stopper (rx/filter (ptk/type? ::initialize-persistence) stream)
|
||||
commits (l/atom [])
|
||||
saving? (l/atom false)
|
||||
|
||||
local-file?
|
||||
#(as-> (:file-id %) event-file-id
|
||||
(or (nil? event-file-id)
|
||||
(= event-file-id file-id)))
|
||||
|
||||
library-file?
|
||||
#(as-> (:file-id %) event-file-id
|
||||
(and (some? event-file-id)
|
||||
(not= event-file-id file-id)))
|
||||
|
||||
on-dirty
|
||||
(fn []
|
||||
;; Enable reload stopper
|
||||
(swap! st/ongoing-tasks conj :workspace-change)
|
||||
(st/emit! (update-persistence-status {:status :pending})))
|
||||
|
||||
on-saving
|
||||
(fn []
|
||||
(reset! saving? true)
|
||||
(st/emit! (update-persistence-status {:status :saving})))
|
||||
|
||||
on-saved
|
||||
(fn []
|
||||
;; Disable reload stopper
|
||||
(swap! st/ongoing-tasks disj :workspace-change)
|
||||
(st/emit! (update-persistence-status {:status :saved}))
|
||||
(reset! saving? false))]
|
||||
|
||||
(rx/merge
|
||||
(->> stream
|
||||
(rx/filter dch/commit-changes?)
|
||||
(rx/map deref)
|
||||
(rx/filter local-file?)
|
||||
(rx/tap on-dirty)
|
||||
(rx/filter (complement empty?))
|
||||
(rx/map (fn [commit]
|
||||
(-> commit
|
||||
(assoc :id (uuid/next))
|
||||
(assoc :file-id file-id))))
|
||||
(rx/observe-on :async)
|
||||
(rx/tap #(swap! commits conj %))
|
||||
(rx/take-until (rx/delay 100 stopper))
|
||||
(rx/finalize (fn []
|
||||
(log/debug :hint "finalize persistence: changes watcher"))))
|
||||
|
||||
(->> (rx/from-atom commits)
|
||||
(rx/filter (complement empty?))
|
||||
(rx/sample-when
|
||||
(rx/merge
|
||||
(rx/filter #(= ::force-persist %) stream)
|
||||
(->> (rx/merge
|
||||
(rx/interval 5000)
|
||||
(->> (rx/from-atom commits)
|
||||
(rx/filter (complement empty?))
|
||||
(rx/debounce 2000)))
|
||||
;; Not sample while saving so there are no race conditions
|
||||
(rx/filter #(not @saving?)))))
|
||||
(rx/tap #(reset! commits []))
|
||||
(rx/tap on-saving)
|
||||
(rx/mapcat (fn [changes]
|
||||
;; NOTE: this is needed for don't start the
|
||||
;; next persistence before this one is
|
||||
;; finished.
|
||||
(if-let [file-revn (dm/get-in @st/state [:workspace-file :revn])]
|
||||
(rx/merge
|
||||
(->> (rx/of (persist-changes file-id file-revn changes commits))
|
||||
(rx/observe-on :async))
|
||||
(->> stream
|
||||
;; We wait for every change to be persisted
|
||||
(rx/filter (ptk/type? ::shapes-changes-persisted-finished))
|
||||
(rx/take 1)
|
||||
(rx/tap on-saved)
|
||||
(rx/ignore)))
|
||||
(rx/empty))))
|
||||
(rx/take-until (rx/delay 100 stopper))
|
||||
(rx/finalize (fn []
|
||||
(log/debug :hint "finalize persistence: save loop"))))
|
||||
|
||||
;; Synchronous changes
|
||||
(->> stream
|
||||
(rx/filter dch/commit-changes?)
|
||||
(rx/map deref)
|
||||
(rx/filter library-file?)
|
||||
(rx/filter (complement #(empty? (:changes %))))
|
||||
(rx/map persist-synchronous-changes)
|
||||
(rx/take-until (rx/delay 100 stopper))
|
||||
(rx/finalize (fn []
|
||||
(log/debug :hint "finalize persistence: synchronous save loop")))))))))
|
||||
|
||||
(defn persist-changes
|
||||
[file-id file-revn changes pending-commits]
|
||||
(log/debug :hint "persist changes" :changes (count changes))
|
||||
(dm/assert! (uuid? file-id))
|
||||
(ptk/reify ::persist-changes
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [sid (:session-id state)
|
||||
|
||||
features (features/get-team-enabled-features state)
|
||||
params {:id file-id
|
||||
:revn file-revn
|
||||
:session-id sid
|
||||
:changes-with-metadata (into [] changes)
|
||||
:features features}]
|
||||
|
||||
(->> (rp/cmd! :update-file params)
|
||||
(rx/mapcat (fn [lagged]
|
||||
(log/debug :hint "changes persisted" :lagged (count lagged))
|
||||
(let [frame-updates
|
||||
(-> (group-by :page-id changes)
|
||||
(update-vals #(into #{} (mapcat :frames) %)))
|
||||
|
||||
commits
|
||||
(->> @pending-commits
|
||||
(map #(assoc % :revn file-revn)))]
|
||||
|
||||
(rx/concat
|
||||
(rx/merge
|
||||
(->> (rx/from frame-updates)
|
||||
(rx/mapcat (fn [[page-id frames]]
|
||||
(->> frames (map (fn [frame-id] [file-id page-id frame-id])))))
|
||||
(rx/map (fn [data]
|
||||
(ptk/data-event ::dwt/update data))))
|
||||
|
||||
(->> (rx/from (concat lagged commits))
|
||||
(rx/merge-map
|
||||
(fn [{:keys [changes] :as entry}]
|
||||
(rx/merge
|
||||
(rx/from
|
||||
(for [[page-id changes] (group-by :page-id changes)]
|
||||
(dch/update-indices page-id changes)))
|
||||
(rx/of (shapes-changes-persisted file-id entry)))))))
|
||||
|
||||
(rx/of (shapes-changes-persisted-finished))))))
|
||||
(rx/catch (fn [cause]
|
||||
(if (instance? js/TypeError cause)
|
||||
(->> (rx/timer 2000)
|
||||
(rx/map (fn [_]
|
||||
(persist-changes file-id file-revn changes pending-commits))))
|
||||
(rx/throw cause)))))))))
|
||||
|
||||
;; Event to be thrown after the changes have been persisted
|
||||
(defn shapes-changes-persisted-finished
|
||||
[]
|
||||
(ptk/reify ::shapes-changes-persisted-finished))
|
||||
|
||||
(defn persist-synchronous-changes
|
||||
[{:keys [file-id changes]}]
|
||||
(dm/assert! (uuid? file-id))
|
||||
(ptk/reify ::persist-synchronous-changes
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [features (features/get-team-enabled-features state)
|
||||
|
||||
sid (:session-id state)
|
||||
file (dm/get-in state [:workspace-libraries file-id])
|
||||
|
||||
params {:id (:id file)
|
||||
:revn (:revn file)
|
||||
:session-id sid
|
||||
:changes changes
|
||||
:features features}]
|
||||
|
||||
(when (:id params)
|
||||
(->> (rp/cmd! :update-file params)
|
||||
(rx/ignore)))))))
|
||||
|
||||
(defn update-persistence-status
|
||||
[{:keys [status reason]}]
|
||||
(ptk/reify ::update-persistence-status
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-persistence
|
||||
(fn [local]
|
||||
(assoc local
|
||||
:reason reason
|
||||
:status status
|
||||
:updated-at (dt/now)))))))
|
||||
|
||||
|
||||
(defn shapes-persisted-event? [event]
|
||||
(= (ptk/type event) ::changes-persisted))
|
||||
|
||||
(defn shapes-changes-persisted
|
||||
[file-id {:keys [revn changes] persisted-session-id :session-id}]
|
||||
(dm/assert! (uuid? file-id))
|
||||
(dm/assert! (int? revn))
|
||||
(dm/assert! (cpc/check-changes! changes))
|
||||
|
||||
(ptk/reify ::shapes-changes-persisted
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
;; NOTE: we don't set the file features context here because
|
||||
;; there are no useful context for code that need to be executed
|
||||
;; on the frontend side
|
||||
(let [current-file-id (:current-file-id state)
|
||||
current-session-id (:session-id state)]
|
||||
(if (and (some? current-file-id)
|
||||
;; If the remote change is from teh current session we skip
|
||||
(not= persisted-session-id current-session-id))
|
||||
(if (= file-id current-file-id)
|
||||
(let [changes (group-by :page-id changes)]
|
||||
(-> state
|
||||
(update-in [:workspace-file :revn] max revn)
|
||||
(update :workspace-data
|
||||
(fn [file]
|
||||
(loop [fdata file
|
||||
entries (seq changes)]
|
||||
(if-let [[page-id changes] (first entries)]
|
||||
(recur (-> fdata
|
||||
(cpc/process-changes changes)
|
||||
(cond-> (some? page-id)
|
||||
(ctst/update-object-indices page-id)))
|
||||
(rest entries))
|
||||
fdata))))))
|
||||
(-> state
|
||||
(update-in [:workspace-libraries file-id :revn] max revn)
|
||||
(update-in [:workspace-libraries file-id :data] cpc/process-changes changes)))
|
||||
|
||||
state)))))
|
|
@ -18,9 +18,9 @@
|
|||
[app.common.record :as cr]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.modal :as md]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.collapse :as dwc]
|
||||
[app.main.data.workspace.specialized-panel :as-alias dwsp]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.colors :as cl]
|
||||
[app.main.data.workspace.grid-layout.editor :as dwge]
|
||||
[app.main.data.workspace.modifiers :as dwm]
|
||||
|
@ -148,8 +148,8 @@
|
|||
layout-initializer (get-layout-initializer type from-frame? calculate-params?)]
|
||||
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dch/update-shapes [id] layout-initializer {:with-objects? true})
|
||||
(dch/update-shapes (dm/get-prop parent :shapes) #(dissoc % :constraints-h :constraints-v))
|
||||
(dwsh/update-shapes [id] layout-initializer {:with-objects? true})
|
||||
(dwsh/update-shapes (dm/get-prop parent :shapes) #(dissoc % :constraints-h :constraints-v))
|
||||
(ptk/data-event :layout/update {:ids [id]})
|
||||
(dwu/commit-undo-transaction undo-id))))))
|
||||
|
||||
|
@ -188,8 +188,8 @@
|
|||
(dwsh/create-artboard-from-selection new-shape-id parent-id group-index (:name (first selected-shapes)))
|
||||
(cl/remove-all-fills [new-shape-id] {:color clr/black :opacity 1})
|
||||
(create-layout-from-id new-shape-id type)
|
||||
(dch/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto))
|
||||
(dch/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix))
|
||||
(dwsh/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto))
|
||||
(dwsh/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix))
|
||||
(dwsh/delete-shapes page-id selected)
|
||||
(ptk/data-event :layout/update {:ids [new-shape-id]})
|
||||
(dwu/commit-undo-transaction undo-id)))
|
||||
|
@ -199,8 +199,8 @@
|
|||
(dwsh/create-artboard-from-selection new-shape-id)
|
||||
(cl/remove-all-fills [new-shape-id] {:color clr/black :opacity 1})
|
||||
(create-layout-from-id new-shape-id type)
|
||||
(dch/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto))
|
||||
(dch/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix))))
|
||||
(dwsh/update-shapes [new-shape-id] #(assoc % :layout-item-h-sizing :auto :layout-item-v-sizing :auto))
|
||||
(dwsh/update-shapes selected #(assoc % :layout-item-h-sizing :fix :layout-item-v-sizing :fix))))
|
||||
|
||||
(rx/of (ptk/data-event :layout/update {:ids [new-shape-id]})
|
||||
(dwu/commit-undo-transaction undo-id)))))))
|
||||
|
@ -213,7 +213,7 @@
|
|||
(let [undo-id (js/Symbol)]
|
||||
(rx/of
|
||||
(dwu/start-undo-transaction undo-id)
|
||||
(dch/update-shapes ids #(apply dissoc % layout-keys))
|
||||
(dwsh/update-shapes ids #(apply dissoc % layout-keys))
|
||||
(ptk/data-event :layout/update {:ids ids})
|
||||
(dwu/commit-undo-transaction undo-id))))))
|
||||
|
||||
|
@ -266,7 +266,7 @@
|
|||
(watch [_ _ _]
|
||||
(let [undo-id (js/Symbol)]
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dch/update-shapes ids (d/patch-object changes))
|
||||
(dwsh/update-shapes ids (d/patch-object changes))
|
||||
(ptk/data-event :layout/update {:ids ids})
|
||||
(dwu/commit-undo-transaction undo-id))))))
|
||||
|
||||
|
@ -280,7 +280,7 @@
|
|||
(watch [_ _ _]
|
||||
(let [undo-id (js/Symbol)]
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dch/update-shapes
|
||||
(dwsh/update-shapes
|
||||
ids
|
||||
(fn [shape]
|
||||
(case type
|
||||
|
@ -313,7 +313,7 @@
|
|||
(if shapes-to-delete
|
||||
(dwsh/delete-shapes shapes-to-delete)
|
||||
(rx/empty))
|
||||
(dch/update-shapes
|
||||
(dwsh/update-shapes
|
||||
ids
|
||||
(fn [shape objects]
|
||||
(case type
|
||||
|
@ -387,7 +387,7 @@
|
|||
(watch [_ _ _]
|
||||
(let [undo-id (js/Symbol)]
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dch/update-shapes
|
||||
(dwsh/update-shapes
|
||||
ids
|
||||
(fn [shape]
|
||||
(case type
|
||||
|
@ -433,7 +433,7 @@
|
|||
:row :layout-grid-rows
|
||||
:column :layout-grid-columns)]
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dch/update-shapes
|
||||
(dwsh/update-shapes
|
||||
ids
|
||||
(fn [shape]
|
||||
(-> shape
|
||||
|
@ -525,9 +525,9 @@
|
|||
parent-ids (->> ids (map #(cfh/get-parent-id objects %)))
|
||||
undo-id (js/Symbol)]
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dch/update-shapes ids (d/patch-object changes))
|
||||
(dch/update-shapes children-ids (partial fix-child-sizing objects changes))
|
||||
(dch/update-shapes
|
||||
(dwsh/update-shapes ids (d/patch-object changes))
|
||||
(dwsh/update-shapes children-ids (partial fix-child-sizing objects changes))
|
||||
(dwsh/update-shapes
|
||||
parent-ids
|
||||
(fn [parent objects]
|
||||
(-> parent
|
||||
|
@ -546,8 +546,7 @@
|
|||
(let [undo-id (js/Symbol)]
|
||||
(rx/of
|
||||
(dwu/start-undo-transaction undo-id)
|
||||
|
||||
(dch/update-shapes
|
||||
(dwsh/update-shapes
|
||||
[layout-id]
|
||||
(fn [shape]
|
||||
(->> ids
|
||||
|
@ -570,7 +569,7 @@
|
|||
(let [undo-id (js/Symbol)]
|
||||
(rx/of
|
||||
(dwu/start-undo-transaction undo-id)
|
||||
(dch/update-shapes
|
||||
(dwsh/update-shapes
|
||||
[layout-id]
|
||||
(fn [shape objects]
|
||||
(case mode
|
||||
|
@ -636,7 +635,7 @@
|
|||
(let [undo-id (js/Symbol)]
|
||||
(rx/of
|
||||
(dwu/start-undo-transaction undo-id)
|
||||
(dch/update-shapes
|
||||
(dwsh/update-shapes
|
||||
[layout-id]
|
||||
(fn [shape objects]
|
||||
(let [cells (->> ids (map #(get-in shape [:layout-grid-cells %])))
|
||||
|
@ -668,7 +667,7 @@
|
|||
(let [undo-id (js/Symbol)]
|
||||
(rx/of
|
||||
(dwu/start-undo-transaction undo-id)
|
||||
(dch/update-shapes
|
||||
(dwsh/update-shapes
|
||||
[layout-id]
|
||||
(fn [shape objects]
|
||||
(let [prev-data (-> (dm/get-in shape [:layout-grid-cells cell-id])
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
[app.common.types.container :as ctn]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.comments :as dc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.edition :as dwe]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
|
@ -26,6 +26,74 @@
|
|||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(def ^:private update-layout-attr? #{:hidden})
|
||||
|
||||
(defn- add-undo-group
|
||||
[changes state]
|
||||
(let [undo (:workspace-undo state)
|
||||
items (:items undo)
|
||||
index (or (:index undo) (dec (count items)))
|
||||
prev-item (when-not (or (empty? items) (= index -1))
|
||||
(get items index))
|
||||
undo-group (:undo-group prev-item)
|
||||
add-undo-group? (and
|
||||
(not (nil? undo-group))
|
||||
(= (get-in changes [:redo-changes 0 :type]) :mod-obj)
|
||||
(= (get-in prev-item [:redo-changes 0 :type]) :add-obj)
|
||||
(contains? (:tags prev-item) :alt-duplication))] ;; This is a copy-and-move with mouse+alt
|
||||
|
||||
(cond-> changes add-undo-group? (assoc :undo-group undo-group))))
|
||||
|
||||
(defn update-shapes
|
||||
([ids update-fn] (update-shapes ids update-fn nil))
|
||||
([ids update-fn {:keys [reg-objects? save-undo? stack-undo? attrs ignore-tree page-id ignore-remote? ignore-touched undo-group with-objects?]
|
||||
:or {reg-objects? false save-undo? true stack-undo? false ignore-remote? false ignore-touched false with-objects? false}}]
|
||||
|
||||
(dm/assert!
|
||||
"expected a valid coll of uuid's"
|
||||
(sm/check-coll-of-uuid! ids))
|
||||
|
||||
(dm/assert! (fn? update-fn))
|
||||
|
||||
(ptk/reify ::update-shapes
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [page-id (or page-id (:current-page-id state))
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
ids (into [] (filter some?) ids)
|
||||
|
||||
update-layout-ids
|
||||
(->> ids
|
||||
(map (d/getf objects))
|
||||
(filter #(some update-layout-attr? (pcb/changed-attrs % objects update-fn {:attrs attrs :with-objects? with-objects?})))
|
||||
(map :id))
|
||||
|
||||
changes (-> (pcb/empty-changes it page-id)
|
||||
(pcb/set-save-undo? save-undo?)
|
||||
(pcb/set-stack-undo? stack-undo?)
|
||||
(cls/generate-update-shapes ids
|
||||
update-fn
|
||||
objects
|
||||
{:attrs attrs
|
||||
:ignore-tree ignore-tree
|
||||
:ignore-touched ignore-touched
|
||||
:with-objects? with-objects?})
|
||||
(cond-> undo-group
|
||||
(pcb/set-undo-group undo-group)))
|
||||
|
||||
changes (add-undo-group changes state)]
|
||||
(rx/concat
|
||||
(if (seq (:redo-changes changes))
|
||||
(let [changes (cond-> changes reg-objects? (pcb/resize-parents ids))
|
||||
changes (cond-> changes ignore-remote? (pcb/ignore-remote))]
|
||||
(rx/of (dch/commit-changes changes)))
|
||||
(rx/empty))
|
||||
|
||||
;; Update layouts for properties marked
|
||||
(if (d/not-empty? update-layout-ids)
|
||||
(rx/of (ptk/data-event :layout/update {:ids update-layout-ids}))
|
||||
(rx/empty))))))))
|
||||
|
||||
(defn add-shape
|
||||
([shape]
|
||||
(add-shape shape {}))
|
||||
|
@ -227,7 +295,7 @@
|
|||
ids (if (boolean? blocked)
|
||||
(into ids (->> ids (mapcat #(cfh/get-children-ids objects %))))
|
||||
ids)]
|
||||
(rx/of (dch/update-shapes ids update-fn {:attrs #{:blocked :hidden} :undo-group undo-group}))))))
|
||||
(rx/of (update-shapes ids update-fn {:attrs #{:blocked :hidden} :undo-group undo-group}))))))
|
||||
|
||||
(defn toggle-visibility-selected
|
||||
[]
|
||||
|
@ -235,7 +303,7 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [selected (wsh/lookup-selected state)]
|
||||
(rx/of (dch/update-shapes selected #(update % :hidden not)))))))
|
||||
(rx/of (update-shapes selected #(update % :hidden not)))))))
|
||||
|
||||
(defn toggle-lock-selected
|
||||
[]
|
||||
|
@ -243,7 +311,7 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [selected (wsh/lookup-selected state)]
|
||||
(rx/of (dch/update-shapes selected #(update % :blocked not)))))))
|
||||
(rx/of (update-shapes selected #(update % :blocked not)))))))
|
||||
|
||||
|
||||
;; FIXME: this need to be refactored
|
||||
|
@ -273,8 +341,8 @@
|
|||
(map (partial vector id)))))))
|
||||
(d/group-by first second)
|
||||
(map (fn [[page-id frame-ids]]
|
||||
(dch/update-shapes frame-ids #(dissoc % :use-for-thumbnail) {:page-id page-id})))))
|
||||
(update-shapes frame-ids #(dissoc % :use-for-thumbnail) {:page-id page-id})))))
|
||||
|
||||
;; And finally: toggle the flag value on all the selected shapes
|
||||
(rx/of (dch/update-shapes selected #(update % :use-for-thumbnail not))
|
||||
(rx/of (update-shapes selected #(update % :use-for-thumbnail not))
|
||||
(dwu/commit-undo-transaction undo-id)))))))
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
[app.main.data.users :as du]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.colors :as mdc]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.drawing :as dwd]
|
||||
[app.main.data.workspace.layers :as dwly]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
|
@ -51,12 +50,12 @@
|
|||
:undo {:tooltip (ds/meta "Z")
|
||||
:command (ds/c-mod "z")
|
||||
:subsections [:edit]
|
||||
:fn #(emit-when-no-readonly dwc/undo)}
|
||||
:fn #(emit-when-no-readonly dwu/undo)}
|
||||
|
||||
:redo {:tooltip (ds/meta "Y")
|
||||
:command [(ds/c-mod "shift+z") (ds/c-mod "y")]
|
||||
:subsections [:edit]
|
||||
:fn #(emit-when-no-readonly dwc/redo)}
|
||||
:fn #(emit-when-no-readonly dwu/redo)}
|
||||
|
||||
:clear-undo {:tooltip (ds/alt "Q")
|
||||
:command "alt+q"
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
[app.common.svg.shapes-builder :as csvg.shapes-builder]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.modifiers :as dwm]
|
||||
|
@ -93,7 +92,7 @@
|
|||
(some? (:current-page-id state))
|
||||
(some? shape))
|
||||
(rx/of
|
||||
(dch/update-shapes
|
||||
(dwsh/update-shapes
|
||||
[id]
|
||||
(fn [shape]
|
||||
(let [{:keys [width height position-data]} modifiers]
|
||||
|
@ -230,7 +229,7 @@
|
|||
shape-ids (cond (cfh/text-shape? shape) [id]
|
||||
(cfh/group-shape? shape) (cfh/get-children-ids objects id))]
|
||||
|
||||
(rx/of (dch/update-shapes shape-ids update-fn))))))
|
||||
(rx/of (dwsh/update-shapes shape-ids update-fn))))))
|
||||
|
||||
(defn update-paragraph-attrs
|
||||
[{:keys [id attrs]}]
|
||||
|
@ -257,7 +256,7 @@
|
|||
(cfh/text-shape? shape) [id]
|
||||
(cfh/group-shape? shape) (cfh/get-children-ids objects id))]
|
||||
|
||||
(rx/of (dch/update-shapes shape-ids update-fn))))))))
|
||||
(rx/of (dwsh/update-shapes shape-ids update-fn))))))))
|
||||
|
||||
(defn update-text-attrs
|
||||
[{:keys [id attrs]}]
|
||||
|
@ -277,7 +276,7 @@
|
|||
shape-ids (cond
|
||||
(cfh/text-shape? shape) [id]
|
||||
(cfh/group-shape? shape) (cfh/get-children-ids objects id))]
|
||||
(rx/of (dch/update-shapes shape-ids #(update-text-content % update-node? d/txt-merge attrs))))))))
|
||||
(rx/of (dwsh/update-shapes shape-ids #(update-text-content % update-node? d/txt-merge attrs))))))))
|
||||
|
||||
|
||||
(defn migrate-node
|
||||
|
@ -337,7 +336,7 @@
|
|||
(dissoc :fills)
|
||||
(d/update-when :content update-content)))]
|
||||
|
||||
(rx/of (dch/update-shapes shape-ids update-shape)))))))
|
||||
(rx/of (dwsh/update-shapes shape-ids update-shape)))))))
|
||||
|
||||
;; --- RESIZE UTILS
|
||||
|
||||
|
@ -390,10 +389,10 @@
|
|||
|
||||
(let [ids (into #{} (filter changed-text?) (keys props))]
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dch/update-shapes ids update-fn {:reg-objects? true
|
||||
:stack-undo? true
|
||||
:ignore-remote? true
|
||||
:ignore-touched true})
|
||||
(dwsh/update-shapes ids update-fn {:reg-objects? true
|
||||
:stack-undo? true
|
||||
:ignore-remote? true
|
||||
:ignore-touched true})
|
||||
(ptk/data-event :layout/update {:ids ids})
|
||||
(dwu/commit-undo-transaction undo-id))))))))
|
||||
|
||||
|
@ -532,7 +531,7 @@
|
|||
(watch [_ state _]
|
||||
(let [position-data (::update-position-data state)]
|
||||
(rx/concat
|
||||
(rx/of (dch/update-shapes
|
||||
(rx/of (dwsh/update-shapes
|
||||
(keys position-data)
|
||||
(fn [shape]
|
||||
(-> shape
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
[app.common.files.helpers :as cfh]
|
||||
[app.common.logging :as l]
|
||||
[app.common.thumbnails :as thc]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.persistence :as-alias dps]
|
||||
[app.main.data.workspace.notifications :as-alias wnt]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.rasterizer :as thr]
|
||||
|
@ -24,9 +25,10 @@
|
|||
[app.util.timers :as tm]
|
||||
[app.util.webapi :as wapi]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(l/set-level! :info)
|
||||
(l/set-level! :warn)
|
||||
|
||||
(declare update-thumbnail)
|
||||
|
||||
|
@ -65,10 +67,9 @@
|
|||
ptk/EffectEvent
|
||||
(effect [_ _ _]
|
||||
(l/dbg :hint "request thumbnail" :requester requester :file-id file-id :page-id page-id :shape-id shape-id :tag tag)
|
||||
(q/enqueue-unique
|
||||
queue
|
||||
(create-request file-id page-id shape-id tag)
|
||||
(partial find-request file-id page-id shape-id tag))))))
|
||||
(q/enqueue-unique queue
|
||||
(create-request file-id page-id shape-id tag)
|
||||
(partial find-request file-id page-id shape-id tag))))))
|
||||
|
||||
;; This function first renders the HTML calling `render/render-frame` that
|
||||
;; returns HTML as a string, then we send that data to the iframe rasterizer
|
||||
|
@ -92,24 +93,36 @@
|
|||
|
||||
(defn clear-thumbnail
|
||||
([file-id page-id frame-id tag]
|
||||
(clear-thumbnail (thc/fmt-object-id file-id page-id frame-id tag)))
|
||||
([object-id]
|
||||
(let [emit-rpc? (volatile! false)]
|
||||
(clear-thumbnail file-id (thc/fmt-object-id file-id page-id frame-id tag)))
|
||||
([file-id object-id]
|
||||
(let [pending (volatile! false)]
|
||||
(ptk/reify ::clear-thumbnail
|
||||
cljs.core/IDeref
|
||||
(-deref [_] object-id)
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [uri (dm/get-in state [:workspace-thumbnails object-id])]
|
||||
(if (some? uri)
|
||||
(do
|
||||
(l/dbg :hint "clear thumbnail" :object-id object-id)
|
||||
(vreset! emit-rpc? true)
|
||||
(tm/schedule-on-idle (partial wapi/revoke-uri uri))
|
||||
(update state :workspace-thumbnails dissoc object-id))
|
||||
(update state :workspace-thumbnails
|
||||
(fn [thumbs]
|
||||
(if-let [uri (get thumbs object-id)]
|
||||
(do (vreset! pending uri)
|
||||
(dissoc thumbs object-id))
|
||||
thumbs))))
|
||||
|
||||
state)))))))
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(if-let [uri @pending]
|
||||
(do
|
||||
(l/trc :hint "clear-thumbnail" :uri uri)
|
||||
(when (str/starts-with? uri "blob:")
|
||||
(tm/schedule-on-idle (partial wapi/revoke-uri uri)))
|
||||
|
||||
(let [params {:file-id file-id
|
||||
:object-id object-id}]
|
||||
(->> (rp/cmd! :delete-file-object-thumbnail params)
|
||||
(rx/catch rx/empty)
|
||||
(rx/ignore))))
|
||||
(rx/empty)))))))
|
||||
|
||||
(defn- assoc-thumbnail
|
||||
[object-id uri]
|
||||
|
@ -181,7 +194,7 @@
|
|||
(defn- extract-root-frame-changes
|
||||
"Process a changes set in a commit to extract the frames that are changing"
|
||||
[page-id [event [old-data new-data]]]
|
||||
(let [changes (-> event deref :changes)
|
||||
(let [changes (:changes event)
|
||||
|
||||
extract-ids
|
||||
(fn [{:keys [page-id type] :as change}]
|
||||
|
@ -239,58 +252,53 @@
|
|||
(rx/buffer 2 1)
|
||||
(rx/share))
|
||||
|
||||
local-changes-s
|
||||
;; All commits stream, indepentendly of the source of the commit
|
||||
all-commits-s
|
||||
(->> stream
|
||||
(rx/filter dch/commit-changes?)
|
||||
(rx/with-latest-from workspace-data-s)
|
||||
(rx/merge-map (partial extract-root-frame-changes page-id))
|
||||
(rx/tap #(l/trc :hint "incoming change" :origin "local" :frame-id (dm/str %))))
|
||||
|
||||
notification-changes-s
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::wnt/handle-file-change))
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/observe-on :async)
|
||||
(rx/with-latest-from workspace-data-s)
|
||||
(rx/merge-map (partial extract-root-frame-changes page-id))
|
||||
(rx/tap #(l/trc :hint "incoming change" :origin "notifications" :frame-id (dm/str %))))
|
||||
|
||||
persistence-changes-s
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::update))
|
||||
(rx/map deref)
|
||||
(rx/filter (fn [[file-id page-id]]
|
||||
(and (= file-id file-id)
|
||||
(= page-id page-id))))
|
||||
(rx/map (fn [[_ _ frame-id]] frame-id))
|
||||
(rx/tap #(l/trc :hint "incoming change" :origin "persistence" :frame-id (dm/str %))))
|
||||
|
||||
all-changes-s
|
||||
(->> (rx/merge
|
||||
;; LOCAL CHANGES
|
||||
local-changes-s
|
||||
;; NOTIFICATIONS CHANGES
|
||||
notification-changes-s
|
||||
;; PERSISTENCE CHANGES
|
||||
persistence-changes-s)
|
||||
|
||||
(rx/tap #(l/trc :hint "inconming change" :origin "local" :frame-id (dm/str %)))
|
||||
(rx/share))
|
||||
|
||||
;; BUFFER NOTIFIER (window of 5s of inactivity)
|
||||
local-commits-s
|
||||
(->> stream
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/filter #(= :local (:source %)))
|
||||
(rx/observe-on :async)
|
||||
(rx/with-latest-from workspace-data-s)
|
||||
(rx/merge-map (partial extract-root-frame-changes page-id))
|
||||
(rx/tap #(l/trc :hint "inconming change" :origin "local" :frame-id (dm/str %)))
|
||||
(rx/share))
|
||||
|
||||
;; BUFFER NOTIFIER: only on local changes, remote changes
|
||||
;; we expect to receive thumbnail uri once it is
|
||||
;; generated va notifications subsystem
|
||||
notifier-s
|
||||
(->> all-changes-s
|
||||
(rx/debounce 1000)
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::dps/commit-persisted))
|
||||
(rx/map deref)
|
||||
(rx/observe-on :async)
|
||||
(rx/with-latest-from workspace-data-s)
|
||||
(rx/merge-map (partial extract-root-frame-changes page-id))
|
||||
(rx/tap #(l/trc :hint "inconming change" :origin "local" :frame-id (dm/str %)))
|
||||
(rx/debounce 5000)
|
||||
(rx/tap #(l/trc :hint "buffer initialized")))]
|
||||
|
||||
(->> (rx/merge
|
||||
;; Perform instant thumbnail cleaning of affected frames
|
||||
;; and interrupt any ongoing update-thumbnail process
|
||||
;; related to current frame-id
|
||||
(->> all-changes-s
|
||||
(rx/map #(clear-thumbnail file-id page-id % "frame")))
|
||||
(->> all-commits-s
|
||||
(rx/map (fn [frame-id]
|
||||
(clear-thumbnail file-id page-id frame-id "frame"))))
|
||||
|
||||
;; Generate thumbnails in batchs, once user becomes
|
||||
;; inactive for some instant
|
||||
(->> all-changes-s
|
||||
;; inactive for some instant only for local changes
|
||||
(->> local-commits-s
|
||||
(rx/buffer-until notifier-s)
|
||||
(rx/mapcat #(into #{} %))
|
||||
(rx/map #(request-thumbnail file-id page-id % "frame" "watch-state-changes"))))
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.workspace.collapse :as dwc]
|
||||
[app.main.data.workspace.modifiers :as dwm]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
|
|
|
@ -11,18 +11,18 @@
|
|||
[app.common.files.changes :as cpc]
|
||||
[app.common.logging :as log]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.data.changes :as dch]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.util.router :as rt]
|
||||
[app.util.time :as dt]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(def discard-transaction-time-millis (* 20 1000))
|
||||
|
||||
;; Change this to :info :debug or :trace to debug this module
|
||||
(log/set-level! :warn)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Undo / Redo
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(def discard-transaction-time-millis (* 20 1000))
|
||||
|
||||
(def ^:private
|
||||
schema:undo-entry
|
||||
|
@ -44,7 +44,6 @@
|
|||
(subvec undo (- cnt MAX-UNDO-SIZE))
|
||||
undo)))
|
||||
|
||||
;; TODO: Review the necessity of this method
|
||||
(defn materialize-undo
|
||||
[_changes index]
|
||||
(ptk/reify ::materialize-undo
|
||||
|
@ -84,8 +83,7 @@
|
|||
(-> state
|
||||
(update-in [:workspace-undo :transaction :undo-changes] #(into undo-changes %))
|
||||
(update-in [:workspace-undo :transaction :redo-changes] #(into % redo-changes))
|
||||
(cond->
|
||||
(nil? (get-in state [:workspace-undo :transaction :undo-group]))
|
||||
(cond-> (nil? (get-in state [:workspace-undo :transaction :undo-group]))
|
||||
(assoc-in [:workspace-undo :transaction :undo-group] undo-group))
|
||||
(assoc-in [:workspace-undo :transaction :tags] tags)))
|
||||
|
||||
|
@ -182,3 +180,125 @@
|
|||
(rx/tap #(js/console.warn (dm/str "FORCE COMMIT TRANSACTION AFTER " (second %) "MS")))
|
||||
(rx/map first)
|
||||
(rx/map commit-undo-transaction))))))
|
||||
|
||||
(defn undo-to-index
|
||||
"Repeat undoing or redoing until dest-index is reached."
|
||||
[dest-index]
|
||||
(ptk/reify ::undo-to-index
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
edition (get-in state [:workspace-local :edition])
|
||||
drawing (get state :workspace-drawing)]
|
||||
(when-not (and (or (some? edition) (some? (:object drawing)))
|
||||
(not (ctl/grid-layout? objects edition)))
|
||||
(let [undo (:workspace-undo state)
|
||||
items (:items undo)
|
||||
index (or (:index undo) (dec (count items)))]
|
||||
(when (and (some? items)
|
||||
(<= -1 dest-index (dec (count items))))
|
||||
(let [changes (vec (apply concat
|
||||
(cond
|
||||
(< dest-index index)
|
||||
(->> (subvec items (inc dest-index) (inc index))
|
||||
(reverse)
|
||||
(map :undo-changes))
|
||||
(> dest-index index)
|
||||
(->> (subvec items (inc index) (inc dest-index))
|
||||
(map :redo-changes))
|
||||
:else [])))]
|
||||
(when (seq changes)
|
||||
(rx/of (materialize-undo changes dest-index)
|
||||
(dch/commit-changes {:redo-changes changes
|
||||
:undo-changes []
|
||||
:origin it
|
||||
:save-undo? false})))))))))))
|
||||
|
||||
(declare ^:private assure-valid-current-page)
|
||||
|
||||
(def undo
|
||||
(ptk/reify ::undo
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
edition (get-in state [:workspace-local :edition])
|
||||
drawing (get state :workspace-drawing)]
|
||||
|
||||
;; Editors handle their own undo's
|
||||
(when (or (and (nil? edition) (nil? (:object drawing)))
|
||||
(ctl/grid-layout? objects edition))
|
||||
(let [undo (:workspace-undo state)
|
||||
items (:items undo)
|
||||
index (or (:index undo) (dec (count items)))]
|
||||
(when-not (or (empty? items) (= index -1))
|
||||
(let [item (get items index)
|
||||
changes (:undo-changes item)
|
||||
undo-group (:undo-group item)
|
||||
|
||||
find-first-group-idx
|
||||
(fn [index]
|
||||
(if (= (dm/get-in items [index :undo-group]) undo-group)
|
||||
(recur (dec index))
|
||||
(inc index)))
|
||||
|
||||
undo-group-index
|
||||
(when undo-group
|
||||
(find-first-group-idx index))]
|
||||
|
||||
(if undo-group
|
||||
(rx/of (undo-to-index (dec undo-group-index)))
|
||||
(rx/of (materialize-undo changes (dec index))
|
||||
(dch/commit-changes {:redo-changes changes
|
||||
:undo-changes []
|
||||
:save-undo? false
|
||||
:origin it})
|
||||
(assure-valid-current-page)))))))))))
|
||||
|
||||
(def redo
|
||||
(ptk/reify ::redo
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
edition (get-in state [:workspace-local :edition])
|
||||
drawing (get state :workspace-drawing)]
|
||||
(when (and (or (nil? edition) (ctl/grid-layout? objects edition))
|
||||
(or (empty? drawing) (= :curve (:tool drawing))))
|
||||
(let [undo (:workspace-undo state)
|
||||
items (:items undo)
|
||||
index (or (:index undo) (dec (count items)))]
|
||||
(when-not (or (empty? items) (= index (dec (count items))))
|
||||
(let [item (get items (inc index))
|
||||
changes (:redo-changes item)
|
||||
undo-group (:undo-group item)
|
||||
find-last-group-idx (fn flgidx [index]
|
||||
(let [item (get items index)]
|
||||
(if (= (:undo-group item) undo-group)
|
||||
(flgidx (inc index))
|
||||
(dec index))))
|
||||
|
||||
redo-group-index (when undo-group
|
||||
(find-last-group-idx (inc index)))]
|
||||
(if undo-group
|
||||
(rx/of (undo-to-index redo-group-index))
|
||||
(rx/of (materialize-undo changes (inc index))
|
||||
(dch/commit-changes {:redo-changes changes
|
||||
:undo-changes []
|
||||
:origin it
|
||||
:save-undo? false})))))))))))
|
||||
|
||||
(defn- assure-valid-current-page
|
||||
[]
|
||||
(ptk/reify ::assure-valid-current-page
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [current_page (:current-page-id state)
|
||||
pages (get-in state [:workspace-data :pages])
|
||||
exists? (some #(= current_page %) pages)
|
||||
|
||||
project-id (:current-project-id state)
|
||||
file-id (:current-file-id state)
|
||||
pparams {:file-id file-id :project-id project-id}
|
||||
qparams {:page-id (first pages)}]
|
||||
(if exists?
|
||||
(rx/empty)
|
||||
(rx/of (rt/nav :workspace pparams qparams)))))))
|
||||
|
|
|
@ -45,6 +45,9 @@
|
|||
(def export
|
||||
(l/derived :export st/state))
|
||||
|
||||
(def persistence
|
||||
(l/derived :persistence st/state))
|
||||
|
||||
;; ---- Dashboard refs
|
||||
|
||||
(def dashboard-local
|
||||
|
|
|
@ -34,8 +34,6 @@
|
|||
(def debug-exclude-events
|
||||
#{:app.main.data.workspace.notifications/handle-pointer-update
|
||||
:app.main.data.workspace.notifications/handle-pointer-send
|
||||
:app.main.data.workspace.persistence/update-persistence-status
|
||||
:app.main.data.workspace.changes/update-indices
|
||||
:app.main.data.websocket/send-message
|
||||
:app.main.data.workspace.selection/change-hover-state})
|
||||
|
||||
|
@ -65,7 +63,7 @@
|
|||
:app.util.router/assign-exception}]
|
||||
(->> (rx/merge
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? :app.main.data.workspace.changes/commit-changes))
|
||||
(rx/filter (ptk/type? :app.main.data.changes/commit))
|
||||
(rx/map #(-> % deref :hint-origin)))
|
||||
(rx/map ptk/type stream))
|
||||
(rx/filter #(not (contains? omitset %)))
|
||||
|
|
|
@ -101,7 +101,7 @@
|
|||
(cond-> (and value is-checkbox?) (assoc :default-checked value))
|
||||
(cond-> (and touched? (:message error)) (assoc "aria-invalid" "true"
|
||||
"aria-describedby" (dm/str "error-" input-name)))
|
||||
(obj/clj->props))
|
||||
(obj/map->obj obj/prop-key-fn))
|
||||
|
||||
checked? (and is-checkbox? (= value true))
|
||||
show-valid? (and show-success? touched? (not error))
|
||||
|
@ -201,7 +201,7 @@
|
|||
:on-blur on-blur
|
||||
;; :placeholder label
|
||||
:on-change on-change)
|
||||
(obj/clj->props))]
|
||||
(obj/map->obj obj/prop-key-fn))]
|
||||
|
||||
[:div {:class (dm/str klass " " (stl/css :textarea-wrapper))}
|
||||
[:label {:class (stl/css :textarea-label)} label]
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
[app.common.data.macros :as dm]
|
||||
[app.main.data.messages :as msg]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.persistence :as dps]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.colors :as dc]
|
||||
[app.main.data.workspace.persistence :as dwp]
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
|
@ -177,6 +177,9 @@
|
|||
|
||||
background-color (:background-color wglobal)]
|
||||
|
||||
(mf/with-effect []
|
||||
(st/emit! (dps/initialize-persistence)))
|
||||
|
||||
;; Setting the layout preset by its name
|
||||
(mf/with-effect [layout-name]
|
||||
(st/emit! (dw/initialize-layout layout-name)))
|
||||
|
@ -188,7 +191,7 @@
|
|||
(mf/with-effect [project-id file-id]
|
||||
(st/emit! (dw/initialize-file project-id file-id))
|
||||
(fn []
|
||||
(st/emit! ::dwp/force-persist
|
||||
(st/emit! ::dps/force-persist
|
||||
(dc/stop-picker)
|
||||
(modal/hide)
|
||||
msg/hide
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
[app.main.data.shortcuts :as scd]
|
||||
[app.main.data.users :as du]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.shortcuts :as sc]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
|
@ -414,8 +414,8 @@
|
|||
::mf/wrap [mf/memo]}
|
||||
[{:keys [on-close]}]
|
||||
(let [select-all (mf/use-fn #(st/emit! (dw/select-all)))
|
||||
undo (mf/use-fn #(st/emit! dwc/undo))
|
||||
redo (mf/use-fn #(st/emit! dwc/redo))]
|
||||
undo (mf/use-fn #(st/emit! dwu/undo))
|
||||
redo (mf/use-fn #(st/emit! dwu/redo))]
|
||||
[:& dropdown-menu {:show true
|
||||
:list-class (stl/css-case :sub-menu true
|
||||
:edit true)
|
||||
|
|
|
@ -25,38 +25,35 @@
|
|||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def ref:workspace-persistence
|
||||
(l/derived :workspace-persistence st/state))
|
||||
(def ref:persistence-status
|
||||
(l/derived :status refs/persistence))
|
||||
|
||||
;; --- Persistence state Widget
|
||||
|
||||
(mf/defc persistence-state-widget
|
||||
{::mf/wrap [mf/memo]}
|
||||
{::mf/wrap [mf/memo]
|
||||
::mf/wrap-props false}
|
||||
[]
|
||||
(let [{:keys [status]} (mf/deref ref:workspace-persistence)]
|
||||
(let [status (mf/deref ref:persistence-status)]
|
||||
[:div {:class (stl/css :persistence-status-widget)}
|
||||
(case status
|
||||
:pending
|
||||
[:div {:class (stl/css-case :status-icon true
|
||||
:pending-status true)
|
||||
[:div {:class (stl/css :status-icon :pending-status)
|
||||
:title (tr "workspace.header.unsaved")}
|
||||
i/status-alert]
|
||||
|
||||
:saving
|
||||
[:div {:class (stl/css-case :status-icon true
|
||||
:saving-status true)
|
||||
[:div {:class (stl/css :status-icon :saving-status)
|
||||
:title (tr "workspace.header.saving")}
|
||||
i/status-update]
|
||||
|
||||
:saved
|
||||
[:div {:class (stl/css-case :status-icon true
|
||||
:saved-status true)
|
||||
[:div {:class (stl/css :status-icon :saved-status)
|
||||
:title (tr "workspace.header.saved")}
|
||||
i/status-tick]
|
||||
|
||||
:error
|
||||
[:div {:class (stl/css-case :status-icon true
|
||||
:error-status true)
|
||||
[:div {:class (stl/css :status-icon :error-status)
|
||||
:title "There was an error saving the data. Please refresh if this persists."}
|
||||
i/status-wrong]
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
[app.common.data.macros :as dm]
|
||||
[app.main.data.events :as ev]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.icons :as i]
|
||||
|
@ -304,7 +304,7 @@
|
|||
:show-detail @show-detail?)
|
||||
:on-pointer-enter #(reset! hover? true)
|
||||
:on-pointer-leave #(reset! hover? false)
|
||||
:on-click #(st/emit! (dwc/undo-to-index idx-entry))}
|
||||
:on-click #(st/emit! (dwu/undo-to-index idx-entry))}
|
||||
|
||||
[:div {:class (stl/css :history-entry-summary)}
|
||||
[:div {:class (stl/css :history-entry-summary-icon)}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input*]]
|
||||
[app.main.ui.components.title-bar :refer [title-bar]]
|
||||
|
@ -44,7 +44,7 @@
|
|||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [update-fn]
|
||||
(st/emit! (dch/update-shapes ids update-fn))))
|
||||
(st/emit! (dwsh/update-shapes ids update-fn))))
|
||||
|
||||
handle-add
|
||||
(mf/use-fn
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
[app.common.geom.rect :as grc]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.select :refer [select]]
|
||||
|
@ -102,7 +102,7 @@
|
|||
[:constraints-h :center])
|
||||
nil ())]
|
||||
|
||||
(st/emit! (dch/update-shapes
|
||||
(st/emit! (dwsh/update-shapes
|
||||
ids
|
||||
#(assoc % constraint new-value))))))
|
||||
|
||||
|
@ -111,7 +111,7 @@
|
|||
(mf/deps ids)
|
||||
(fn [value]
|
||||
(when-not (str/empty? value)
|
||||
(st/emit! (dch/update-shapes
|
||||
(st/emit! (dwsh/update-shapes
|
||||
ids
|
||||
#(assoc % :constraints-h (keyword value)))))))
|
||||
|
||||
|
@ -120,7 +120,7 @@
|
|||
(mf/deps ids)
|
||||
(fn [value]
|
||||
(when-not (str/empty? value)
|
||||
(st/emit! (dch/update-shapes
|
||||
(st/emit! (dwsh/update-shapes
|
||||
ids
|
||||
#(assoc % :constraints-v (keyword value)))))))
|
||||
|
||||
|
@ -128,7 +128,7 @@
|
|||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [_]
|
||||
(st/emit! (dch/update-shapes ids #(update % :fixed-scroll not)))))
|
||||
(st/emit! (dwsh/update-shapes ids #(update % :fixed-scroll not)))))
|
||||
|
||||
options-h
|
||||
(mf/with-memo [constraints-h]
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.main.data.exports :as de]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
|
@ -97,9 +97,9 @@
|
|||
(mf/deps ids)
|
||||
(fn []
|
||||
(let [xspec {:type :png :suffix "" :scale 1}]
|
||||
(st/emit! (dch/update-shapes ids
|
||||
(fn [shape]
|
||||
(assoc shape :exports (into [xspec] (:exports shape)))))))))
|
||||
(st/emit! (dwsh/update-shapes ids
|
||||
(fn [shape]
|
||||
(assoc shape :exports (into [xspec] (:exports shape)))))))))
|
||||
|
||||
delete-export
|
||||
(mf/use-fn
|
||||
|
@ -110,16 +110,16 @@
|
|||
(mapv second)))
|
||||
|
||||
remove (fn [shape] (update shape :exports remove-fill-by-index position))]
|
||||
(st/emit! (dch/update-shapes ids remove)))))
|
||||
(st/emit! (dwsh/update-shapes ids remove)))))
|
||||
|
||||
on-scale-change
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [index event]
|
||||
(let [scale (d/parse-double event)]
|
||||
(st/emit! (dch/update-shapes ids
|
||||
(fn [shape]
|
||||
(assoc-in shape [:exports index :scale] scale)))))))
|
||||
(st/emit! (dwsh/update-shapes ids
|
||||
(fn [shape]
|
||||
(assoc-in shape [:exports index :scale] scale)))))))
|
||||
|
||||
on-suffix-change
|
||||
(mf/use-fn
|
||||
|
@ -129,26 +129,26 @@
|
|||
index (-> (dom/get-current-target event)
|
||||
(dom/get-data "value")
|
||||
(d/parse-integer))]
|
||||
(st/emit! (dch/update-shapes ids
|
||||
(fn [shape]
|
||||
(assoc-in shape [:exports index :suffix] value)))))))
|
||||
(st/emit! (dwsh/update-shapes ids
|
||||
(fn [shape]
|
||||
(assoc-in shape [:exports index :suffix] value)))))))
|
||||
|
||||
on-type-change
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [index event]
|
||||
(let [type (keyword event)]
|
||||
(st/emit! (dch/update-shapes ids
|
||||
(fn [shape]
|
||||
(assoc-in shape [:exports index :type] type)))))))
|
||||
(st/emit! (dwsh/update-shapes ids
|
||||
(fn [shape]
|
||||
(assoc-in shape [:exports index :type] type)))))))
|
||||
|
||||
on-remove-all
|
||||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn []
|
||||
(st/emit! (dch/update-shapes ids
|
||||
(fn [shape]
|
||||
(assoc shape :exports []))))))
|
||||
(st/emit! (dwsh/update-shapes ids
|
||||
(fn [shape]
|
||||
(assoc shape :exports []))))))
|
||||
manage-key-down
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input*]]
|
||||
[app.main.ui.components.select :refer [select]]
|
||||
|
@ -55,7 +55,7 @@
|
|||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn [prop value]
|
||||
(st/emit! (dch/update-shapes ids #(assoc % prop value)))))
|
||||
(st/emit! (dwsh/update-shapes ids #(assoc % prop value)))))
|
||||
|
||||
handle-change-blend-mode
|
||||
(mf/use-fn
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
[app.common.types.shape.radius :as ctsr]
|
||||
[app.main.constants :refer [size-presets]]
|
||||
[app.main.data.workspace :as udw]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.interactions :as dwi]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
|
@ -249,13 +249,13 @@
|
|||
(mf/use-fn
|
||||
(mf/deps ids-with-children)
|
||||
(fn [update-fn]
|
||||
(dch/update-shapes ids-with-children
|
||||
(fn [shape]
|
||||
(if (ctsr/has-radius? shape)
|
||||
(update-fn shape)
|
||||
shape))
|
||||
{:reg-objects? true
|
||||
:attrs [:rx :ry :r1 :r2 :r3 :r4]})))
|
||||
(dwsh/update-shapes ids-with-children
|
||||
(fn [shape]
|
||||
(if (ctsr/has-radius? shape)
|
||||
(update-fn shape)
|
||||
shape))
|
||||
{:reg-objects? true
|
||||
:attrs [:rx :ry :r1 :r2 :r3 :r4]})))
|
||||
|
||||
on-switch-to-radius-1
|
||||
(mf/use-fn
|
||||
|
@ -317,7 +317,7 @@
|
|||
(mf/deps ids)
|
||||
(fn [event]
|
||||
(let [value (-> event dom/get-target dom/checked?)]
|
||||
(st/emit! (dch/update-shapes ids (fn [shape] (assoc shape :show-content (not value))))))))
|
||||
(st/emit! (dwsh/update-shapes ids (fn [shape] (assoc shape :show-content (not value))))))))
|
||||
|
||||
on-change-show-in-viewer
|
||||
(mf/use-fn
|
||||
|
@ -327,7 +327,7 @@
|
|||
undo-id (js/Symbol)]
|
||||
(do
|
||||
(st/emit! (dwu/start-undo-transaction undo-id)
|
||||
(dch/update-shapes ids (fn [shape] (assoc shape :hide-in-viewer (not value)))))
|
||||
(dwsh/update-shapes ids (fn [shape] (assoc shape :hide-in-viewer (not value)))))
|
||||
|
||||
(when-not value
|
||||
;; when a frame is no longer shown in view mode, cannot have
|
||||
|
@ -399,7 +399,7 @@
|
|||
:placeholder (if (= :multiple (:width values)) (tr "settings.multiple") "--")
|
||||
:on-change on-width-change
|
||||
:disabled disabled-width-sizing?
|
||||
:className (stl/css :numeric-input)
|
||||
:class (stl/css :numeric-input)
|
||||
:value (:width values)}]]
|
||||
[:div {:class (stl/css-case :height true
|
||||
:disabled disabled-height-sizing?)
|
||||
|
@ -410,7 +410,7 @@
|
|||
:placeholder (if (= :multiple (:height values)) (tr "settings.multiple") "--")
|
||||
:on-change on-height-change
|
||||
:disabled disabled-height-sizing?
|
||||
:className (stl/css :numeric-input)
|
||||
:class (stl/css :numeric-input)
|
||||
:value (:height values)}]]
|
||||
[:button {:class (stl/css-case
|
||||
:lock-size-btn true
|
||||
|
@ -430,7 +430,7 @@
|
|||
:placeholder (if (= :multiple (:x values)) (tr "settings.multiple") "--")
|
||||
:on-change on-pos-x-change
|
||||
:disabled disabled-position-x?
|
||||
:className (stl/css :numeric-input)
|
||||
:class (stl/css :numeric-input)
|
||||
:value (:x values)}]]
|
||||
|
||||
[:div {:class (stl/css-case :y-position true
|
||||
|
@ -441,7 +441,7 @@
|
|||
:placeholder (if (= :multiple (:y values)) (tr "settings.multiple") "--")
|
||||
:disabled disabled-position-y?
|
||||
:on-change on-pos-y-change
|
||||
:className (stl/css :numeric-input)
|
||||
:class (stl/css :numeric-input)
|
||||
:value (:y values)}]]])
|
||||
(when (or (options :rotation) (options :radius))
|
||||
[:div {:class (stl/css :rotation-radius)}
|
||||
|
@ -457,7 +457,7 @@
|
|||
:data-wrap true
|
||||
:placeholder (if (= :multiple (:rotation values)) (tr "settings.multiple") "--")
|
||||
:on-change on-rotation-change
|
||||
:className (stl/css :numeric-input)
|
||||
:class (stl/css :numeric-input)
|
||||
:value (:rotation values)}]])
|
||||
|
||||
(when (options :radius)
|
||||
|
@ -473,7 +473,7 @@
|
|||
:ref radius-input-ref
|
||||
:min 0
|
||||
:on-change on-radius-1-change
|
||||
:className (stl/css :numeric-input)
|
||||
:class (stl/css :numeric-input)
|
||||
:value (:rx values)}]]
|
||||
|
||||
@radius-multi?
|
||||
|
@ -485,7 +485,7 @@
|
|||
:placeholder "Mixed"
|
||||
:min 0
|
||||
:on-change on-radius-multi-change
|
||||
:className (stl/css :numeric-input)
|
||||
:class (stl/css :numeric-input)
|
||||
:value (if all-equal? (:rx values) nil)}]]
|
||||
|
||||
|
||||
|
@ -497,7 +497,7 @@
|
|||
{:placeholder "--"
|
||||
:min 0
|
||||
:on-change on-radius-r1-change
|
||||
:className (stl/css :numeric-input)
|
||||
:class (stl/css :numeric-input)
|
||||
:value (:r1 values)}]]
|
||||
|
||||
[:div {:class (stl/css :small-input)
|
||||
|
@ -506,7 +506,7 @@
|
|||
{:placeholder "--"
|
||||
:min 0
|
||||
:on-change on-radius-r2-change
|
||||
:className (stl/css :numeric-input)
|
||||
:class (stl/css :numeric-input)
|
||||
:value (:r2 values)}]]
|
||||
|
||||
[:div {:class (stl/css :small-input)
|
||||
|
@ -515,7 +515,7 @@
|
|||
{:placeholder "--"
|
||||
:min 0
|
||||
:on-change on-radius-r4-change
|
||||
:className (stl/css :numeric-input)
|
||||
:class (stl/css :numeric-input)
|
||||
:value (:r4 values)}]]
|
||||
|
||||
[:div {:class (stl/css :small-input)
|
||||
|
@ -524,7 +524,7 @@
|
|||
{:placeholder "--"
|
||||
:min 0
|
||||
:on-change on-radius-r3-change
|
||||
:className (stl/css :numeric-input)
|
||||
:class (stl/css :numeric-input)
|
||||
:value (:r3 values)}]]])]
|
||||
[:button {:class (stl/css-case :radius-mode true
|
||||
:selected (= radius-mode :radius-4))
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
[app.common.data.macros :as dm]
|
||||
[app.common.math :as mth]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.colors :as dc]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input*]]
|
||||
|
@ -73,7 +73,7 @@
|
|||
(mf/use-fn
|
||||
(mf/deps ids index)
|
||||
(fn []
|
||||
(st/emit! (dch/update-shapes ids #(update % :shadow remove-shadow-by-index index)))))
|
||||
(st/emit! (dwsh/update-shapes ids #(update % :shadow remove-shadow-by-index index)))))
|
||||
|
||||
on-drop
|
||||
(mf/use-fn
|
||||
|
@ -102,7 +102,7 @@
|
|||
([index attr update-ref]
|
||||
(fn [value]
|
||||
(when (mth/finite? value)
|
||||
(st/emit! (dch/update-shapes ids #(assoc-in % [:shadow index attr] value)))
|
||||
(st/emit! (dwsh/update-shapes ids #(assoc-in % [:shadow index attr] value)))
|
||||
(when-let [update-node (and update-ref (mf/ref-val update-ref))]
|
||||
(dom/set-value! update-node value))))))
|
||||
|
||||
|
@ -110,7 +110,7 @@
|
|||
(mf/use-fn
|
||||
(mf/deps ids index)
|
||||
(fn [color]
|
||||
(st/emit! (dch/update-shapes
|
||||
(st/emit! (dwsh/update-shapes
|
||||
ids
|
||||
#(assoc-in % [:shadow index :color] (d/without-nils color))))))
|
||||
|
||||
|
@ -119,7 +119,7 @@
|
|||
(mf/deps ids index value)
|
||||
(fn [_color _opacity]
|
||||
(when-not (string? (:color value))
|
||||
(st/emit! (dch/update-shapes
|
||||
(st/emit! (dwsh/update-shapes
|
||||
ids
|
||||
#(assoc-in % [:shadow index :color]
|
||||
(dissoc (:color value) :id :file-id)))))))
|
||||
|
@ -128,7 +128,7 @@
|
|||
(mf/use-fn
|
||||
(mf/deps ids index)
|
||||
(fn []
|
||||
(st/emit! (dch/update-shapes ids #(update-in % [:shadow index :hidden] not)))))
|
||||
(st/emit! (dwsh/update-shapes ids #(update-in % [:shadow index :hidden] not)))))
|
||||
|
||||
on-toggle-open-shadow
|
||||
(fn []
|
||||
|
@ -139,7 +139,7 @@
|
|||
(mf/deps ids index)
|
||||
(fn [event]
|
||||
(let [value (keyword event)]
|
||||
(st/emit! (dch/update-shapes ids #(assoc-in % [:shadow index :style] value))))))
|
||||
(st/emit! (dwsh/update-shapes ids #(assoc-in % [:shadow index :style] value))))))
|
||||
|
||||
type-options [{:value "drop-shadow" :label (tr "workspace.options.shadow-options.drop-shadow")}
|
||||
{:value "inner-shadow" :label (tr "workspace.options.shadow-options.inner-shadow")}]
|
||||
|
@ -268,7 +268,7 @@
|
|||
(mf/use-fn
|
||||
(mf/deps ids)
|
||||
(fn []
|
||||
(st/emit! (dch/update-shapes ids #(dissoc % :shadow)))))
|
||||
(st/emit! (dwsh/update-shapes ids #(dissoc % :shadow)))))
|
||||
|
||||
handle-reorder
|
||||
(mf/use-fn
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.title-bar :refer [title-bar]]
|
||||
[app.main.ui.icons :as i]
|
||||
|
@ -66,7 +66,7 @@
|
|||
(fn [attr value]
|
||||
(let [update-fn
|
||||
(fn [shape] (assoc-in shape (concat [:svg-attrs] attr) value))]
|
||||
(st/emit! (dch/update-shapes ids update-fn)))))
|
||||
(st/emit! (dwsh/update-shapes ids update-fn)))))
|
||||
|
||||
handle-delete
|
||||
(mf/use-fn
|
||||
|
@ -81,7 +81,7 @@
|
|||
(empty? (get-in shape [:svg-attrs :style]))
|
||||
(update :svg-attrs dissoc :style))]
|
||||
shape))]
|
||||
(st/emit! (dch/update-shapes ids update-fn)))))]
|
||||
(st/emit! (dwsh/update-shapes ids update-fn)))))]
|
||||
|
||||
(when-not (empty? attrs)
|
||||
[:div {:class (stl/css :element-set)}
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.text :as txt]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.shortcuts :as sc]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
|
@ -127,8 +127,8 @@
|
|||
grow-type (keyword value)]
|
||||
(st/emit!
|
||||
(dwu/start-undo-transaction uid)
|
||||
(dch/update-shapes ids #(assoc % :grow-type grow-type)))
|
||||
;; We asynchronously commit so every sychronous event is resolved first and inside the transaction
|
||||
(dwsh/update-shapes ids #(assoc % :grow-type grow-type)))
|
||||
;; We asynchronously commit so every sychronous event is resolved first and inside the transaction
|
||||
(ts/schedule #(st/emit! (dwu/commit-undo-transaction uid))))
|
||||
(when (some? on-blur) (on-blur))))]
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
[app.common.text :as txt]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.changes :as ch]
|
||||
[app.main.data.changes :as ch]
|
||||
[app.main.data.workspace.groups :as dwg]
|
||||
[app.main.data.workspace.media :as dwm]
|
||||
[app.main.store :as st]
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
[app.common.types.shape.radius :as ctsr]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace :as udw]
|
||||
[app.main.data.workspace.changes :as dwc]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
|
@ -118,25 +117,25 @@
|
|||
:get #(-> % proxy->shape :name)
|
||||
:set (fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :name value)))))}
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :name value)))))}
|
||||
|
||||
{:name "blocked"
|
||||
:get #(-> % proxy->shape :blocked boolean)
|
||||
:set (fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :blocked value)))))}
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :blocked value)))))}
|
||||
|
||||
{:name "hidden"
|
||||
:get #(-> % proxy->shape :hidden boolean)
|
||||
:set (fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :hidden value)))))}
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :hidden value)))))}
|
||||
|
||||
{:name "proportionLock"
|
||||
:get #(-> % proxy->shape :proportion-lock boolean)
|
||||
:set (fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :proportion-lock value)))))}
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :proportion-lock value)))))}
|
||||
|
||||
{:name "constraintsHorizontal"
|
||||
:get #(-> % proxy->shape :constraints-h d/name)
|
||||
|
@ -144,7 +143,7 @@
|
|||
(let [id (obj/get self "$id")
|
||||
value (keyword value)]
|
||||
(when (contains? cts/horizontal-constraint-types value)
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :constraints-h value))))))}
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :constraints-h value))))))}
|
||||
|
||||
{:name "constraintsVertical"
|
||||
:get #(-> % proxy->shape :constraints-v d/name)
|
||||
|
@ -152,7 +151,7 @@
|
|||
(let [id (obj/get self "$id")
|
||||
value (keyword value)]
|
||||
(when (contains? cts/vertical-constraint-types value)
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :constraints-v value))))))}
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :constraints-v value))))))}
|
||||
|
||||
{:name "borderRadius"
|
||||
:get #(-> % proxy->shape :rx)
|
||||
|
@ -161,8 +160,8 @@
|
|||
shape (proxy->shape self)]
|
||||
(when (us/safe-int? value)
|
||||
(when (or (not (ctsr/has-radius? shape)) (ctsr/radius-4? shape))
|
||||
(st/emit! (dwc/update-shapes [id] ctsr/switch-to-radius-1)))
|
||||
(st/emit! (dwc/update-shapes [id] #(ctsr/set-radius-1 % value))))))}
|
||||
(st/emit! (dwsh/update-shapes [id] ctsr/switch-to-radius-1)))
|
||||
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-1 % value))))))}
|
||||
|
||||
{:name "borderRadiusTopLeft"
|
||||
:get #(-> % proxy->shape :r1)
|
||||
|
@ -171,8 +170,8 @@
|
|||
shape (proxy->shape self)]
|
||||
(when (us/safe-int? value)
|
||||
(when (or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape)))
|
||||
(st/emit! (dwc/update-shapes [id] ctsr/switch-to-radius-4)))
|
||||
(st/emit! (dwc/update-shapes [id] #(ctsr/set-radius-4 % :r1 value))))))}
|
||||
(st/emit! (dwsh/update-shapes [id] ctsr/switch-to-radius-4)))
|
||||
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-4 % :r1 value))))))}
|
||||
|
||||
{:name "borderRadiusTopRight"
|
||||
:get #(-> % proxy->shape :r2)
|
||||
|
@ -181,8 +180,8 @@
|
|||
shape (proxy->shape self)]
|
||||
(when (us/safe-int? value)
|
||||
(when (or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape)))
|
||||
(st/emit! (dwc/update-shapes [id] ctsr/switch-to-radius-4)))
|
||||
(st/emit! (dwc/update-shapes [id] #(ctsr/set-radius-4 % :r2 value))))))}
|
||||
(st/emit! (dwsh/update-shapes [id] ctsr/switch-to-radius-4)))
|
||||
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-4 % :r2 value))))))}
|
||||
|
||||
{:name "borderRadiusBottomRight"
|
||||
:get #(-> % proxy->shape :r3)
|
||||
|
@ -191,8 +190,8 @@
|
|||
shape (proxy->shape self)]
|
||||
(when (us/safe-int? value)
|
||||
(when (or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape)))
|
||||
(st/emit! (dwc/update-shapes [id] ctsr/switch-to-radius-4)))
|
||||
(st/emit! (dwc/update-shapes [id] #(ctsr/set-radius-4 % :r3 value))))))}
|
||||
(st/emit! (dwsh/update-shapes [id] ctsr/switch-to-radius-4)))
|
||||
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-4 % :r3 value))))))}
|
||||
|
||||
{:name "borderRadiusBottomLeft"
|
||||
:get #(-> % proxy->shape :r4)
|
||||
|
@ -201,15 +200,15 @@
|
|||
shape (proxy->shape self)]
|
||||
(when (us/safe-int? value)
|
||||
(when (or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape)))
|
||||
(st/emit! (dwc/update-shapes [id] ctsr/switch-to-radius-4)))
|
||||
(st/emit! (dwc/update-shapes [id] #(ctsr/set-radius-4 % :r4 value))))))}
|
||||
(st/emit! (dwsh/update-shapes [id] ctsr/switch-to-radius-4)))
|
||||
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-4 % :r4 value))))))}
|
||||
|
||||
{:name "opacity"
|
||||
:get #(-> % proxy->shape :opacity)
|
||||
:set (fn [self value]
|
||||
(let [id (obj/get self "$id")]
|
||||
(when (and (us/safe-number? value) (>= value 0) (<= value 1))
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :opacity value))))))}
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :opacity value))))))}
|
||||
|
||||
{:name "blendMode"
|
||||
:get #(-> % proxy->shape :blend-mode (d/nilv :normal) d/name)
|
||||
|
@ -217,7 +216,7 @@
|
|||
(let [id (obj/get self "$id")
|
||||
value (keyword value)]
|
||||
(when (contains? cts/blend-modes value)
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :blend-mode value))))))}
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :blend-mode value))))))}
|
||||
|
||||
{:name "shadows"
|
||||
:get #(-> % proxy->shape :shadow array-to-js)
|
||||
|
@ -236,13 +235,13 @@
|
|||
:hidden false}
|
||||
(utils/from-js val #{:style :type})))
|
||||
value)]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :shadow value)))))}
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :shadow value)))))}
|
||||
|
||||
{:name "blur"
|
||||
:get #(-> % proxy->shape :blur utils/to-js)
|
||||
:set (fn [self value]
|
||||
(if (nil? value)
|
||||
(st/emit! (dwc/update-shapes [id] #(dissoc % :blur)))
|
||||
(st/emit! (dwsh/update-shapes [id] #(dissoc % :blur)))
|
||||
(let [id (obj/get self "$id")
|
||||
value
|
||||
(d/patch-object
|
||||
|
@ -251,14 +250,14 @@
|
|||
:value 4
|
||||
:hidden false}
|
||||
(utils/from-js value))]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :blur value))))))}
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :blur value))))))}
|
||||
|
||||
{:name "exports"
|
||||
:get #(-> % proxy->shape :exports array-to-js)
|
||||
:set (fn [self value]
|
||||
(let [id (obj/get self "$id")
|
||||
value (mapv #(utils/from-js %) value)]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :exports value)))))}
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :exports value)))))}
|
||||
|
||||
;; Geometry properties
|
||||
{:name "x"
|
||||
|
@ -354,14 +353,14 @@
|
|||
:set (fn [self value]
|
||||
(let [id (obj/get self "$id")
|
||||
value (mapv #(utils/from-js %) value)]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :fills value)))))}
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :fills value)))))}
|
||||
|
||||
{:name "strokes"
|
||||
:get #(-> % proxy->shape :strokes array-to-js)
|
||||
:set (fn [self value]
|
||||
(let [id (obj/get self "$id")
|
||||
value (mapv #(utils/from-js % #{:stroke-style :stroke-alignment}) value)]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :strokes value)))))}
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :strokes value)))))}
|
||||
|
||||
{:name "layoutChild"
|
||||
:get
|
||||
|
@ -421,7 +420,7 @@
|
|||
:set (fn [self value]
|
||||
(let [id (obj/get self "$id")
|
||||
value (mapv #(utils/from-js %) value)]
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :grids value)))))}
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :grids value)))))}
|
||||
|
||||
{:name "horizontalSizing"
|
||||
:get #(-> % proxy->shape :layout-item-h-sizing (d/nilv :fix) d/name)
|
||||
|
@ -463,7 +462,7 @@
|
|||
ted/import-content
|
||||
ted/create-editor-state)]
|
||||
(st/emit! (dwt/update-editor-state shape editor))))
|
||||
(st/emit! (dwc/update-shapes [id] #(txt/change-text % value)))))}
|
||||
(st/emit! (dwsh/update-shapes [id] #(txt/change-text % value)))))}
|
||||
|
||||
{:name "growType"
|
||||
:get #(-> % proxy->shape :grow-type d/name)
|
||||
|
@ -472,7 +471,7 @@
|
|||
(let [id (obj/get self "$id")
|
||||
value (keyword value)]
|
||||
(when (contains? #{:auto-width :auto-height :fixed} value)
|
||||
(st/emit! (dwc/update-shapes [id] #(assoc % :grow-type value))))))}
|
||||
(st/emit! (dwsh/update-shapes [id] #(assoc % :grow-type value))))))}
|
||||
|
||||
{:name "fontId"
|
||||
:get #(-> % proxy->shape text-props :font-id)
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
"A collection of helpers for work with javascript objects."
|
||||
(:refer-clojure :exclude [set! new get get-in merge clone contains? array? into-array])
|
||||
(:require
|
||||
[cuerdas.core :as str]))
|
||||
[cuerdas.core :as str]
|
||||
;; FIXME: we use goog.string here for performance reasons, pending
|
||||
;; to apply this optimizations directly to cuerdas.
|
||||
[goog.string :as gstr]))
|
||||
|
||||
(defn array?
|
||||
[o]
|
||||
|
@ -105,28 +108,51 @@
|
|||
[obj prop]
|
||||
(js* "~{} in ~{}" prop obj))
|
||||
|
||||
(defn- transform-prop-key
|
||||
[s]
|
||||
(let [result (js* "~{}.replace(\":\", \"-\").replace(/-./g, x=>x[1].toUpperCase())", s)]
|
||||
(if ^boolean (gstr/startsWith s "-")
|
||||
(gstr/capitalize result)
|
||||
result)))
|
||||
|
||||
(defn prop-key-fn
|
||||
[k]
|
||||
(when (string? k)
|
||||
(cond
|
||||
(or (= k "class")
|
||||
(= k "class-name"))
|
||||
"className"
|
||||
|
||||
(gstr/startsWith k "data-")
|
||||
k
|
||||
|
||||
:else
|
||||
(transform-prop-key k))))
|
||||
|
||||
(defn map->obj
|
||||
[x]
|
||||
(cond
|
||||
(nil? x)
|
||||
nil
|
||||
"A simplified version of clj->js with focus on performance"
|
||||
([x] (map->obj x identity))
|
||||
([x ^function key-fn]
|
||||
(cond
|
||||
(nil? x)
|
||||
nil
|
||||
|
||||
(keyword? x)
|
||||
(name x)
|
||||
(keyword? x)
|
||||
(name x)
|
||||
|
||||
(map? x)
|
||||
(reduce-kv (fn [m k v]
|
||||
(let [k (if (keyword? k) (name k) k)]
|
||||
(unchecked-set m k (^function map->obj v))
|
||||
m))
|
||||
#js {}
|
||||
x)
|
||||
(map? x)
|
||||
(reduce-kv (fn [m k v]
|
||||
(let [k (if (keyword? k) (name k) k)]
|
||||
(unchecked-set m (key-fn k) (map->obj v key-fn))
|
||||
m))
|
||||
#js {}
|
||||
x)
|
||||
|
||||
(coll? x)
|
||||
(reduce (fn [arr v]
|
||||
(.push arr v)
|
||||
arr)
|
||||
(array)
|
||||
x)
|
||||
(coll? x)
|
||||
(reduce (fn [arr v]
|
||||
(.push arr v)
|
||||
arr)
|
||||
(array)
|
||||
x)
|
||||
|
||||
:else x))
|
||||
:else x)))
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
[app.common.uri :as u]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.changes :as dwc]
|
||||
[app.main.data.dashboard.shortcuts]
|
||||
[app.main.data.preview :as dp]
|
||||
[app.main.data.viewer.shortcuts]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.path.shortcuts]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.shortcuts]
|
||||
|
@ -52,8 +52,6 @@
|
|||
(def debug-exclude-events
|
||||
#{:app.main.data.workspace.notifications/handle-pointer-update
|
||||
:app.main.data.workspace.notifications/handle-pointer-send
|
||||
:app.main.data.workspace.persistence/update-persistence-status
|
||||
:app.main.data.workspace.changes/update-indices
|
||||
:app.main.data.websocket/send-message
|
||||
:app.main.data.workspace.selection/change-hover-state})
|
||||
|
||||
|
@ -303,7 +301,7 @@
|
|||
|
||||
(let [file-id (:current-file-id @st/state)
|
||||
changes (t/decode-str changes*)]
|
||||
(st/emit! (dch/commit-changes {:redo-changes changes
|
||||
(st/emit! (dwc/commit-changes {:redo-changes changes
|
||||
:undo-changes []
|
||||
:save-undo? true
|
||||
:file-id file-id}))))
|
||||
|
|
|
@ -3,12 +3,13 @@
|
|||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns frontend-tests.basic-shapes-test
|
||||
(:require
|
||||
[app.common.test-helpers.files :as cthf]
|
||||
[app.common.test-helpers.ids-map :as cthi]
|
||||
[app.common.test-helpers.shapes :as cths]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[cljs.test :as t :include-macros true]
|
||||
[frontend-tests.helpers.state :as ths]))
|
||||
|
||||
|
@ -21,12 +22,12 @@
|
|||
(-> (cthf/sample-file :file1 :page-label :page1)
|
||||
(cths/add-sample-shape :shape1)))
|
||||
|
||||
;; ==== Action
|
||||
;; ==== Action
|
||||
events
|
||||
[(dch/update-shapes [(cthi/id :shape1)]
|
||||
#(assoc % :fills
|
||||
(cths/sample-fills-color :fill-color
|
||||
"#fabada")))]]
|
||||
[(dwsh/update-shapes [(cthi/id :shape1)]
|
||||
#(assoc % :fills
|
||||
(cths/sample-fills-color :fill-color
|
||||
"#fabada")))]]
|
||||
|
||||
(ths/run-store
|
||||
store done events
|
||||
|
@ -40,7 +41,7 @@
|
|||
fills' (:fills shape1')
|
||||
fill' (first fills')]
|
||||
|
||||
;; ==== Check
|
||||
;; ==== Check
|
||||
(t/is (some? shape1'))
|
||||
(t/is (= (count fills') 1))
|
||||
(t/is (= (:fill-color fill') "#fabada"))
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
event occurs, and then call the function with the final state at
|
||||
this point."
|
||||
[state done completed-cb]
|
||||
(let [store (ptk/store {:state state :on-error on-error})
|
||||
(let [store (ptk/store {:state state :on-error on-error})
|
||||
stream (ptk/input-stream store)
|
||||
stream (->> stream
|
||||
(rx/take-until (rx/filter #(= :the/end %) stream))
|
||||
|
|
|
@ -38,21 +38,3 @@
|
|||
{:name "Rect 1"}))
|
||||
shape (thp/get-shape state :shape1)]
|
||||
(t/is (= (:name shape) "Rect 1")))))
|
||||
|
||||
(t/deftest asynctest
|
||||
(t/testing "asynctest"
|
||||
(t/async done
|
||||
(let [state {}
|
||||
color {:color clr/white}
|
||||
|
||||
store (the/prepare-store state done
|
||||
(fn [new-state]
|
||||
(t/is (= (get-in new-state [:workspace-data
|
||||
:recent-colors])
|
||||
[color]))))]
|
||||
|
||||
(ptk/emit!
|
||||
store
|
||||
(dwl/add-recent-color color)
|
||||
:the/end)))))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue