Migrate proxies to new format

This commit is contained in:
alonso.torres 2024-11-22 11:05:10 +01:00 committed by Andrey Antukh
parent e16ec9c719
commit eccc4226c7
18 changed files with 4355 additions and 4212 deletions

View file

@ -12,7 +12,6 @@
[app.common.files.changes-builder :as cb] [app.common.files.changes-builder :as cb]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.record :as cr]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.text :as txt] [app.common.text :as txt]
[app.common.types.color :as ctc] [app.common.types.color :as ctc]
@ -59,406 +58,432 @@
(st/emit! (ch/commit-changes changes)) (st/emit! (ch/commit-changes changes))
(shape/shape-proxy plugin-id (:id shape)))) (shape/shape-proxy plugin-id (:id shape))))
(deftype PenpotContext [$plugin] (defn create-context
Object [plugin-id]
(addListener (obj/reify {:name "PenpotContext"}
[_ type callback props] ;; Private properties
(events/add-listener type $plugin callback props)) :$plugin {:enumerable false :get (fn [] plugin-id)}
(removeListener ;; Public properties
[_ listener-id] :root
(events/remove-listener listener-id)) {:this true
:get #(.getRoot ^js %)}
(getViewport :currentFile
[_] {:this true
(viewport/viewport-proxy $plugin)) :get #(.getFile ^js %)}
(getFile :currentPage
[_] {:this true
(when (some? (:current-file-id @st/state)) :get #(.getPage ^js %)}
(file/file-proxy $plugin (:current-file-id @st/state))))
(getPage :theme
[_] {:this true
(let [file-id (:current-file-id @st/state) :get #(.getTheme ^js %)}
page-id (:current-page-id @st/state)]
(when (and (some? file-id) (some? page-id))
(page/page-proxy $plugin file-id page-id))))
(getSelectedShapes :selection
[_] {:this true
(let [selection (get-in @st/state [:workspace-local :selected])] :get #(.getSelectedShapes ^js %)
(apply array (sequence (map (partial shape/shape-proxy $plugin)) selection)))) :set
(fn [_ shapes]
(cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :selection shapes)
(shapesColors :else
[_ shapes] (let [ids (into (d/ordered-set) (map #(obj/get % "$id")) shapes)]
(cond (st/emit! (dws/select-shapes ids)))))}
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :shapesColors-shapes shapes)
:else :viewport
(let [objects (u/locate-objects) {:this true
shapes (->> shapes :get #(.getViewport ^js %)}
(map #(obj/get % "$id"))
(mapcat #(cfh/get-children-with-self objects %)))
file-id (:current-file-id @st/state)
shared-libs (:workspace-libraries @st/state)]
(->> (ctc/extract-all-colors shapes file-id shared-libs) :currentUser
(group-by :attrs) {:this true
(format/format-array format/format-color-result))))) :get #(.getCurrentUser ^js %)}
(replaceColor :activeUsers
[_ shapes old-color new-color] {:this true
:get #(.getActiveUsers ^js %)}
(let [old-color (parser/parse-color old-color) :fonts
new-color (parser/parse-color new-color)] {:get (fn [] (fonts/fonts-subcontext plugin-id))}
:library
{:get (fn [] (library/library-subcontext plugin-id))}
:history
{:get (fn [] (history/history-subcontext plugin-id))}
;; Methods
:addListener
(fn [type callback props]
(events/add-listener type plugin-id callback props))
:removeListener
(fn [listener-id]
(events/remove-listener listener-id))
:getViewport
(fn []
(viewport/viewport-proxy plugin-id))
:getFile
(fn []
(when (some? (:current-file-id @st/state))
(file/file-proxy plugin-id (:current-file-id @st/state))))
:getPage
(fn []
(let [file-id (:current-file-id @st/state)
page-id (:current-page-id @st/state)]
(when (and (some? file-id) (some? page-id))
(page/page-proxy plugin-id file-id page-id))))
:getSelectedShapes
(fn []
(let [selection (get-in @st/state [:workspace-local :selected])]
(apply array (sequence (map (partial shape/shape-proxy plugin-id)) selection))))
:shapesColors
(fn [shapes]
(cond (cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes))) (or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :replaceColor-shapes shapes) (u/display-not-valid :shapesColors-shapes shapes)
(not (sm/validate ::ctc/color old-color))
(u/display-not-valid :replaceColor-oldColor old-color)
(not (sm/validate ::ctc/color new-color))
(u/display-not-valid :replaceColor-newColor new-color)
:else :else
(let [file-id (:current-file-id @st/state) (let [objects (u/locate-objects)
shared-libs (:workspace-libraries @st/state) shapes (->> shapes
objects (u/locate-objects) (map #(obj/get % "$id"))
shapes (mapcat #(cfh/get-children-with-self objects %)))
(->> shapes file-id (:current-file-id @st/state)
(map #(obj/get % "$id")) shared-libs (:workspace-libraries @st/state)]
(mapcat #(cfh/get-children-with-self objects %)))
shapes-by-color (->> (ctc/extract-all-colors shapes file-id shared-libs)
(->> (ctc/extract-all-colors shapes file-id shared-libs) (group-by :attrs)
(group-by :attrs))] (format/format-array format/format-color-result)))))
(st/emit! (dwc/change-color-in-selected new-color (get shapes-by-color old-color) old-color))))))
(getRoot :replaceColor
[_] (fn [shapes old-color new-color]
(when (and (some? (:current-file-id @st/state)) (let [old-color (parser/parse-color old-color)
(some? (:current-page-id @st/state))) new-color (parser/parse-color new-color)]
(shape/shape-proxy $plugin uuid/zero))) (cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :replaceColor-shapes shapes)
(getTheme (not (sm/validate ::ctc/color old-color))
[_] (u/display-not-valid :replaceColor-oldColor old-color)
(let [theme (get-in @st/state [:profile :theme])]
(if (or (not theme) (= theme "default"))
"dark"
(get-in @st/state [:profile :theme]))))
(getCurrentUser (not (sm/validate ::ctc/color new-color))
[_] (u/display-not-valid :replaceColor-newColor new-color)
(user/current-user-proxy $plugin (:session-id @st/state)))
(getActiveUsers :else
[_] (let [file-id (:current-file-id @st/state)
(apply array shared-libs (:workspace-libraries @st/state)
(->> (:workspace-presence @st/state) objects (u/locate-objects)
(vals) shapes
(remove #(= (:id %) (:session-id @st/state))) (->> shapes
(map #(user/active-user-proxy $plugin (:id %)))))) (map #(obj/get % "$id"))
(mapcat #(cfh/get-children-with-self objects %)))
(uploadMediaUrl shapes-by-color
[_ name url] (->> (ctc/extract-all-colors shapes file-id shared-libs)
(cond (group-by :attrs))]
(not (string? name)) (st/emit! (dwc/change-color-in-selected new-color (get shapes-by-color old-color) old-color))))))
(u/display-not-valid :uploadMedia-name name)
(not (string? url)) :getRoot
(u/display-not-valid :uploadMedia-url url) (fn []
(when (and (some? (:current-file-id @st/state))
(some? (:current-page-id @st/state)))
(shape/shape-proxy plugin-id uuid/zero)))
:else :getTheme
(fn []
(let [theme (get-in @st/state [:profile :theme])]
(if (or (not theme) (= theme "default"))
"dark"
(get-in @st/state [:profile :theme]))))
:getCurrentUser
(fn []
(user/current-user-proxy plugin-id (:session-id @st/state)))
:getActiveUsers
(fn []
(apply array
(->> (:workspace-presence @st/state)
(vals)
(remove #(= (:id %) (:session-id @st/state)))
(map #(user/active-user-proxy plugin-id (:id %))))))
:uploadMediaUrl
(fn [name url]
(cond
(not (string? name))
(u/display-not-valid :uploadMedia-name name)
(not (string? url))
(u/display-not-valid :uploadMedia-url url)
:else
(let [file-id (:current-file-id @st/state)]
(js/Promise.
(fn [resolve reject]
(->> (dwm/upload-media-url name file-id url)
(rx/take 1)
(rx/map format/format-image)
(rx/subs! resolve reject)))))))
:uploadMediaData
(fn [name data mime-type]
(let [file-id (:current-file-id @st/state)] (let [file-id (:current-file-id @st/state)]
(js/Promise. (js/Promise.
(fn [resolve reject] (fn [resolve reject]
(->> (dwm/upload-media-url name file-id url) (->> (dwm/process-blobs
{:file-id file-id
:local? false
:name name
:blobs [(js/Blob. #js [data] #js {:type mime-type})]
:on-image identity
:on-svg identity})
(rx/take 1) (rx/take 1)
(rx/map format/format-image) (rx/map format/format-image)
(rx/subs! resolve reject))))))) (rx/subs! resolve reject))))))
(uploadMediaData :group
[_ name data mime-type] (fn [shapes]
(let [file-id (:current-file-id @st/state)] (cond
(js/Promise. (or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(fn [resolve reject] (u/display-not-valid :group-shapes shapes)
(->> (dwm/process-blobs
{:file-id file-id
:local? false
:name name
:blobs [(js/Blob. #js [data] #js {:type mime-type})]
:on-image identity
:on-svg identity})
(rx/take 1)
(rx/map format/format-image)
(rx/subs! resolve reject))))))
(group :else
[_ shapes] (let [file-id (:current-file-id @st/state)
(cond page-id (:current-page-id @st/state)
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes))) id (uuid/next)
(u/display-not-valid :group-shapes shapes) ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dwg/group-shapes id ids))
(shape/shape-proxy plugin-id file-id page-id id))))
:else :ungroup
(let [file-id (:current-file-id @st/state) (fn [group & rest]
page-id (:current-page-id @st/state) (cond
id (uuid/next) (not (shape/shape-proxy? group))
ids (into #{} (map #(obj/get % "$id")) shapes)] (u/display-not-valid :ungroup group)
(st/emit! (dwg/group-shapes id ids))
(shape/shape-proxy $plugin file-id page-id id))))
(ungroup (and (some? rest) (not (every? shape/shape-proxy? rest)))
[_ group & rest] (u/display-not-valid :ungroup rest)
(cond :else
(not (shape/shape-proxy? group)) (let [shapes (concat [group] rest)
(u/display-not-valid :ungroup group) ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dwg/ungroup-shapes ids)))))
(and (some? rest) (not (every? shape/shape-proxy? rest))) :createBoard
(u/display-not-valid :ungroup rest) (fn []
(create-shape plugin-id :frame))
:else :createRectangle
(let [shapes (concat [group] rest) (fn []
ids (into #{} (map #(obj/get % "$id")) shapes)] (create-shape plugin-id :rect))
(st/emit! (dwg/ungroup-shapes ids)))))
(createBoard :createEllipse
[_] (fn []
(create-shape $plugin :frame)) (create-shape plugin-id :circle))
(createRectangle :createPath
[_] (fn []
(create-shape $plugin :rect)) (let [page-id (:current-page-id @st/state)
(createEllipse
[_]
(create-shape $plugin :circle))
(createPath
[_]
(let [page-id (:current-page-id @st/state)
page (dm/get-in @st/state [:workspace-data :pages-index page-id])
shape (cts/setup-shape
{:type :path
:content [{:command :move-to :params {:x 0 :y 0}}
{:command :line-to :params {:x 100 :y 100}}]})
changes
(-> (cb/empty-changes)
(cb/with-page page)
(cb/with-objects (:objects page))
(cb/add-object shape))]
(st/emit! (ch/commit-changes changes))
(shape/shape-proxy $plugin (:id shape))))
(createText
[_ text]
(cond
(or (not (string? text)) (empty? text))
(u/display-not-valid :createText text)
:else
(let [file-id (:current-file-id @st/state)
page-id (:current-page-id @st/state)
page (dm/get-in @st/state [:workspace-data :pages-index page-id]) page (dm/get-in @st/state [:workspace-data :pages-index page-id])
shape (-> (cts/setup-shape {:type :text :x 0 :y 0 :grow-type :auto-width}) shape (cts/setup-shape
(txt/change-text text) {:type :path
(assoc :position-data nil)) :content [{:command :move-to :params {:x 0 :y 0}}
{:command :line-to :params {:x 100 :y 100}}]})
changes changes
(-> (cb/empty-changes) (-> (cb/empty-changes)
(cb/with-page page) (cb/with-page page)
(cb/with-objects (:objects page)) (cb/with-objects (:objects page))
(cb/add-object shape))] (cb/add-object shape))]
(st/emit! (ch/commit-changes changes)) (st/emit! (ch/commit-changes changes))
(shape/shape-proxy $plugin file-id page-id (:id shape))))) (shape/shape-proxy plugin-id (:id shape))))
(createShapeFromSvg :createText
[_ svg-string] (fn [text]
(cond
(or (not (string? svg-string)) (empty? svg-string))
(u/display-not-valid :createShapeFromSvg svg-string)
:else
(let [id (uuid/next)
file-id (:current-file-id @st/state)
page-id (:current-page-id @st/state)]
(st/emit! (dwm/create-svg-shape id "svg" svg-string (gpt/point 0 0)))
(shape/shape-proxy $plugin file-id page-id id))))
(createBoolean [_ bool-type shapes]
(let [bool-type (keyword bool-type)]
(cond (cond
(not (contains? cts/bool-types bool-type)) (or (not (string? text)) (empty? text))
(u/display-not-valid :createBoolean-boolType bool-type) (u/display-not-valid :createText text)
(or (not (array? shapes)) (empty? shapes) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :createBoolean-shapes shapes)
:else :else
(let [ids (into #{} (map #(obj/get % "$id")) shapes) (let [file-id (:current-file-id @st/state)
id-ret (atom nil)] page-id (:current-page-id @st/state)
(st/emit! (dwb/create-bool bool-type ids {:id-ret id-ret})) page (dm/get-in @st/state [:workspace-data :pages-index page-id])
(shape/shape-proxy $plugin @id-ret))))) shape (-> (cts/setup-shape {:type :text :x 0 :y 0 :grow-type :auto-width})
(txt/change-text text)
(assoc :position-data nil))
changes
(-> (cb/empty-changes)
(cb/with-page page)
(cb/with-objects (:objects page))
(cb/add-object shape))]
(st/emit! (ch/commit-changes changes))
(shape/shape-proxy plugin-id file-id page-id (:id shape)))))
(generateMarkup :createShapeFromSvg
[_ shapes options] (fn [svg-string]
(let [type (d/nilv (obj/get options "type") "html")]
(cond (cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes))) (or (not (string? svg-string)) (empty? svg-string))
(u/display-not-valid :generateMarkup-shapes shapes) (u/display-not-valid :createShapeFromSvg svg-string)
(and (some? type) (not (contains? #{"html" "svg"} type)))
(u/display-not-valid :generateMarkup-type type)
:else :else
(let [objects (u/locate-objects) (let [id (uuid/next)
shapes (into [] (map u/proxy->shape) shapes)] file-id (:current-file-id @st/state)
(cg/generate-markup-code objects type shapes))))) page-id (:current-page-id @st/state)]
(st/emit! (dwm/create-svg-shape id "svg" svg-string (gpt/point 0 0)))
(shape/shape-proxy plugin-id file-id page-id id))))
(generateStyle :createBoolean
[_ shapes options] (fn [bool-type shapes]
(let [type (d/nilv (obj/get options "type") "css") (let [bool-type (keyword bool-type)]
prelude? (d/nilv (obj/get options "withPrelude") false) (cond
children? (d/nilv (obj/get options "includeChildren") true)] (not (contains? cts/bool-types bool-type))
(u/display-not-valid :createBoolean-boolType bool-type)
(or (not (array? shapes)) (empty? shapes) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :createBoolean-shapes shapes)
:else
(let [ids (into #{} (map #(obj/get % "$id")) shapes)
id-ret (atom nil)]
(st/emit! (dwb/create-bool bool-type ids {:id-ret id-ret}))
(shape/shape-proxy plugin-id @id-ret)))))
:generateMarkup
(fn [shapes options]
(let [type (d/nilv (obj/get options "type") "html")]
(cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :generateMarkup-shapes shapes)
(and (some? type) (not (contains? #{"html" "svg"} type)))
(u/display-not-valid :generateMarkup-type type)
:else
(let [objects (u/locate-objects)
shapes (into [] (map u/proxy->shape) shapes)]
(cg/generate-markup-code objects type shapes)))))
:generateStyle
(fn [shapes options]
(let [type (d/nilv (obj/get options "type") "css")
prelude? (d/nilv (obj/get options "withPrelude") false)
children? (d/nilv (obj/get options "includeChildren") true)]
(cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :generateStyle-shapes shapes)
(and (some? type) (not (contains? #{"css"} type)))
(u/display-not-valid :generateStyle-type type)
(and (some? prelude?) (not (boolean? prelude?)))
(u/display-not-valid :generateStyle-withPrelude prelude?)
(and (some? children?) (not (boolean? children?)))
(u/display-not-valid :generateStyle-includeChildren children?)
:else
(let [objects (u/locate-objects)
shapes
(->> (into #{} (map u/proxy->shape) shapes)
(cfh/clean-loops objects))
shapes-with-children
(if children?
(->> shapes
(mapcat #(cfh/get-children-with-self objects (:id %))))
shapes)]
(cg/generate-style-code
objects type shapes shapes-with-children {:with-prelude? prelude?})))))
:openViewer
(fn []
(let [params {:page-id (:current-page-id @st/state)
:file-id (:current-file-id @st/state)
:section "interactions"}]
(st/emit! (dw/go-to-viewer params))))
:createPage
(fn []
(let [file-id (:current-file-id @st/state)
id (uuid/next)]
(st/emit! (dw/create-page {:page-id id :file-id file-id}))
(page/page-proxy plugin-id file-id id)))
:openPage
(fn [page]
(let [id (obj/get page "$id")]
(st/emit! (dw/go-to-page id))))
:alignHorizontal
(fn [shapes direction]
(let [dir (case direction
"left" :hleft
"center" :hcenter
"right" :hright
nil)]
(cond
(nil? dir)
(u/display-not-valid :alignHorizontal-direction "Direction not valid")
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :alignHorizontal-shapes "Not valid shapes")
:else
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dw/align-objects dir ids))))))
:alignVertical
(fn [shapes direction]
(let [dir (case direction
"top" :vtop
"center" :vcenter
"bottom" :vbottom
nil)]
(cond
(nil? dir)
(u/display-not-valid :alignVertical-direction "Direction not valid")
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :alignVertical-shapes "Not valid shapes")
:else
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dw/align-objects dir ids))))))
:distributeHorizontal
(fn [shapes]
(cond (cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes))) (or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :generateStyle-shapes shapes) (u/display-not-valid :distributeHorizontal-shapes "Not valid shapes")
(and (some? type) (not (contains? #{"css"} type)))
(u/display-not-valid :generateStyle-type type)
(and (some? prelude?) (not (boolean? prelude?)))
(u/display-not-valid :generateStyle-withPrelude prelude?)
(and (some? children?) (not (boolean? children?)))
(u/display-not-valid :generateStyle-includeChildren children?)
:else
(let [objects (u/locate-objects)
shapes
(->> (into #{} (map u/proxy->shape) shapes)
(cfh/clean-loops objects))
shapes-with-children
(if children?
(->> shapes
(mapcat #(cfh/get-children-with-self objects (:id %))))
shapes)]
(cg/generate-style-code
objects type shapes shapes-with-children {:with-prelude? prelude?})))))
(openViewer
[_]
(let [params {:page-id (:current-page-id @st/state)
:file-id (:current-file-id @st/state)
:section "interactions"}]
(st/emit! (dw/go-to-viewer params))))
(createPage
[_]
(let [file-id (:current-file-id @st/state)
id (uuid/next)]
(st/emit! (dw/create-page {:page-id id :file-id file-id}))
(page/page-proxy $plugin file-id id)))
(openPage
[_ page]
(let [id (obj/get page "$id")]
(st/emit! (dw/go-to-page id))))
(alignHorizontal
[_ shapes direction]
(let [dir (case direction
"left" :hleft
"center" :hcenter
"right" :hright
nil)]
(cond
(nil? dir)
(u/display-not-valid :alignHorizontal-direction "Direction not valid")
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :alignHorizontal-shapes "Not valid shapes")
:else :else
(let [ids (into #{} (map #(obj/get % "$id")) shapes)] (let [ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dw/align-objects dir ids)))))) (st/emit! (dw/distribute-objects :horizontal ids)))))
(alignVertical :distributeVertical
[_ shapes direction] (fn [shapes]
(let [dir (case direction
"top" :vtop
"center" :vcenter
"bottom" :vbottom
nil)]
(cond (cond
(nil? dir)
(u/display-not-valid :alignVertical-direction "Direction not valid")
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes))) (or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :alignVertical-shapes "Not valid shapes") (u/display-not-valid :distributeVertical-shapes "Not valid shapes")
:else :else
(let [ids (into #{} (map #(obj/get % "$id")) shapes)] (let [ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dw/align-objects dir ids)))))) (st/emit! (dw/distribute-objects :vertical ids)))))
(distributeHorizontal :flatten
[_ shapes] (fn [shapes]
(cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :distributeHorizontal-shapes "Not valid shapes")
:else
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dw/distribute-objects :horizontal ids)))))
(distributeVertical
[_ shapes]
(cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :distributeVertical-shapes "Not valid shapes")
:else
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dw/distribute-objects :vertical ids)))))
(flatten
[_ shapes]
(cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :flatten-shapes "Not valid shapes")
:else
(let [ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dw/convert-selected-to-path ids))))))
(defn create-context
[plugin-id]
(cr/add-properties!
(PenpotContext. plugin-id)
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
{:name "root" :get #(.getRoot ^js %)}
{:name "currentFile" :get #(.getFile ^js %)}
{:name "currentPage" :get #(.getPage ^js %)}
{:name "theme" :get #(.getTheme ^js %)}
{:name "selection"
:get #(.getSelectedShapes ^js %)
:set
(fn [_ shapes]
(cond (cond
(or (not (array? shapes)) (not (every? shape/shape-proxy? shapes))) (or (not (array? shapes)) (not (every? shape/shape-proxy? shapes)))
(u/display-not-valid :selection shapes) (u/display-not-valid :flatten-shapes "Not valid shapes")
:else :else
(let [ids (into (d/ordered-set) (map #(obj/get % "$id")) shapes)] (let [ids (into #{} (map #(obj/get % "$id")) shapes)]
(st/emit! (dws/select-shapes ids)))))} (st/emit! (dw/convert-selected-to-path ids)))))))
{:name "viewport" :get #(.getViewport ^js %)}
{:name "currentUser" :get #(.getCurrentUser ^js %)}
{:name "activeUsers" :get #(.getActiveUsers ^js %)}
{:name "fonts" :get (fn [_] (fonts/fonts-subcontext plugin-id))}
{:name "library" :get (fn [_] (library/library-subcontext plugin-id))}
{:name "history" :get (fn [_] (history/history-subcontext plugin-id))}))

View file

@ -7,7 +7,6 @@
(ns app.plugins.comments (ns app.plugins.comments
(:require (:require
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.record :as crc]
[app.common.spec :as us] [app.common.spec :as us]
[app.main.data.comments :as dc] [app.main.data.comments :as dc]
[app.main.data.workspace.comments :as dwc] [app.main.data.workspace.comments :as dwc]
@ -19,159 +18,170 @@
[app.plugins.shape :as shape] [app.plugins.shape :as shape]
[app.plugins.user :as user] [app.plugins.user :as user]
[app.plugins.utils :as u] [app.plugins.utils :as u]
[app.util.object :as obj]
[beicon.v2.core :as rx])) [beicon.v2.core :as rx]))
(deftype CommentProxy [$plugin $file $page $thread $id]
Object
(remove [_]
(js/Promise.
(fn [resolve reject]
(cond
(not (r/check-permission $plugin "comment:write"))
(do
(u/display-not-valid :remove "Plugin doesn't have 'comment:write' permission")
(reject "Plugin doesn't have 'comment:write' permission"))
:else
(->> (rp/cmd! :delete-comment {:id $id})
(rx/tap #(st/emit! (dc/retrieve-comment-threads $file)))
(rx/subs! #(resolve) reject)))))))
(defn comment-proxy? [p] (defn comment-proxy? [p]
(instance? CommentProxy p)) (obj/type-of? p "CommentProxy"))
(defn comment-proxy (defn comment-proxy
[plugin-id file-id page-id thread-id users data] [plugin-id file-id page-id thread-id users data]
(let [data* (atom data)] (let [data* (atom data)]
(crc/add-properties! (obj/reify {:name "CommentProxy"}
(CommentProxy. plugin-id file-id page-id thread-id (:id data)) ;; Private properties
{:name "$plugin" :enumerable false :get (constantly plugin-id)} :$plugin {:enumerable false :get (fn [] plugin-id)}
{:name "$file" :enumerable false :get (constantly file-id)} :$file {:enumerable false :get (fn [] file-id)}
{:name "$page" :enumerable false :get (constantly page-id)} :$page {:enumerable false :get (fn [] page-id)}
{:name "$thread" :enumerable false :get (constantly thread-id)} :$thread {:enumerable false :get (fn [] thread-id)}
{:name "$id" :enumerable false :get (constantly (:id data))} :$id {:enumerable false :get (fn [] (:id data))}
{:name "user" :get (fn [_] (user/user-proxy plugin-id (get users (:owner-id data))))} ;; Public properties
{:name "date" :get (fn [_] (:created-at data))} :user
{:get
(fn [] (user/user-proxy plugin-id (get users (:owner-id data))))}
{:name "content" :date
:get (fn [_] (:content @data*)) {:get
:set (fn [] (:created-at data))}
(fn [_ content]
(let [profile (:profile @st/state)]
(cond
(or (not (string? content)) (empty? content))
(u/display-not-valid :content "Not valid")
(not= (:id profile) (:owner-id data)) :content
(u/display-not-valid :content "Cannot change content from another user's comments") {:get
(fn [] (:content @data*))
(not (r/check-permission plugin-id "comment:write")) :set
(u/display-not-valid :content "Plugin doesn't have 'comment:write' permission") (fn [content]
(let [profile (:profile @st/state)]
(cond
(or (not (string? content)) (empty? content))
(u/display-not-valid :content "Not valid")
:else (not= (:id profile) (:owner-id data))
(->> (rp/cmd! :update-comment {:id (:id data) :content content}) (u/display-not-valid :content "Cannot change content from another user's comments")
(rx/tap #(st/emit! (dc/retrieve-comment-threads file-id)))
(rx/subs! #(swap! data* assoc :content content))))))})))
(deftype CommentThreadProxy [$plugin $file $page $users $id owner] (not (r/check-permission plugin-id "comment:write"))
Object (u/display-not-valid :content "Plugin doesn't have 'comment:write' permission")
(findComments
[_]
(js/Promise.
(fn [resolve reject]
(cond
(not (r/check-permission $plugin "comment:read"))
(do
(u/display-not-valid :findComments "Plugin doesn't have 'comment:read' permission")
(reject "Plugin doesn't have 'comment:read' permission"))
:else :else
(->> (rp/cmd! :get-comments {:thread-id $id}) (->> (rp/cmd! :update-comment {:id (:id data) :content content})
(rx/subs! (rx/tap #(st/emit! (dc/retrieve-comment-threads file-id)))
(fn [comments] (rx/subs! #(swap! data* assoc :content content))))))}
(resolve
(format/format-array
#(comment-proxy $plugin $file $page $id $users %) comments)))
reject))))))
(reply ;; Public methods
[_ content] :remove
(cond (fn []
(not (r/check-permission $plugin "comment:write"))
(u/display-not-valid :reply "Plugin doesn't have 'comment:write' permission")
(or (not (string? content)) (empty? content))
(u/display-not-valid :reply "Not valid")
:else
(js/Promise.
(fn [resolve reject]
(->> (rp/cmd! :create-comment {:thread-id $id :content content})
(rx/subs! #(resolve (comment-proxy $plugin $file $page $id $users %)) reject))))))
(remove [_]
(let [profile (:profile @st/state)]
(cond
(not (r/check-permission $plugin "comment:write"))
(u/display-not-valid :remove "Plugin doesn't have 'comment:write' permission")
(not= (:id profile) owner)
(u/display-not-valid :remove "Cannot change content from another user's comments")
:else
(js/Promise. (js/Promise.
(fn [resolve] (fn [resolve reject]
(js/Promise. (cond
(st/emit! (dc/delete-comment-thread-on-workspace {:id $id} #(resolve)))))))))) (not (r/check-permission plugin-id "comment:write"))
(do
(u/display-not-valid :remove "Plugin doesn't have 'comment:write' permission")
(reject "Plugin doesn't have 'comment:write' permission"))
:else
(->> (rp/cmd! :delete-comment {:id (:id data)})
(rx/tap #(st/emit! (dc/retrieve-comment-threads file-id)))
(rx/subs! #(resolve) reject)))))))))
(defn comment-thread-proxy? [p] (defn comment-thread-proxy? [p]
(instance? CommentThreadProxy p)) (obj/type-of? p "CommentThreadProxy"))
(defn comment-thread-proxy (defn comment-thread-proxy
[plugin-id file-id page-id users data] [plugin-id file-id page-id users data]
(let [data* (atom data)] (let [data* (atom data)]
(crc/add-properties! (obj/reify {:name "CommentThreadProxy"}
(CommentThreadProxy. plugin-id file-id page-id users (:id data) (:owner-id data)) :$plugin {:enumerable false :get (fn [] plugin-id)}
{:name "$plugin" :enumerable false :get (constantly plugin-id)} :$file {:enumerable false :get (fn [] file-id)}
{:name "$file" :enumerable false :get (constantly file-id)} :$page {:enumerable false :get (fn [] page-id)}
{:name "$page" :enumerable false :get (constantly page-id)} :$id {:enumerable false :get (fn [] (:id data))}
{:name "$id" :enumerable false :get (constantly (:id data))} :$users {:enumerable false :get (fn [] users)}
{:name "$users" :enumerable false :get (constantly users)}
{:name "page" :enumerable false :get (fn [_] (u/locate-page file-id page-id))}
{:name "seqNumber" :get (fn [_] (:seqn data))} :page {:enumerable false :get #(u/locate-page file-id page-id)}
{:name "owner" :get (fn [_] (user/user-proxy plugin-id (get users (:owner-id data))))} :seqNumber {:get #(:seqn data)}
{:name "board" :get (fn [_] (shape/shape-proxy plugin-id file-id page-id (:frame-id data)))} :owner {:get #(user/user-proxy plugin-id (get users (:owner-id data)))}
:board {:get #(shape/shape-proxy plugin-id file-id page-id (:frame-id data))}
{:name "position" :position
:get (fn [_] (format/format-point (:position @data*))) {:get
:set (fn []
(fn [_ position] (format/format-point (:position @data*)))
(let [position (parser/parse-point position)]
(cond
(or (not (us/safe-number? (:x position))) (not (us/safe-number? (:y position))))
(u/display-not-valid :position "Not valid point")
(not (r/check-permission plugin-id "comment:write")) :set
(u/display-not-valid :position "Plugin doesn't have 'comment:write' permission") (fn [position]
(let [position (parser/parse-point position)]
(cond
(or (not (us/safe-number? (:x position))) (not (us/safe-number? (:y position))))
(u/display-not-valid :position "Not valid point")
:else (not (r/check-permission plugin-id "comment:write"))
(do (st/emit! (dwc/update-comment-thread-position @data* [(:x position) (:y position)])) (u/display-not-valid :position "Plugin doesn't have 'comment:write' permission")
(swap! data* assoc :position (gpt/point position))))))}
{:name "resolved" :else
:get (fn [_] (:is-resolved @data*)) (do (st/emit! (dwc/update-comment-thread-position @data* [(:x position) (:y position)]))
:set (swap! data* assoc :position (gpt/point position))))))}
(fn [_ is-resolved]
:resolved
{:get
(fn [] (:is-resolved @data*))
:set
(fn [is-resolved]
(cond
(not (boolean? is-resolved))
(u/display-not-valid :resolved "Not a boolean type")
(not (r/check-permission plugin-id "comment:write"))
(u/display-not-valid :resolved "Plugin doesn't have 'comment:write' permission")
:else
(do (st/emit! (dc/update-comment-thread (assoc @data* :is-resolved is-resolved)))
(swap! data* assoc :is-resolved is-resolved))))}
:findComments
(fn []
(js/Promise.
(fn [resolve reject]
(cond
(not (r/check-permission plugin-id "comment:read"))
(do
(u/display-not-valid :findComments "Plugin doesn't have 'comment:read' permission")
(reject "Plugin doesn't have 'comment:read' permission"))
:else
(->> (rp/cmd! :get-comments {:thread-id (:id data)})
(rx/subs!
(fn [comments]
(resolve
(format/format-array
#(comment-proxy plugin-id file-id page-id (:id data) users %) comments)))
reject))))))
:reply
(fn [content]
(cond (cond
(not (boolean? is-resolved))
(u/display-not-valid :resolved "Not a boolean type")
(not (r/check-permission plugin-id "comment:write")) (not (r/check-permission plugin-id "comment:write"))
(u/display-not-valid :resolved "Plugin doesn't have 'comment:write' permission") (u/display-not-valid :reply "Plugin doesn't have 'comment:write' permission")
(or (not (string? content)) (empty? content))
(u/display-not-valid :reply "Not valid")
:else :else
(do (st/emit! (dc/update-comment-thread (assoc @data* :is-resolved is-resolved))) (js/Promise.
(swap! data* assoc :is-resolved is-resolved))))}))) (fn [resolve reject]
(->> (rp/cmd! :create-comment {:thread-id (:id data) :content content})
(rx/subs! #(resolve (comment-proxy plugin-id file-id page-id (:id data) users %)) reject))))))
:remove
(fn []
(let [profile (:profile @st/state)
owner (get users (:owner-id data))]
(cond
(not (r/check-permission plugin-id "comment:write"))
(u/display-not-valid :remove "Plugin doesn't have 'comment:write' permission")
(not= (:id profile) owner)
(u/display-not-valid :remove "Cannot change content from another user's comments")
:else
(js/Promise.
(fn [resolve]
(st/emit! (dc/delete-comment-thread-on-workspace {:id (:id data)} #(resolve)))))))))))

View file

@ -62,15 +62,16 @@
(defmethod handle-state-change "shapechange" (defmethod handle-state-change "shapechange"
[_ plugin-id old-val new-val props] [_ plugin-id old-val new-val props]
(let [shape-id (-> (obj/get props "shapeId") parser/parse-id) (if-let [shape-id (-> (obj/get props "shapeId") parser/parse-id)]
old-shape (wsh/lookup-shape old-val shape-id) (let [old-shape (wsh/lookup-shape old-val shape-id)
new-shape (wsh/lookup-shape new-val shape-id) new-shape (wsh/lookup-shape new-val shape-id)
file-id (:current-file-id new-val) file-id (:current-file-id new-val)
page-id (:current-page-id new-val)] page-id (:current-page-id new-val)]
(if (and (identical? old-shape new-shape) (some? plugin-id) (some? file-id) (some? page-id) (some? shape-id)) (if (and (identical? old-shape new-shape) (some? plugin-id) (some? file-id) (some? page-id) (some? shape-id))
::not-changed ::not-changed
(shape/shape-proxy plugin-id file-id page-id shape-id)))) (shape/shape-proxy plugin-id file-id page-id shape-id)))
::not-changed))
(defmethod handle-state-change "contentsave" (defmethod handle-state-change "contentsave"
[_ _ old-val new-val _] [_ _ old-val new-val _]

View file

@ -8,7 +8,6 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.record :as crc]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.config :as cf] [app.config :as cf]
[app.main.data.exports.files :as exports.files] [app.main.data.exports.files :as exports.files]
@ -18,6 +17,7 @@
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.store :as st] [app.main.store :as st]
[app.main.worker :as uw] [app.main.worker :as uw]
[app.plugins.format :as format]
[app.plugins.page :as page] [app.plugins.page :as page]
[app.plugins.parser :as parser] [app.plugins.parser :as parser]
[app.plugins.register :as r] [app.plugins.register :as r]
@ -28,14 +28,16 @@
[app.util.time :as dt] [app.util.time :as dt]
[beicon.v2.core :as rx])) [beicon.v2.core :as rx]))
(defn file-version-proxy?
[proxy]
(obj/type-of? proxy "FileVersionProxy"))
(defn file-version-proxy (defn file-version-proxy
[plugin-id file-id users data] [plugin-id file-id users data]
(let [data (atom data)] (let [data (atom data)]
(obj/reify {:name "FileVersionProxy"} (obj/reify {:name "FileVersionProxy"}
:$plugin {:get (fn [] plugin-id) :enumerable false} :$plugin {:enumerable false :get (fn [] plugin-id)}
:$file {:get (fn [] file-id) :enumerable false} :$file {:enumerable false :get (fn [] file-id)}
:$version {:get (fn [] (:id @data)) :enumerable false}
:$data {:get (fn [] @data) :enumerable false}
:label :label
{:get #(:label @data) {:get #(:label @data)
@ -111,213 +113,209 @@
(resolve (file-version-proxy plugin-id file-id users @data)))) (resolve (file-version-proxy plugin-id file-id users @data))))
reject)))))))))) reject))))))))))
(deftype FileProxy [$plugin $id]
Object
(getPages [_]
(let [file (u/locate-file $id)]
(apply array (sequence (map #(page/page-proxy $plugin $id %)) (dm/get-in file [:data :pages])))))
;; Plugin data
(getPluginData
[self key]
(cond
(not (string? key))
(u/display-not-valid :getPluginData-key key)
:else
(let [file (u/proxy->file self)]
(dm/get-in file [:data :plugin-data (keyword "plugin" (str $plugin)) key]))))
(setPluginData
[_ key value]
(cond
(or (not (string? key)) (empty? key))
(u/display-not-valid :setPluginData-key key)
(and (some? value) (not (string? value)))
(u/display-not-valid :setPluginData-value value)
(not (r/check-permission $plugin "content:write"))
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
:else
(st/emit! (dw/set-plugin-data $id :file (keyword "plugin" (str $plugin)) key value))))
(getPluginDataKeys
[self]
(let [file (u/proxy->file self)]
(apply array (keys (dm/get-in file [:data :plugin-data (keyword "plugin" (str $plugin))])))))
(getSharedPluginData
[self namespace key]
(cond
(not (string? namespace))
(u/display-not-valid :getSharedPluginData-namespace namespace)
(not (string? key))
(u/display-not-valid :getSharedPluginData-key key)
:else
(let [file (u/proxy->file self)]
(dm/get-in file [:data :plugin-data (keyword "shared" namespace) key]))))
(setSharedPluginData
[_ namespace key value]
(cond
(or (not (string? namespace)) (empty? namespace))
(u/display-not-valid :setSharedPluginData-namespace namespace)
(or (not (string? key)) (empty? key))
(u/display-not-valid :setSharedPluginData-key key)
(and (some? value) (not (string? value)))
(u/display-not-valid :setSharedPluginData-value value)
(not (r/check-permission $plugin "content:write"))
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
:else
(st/emit! (dw/set-plugin-data $id :file (keyword "shared" namespace) key value))))
(getSharedPluginDataKeys
[self namespace]
(cond
(not (string? namespace))
(u/display-not-valid :getSharedPluginDataKeys namespace)
:else
(let [file (u/proxy->file self)]
(apply array (keys (dm/get-in file [:data :plugin-data (keyword "shared" namespace)]))))))
(createPage
[_]
(cond
(not (r/check-permission $plugin "content:write"))
(u/display-not-valid :createPage "Plugin doesn't have 'content:write' permission")
:else
(let [page-id (uuid/next)]
(st/emit! (dw/create-page {:page-id page-id :file-id $id}))
(page/page-proxy $plugin $id page-id))))
(export
[self format type]
(let [type (or (parser/parse-keyword type) :all)]
(cond
(not (contains? #{"penpot" "zip"} format))
(u/display-not-valid :format type)
(not (contains? (set exports.files/valid-types) type))
(u/display-not-valid :type type)
:else
(let [file (u/proxy->file self)
features (features/get-team-enabled-features @st/state)
team-id (:current-team-id @st/state)
format (case format
"penpot" (if (contains? cf/flags :export-file-v3)
:binfile-v3
:binfile-v1)
"zip" :legacy-zip)]
(js/Promise.
(fn [resolve reject]
(->> (uw/ask-many!
{:cmd :export-files
:format format
:type type
:team-id team-id
:features features
:files [file]})
(rx/mapcat
(fn [msg]
(case (:type msg)
:error
(rx/throw (ex-info "cannot export file" {:type :export-file}))
:progress
(rx/empty)
:finish
(http/send! {:method :get
:uri (:uri msg)
:mode :no-cors
:response-type :buffer}))))
(rx/take 1)
(rx/map (fn [data] (js/Uint8Array. data)))
(rx/subs! resolve reject))))))))
(findVersions
[_ criteria]
(let [user (obj/get criteria "createdBy" nil)]
(js/Promise.
(fn [resolve reject]
(cond
(not (r/check-permission $plugin "content:read"))
(u/reject-not-valid reject :findVersions "Plugin doesn't have 'content:read' permission")
(and (not user) (not (user/user-proxy? user)))
(u/reject-not-valid reject :findVersions-user "Created by user is not a valid user object")
:else
(->> (rx/zip (rp/cmd! :get-team-users {:file-id $id})
(rp/cmd! :get-file-snapshots {:file-id $id}))
(rx/take 1)
(rx/subs!
(fn [[users snapshots]]
(let [users (d/index-by :id users)]
(->> snapshots
(filter #(= (dm/str (:profile-id %)) (obj/get user "id")))
(map #(file-version-proxy $plugin $id users %))
(sequence)
(apply array)
(resolve))))
reject)))))))
(saveVersion
[_ label]
(let [users-promise
(js/Promise.
(fn [resolve reject]
(->> (rp/cmd! :get-team-users {:file-id $id})
(rx/subs! resolve reject))))
create-version-promise
(js/Promise.
(fn [resolve reject]
(cond
(not (r/check-permission $plugin "content:write"))
(u/reject-not-valid reject :findVersions "Plugin doesn't have 'content:write' permission")
:else
(st/emit! (dwv/create-version-from-plugins $id label resolve reject)))))]
(-> (js/Promise.all #js [users-promise create-version-promise])
(.then
(fn [[users data]]
(let [users (d/index-by :id users)]
(file-version-proxy $plugin $id users data))))))))
(crc/define-properties!
FileProxy
{:name js/Symbol.toStringTag
:get (fn [] (str "FileProxy"))})
(defn file-proxy? [p] (defn file-proxy? [p]
(instance? FileProxy p)) (obj/type-of? p "FileProxy"))
(defn file-proxy (defn file-proxy
[plugin-id id] [plugin-id id]
(crc/add-properties!
(FileProxy. plugin-id id)
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
{:name "$id" :enumerable false :get (constantly id)}
{:name "id" (obj/reify {:name "FileProxy"}
:get #(dm/str (obj/get % "$id"))} :$plugin {:enumerable false :get (fn [] plugin-id)}
:$id {:enumerable false :get (fn [] id)}
{:name "name" :id
:get #(-> % u/proxy->file :name)} {:get #(format/format-id id)}
{:name "pages" :name
:get #(.getPages ^js %)})) {:get #(-> (u/locate-file id) :name)}
:pages
{:this true
:get #(.getPages ^js %)}
:getPages
(fn []
(let [file (u/locate-file id)]
(apply array (sequence (map #(page/page-proxy plugin-id id %)) (dm/get-in file [:data :pages])))))
;; Plugin data
:getPluginData
(fn [key]
(cond
(not (string? key))
(u/display-not-valid :getPluginData-key key)
:else
(let [file (u/locate-file id)]
(dm/get-in file [:data :plugin-data (keyword "plugin" (str plugin-id)) key]))))
:setPluginData
(fn [key value]
(cond
(or (not (string? key)) (empty? key))
(u/display-not-valid :setPluginData-key key)
(not (string? value))
(u/display-not-valid :setPluginData-value value)
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
:else
(st/emit! (dw/set-plugin-data id :file (keyword "plugin" (str plugin-id)) key value))))
:getPluginDataKeys
(fn []
(let [file (u/locate-file id)]
(apply array (keys (dm/get-in file [:data :plugin-data (keyword "plugin" (dm/str plugin-id))])))))
:getSharedPluginData
(fn [namespace key]
(cond
(not (string? namespace))
(u/display-not-valid :getSharedPluginData-namespace namespace)
(not (string? key))
(u/display-not-valid :getSharedPluginData-key key)
:else
(let [file (u/locate-file id)]
(dm/get-in file [:data :plugin-data (keyword "shared" namespace) key]))))
:setSharedPluginData
(fn [namespace key value]
(cond
(or (not (string? namespace)) (empty? namespace))
(u/display-not-valid :setSharedPluginData-namespace namespace)
(or (not (string? key)) (empty? key))
(u/display-not-valid :setSharedPluginData-key key)
(not (string? value))
(u/display-not-valid :setSharedPluginData-value value)
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
:else
(st/emit! (dw/set-plugin-data id :file (keyword "shared" namespace) key value))))
:getSharedPluginDataKeys
(fn [namespace]
(cond
(not (string? namespace))
(u/display-not-valid :getSharedPluginDataKeys namespace)
:else
(let [file (u/locate-file id)]
(apply array (keys (dm/get-in file [:data :plugin-data (keyword "shared" namespace)]))))))
:createPage
(fn []
(cond
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :createPage "Plugin doesn't have 'content:write' permission")
:else
(let [page-id (uuid/next)]
(st/emit! (dw/create-page {:page-id page-id :file-id id}))
(page/page-proxy plugin-id id page-id))))
:export
(fn [format type]
(js/Promise.
(fn [resolve reject]
(let [type (or (parser/parse-keyword type) :all)]
(cond
(and (some? format) (not (contains? #{"penpot" "zip"} format)))
(u/reject-not-valid reject :format (dm/str "Invalid format: " format))
(not (contains? (set exports.files/valid-types) type))
(u/reject-not-valid reject :format (dm/str "Invalid type: " type))
:else
(let [file (u/locate-file id)
features (features/get-team-enabled-features @st/state)
team-id (:current-team-id @st/state)
format (case format
"zip" :legacy-zip
(if (contains? cf/flags :export-file-v3)
:binfile-v3
:binfile-v1))]
(->> (uw/ask-many!
{:cmd :export-files
:format format
:type type
:team-id team-id
:features features
:files [file]})
(rx/mapcat
(fn [msg]
(.log js/console msg)
(case (:type msg)
:error
(rx/throw (ex-info "cannot export file" {:type :export-file}))
:progress
(rx/empty)
:finish
(http/send! {:method :get
:uri (:uri msg)
:mode :no-cors
:response-type :buffer}))))
(rx/take 1)
(rx/map #(js/Uint8Array. (:body %)))
(rx/subs! resolve reject))))))))
:findVersions
(fn [criteria]
(let [user (obj/get criteria "createdBy" nil)]
(js/Promise.
(fn [resolve reject]
(cond
(not (r/check-permission plugin-id "content:read"))
(u/reject-not-valid reject :findVersions "Plugin doesn't have 'content:read' permission")
(and (some? user) (not (user/user-proxy? user)))
(u/reject-not-valid reject :findVersions-user "Created by user is not a valid user object")
:else
(->> (rx/zip (rp/cmd! :get-team-users {:file-id id})
(rp/cmd! :get-file-snapshots {:file-id id}))
(rx/take 1)
(rx/subs!
(fn [[users snapshots]]
(let [users (d/index-by :id users)]
(->> snapshots
(filter #(or (not (obj/get user "id"))
(= (dm/str (:profile-id %))
(obj/get user "id"))))
(map #(file-version-proxy plugin-id id users %))
(sequence)
(apply array)
(resolve))))
reject)))))))
:saveVersion
(fn [label]
(let [users-promise
(js/Promise.
(fn [resolve reject]
(->> (rp/cmd! :get-team-users {:file-id id})
(rx/subs! resolve reject))))
create-version-promise
(js/Promise.
(fn [resolve reject]
(cond
(not (r/check-permission plugin-id "content:write"))
(u/reject-not-valid reject :findVersions "Plugin doesn't have 'content:write' permission")
:else
(st/emit! (dwv/create-version-from-plugins id label resolve reject)))))]
(-> (js/Promise.all #js [users-promise create-version-promise])
(.then
(fn [[users data]]
(let [users (d/index-by :id users)]
(file-version-proxy plugin-id id users data)))))))))

View file

@ -7,7 +7,6 @@
(ns app.plugins.flex (ns app.plugins.flex
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.record :as crc]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.shape-layout :as dwsl]
@ -21,491 +20,485 @@
;; Define in `app.plugins.shape` we do this way to prevent circular dependency ;; Define in `app.plugins.shape` we do this way to prevent circular dependency
(def shape-proxy? nil) (def shape-proxy? nil)
(deftype FlexLayout [$plugin $file $page $id]
Object
(remove
[_]
(st/emit! (dwsl/remove-layout #{$id})))
(appendChild
[_ child]
(cond
(not (shape-proxy? child))
(u/display-not-valid :appendChild child)
:else
(let [child-id (obj/get child "$id")]
(st/emit! (dwt/move-shapes-to-frame #{child-id} $id nil nil)
(ptk/data-event :layout/update {:ids [$id]}))))))
(defn flex-layout-proxy? [p] (defn flex-layout-proxy? [p]
(instance? FlexLayout p)) (obj/type-of? p "FlexLayoutProxy"))
(defn flex-layout-proxy (defn flex-layout-proxy
[plugin-id file-id page-id id] [plugin-id file-id page-id id]
(-> (FlexLayout. plugin-id file-id page-id id) (obj/reify {:name "FlexLayoutProxy"}
(crc/add-properties! :$plugin {:enumerable false :get (fn [] plugin-id)}
{:name "$plugin" :enumerable false :get (constantly plugin-id)} :$id {:enumerable false :get (fn [] id)}
{:name "$id" :enumerable false :get (constantly id)} :$file {:enumerable false :get (fn [] file-id)}
{:name "$file" :enumerable false :get (constantly file-id)} :$page {:enumerable false :get (fn [] page-id)}
{:name "$page" :enumerable false :get (constantly page-id)}
{:name "dir" :dir
:get #(-> % u/proxy->shape :layout-flex-dir d/name) {:this true
:set :get #(-> % u/proxy->shape :layout-flex-dir d/name)
(fn [self value] :set
(let [value (keyword value)] (fn [_ value]
(cond (let [value (keyword value)]
(not (contains? ctl/flex-direction-types value)) (cond
(u/display-not-valid :dir value) (not (contains? ctl/flex-direction-types value))
(u/display-not-valid :dir value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :dir "Plugin doesn't have 'content:write' permission") (u/display-not-valid :dir "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-flex-dir value})))))}
(st/emit! (dwsl/update-layout #{id} {:layout-flex-dir value}))))))}
{:name "wrap" :wrap
:get #(-> % u/proxy->shape :layout-wrap-type d/name) {:this true
:set :get #(-> % u/proxy->shape :layout-wrap-type d/name)
(fn [self value] :set
(let [value (keyword value)] (fn [_ value]
(cond (let [value (keyword value)]
(not (contains? ctl/wrap-types value)) (cond
(u/display-not-valid :wrap value) (not (contains? ctl/wrap-types value))
(u/display-not-valid :wrap value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :wrap "Plugin doesn't have 'content:write' permission") (u/display-not-valid :wrap "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-wrap-type value})))))}
(st/emit! (dwsl/update-layout #{id} {:layout-wrap-type value}))))))}
{:name "alignItems" :alignItems
:get #(-> % u/proxy->shape :layout-align-items d/name) {:this true
:set :get #(-> % u/proxy->shape :layout-align-items d/name)
(fn [self value] :set
(let [value (keyword value)] (fn [_ value]
(cond (let [value (keyword value)]
(not (contains? ctl/align-items-types value)) (cond
(u/display-not-valid :alignItems value) (not (contains? ctl/align-items-types value))
(u/display-not-valid :alignItems value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :alignItems "Plugin doesn't have 'content:write' permission") (u/display-not-valid :alignItems "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-align-items value})))))}
(st/emit! (dwsl/update-layout #{id} {:layout-align-items value}))))))}
{:name "alignContent" :alignContent
:get #(-> % u/proxy->shape :layout-align-content d/name) {:this true
:set :get #(-> % u/proxy->shape :layout-align-content d/name)
(fn [self value] :set
(let [value (keyword value)] (fn [_ value]
(cond (let [value (keyword value)]
(not (contains? ctl/align-content-types value)) (cond
(u/display-not-valid :alignContent value) (not (contains? ctl/align-content-types value))
(u/display-not-valid :alignContent value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :alignContent "Plugin doesn't have 'content:write' permission") (u/display-not-valid :alignContent "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-align-content value})))))}
(st/emit! (dwsl/update-layout #{id} {:layout-align-content value}))))))}
{:name "justifyItems" :justifyItems
:get #(-> % u/proxy->shape :layout-justify-items d/name) {:this true
:set :get #(-> % u/proxy->shape :layout-justify-items d/name)
(fn [self value] :set
(let [value (keyword value)] (fn [_ value]
(cond (let [value (keyword value)]
(not (contains? ctl/justify-items-types value)) (cond
(u/display-not-valid :justifyItems value) (not (contains? ctl/justify-items-types value))
(u/display-not-valid :justifyItems value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :justifyItems "Plugin doesn't have 'content:write' permission") (u/display-not-valid :justifyItems "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-justify-items value})))))}
(st/emit! (dwsl/update-layout #{id} {:layout-justify-items value}))))))}
{:name "justifyContent" :justifyContent
:get #(-> % u/proxy->shape :layout-justify-content d/name) {:this true
:set :get #(-> % u/proxy->shape :layout-justify-content d/name)
(fn [self value] :set
(let [value (keyword value)] (fn [_ value]
(cond (let [value (keyword value)]
(not (contains? ctl/justify-content-types value)) (cond
(u/display-not-valid :justifyContent value) (not (contains? ctl/justify-content-types value))
(u/display-not-valid :justifyContent value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :justifyContent "Plugin doesn't have 'content:write' permission") (u/display-not-valid :justifyContent "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-justify-content value})))))}
(st/emit! (dwsl/update-layout #{id} {:layout-justify-content value}))))))}
{:name "rowGap" :rowGap
:get #(-> % u/proxy->shape :layout-gap :row-gap (d/nilv 0)) {:this true
:set :get #(-> % u/proxy->shape :layout-gap :row-gap (d/nilv 0))
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-int? value)) (cond
(u/display-not-valid :rowGap value) (not (us/safe-int? value))
(u/display-not-valid :rowGap value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :rowGap "Plugin doesn't have 'content:write' permission") (u/display-not-valid :rowGap "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-gap {:row-gap value}}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:row-gap value}})))))}
{:name "columnGap" :columnGap
:get #(-> % u/proxy->shape :layout-gap :column-gap (d/nilv 0)) {:this true
:set :get #(-> % u/proxy->shape :layout-gap :column-gap (d/nilv 0))
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-int? value)) (cond
(u/display-not-valid :columnGap value) (not (us/safe-int? value))
(u/display-not-valid :columnGap value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :columnGap "Plugin doesn't have 'content:write' permission") (u/display-not-valid :columnGap "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-gap {:column-gap value}}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-gap {:column-gap value}})))))}
{:name "verticalPadding" :verticalPadding
:get #(-> % u/proxy->shape :layout-padding :p1 (d/nilv 0)) {:this true
:set :get #(-> % u/proxy->shape :layout-padding :p1 (d/nilv 0))
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-int? value)) (cond
(u/display-not-valid :verticalPadding value) (not (us/safe-int? value))
(u/display-not-valid :verticalPadding value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :verticalPadding "Plugin doesn't have 'content:write' permission") (u/display-not-valid :verticalPadding "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value :p3 value}}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value :p3 value}})))))}
{:name "horizontalPadding" :horizontalPadding
:get #(-> % u/proxy->shape :layout-padding :p2 (d/nilv 0)) {:this true
:set :get #(-> % u/proxy->shape :layout-padding :p2 (d/nilv 0))
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-int? value)) (cond
(u/display-not-valid :horizontalPadding value) (not (us/safe-int? value))
(u/display-not-valid :horizontalPadding value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :horizontalPadding "Plugin doesn't have 'content:write' permission") (u/display-not-valid :horizontalPadding "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value :p4 value}})))))}
{:name "topPadding" :topPadding
:get #(-> % u/proxy->shape :layout-padding :p1 (d/nilv 0)) {:this true
:set :get #(-> % u/proxy->shape :layout-padding :p1 (d/nilv 0))
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-int? value)) (cond
(u/display-not-valid :topPadding value) (not (us/safe-int? value))
(u/display-not-valid :topPadding value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :topPadding "Plugin doesn't have 'content:write' permission") (u/display-not-valid :topPadding "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value}}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p1 value}})))))}
{:name "rightPadding" :rightPadding
:get #(-> % u/proxy->shape :layout-padding :p2 (d/nilv 0)) {:this true
:set :get #(-> % u/proxy->shape :layout-padding :p2 (d/nilv 0))
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-int? value)) (cond
(u/display-not-valid :rightPadding value) (not (us/safe-int? value))
(u/display-not-valid :rightPadding value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :rightPadding "Plugin doesn't have 'content:write' permission") (u/display-not-valid :rightPadding "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value}}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p2 value}})))))}
{:name "bottomPadding" :bottomPadding
:get #(-> % u/proxy->shape :layout-padding :p3 (d/nilv 0)) {:this true
:set :get #(-> % u/proxy->shape :layout-padding :p3 (d/nilv 0))
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-int? value)) (cond
(u/display-not-valid :bottomPadding value) (not (us/safe-int? value))
(u/display-not-valid :bottomPadding value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :bottomPadding "Plugin doesn't have 'content:write' permission") (u/display-not-valid :bottomPadding "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p3 value}}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p3 value}})))))}
{:name "leftPadding" :leftPadding
:get #(-> % u/proxy->shape :layout-padding :p4 (d/nilv 0)) {:this true
:set :get #(-> % u/proxy->shape :layout-padding :p4 (d/nilv 0))
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-int? value)) (cond
(u/display-not-valid :leftPadding value) (not (us/safe-int? value))
(u/display-not-valid :leftPadding value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :leftPadding "Plugin doesn't have 'content:write' permission") (u/display-not-valid :leftPadding "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-padding {:p4 value}}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-padding {:p4 value}})))))})))
:remove
(fn []
(st/emit! (dwsl/remove-layout #{id})))
:appendChild
(fn [child]
(cond
(not (shape-proxy? child))
(u/display-not-valid :appendChild child)
:else
(let [child-id (obj/get child "$id")]
(st/emit! (dwt/move-shapes-to-frame #{child-id} id nil nil)
(ptk/data-event :layout/update {:ids [id]})))))))
(deftype LayoutChildProxy [$plugin $file $page $id])
(defn layout-child-proxy? [p] (defn layout-child-proxy? [p]
(instance? LayoutChildProxy p)) (obj/type-of? p "LayoutChildProxy"))
(defn layout-child-proxy (defn layout-child-proxy
[plugin-id file-id page-id id] [plugin-id file-id page-id id]
(-> (LayoutChildProxy. plugin-id file-id page-id id) (obj/reify {:name "LayoutChildProxy"}
(crc/add-properties! :$plugin {:enumerable false :get (fn [] plugin-id)}
{:name "$plugin" :enumerable false :get (constantly plugin-id)} :$id {:enumerable false :get (fn [] id)}
{:name "$id" :enumerable false :get (constantly id)} :$file {:enumerable false :get (fn [] file-id)}
{:name "$file" :enumerable false :get (constantly file-id)} :$page {:enumerable false :get (fn [] page-id)}
{:name "$page" :enumerable false :get (constantly page-id)}
{:name "absolute" :absolute
:get #(-> % u/proxy->shape :layout-item-absolute boolean) {:this true
:set :get #(-> % u/proxy->shape :layout-item-absolute boolean)
(fn [self value] :set
(cond (fn [_ value]
(not (boolean? value)) (cond
(u/display-not-valid :absolute value) (not (boolean? value))
(u/display-not-valid :absolute value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :absolute "Plugin doesn't have 'content:write' permission") (u/display-not-valid :absolute "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout #{id} {:layout-item-absolute value}))))}
(st/emit! (dwsl/update-layout #{id} {:layout-item-absolute value})))))}
{:name "zIndex" :zIndex
:get #(-> % u/proxy->shape :layout-item-z-index (d/nilv 0)) {:this true
:set :get #(-> % u/proxy->shape :layout-item-z-index (d/nilv 0))
(fn [self value] :set
(cond (fn [_ value]
(us/safe-int? value) (cond
(u/display-not-valid :zIndex value) (us/safe-int? value)
(u/display-not-valid :zIndex value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :zIndex "Plugin doesn't have 'content:write' permission") (u/display-not-valid :zIndex "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-z-index value}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-z-index value})))))}
{:name "horizontalSizing" :horizontalSizing
:get #(-> % u/proxy->shape :layout-item-h-sizing (d/nilv :fix) d/name) {:this true
:set :get #(-> % u/proxy->shape :layout-item-h-sizing (d/nilv :fix) d/name)
(fn [self value] :set
(let [value (keyword value)] (fn [_ value]
(cond (let [value (keyword value)]
(not (contains? ctl/item-h-sizing-types value)) (cond
(u/display-not-valid :horizontalPadding value) (not (contains? ctl/item-h-sizing-types value))
(u/display-not-valid :horizontalPadding value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :horizontalPadding "Plugin doesn't have 'content:write' permission") (u/display-not-valid :horizontalPadding "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-h-sizing value})))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-h-sizing value}))))))}
{:name "verticalSizing" :verticalSizing
:get #(-> % u/proxy->shape :layout-item-v-sizing (d/nilv :fix) d/name) {:this true
:set :get #(-> % u/proxy->shape :layout-item-v-sizing (d/nilv :fix) d/name)
(fn [self value] :set
(let [value (keyword value)] (fn [_ value]
(cond (let [value (keyword value)]
(not (contains? ctl/item-v-sizing-types value)) (cond
(u/display-not-valid :verticalSizing value) (not (contains? ctl/item-v-sizing-types value))
(u/display-not-valid :verticalSizing value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :verticalSizing "Plugin doesn't have 'content:write' permission") (u/display-not-valid :verticalSizing "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-v-sizing value})))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-v-sizing value}))))))}
{:name "alignSelf" :alignSelf
:get #(-> % u/proxy->shape :layout-item-align-self (d/nilv :auto) d/name) {:this true
:set :get #(-> % u/proxy->shape :layout-item-align-self (d/nilv :auto) d/name)
(fn [self value] :set
(let [value (keyword value)] (fn [_ value]
(cond (let [value (keyword value)]
(not (contains? ctl/item-align-self-types value)) (cond
(u/display-not-valid :alignSelf value) (not (contains? ctl/item-align-self-types value))
(u/display-not-valid :alignSelf value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :alignSelf "Plugin doesn't have 'content:write' permission") (u/display-not-valid :alignSelf "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-align-self value})))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-align-self value}))))))}
{:name "verticalMargin" :verticalMargin
:get #(-> % u/proxy->shape :layout-item-margin :m1 (d/nilv 0)) {:this true
:set :get #(-> % u/proxy->shape :layout-item-margin :m1 (d/nilv 0))
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-number? value)) (cond
(u/display-not-valid :verticalMargin value) (not (us/safe-number? value))
(u/display-not-valid :verticalMargin value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :verticalMargin "Plugin doesn't have 'content:write' permission") (u/display-not-valid :verticalMargin "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value :m3 value}}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value :m3 value}})))))}
{:name "horizontalMargin" :horizontalMargin
:get #(-> % u/proxy->shape :layout-item-margin :m2 (d/nilv 0)) {:this true
:set :get #(-> % u/proxy->shape :layout-item-margin :m2 (d/nilv 0))
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-number? value)) (cond
(u/display-not-valid :horizontalMargin value) (not (us/safe-number? value))
(u/display-not-valid :horizontalMargin value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :horizontalMargin "Plugin doesn't have 'content:write' permission") (u/display-not-valid :horizontalMargin "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value :m4 value}}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value :m4 value}})))))}
{:name "topMargin" :topMargin
:get #(-> % u/proxy->shape :layout-item-margin :m1 (d/nilv 0)) {:this true
:set :get #(-> % u/proxy->shape :layout-item-margin :m1 (d/nilv 0))
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-number? value)) (cond
(u/display-not-valid :topMargin value) (not (us/safe-number? value))
(u/display-not-valid :topMargin value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :topMargin "Plugin doesn't have 'content:write' permission") (u/display-not-valid :topMargin "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value}}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m1 value}})))))}
{:name "rightMargin" :rightMargin
:get #(-> % u/proxy->shape :layout-item-margin :m2 (d/nilv 0)) {:this true
:set :get #(-> % u/proxy->shape :layout-item-margin :m2 (d/nilv 0))
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-number? value)) (cond
(u/display-not-valid :rightMargin value) (not (us/safe-number? value))
(u/display-not-valid :rightMargin value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :rightMargin "Plugin doesn't have 'content:write' permission") (u/display-not-valid :rightMargin "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value}}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m2 value}})))))}
{:name "bottomMargin" :bottomMargin
:get #(-> % u/proxy->shape :layout-item-margin :m3 (d/nilv 0)) {:this true
:set :get #(-> % u/proxy->shape :layout-item-margin :m3 (d/nilv 0))
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-number? value)) (cond
(u/display-not-valid :bottomMargin value) (not (us/safe-number? value))
(u/display-not-valid :bottomMargin value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :bottomMargin "Plugin doesn't have 'content:write' permission") (u/display-not-valid :bottomMargin "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m3 value}}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m3 value}})))))}
{:name "leftMargin" :leftMargin
:get #(-> % u/proxy->shape :layout-item-margin :m4 (d/nilv 0)) {:this true
:set :get #(-> % u/proxy->shape :layout-item-margin :m4 (d/nilv 0))
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-number? value)) (cond
(u/display-not-valid :leftMargin value) (not (us/safe-number? value))
(u/display-not-valid :leftMargin value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :leftMargin "Plugin doesn't have 'content:write' permission") (u/display-not-valid :leftMargin "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m4 value}}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-margin {:m4 value}})))))}
{:name "maxWidth" :maxWidth
:get #(-> % u/proxy->shape :layout-item-max-w) {:this true
:set :get #(-> % u/proxy->shape :layout-item-max-w)
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-number? value)) (cond
(u/display-not-valid :maxWidth value) (not (us/safe-number? value))
(u/display-not-valid :maxWidth value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :maxWidth "Plugin doesn't have 'content:write' permission") (u/display-not-valid :maxWidth "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-w value}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-w value})))))}
{:name "minWidth" :minWidth
:get #(-> % u/proxy->shape :layout-item-min-w) {:this true
:set :get #(-> % u/proxy->shape :layout-item-min-w)
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-number? value)) (cond
(u/display-not-valid :minWidth value) (not (us/safe-number? value))
(u/display-not-valid :minWidth value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :minWidth "Plugin doesn't have 'content:write' permission") (u/display-not-valid :minWidth "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-w value}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-w value})))))}
{:name "maxHeight" :maxHeight
:get #(-> % u/proxy->shape :layout-item-max-h) {:this true
:set :get #(-> % u/proxy->shape :layout-item-max-h)
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-number? value)) (cond
(u/display-not-valid :maxHeight value) (not (us/safe-number? value))
(u/display-not-valid :maxHeight value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :maxHeight "Plugin doesn't have 'content:write' permission") (u/display-not-valid :maxHeight "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-h value}))))}
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-max-h value})))))}
{:name "minHeight" :minHeight
:get #(-> % u/proxy->shape :layout-item-min-h) {:this true
:set :get #(-> % u/proxy->shape :layout-item-min-h)
(fn [self value] :set
(cond (fn [_ value]
(not (us/safe-number? value)) (cond
(u/display-not-valid :minHeight value) (not (us/safe-number? value))
(u/display-not-valid :minHeight value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :minHeight "Plugin doesn't have 'content:write' permission") (u/display-not-valid :minHeight "Plugin doesn't have 'content:write' permission")
:else :else
(let [id (obj/get self "$id")] (st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-h value}))))}))
(st/emit! (dwsl/update-layout-child #{id} {:layout-item-min-h value})))))})))

View file

@ -7,10 +7,10 @@
(ns app.plugins.fonts (ns app.plugins.fonts
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.record :as cr]
[app.main.data.workspace.texts :as dwt] [app.main.data.workspace.texts :as dwt]
[app.main.fonts :as fonts] [app.main.fonts :as fonts]
[app.main.store :as st] [app.main.store :as st]
[app.plugins.format :as format]
[app.plugins.register :as r] [app.plugins.register :as r]
[app.plugins.shape :as shape] [app.plugins.shape :as shape]
[app.plugins.text :as text] [app.plugins.text :as text]
@ -18,117 +18,133 @@
[app.util.object :as obj] [app.util.object :as obj]
[cuerdas.core :as str])) [cuerdas.core :as str]))
(deftype PenpotFontVariant [name fontVariantId fontWeight fontStyle]) (defn font-variant-proxy? [p]
(obj/type-of? p "FontVariantProxy"))
(defn variant-proxy? [p] (defn font-variant-proxy [name id weight style]
(instance? PenpotFontVariant p)) (obj/reify {:name "FontVariantProxy"}
:name {:get (fn [] name)}
(deftype PenpotFont [name fontId fontFamily fontStyle fontVariantId fontWeight variants] :fontVariantId {:get (fn [] id)}
Object :fontWeight {:get (fn [] weight)}
:fontStyle {:get (fn [] style)}))
(applyToText [_ text variant]
(cond
(not (shape/shape-proxy? text))
(u/display-not-valid :applyToText text)
(not (r/check-permission (obj/get text "$plugin") "content:write"))
(u/display-not-valid :applyToText "Plugin doesn't have 'content:write' permission")
:else
(let [id (obj/get text "$id")
values {:font-id fontId
:font-family fontFamily
:font-style (d/nilv (obj/get variant "fontStyle") fontStyle)
:font-variant-id (d/nilv (obj/get variant "fontVariantId") fontVariantId)
:font-weight (d/nilv (obj/get variant "fontWeight") fontWeight)}]
(st/emit! (dwt/update-attrs id values)))))
(applyToRange [_ range variant]
(cond
(not (text/text-range? range))
(u/display-not-valid :applyToRange range)
(not (r/check-permission (obj/get range "$plugin") "content:write"))
(u/display-not-valid :applyToRange "Plugin doesn't have 'content:write' permission")
:else
(let [id (obj/get range "$id")
start (obj/get range "start")
end (obj/get range "end")
values {:font-id fontId
:font-family fontFamily
:font-style (d/nilv (obj/get variant "fontStyle") fontStyle)
:font-variant-id (d/nilv (obj/get variant "fontVariantId") fontVariantId)
:font-weight (d/nilv (obj/get variant "fontWeight") fontWeight)}]
(st/emit! (dwt/update-text-range id start end values))))))
(defn font-proxy? [p] (defn font-proxy? [p]
(instance? PenpotFont p)) (obj/type-of? p "FontProxy"))
(defn font-proxy (defn font-proxy
[{:keys [id family name variants] :as font}] [{:keys [id family name variants] :as font}]
(when (some? font) (when (some? font)
(let [default-variant (fonts/get-default-variant font)] (let [default-variant (fonts/get-default-variant font)]
(PenpotFont. (obj/reify {:name "FontProxy"}
name :name {:get (fn [] name)}
id :fontId {:get (fn [] id)}
family :fontFamily {:get (fn [] family)}
(:style default-variant) :fontStyle {:get (fn [] (:style default-variant))}
(:id default-variant) :fontVariantId {:get (fn [] (:id default-variant))}
(:weight default-variant) :fontWeight {:get (fn [] (:weight default-variant))}
(apply
array
(->> variants
(map (fn [{:keys [id name style weight]}]
(PenpotFontVariant. name id weight style)))))))))
(deftype PenpotFontsSubcontext [$plugin] :variants
Object {:get
(findById (fn []
[_ id] (format/format-array
(cond (fn [{:keys [id name style weight]}]
(not (string? id)) (font-variant-proxy name id weight style))
(u/display-not-valid :findbyId id) variants))}
:else :applyToText
(font-proxy (d/seek #(str/includes? (str/lower (:id %)) (str/lower id)) (vals @fonts/fontsdb))))) (fn [text variant]
(cond
(not (shape/shape-proxy? text))
(u/display-not-valid :applyToText text)
(findByName (not (r/check-permission (obj/get text "$plugin") "content:write"))
[_ name] (u/display-not-valid :applyToText "Plugin doesn't have 'content:write' permission")
(cond
(not (string? name))
(u/display-not-valid :findByName name)
:else :else
(font-proxy (d/seek #(str/includes? (str/lower (:name %)) (str/lower name)) (vals @fonts/fontsdb))))) (let [id (obj/get text "$id")
values {:font-id id
:font-family family
:font-style (d/nilv (obj/get variant "fontStyle") (:style default-variant))
:font-variant-id (d/nilv (obj/get variant "fontVariantId") (:id default-variant))
:font-weight (d/nilv (obj/get variant "fontWeight") (:wegith default-variant))}]
(st/emit! (dwt/update-attrs id values)))))
(findAllById :applyToRange
[_ id] (fn [range variant]
(cond (cond
(not (string? id)) (not (text/text-range-proxy? range))
(u/display-not-valid :findAllById name) (u/display-not-valid :applyToRange range)
:else (not (r/check-permission (obj/get range "$plugin") "content:write"))
(apply array (->> (vals @fonts/fontsdb) (u/display-not-valid :applyToRange "Plugin doesn't have 'content:write' permission")
(filter #(str/includes? (str/lower (:id %)) (str/lower id)))
(map font-proxy)))))
(findAllByName :else
[_ name] (let [id (obj/get range "$id")
(cond start (obj/get range "start")
(not (string? name)) end (obj/get range "end")
(u/display-not-valid :findAllByName name) values {:font-id id
:font-family family
:else :font-style (d/nilv (obj/get variant "fontStyle") (:style default-variant))
(apply array (->> (vals @fonts/fontsdb) :font-variant-id (d/nilv (obj/get variant "fontVariantId") (:id default-variant))
(filter #(str/includes? (str/lower (:name %)) (str/lower name))) :font-weight (d/nilv (obj/get variant "fontWeight") (:weight default-variant))}]
(map font-proxy)))))) (st/emit! (dwt/update-text-range id start end values)))))))))
(defn fonts-subcontext (defn fonts-subcontext
[plugin-id] [plugin-id]
(cr/add-properties! (obj/reify {:name "PenpotFontsSubcontext"}
(PenpotFontsSubcontext. plugin-id) :$plugin {:name "" :enumerable false :get (constantly plugin-id)}
{:name "$plugin" :enumerable false :get (constantly plugin-id)}
{:name "all" :get :all
(fn [_] {:get
(apply array (->> @fonts/fontsdb vals (map font-proxy))))})) (fn []
(format/format-array
font-proxy
(vals @fonts/fontsdb)))}
:findById
(fn [id]
(cond
(not (string? id))
(u/display-not-valid :findbyId id)
:else
(->> (vals @fonts/fontsdb)
(d/seek #(str/includes? (str/lower (:id %)) (str/lower id)))
(font-proxy))))
:findByName
(fn [name]
(cond
(not (string? name))
(u/display-not-valid :findByName name)
:else
(->> (vals @fonts/fontsdb)
(d/seek #(str/includes? (str/lower (:name %)) (str/lower name)))
(font-proxy))))
:findAllById
(fn [id]
(cond
(not (string? id))
(u/display-not-valid :findAllById name)
:else
(format/format-array
(fn [font]
(when (str/includes? (str/lower (:id font)) (str/lower id))
(font-proxy font)))
(vals @fonts/fontsdb))))
:findAllByName
(fn [name]
(cond
(not (string? name))
(u/display-not-valid :findAllByName name)
:else
(format/format-array
(fn [font]
(when (str/includes? (str/lower (:name font)) (str/lower name))
(font-proxy font)))
(vals @fonts/fontsdb))))))

File diff suppressed because it is too large Load diff

View file

@ -6,47 +6,41 @@
(ns app.plugins.history (ns app.plugins.history
(:require (:require
[app.common.record :as crc]
[app.main.data.workspace.undo :as dwu] [app.main.data.workspace.undo :as dwu]
[app.main.store :as st] [app.main.store :as st]
[app.plugins.register :as r] [app.plugins.register :as r]
[app.plugins.utils :as u])) [app.plugins.utils :as u]
[app.util.object :as obj]))
(deftype HistorySubcontext [$plugin]
Object
(undoBlockBegin
[_]
(cond
(not (r/check-permission $plugin "content:write"))
(u/display-not-valid :resize "Plugin doesn't have 'content:write' permission")
:else
(let [id (js/Symbol)]
(st/emit! (dwu/start-undo-transaction id))
id)))
(undoBlockFinish
[_ block-id]
(cond
(not (r/check-permission $plugin "content:write"))
(u/display-not-valid :resize "Plugin doesn't have 'content:write' permission")
(not block-id)
(u/display-not-valid :undoBlockFinish block-id)
:else
(st/emit! (dwu/commit-undo-transaction block-id)))))
(crc/define-properties!
HistorySubcontext
{:name js/Symbol.toStringTag
:get (fn [] (str "HistorySubcontext"))})
(defn history-subcontext? [p] (defn history-subcontext? [p]
(instance? HistorySubcontext p)) (obj/type-of? p "HistorySubcontext"))
(defn history-subcontext (defn history-subcontext
[plugin-id] [plugin-id]
(HistorySubcontext. plugin-id)) (obj/reify {:name "HistorySubcontext"}
:$plugin {:enumerable false :get (fn [] plugin-id)}
:undoBlockBegin
(fn []
(cond
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :resize "Plugin doesn't have 'content:write' permission")
:else
(let [id (js/Symbol)]
(st/emit! (dwu/start-undo-transaction id))
id)))
:undoBlockFinish
(fn [block-id]
(cond
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :resize "Plugin doesn't have 'content:write' permission")
(not block-id)
(u/display-not-valid :undoBlockFinish block-id)
:else
(st/emit! (dwu/commit-undo-transaction block-id))))))

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,6 @@
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.record :as crc]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.comments :as dc] [app.main.data.comments :as dc]
@ -31,390 +30,394 @@
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[cuerdas.core :as str])) [cuerdas.core :as str]))
(deftype FlowProxy [$plugin $file $page $id] (declare page-proxy)
Object
(remove [_]
(st/emit! (dwi/remove-flow $page $id))))
(defn flow-proxy? [p] (defn flow-proxy? [p]
(instance? FlowProxy p)) (obj/type-of? p "FlowProxy"))
(defn flow-proxy (defn flow-proxy
[plugin-id file-id page-id id] [plugin-id file-id page-id id]
(crc/add-properties! (obj/reify {:name "FlowProxy"}
(FlowProxy. plugin-id file-id page-id id) :$plugin {:enumerable false :get (fn [] plugin-id)}
{:name "$plugin" :enumerable false :get (constantly plugin-id)} :$file {:enumerable false :get (fn [] file-id)}
{:name "$file" :enumerable false :get (constantly file-id)} :$page {:enumerable false :get (fn [] page-id)}
{:name "$page" :enumerable false :get (constantly page-id)} :$id {:enumerable false :get (fn [] id)}
{:name "$id" :enumerable false :get (constantly id)}
{:name "page" :enumerable false :get (fn [_] (u/locate-page file-id page-id))}
{:name "name" :page
:get #(-> % u/proxy->flow :name) {:enumerable false
:set :get
(fn [_ value] (fn []
(cond (page-proxy plugin-id file-id page-id))}
(or (not (string? value)) (empty? value))
(u/display-not-valid :name value)
:else :name
(st/emit! (dwi/update-flow page-id id #(assoc % :name value)))))} {:this true
:get #(-> % u/proxy->flow :name)
:set
(fn [_ value]
(cond
(or (not (string? value)) (empty? value))
(u/display-not-valid :name value)
{:name "startingBoard" :else
:get (st/emit! (dwi/update-flow page-id id #(assoc % :name value)))))}
(fn [self]
(let [frame (-> self u/proxy->flow :starting-frame)]
(u/locate-shape file-id page-id frame)))
:set
(fn [_ value]
(cond
(not (shape/shape-proxy? value))
(u/display-not-valid :startingBoard value)
:else :startingBoard
(st/emit! (dwi/update-flow page-id id #(assoc % :starting-frame (obj/get value "$id"))))))})) {:this true
:get
(fn [self]
(when-let [frame (-> self u/proxy->flow :starting-frame)]
(shape/shape-proxy file-id page-id frame)))
:set
(fn [_ value]
(cond
(not (shape/shape-proxy? value))
(u/display-not-valid :startingBoard value)
(deftype PageProxy [$plugin $file $id] :else
Object (st/emit! (dwi/update-flow page-id id #(assoc % :starting-frame (obj/get value "$id"))))))}
(getShapeById
[_ shape-id]
(cond
(not (string? shape-id))
(u/display-not-valid :getShapeById shape-id)
:else :remove
(let [shape-id (uuid/uuid shape-id) (fn []
shape (u/locate-shape $file $id shape-id)] (st/emit! (dwi/remove-flow page-id id)))))
(when (some? shape)
(shape/shape-proxy $plugin $file $id shape-id)))))
(getRoot (defn page-proxy? [proxy]
[_] (obj/type-of? proxy "PageProxy"))
(shape/shape-proxy $plugin $file $id uuid/zero))
(findShapes
[_ criteria]
;; Returns a lazy (iterable) of all available shapes
(let [criteria (parser/parse-criteria criteria)
match-criteria?
(if (some? criteria)
(fn [[_ shape]]
(and
(or (not (:name criteria))
(= (str/lower (:name criteria)) (str/lower (:name shape))))
(or (not (:name-like criteria))
(str/includes? (str/lower (:name shape)) (str/lower (:name-like criteria))))
(or (not (:type criteria))
(= (:type criteria) (:type shape)))))
identity)]
(when (and (some? $file) (some? $id))
(let [page (u/locate-page $file $id)
xf (comp
(filter match-criteria?)
(map #(shape/shape-proxy $plugin $file $id (first %))))]
(apply array (sequence xf (:objects page)))))))
;; Plugin data
(getPluginData
[self key]
(cond
(not (string? key))
(u/display-not-valid :page-plugin-data-key key)
:else
(let [page (u/proxy->page self)]
(dm/get-in page [:plugin-data (keyword "plugin" (str $plugin)) key]))))
(setPluginData
[_ key value]
(cond
(not (string? key))
(u/display-not-valid :setPluginData-key key)
(and (some? value) (not (string? value)))
(u/display-not-valid :setPluginData-value value)
(not (r/check-permission $plugin "content:write"))
(u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
:else
(st/emit! (dw/set-plugin-data $file :page $id (keyword "plugin" (str $plugin)) key value))))
(getPluginDataKeys
[self]
(let [page (u/proxy->page self)]
(apply array (keys (dm/get-in page [:plugin-data (keyword "plugin" (str $plugin))])))))
(getSharedPluginData
[self namespace key]
(cond
(not (string? namespace))
(u/display-not-valid :page-plugin-data-namespace namespace)
(not (string? key))
(u/display-not-valid :page-plugin-data-key key)
:else
(let [page (u/proxy->page self)]
(dm/get-in page [:plugin-data (keyword "shared" namespace) key]))))
(setSharedPluginData
[_ namespace key value]
(cond
(not (string? namespace))
(u/display-not-valid :setSharedPluginData-namespace namespace)
(not (string? key))
(u/display-not-valid :setSharedPluginData-key key)
(and (some? value) (not (string? value)))
(u/display-not-valid :setSharedPluginData-value value)
(not (r/check-permission $plugin "content:write"))
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
:else
(st/emit! (dw/set-plugin-data $file :page $id (keyword "shared" namespace) key value))))
(getSharedPluginDataKeys
[self namespace]
(cond
(not (string? namespace))
(u/display-not-valid :page-plugin-data-namespace namespace)
:else
(let [page (u/proxy->page self)]
(apply array (keys (dm/get-in page [:plugin-data (keyword "shared" namespace)]))))))
(openPage
[_]
(cond
(not (r/check-permission $plugin "content:read"))
(u/display-not-valid :openPage "Plugin doesn't have 'content:read' permission")
:else
(st/emit! (dw/go-to-page $id))))
(createFlow
[_ name frame]
(cond
(or (not (string? name)) (empty? name))
(u/display-not-valid :createFlow-name name)
(not (shape/shape-proxy? frame))
(u/display-not-valid :createFlow-frame frame)
:else
(let [flow-id (uuid/next)]
(st/emit! (dwi/add-flow flow-id $id name (obj/get frame "$id")))
(flow-proxy $plugin $file $id flow-id))))
(removeFlow
[_ flow]
(cond
(not (flow-proxy? flow))
(u/display-not-valid :removeFlow-flow flow)
:else
(st/emit! (dwi/remove-flow $id (obj/get flow "$id")))))
(addRulerGuide
[_ orientation value board]
(let [shape (u/proxy->shape board)]
(cond
(not (us/safe-number? value))
(u/display-not-valid :addRulerGuide "Value not a safe number")
(not (contains? #{"vertical" "horizontal"} orientation))
(u/display-not-valid :addRulerGuide "Orientation should be either 'vertical' or 'horizontal'")
(and (some? shape)
(or (not (shape/shape-proxy? board))
(not (cfh/frame-shape? shape))))
(u/display-not-valid :addRulerGuide "The shape is not a board")
(not (r/check-permission $plugin "content:write"))
(u/display-not-valid :addRulerGuide "Plugin doesn't have 'content:write' permission")
:else
(let [id (uuid/next)]
(st/emit!
(dwgu/update-guides
(d/without-nils
{:id id
:axis (parser/orientation->axis orientation)
:position value
:frame-id (when board (obj/get board "$id"))})))
(rg/ruler-guide-proxy $plugin $file $id id)))))
(removeRulerGuide
[_ value]
(cond
(not (rg/ruler-guide-proxy? value))
(u/display-not-valid :removeRulerGuide "Guide not provided")
(not (r/check-permission $plugin "content:write"))
(u/display-not-valid :removeRulerGuide "Plugin doesn't have 'comment:write' permission")
:else
(let [guide (u/proxy->ruler-guide value)]
(st/emit! (dwgu/remove-guide guide)))))
(addCommentThread
[_ content position board]
(let [shape (when board (u/proxy->shape board))
position (parser/parse-point position)]
(cond
(or (not (string? content)) (empty? content))
(u/display-not-valid :addCommentThread "Content not valid")
(or (not (us/safe-number? (:x position)))
(not (us/safe-number? (:y position))))
(u/display-not-valid :addCommentThread "Position not valid")
(and (some? board) (or (not (shape/shape-proxy? board)) (not (cfh/frame-shape? shape))))
(u/display-not-valid :addCommentThread "Board not valid")
(not (r/check-permission $plugin "comment:write"))
(u/display-not-valid :addCommentThread "Plugin doesn't have 'comment:write' permission")
:else
(let [position
(cond-> position
(some? board)
(-> (update :x - (:x board))
(update :y - (:y board))))]
(js/Promise.
(fn [resolve]
(st/emit!
(dc/create-thread-on-workspace
{:file-id $file
:page-id $id
:position (gpt/point position)
:content content}
(fn [data]
(->> (rp/cmd! :get-team-users {:file-id $file})
(rx/subs!
(fn [users]
(let [users (d/index-by :id users)]
(resolve (pc/comment-thread-proxy $plugin $file $id users data)))))))
false))))))))
(removeCommentThread
[_ thread]
(cond
(not (pc/comment-thread-proxy? thread))
(u/display-not-valid :removeCommentThread "Comment thread not valid")
(not (r/check-permission $plugin "comment:write"))
(u/display-not-valid :removeCommentThread "Plugin doesn't have 'content:write' permission")
:else
(js/Promise.
(fn [resolve]
(let [thread-id (obj/get thread "$id")]
(js/Promise.
(st/emit! (dc/delete-comment-thread-on-workspace {:id thread-id} #(resolve)))))))))
(findCommentThreads
[_ criteria]
(let [only-yours (boolean (obj/get criteria "onlyYours" false))
show-resolved (boolean (obj/get criteria "showResolved" true))
user-id (-> @st/state :profile :id)]
(js/Promise.
(fn [resolve reject]
(cond
(not (r/check-permission $plugin "comment:read"))
(do
(u/display-not-valid :findCommentThreads "Plugin doesn't have 'comment:read' permission")
(reject "Plugin doesn't have 'comment:read' permission"))
:else
(->> (rx/zip (rp/cmd! :get-team-users {:file-id $file})
(rp/cmd! :get-comment-threads {:file-id $file}))
(rx/take 1)
(rx/subs!
(fn [[users comments]]
(let [users (d/index-by :id users)
comments
(cond->> comments
(not show-resolved)
(filter (comp not :is-resolved))
only-yours
(filter #(contains? (:participants %) user-id)))]
(resolve
(format/format-array
#(pc/comment-thread-proxy $plugin $file $id users %) comments))))
reject))))))))
(crc/define-properties!
PageProxy
{:name js/Symbol.toStringTag
:get (fn [] (str "PageProxy"))})
(defn page-proxy? [p]
(instance? PageProxy p))
(defn page-proxy (defn page-proxy
[plugin-id file-id id] [plugin-id file-id id]
(crc/add-properties! (obj/reify {:name "PageProxy"}
(PageProxy. plugin-id file-id id) :$plugin {:enumerable false :get (fn [] plugin-id)}
{:name "$plugin" :enumerable false :get (constantly plugin-id)} :$file {:enumerable false :get (fn [] file-id)}
{:name "$id" :enumerable false :get (constantly id)} :$id {:enumerable false :get (fn [] id)}
{:name "$file" :enumerable false :get (constantly file-id)}
{:name "id" :id
:get #(dm/str (obj/get % "$id"))} {:get #(dm/str id)}
{:name "name" :name
:get #(-> % u/proxy->page :name) {:this true
:set :get #(-> % u/proxy->page :name)
(fn [_ value] :set
(fn [_ value]
(cond
(not (string? value))
(u/display-not-valid :name value)
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :name "Plugin doesn't have 'content:write' permission")
:else
(st/emit! (dw/rename-page id value))))}
:getRoot
(fn []
(shape/shape-proxy plugin-id file-id id uuid/zero))
:root
{:this true
:enumerable false
:get #(.getRoot ^js %)}
:background
{:this true
:get #(or (-> % u/proxy->page :background) cc/canvas)
:set
(fn [_ value]
(cond
(or (not (string? value)) (not (cc/valid-hex-color? value)))
(u/display-not-valid :background value)
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :background "Plugin doesn't have 'content:write' permission")
:else
(st/emit! (dw/change-canvas-color id {:color value}))))}
:flows
{:this true
:get
(fn [self]
(let [flows (d/nilv (-> (u/proxy->page self) :flows) [])]
(->> (vals flows)
(format/format-array #(flow-proxy plugin-id file-id id (:id %))))))}
:rulerGuides
{:this true
:get
(fn [self]
(let [guides (-> (u/proxy->page self) :guides)]
(->> guides
(vals)
(filter #(nil? (:frame-id %)))
(format/format-array #(rg/ruler-guide-proxy plugin-id file-id id (:id %))))))}
:getShapeById
(fn [shape-id]
(cond (cond
(not (string? value)) (not (string? shape-id))
(u/display-not-valid :name value) (u/display-not-valid :getShapeById shape-id)
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :name "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dw/rename-page id value))))} (let [shape-id (uuid/uuid shape-id)
shape (u/locate-shape file-id id shape-id)]
(when (some? shape)
(shape/shape-proxy plugin-id file-id id shape-id)))))
{:name "root" :findShapes
:enumerable false (fn [criteria]
:get #(.getRoot ^js %)} ;; Returns a lazy (iterable) of all available shapes
(let [criteria (parser/parse-criteria criteria)
match-criteria?
(if (some? criteria)
(fn [[_ shape]]
(and
(or (not (:name criteria))
(= (str/lower (:name criteria)) (str/lower (:name shape))))
{:name "background" (or (not (:name-like criteria))
:enumerable false (str/includes? (str/lower (:name shape)) (str/lower (:name-like criteria))))
:get #(or (-> % u/proxy->page :background) cc/canvas)
:set (or (not (:type criteria))
(fn [_ value] (= (:type criteria) (:type shape)))))
identity)]
(when (and (some? file-id) (some? id))
(let [page (u/locate-page file-id id)
xf (comp
(filter match-criteria?)
(map #(shape/shape-proxy plugin-id file-id id (first %))))]
(apply array (sequence xf (:objects page)))))))
;; Plugin data
:getPluginData
(fn [key]
(cond (cond
(or (not (string? value)) (not (cc/valid-hex-color? value))) (not (string? key))
(u/display-not-valid :background value) (u/display-not-valid :page-plugin-data-key key)
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :background "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dw/change-canvas-color id {:color value}))))} (let [page (u/locate-page file-id id)]
(dm/get-in page [:plugin-data (keyword "plugin" (str plugin-id)) key]))))
{:name "flows" :setPluginData
:get (fn [key value]
(fn [self] (cond
(let [flows (d/nilv (-> (u/proxy->page self) :flows) [])] (not (string? key))
(format/format-array #(flow-proxy plugin-id file-id id (:id %)) flows)))} (u/display-not-valid :setPluginData-key key)
{:name "rulerGuides" (and (some? value) (not (string? value)))
:get (u/display-not-valid :setPluginData-value value)
(fn [self]
(let [guides (-> (u/proxy->page self) :guides)] (not (r/check-permission plugin-id "content:write"))
(->> guides (u/display-not-valid :setPluginData "Plugin doesn't have 'content:write' permission")
(vals)
(filter #(nil? (:frame-id %))) :else
(format/format-array #(rg/ruler-guide-proxy plugin-id file-id id (:id %))))))})) (st/emit! (dw/set-plugin-data file-id :page id (keyword "plugin" (str plugin-id)) key value))))
:getPluginDataKeys
(fn []
(let [page (u/locate-page file-id id)]
(apply array (keys (dm/get-in page [:plugin-data (keyword "plugin" (str plugin-id))])))))
:getSharedPluginData
(fn [namespace key]
(cond
(not (string? namespace))
(u/display-not-valid :page-plugin-data-namespace namespace)
(not (string? key))
(u/display-not-valid :page-plugin-data-key key)
:else
(let [page (u/locate-page file-id id)]
(dm/get-in page [:plugin-data (keyword "shared" namespace) key]))))
:setSharedPluginData
(fn [namespace key value]
(cond
(not (string? namespace))
(u/display-not-valid :setSharedPluginData-namespace namespace)
(not (string? key))
(u/display-not-valid :setSharedPluginData-key key)
(and (some? value) (not (string? value)))
(u/display-not-valid :setSharedPluginData-value value)
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :setSharedPluginData "Plugin doesn't have 'content:write' permission")
:else
(st/emit! (dw/set-plugin-data file-id :page id (keyword "shared" namespace) key value))))
:getSharedPluginDataKeys
(fn [self namespace]
(cond
(not (string? namespace))
(u/display-not-valid :page-plugin-data-namespace namespace)
:else
(let [page (u/proxy->page self)]
(apply array (keys (dm/get-in page [:plugin-data (keyword "shared" namespace)]))))))
:openPage
(fn []
(cond
(not (r/check-permission plugin-id "content:read"))
(u/display-not-valid :openPage "Plugin doesn't have 'content:read' permission")
:else
(st/emit! (dw/go-to-page id))))
:createFlow
(fn [name frame]
(cond
(or (not (string? name)) (empty? name))
(u/display-not-valid :createFlow-name name)
(not (shape/shape-proxy? frame))
(u/display-not-valid :createFlow-frame frame)
:else
(let [flow-id (uuid/next)]
(st/emit! (dwi/add-flow flow-id id name (obj/get frame "$id")))
(flow-proxy plugin-id file-id id flow-id))))
:removeFlow
(fn [flow]
(cond
(not (flow-proxy? flow))
(u/display-not-valid :removeFlow-flow flow)
:else
(st/emit! (dwi/remove-flow id (obj/get flow "$id")))))
:addRulerGuide
(fn [orientation value board]
(let [shape (u/proxy->shape board)]
(cond
(not (us/safe-number? value))
(u/display-not-valid :addRulerGuide "Value not a safe number")
(not (contains? #{"vertical" "horizontal"} orientation))
(u/display-not-valid :addRulerGuide "Orientation should be either 'vertical' or 'horizontal'")
(and (some? shape)
(or (not (shape/shape-proxy? board))
(not (cfh/frame-shape? shape))))
(u/display-not-valid :addRulerGuide "The shape is not a board")
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :addRulerGuide "Plugin doesn't have 'content:write' permission")
:else
(let [ruler-id (uuid/next)]
(st/emit!
(dwgu/update-guides
(d/without-nils
{:id ruler-id
:axis (parser/orientation->axis orientation)
:position value
:frame-id (when board (obj/get board "$id"))})))
(rg/ruler-guide-proxy plugin-id file-id id ruler-id)))))
:removeRulerGuide
(fn [value]
(cond
(not (rg/ruler-guide-proxy? value))
(u/display-not-valid :removeRulerGuide "Guide not provided")
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :removeRulerGuide "Plugin doesn't have 'comment:write' permission")
:else
(let [guide (u/proxy->ruler-guide value)]
(st/emit! (dwgu/remove-guide guide)))))
:addCommentThread
(fn [content position board]
(let [shape (when board (u/proxy->shape board))
position (parser/parse-point position)]
(cond
(or (not (string? content)) (empty? content))
(u/display-not-valid :addCommentThread "Content not valid")
(or (not (us/safe-number? (:x position)))
(not (us/safe-number? (:y position))))
(u/display-not-valid :addCommentThread "Position not valid")
(and (some? board) (or (not (shape/shape-proxy? board)) (not (cfh/frame-shape? shape))))
(u/display-not-valid :addCommentThread "Board not valid")
(not (r/check-permission plugin-id "comment:write"))
(u/display-not-valid :addCommentThread "Plugin doesn't have 'comment:write' permission")
:else
(let [position
(cond-> position
(some? board)
(-> (update :x - (:x board))
(update :y - (:y board))))]
(js/Promise.
(fn [resolve]
(st/emit!
(dc/create-thread-on-workspace
{:file-id file-id
:page-id id
:position (gpt/point position)
:content content}
(fn [data]
(->> (rp/cmd! :get-team-users {:file-id file-id})
(rx/subs!
(fn [users]
(let [users (d/index-by :id users)]
(resolve (pc/comment-thread-proxy plugin-id file-id id users data)))))))
false))))))))
:removeCommentThread
(fn [thread]
(cond
(not (pc/comment-thread-proxy? thread))
(u/display-not-valid :removeCommentThread "Comment thread not valid")
(not (r/check-permission plugin-id "comment:write"))
(u/display-not-valid :removeCommentThread "Plugin doesn't have 'content:write' permission")
:else
(js/Promise.
(fn [resolve]
(let [thread-id (obj/get thread "$id")]
(js/Promise.
(st/emit! (dc/delete-comment-thread-on-workspace {:id thread-id} #(resolve)))))))))
:findCommentThreads
(fn [criteria]
(let [only-yours (boolean (obj/get criteria "onlyYours" false))
show-resolved (boolean (obj/get criteria "showResolved" true))
user-id (-> @st/state :profile :id)]
(js/Promise.
(fn [resolve reject]
(cond
(not (r/check-permission plugin-id "comment:read"))
(do
(u/display-not-valid :findCommentThreads "Plugin doesn't have 'comment:read' permission")
(reject "Plugin doesn't have 'comment:read' permission"))
:else
(->> (rx/zip (rp/cmd! :get-team-users {:file-id file-id})
(rp/cmd! :get-comment-threads {:file-id file-id}))
(rx/take 1)
(rx/subs!
(fn [[users comments]]
(let [users (d/index-by :id users)
comments
(cond->> comments
(not show-resolved)
(filter (comp not :is-resolved))
only-yours
(filter #(contains? (:participants %) user-id)))]
(resolve
(format/format-array
#(pc/comment-thread-proxy plugin-id file-id id users %) comments))))
reject)))))))))

View file

@ -45,7 +45,7 @@
(conj "content:read") (conj "content:read")
(contains? permissions "library:write") (contains? permissions "library:write")
(conj "content:write") (conj "library:read")
(contains? permissions "comment:write") (contains? permissions "comment:write")
(conj "comment:read")) (conj "comment:read"))

View file

@ -8,7 +8,6 @@
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.record :as crc]
[app.common.spec :as us] [app.common.spec :as us]
[app.main.data.workspace.guides :as dwgu] [app.main.data.workspace.guides :as dwgu]
[app.main.store :as st] [app.main.store :as st]
@ -20,80 +19,82 @@
(def shape-proxy identity) (def shape-proxy identity)
(def shape-proxy? identity) (def shape-proxy? identity)
(deftype RulerGuideProxy [$plugin $file $page $id]
Object
(remove [self]
(let [guide (u/proxy->ruler-guide self)]
(st/emit! (dwgu/remove-guide guide)))))
(defn ruler-guide-proxy? [p] (defn ruler-guide-proxy? [p]
(instance? RulerGuideProxy p)) (obj/type-of? p "RulerGuideProxy"))
(defn ruler-guide-proxy (defn ruler-guide-proxy
[plugin-id file-id page-id id] [plugin-id file-id page-id id]
(crc/add-properties! (obj/reify {:name "RuleGuideProxy"}
(RulerGuideProxy. plugin-id file-id page-id id) :$plugin {:enumerable false :get (constantly plugin-id)}
{:name "$plugin" :enumerable false :get (constantly plugin-id)} :$file {:enumerable false :get (constantly file-id)}
{:name "$file" :enumerable false :get (constantly file-id)} :$page {:enumerable false :get (constantly page-id)}
{:name "$page" :enumerable false :get (constantly page-id)} :$id {:enumerable false :get (constantly id)}
{:name "$id" :enumerable false :get (constantly id)}
{:name "board" :enumerable false :board
:get {:this true
(fn [self] :enumerable false
(let [board-id (-> self u/proxy->ruler-guide :frame-id)] :get
(when board-id (fn [self]
(shape-proxy plugin-id file-id page-id board-id)))) (let [board-id (-> self u/proxy->ruler-guide :frame-id)]
(when board-id
(shape-proxy plugin-id file-id page-id board-id))))
:set :set
(fn [self value] (fn [self value]
(let [shape (u/locate-shape file-id page-id (obj/get value "$id"))] (let [shape (u/locate-shape file-id page-id (obj/get value "$id"))]
(cond (cond
(not (shape-proxy? value)) (not (shape-proxy? value))
(u/display-not-valid :board "The board is not a shape proxy") (u/display-not-valid :board "The board is not a shape proxy")
(not (cfh/frame-shape? shape)) (not (cfh/frame-shape? shape))
(u/display-not-valid :board "The shape is not a board") (u/display-not-valid :board "The shape is not a board")
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :board "Plugin doesn't have 'content:write' permission") (u/display-not-valid :board "Plugin doesn't have 'content:write' permission")
:else :else
(let [board-id (when value (obj/get value "$id")) (let [board-id (when value (obj/get value "$id"))
guide (-> self u/proxy->ruler-guide)] guide (-> self u/proxy->ruler-guide)]
(st/emit! (dwgu/update-guides (assoc guide :frame-id board-id)))))))} (st/emit! (dwgu/update-guides (assoc guide :frame-id board-id)))))))}
{:name "orientation" :orientation
:get #(-> % u/proxy->ruler-guide :axis format/axis->orientation)} {:this true
:get #(-> % u/proxy->ruler-guide :axis format/axis->orientation)}
{:name "position" :position
:get {:this true
(fn [self] :get
(let [guide (u/proxy->ruler-guide self)] (fn [self]
(if (:frame-id guide) (let [guide (u/proxy->ruler-guide self)]
(let [objects (u/locate-objects file-id page-id) (if (:frame-id guide)
board-pos (dm/get-in objects [(:frame-id guide) (:axis guide)]) (let [objects (u/locate-objects file-id page-id)
position (:position guide)] board-pos (dm/get-in objects [(:frame-id guide) (:axis guide)])
(- position board-pos)) position (:position guide)]
(- position board-pos))
;; No frame ;; No frame
(:position guide)))) (:position guide))))
:set :set
(fn [self value] (fn [self value]
(cond (cond
(not (us/safe-number? value)) (not (us/safe-number? value))
(u/display-not-valid :position "Not valid position") (u/display-not-valid :position "Not valid position")
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :position "Plugin doesn't have 'content:write' permission") (u/display-not-valid :position "Plugin doesn't have 'content:write' permission")
:else :else
(let [guide (u/proxy->ruler-guide self) (let [guide (u/proxy->ruler-guide self)
position position
(if (:frame-id guide) (if (:frame-id guide)
(let [objects (u/locate-objects file-id page-id) (let [objects (u/locate-objects file-id page-id)
board-pos (dm/get-in objects [(:frame-id guide) (:axis guide)])] board-pos (dm/get-in objects [(:frame-id guide) (:axis guide)])]
(+ board-pos value)) (+ board-pos value))
value)] value)]
(st/emit! (dwgu/update-guides (assoc guide :position position))))))})) (st/emit! (dwgu/update-guides (assoc guide :position position))))))}
:remove
(fn []
(let [guide (u/locate-ruler-guide file-id page-id id)]
(st/emit! (dwgu/remove-guide guide))))))

File diff suppressed because it is too large Load diff

View file

@ -27,21 +27,16 @@
;; This regex seems duplicated but probably in the future when we support diferent units ;; This regex seems duplicated but probably in the future when we support diferent units
;; this will need to reflect changes for each property ;; this will need to reflect changes for each property
(def font-size-re #"^\d*\.?\d*$") (def ^:private font-size-re #"^\d*\.?\d*$")
(def line-height-re #"^\d*\.?\d*$") (def ^:private line-height-re #"^\d*\.?\d*$")
(def letter-spacing-re #"^\d*\.?\d*$") (def ^:private letter-spacing-re #"^\d*\.?\d*$")
(def text-transform-re #"uppercase|capitalize|lowercase|none") (def ^:private text-transform-re #"uppercase|capitalize|lowercase|none")
(def text-decoration-re #"underline|line-through|none") (def ^:private text-decoration-re #"underline|line-through|none")
(def text-direction-re #"ltr|rtl") (def ^:private text-direction-re #"ltr|rtl")
(def text-align-re #"left|center|right|justify") (def ^:private text-align-re #"left|center|right|justify")
(def vertical-align-re #"top|center|bottom") (def ^:private vertical-align-re #"top|center|bottom")
(defn mixed-value (defn- font-data
[values]
(let [s (set values)]
(if (= (count s) 1) (first s) "mixed")))
(defn font-data
[font variant] [font variant]
(d/without-nils (d/without-nils
{:font-id (:id font) {:font-id (:id font)
@ -50,284 +45,326 @@
:font-style (:style variant) :font-style (:style variant)
:font-weight (:weight variant)})) :font-weight (:weight variant)}))
(defn variant-data (defn- variant-data
[variant] [variant]
(d/without-nils (d/without-nils
{:font-variant-id (:id variant) {:font-variant-id (:id variant)
:font-style (:style variant) :font-style (:style variant)
:font-weight (:weight variant)})) :font-weight (:weight variant)}))
(deftype TextRange [$plugin $file $page $id start end] (defn- text-props
Object
(applyTypography [_ typography]
(let [typography (u/proxy->library-typography typography)
attrs (-> typography
(assoc :typography-ref-file $file)
(assoc :typography-ref-id (:id typography))
(dissoc :id :name))]
(st/emit! (dwt/update-text-range $id start end attrs)))))
(defn text-range?
[range]
(instance? TextRange range))
(defn text-props
[shape] [shape]
(d/merge (d/merge
(dwt/current-root-values {:shape shape :attrs txt/root-attrs}) (dwt/current-root-values {:shape shape :attrs txt/root-attrs})
(dwt/current-paragraph-values {:shape shape :attrs txt/paragraph-attrs}) (dwt/current-paragraph-values {:shape shape :attrs txt/paragraph-attrs})
(dwt/current-text-values {:shape shape :attrs txt/text-node-attrs}))) (dwt/current-text-values {:shape shape :attrs txt/text-node-attrs})))
(defn text-range (defn text-range-proxy?
[range]
(obj/type-of? range "TextRange"))
(defn text-range-proxy
[plugin-id file-id page-id id start end] [plugin-id file-id page-id id start end]
(-> (TextRange. plugin-id file-id page-id id start end) (obj/reify {:name "TextRange"}
(crc/add-properties! :$plugin {:enumerable false :get (constantly plugin-id)}
{:name "$plugin" :enumerable false :get (constantly plugin-id)} :$id {:enumerable false :get (constantly id)}
{:name "$id" :enumerable false :get (constantly id)} :$file {:enumerable false :get (constantly file-id)}
{:name "$file" :enumerable false :get (constantly file-id)} :$page {:enumerable false :get (constantly page-id)}
{:name "$page" :enumerable false :get (constantly page-id)}
{:name "shape" :shape
:get #(-> % u/proxy->shape)} {:this true
:get #(-> % u/proxy->shape)}
{:name "characters" :characters
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :text) (str/join "")))} (fn [self]
(let [range-data
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :text) (str/join ""))))}
{:name "fontId" :fontId
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :font-id) mixed-value)) (fn [self]
(let [range-data
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :font-id) u/mixed-value)))
:set :set
(fn [_ value] (fn [_ value]
(let [font (when (string? value) (fonts/get-font-data value)) (let [font (when (string? value) (fonts/get-font-data value))
variant (fonts/get-default-variant font)] variant (fonts/get-default-variant font)]
(cond (cond
(not font) (not font)
(u/display-not-valid :fontId value) (u/display-not-valid :fontId value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :fontId "Plugin doesn't have 'content:write' permission") (u/display-not-valid :fontId "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dwt/update-text-range id start end (font-data font variant))))))} (st/emit! (dwt/update-text-range id start end (font-data font variant))))))}
{:name "fontFamily" :fontFamily
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :font-family) mixed-value)) (fn [self]
(let [range-data
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :font-family) u/mixed-value)))
:set :set
(fn [_ value] (fn [_ value]
(let [font (fonts/find-font-data {:family value}) (let [font (fonts/find-font-data {:family value})
variant (fonts/get-default-variant font)] variant (fonts/get-default-variant font)]
(cond (cond
(not (string? value)) (not (string? value))
(u/display-not-valid :fontFamily value) (u/display-not-valid :fontFamily value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :fontFamily "Plugin doesn't have 'content:write' permission") (u/display-not-valid :fontFamily "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dwt/update-text-range id start end (font-data font variant))))))} (st/emit! (dwt/update-text-range id start end (font-data font variant))))))}
{:name "fontVariantId" :fontVariantId
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :font-variant-id) mixed-value)) (fn [self]
:set (let [range-data
(fn [self value] (-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
(let [font (fonts/get-font-data (obj/get self "fontId")) (->> range-data (map :font-variant-id) u/mixed-value)))
variant (fonts/get-variant font value)] :set
(cond (fn [self value]
(not (string? value)) (let [font (fonts/get-font-data (obj/get self "fontId"))
(u/display-not-valid :fontVariantId value) variant (fonts/get-variant font value)]
(cond
(not (string? value))
(u/display-not-valid :fontVariantId value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :fontVariantId "Plugin doesn't have 'content:write' permission") (u/display-not-valid :fontVariantId "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))} (st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
{:name "fontSize" :fontSize
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :font-size) mixed-value)) (fn [self]
:set (let [range-data
(fn [_ value] (-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
(let [value (str/trim (dm/str value))] (->> range-data (map :font-size) u/mixed-value)))
(cond :set
(or (empty? value) (not (re-matches font-size-re value))) (fn [_ value]
(u/display-not-valid :fontSize value) (let [value (str/trim (dm/str value))]
(cond
(or (empty? value) (not (re-matches font-size-re value)))
(u/display-not-valid :fontSize value)
(not (r/check-permission plugin-id "content:write")) (not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :fontSize "Plugin doesn't have 'content:write' permission") (u/display-not-valid :fontSize "Plugin doesn't have 'content:write' permission")
:else :else
(st/emit! (dwt/update-text-range id start end {:font-size value})))))} (st/emit! (dwt/update-text-range id start end {:font-size value})))))}
{:name "fontWeight" :fontWeight
:get #(let [range-data {:this true
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))] :get
(->> range-data (map :font-weight) mixed-value)) (fn [self]
:set (let [range-data
(fn [self value] (-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
(let [font (fonts/get-font-data (obj/get self "fontId")) (->> range-data (map :font-weight) u/mixed-value)))
weight (dm/str value)
style (obj/get self "fontStyle")
variant
(or
(fonts/find-variant font {:style style :weight weight})
(fonts/find-variant font {:weight weight}))]
(cond
(nil? variant)
(u/display-not-valid :fontWeight (dm/str "Font weight '" value "' not supported for the current font"))
(not (r/check-permission plugin-id "content:write")) :set
(u/display-not-valid :fontWeight "Plugin doesn't have 'content:write' permission") (fn [self value]
(let [font (fonts/get-font-data (obj/get self "fontId"))
weight (dm/str value)
style (obj/get self "fontStyle")
variant
(or
(fonts/find-variant font {:style style :weight weight})
(fonts/find-variant font {:weight weight}))]
(cond
(nil? variant)
(u/display-not-valid :fontWeight (dm/str "Font weight '" value "' not supported for the current font"))
:else (not (r/check-permission plugin-id "content:write"))
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))} (u/display-not-valid :fontWeight "Plugin doesn't have 'content:write' permission")
{:name "fontStyle" :else
:get #(let [range-data (st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :font-style) mixed-value))
:set
(fn [self value]
(let [font (fonts/get-font-data (obj/get self "fontId"))
style (dm/str value)
weight (obj/get self "fontWeight")
variant
(or
(fonts/find-variant font {:weight weight :style style})
(fonts/find-variant font {:style style}))]
(cond
(nil? variant)
(u/display-not-valid :fontStyle (dm/str "Font style '" value "' not supported for the current font"))
(not (r/check-permission plugin-id "content:write")) :fontStyle
(u/display-not-valid :fontStyle "Plugin doesn't have 'content:write' permission") {:this true
:get
(fn [self]
(let [range-data
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :font-style) u/mixed-value)))
:set
(fn [self value]
(let [font (fonts/get-font-data (obj/get self "fontId"))
style (dm/str value)
weight (obj/get self "fontWeight")
variant
(or
(fonts/find-variant font {:weight weight :style style})
(fonts/find-variant font {:style style}))]
(cond
(nil? variant)
(u/display-not-valid :fontStyle (dm/str "Font style '" value "' not supported for the current font"))
:else (not (r/check-permission plugin-id "content:write"))
(st/emit! (dwt/update-text-range id start end (variant-data variant))))))} (u/display-not-valid :fontStyle "Plugin doesn't have 'content:write' permission")
{:name "lineHeight" :else
:get #(let [range-data (st/emit! (dwt/update-text-range id start end (variant-data variant))))))}
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :line-height) mixed-value))
:set
(fn [_ value]
(let [value (str/trim (dm/str value))]
(cond
(or (empty? value) (not (re-matches line-height-re value)))
(u/display-not-valid :lineHeight value)
(not (r/check-permission plugin-id "content:write")) :lineHeight
(u/display-not-valid :lineHeight "Plugin doesn't have 'content:write' permission") {:this true
:get
(fn [self]
(let [range-data
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :line-height) u/mixed-value)))
:set
(fn [_ value]
(let [value (str/trim (dm/str value))]
(cond
(or (empty? value) (not (re-matches line-height-re value)))
(u/display-not-valid :lineHeight value)
:else (not (r/check-permission plugin-id "content:write"))
(st/emit! (dwt/update-text-range id start end {:line-height value})))))} (u/display-not-valid :lineHeight "Plugin doesn't have 'content:write' permission")
{:name "letterSpacing" :else
:get #(let [range-data (st/emit! (dwt/update-text-range id start end {:line-height value})))))}
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :letter-spacing) mixed-value))
:set
(fn [_ value]
(let [value (str/trim (dm/str value))]
(cond
(or (empty? value) (re-matches letter-spacing-re value))
(u/display-not-valid :letterSpacing value)
(not (r/check-permission plugin-id "content:write")) :letterSpacing
(u/display-not-valid :letterSpacing "Plugin doesn't have 'content:write' permission") {:this true
:get
(fn [self]
(let [range-data
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :letter-spacing) u/mixed-value)))
:set
(fn [_ value]
(let [value (str/trim (dm/str value))]
(cond
(or (empty? value) (re-matches letter-spacing-re value))
(u/display-not-valid :letterSpacing value)
:else (not (r/check-permission plugin-id "content:write"))
(st/emit! (dwt/update-text-range id start end {:letter-spacing value})))))} (u/display-not-valid :letterSpacing "Plugin doesn't have 'content:write' permission")
{:name "textTransform" :else
:get #(let [range-data (st/emit! (dwt/update-text-range id start end {:letter-spacing value})))))}
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :text-transform) mixed-value))
:set
(fn [_ value]
(cond
(and (string? value) (re-matches text-transform-re value))
(u/display-not-valid :textTransform value)
(not (r/check-permission plugin-id "content:write")) :textTransform
(u/display-not-valid :textTransform "Plugin doesn't have 'content:write' permission") {:this true
:get
(fn [self]
(let [range-data
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :text-transform) u/mixed-value)))
:set
(fn [_ value]
(cond
(and (string? value) (not (re-matches text-transform-re value)))
(u/display-not-valid :textTransform value)
:else (not (r/check-permission plugin-id "content:write"))
(st/emit! (dwt/update-text-range id start end {:text-transform value}))))} (u/display-not-valid :textTransform "Plugin doesn't have 'content:write' permission")
{:name "textDecoration" :else
:get #(let [range-data (st/emit! (dwt/update-text-range id start end {:text-transform value}))))}
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :text-decoration) mixed-value))
:set
(fn [_ value]
(cond
(and (string? value) (re-matches text-decoration-re value))
(u/display-not-valid :textDecoration value)
(not (r/check-permission plugin-id "content:write")) :textDecoration
(u/display-not-valid :textDecoration "Plugin doesn't have 'content:write' permission") {:this true
:get
(fn [self]
(let [range-data
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :text-decoration) u/mixed-value)))
:set
(fn [_ value]
(cond
(and (string? value) (re-matches text-decoration-re value))
(u/display-not-valid :textDecoration value)
:else (not (r/check-permission plugin-id "content:write"))
(st/emit! (dwt/update-text-range id start end {:text-decoration value}))))} (u/display-not-valid :textDecoration "Plugin doesn't have 'content:write' permission")
{:name "direction" :else
:get #(let [range-data (st/emit! (dwt/update-text-range id start end {:text-decoration value}))))}
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :direction) mixed-value))
:set
(fn [_ value]
(cond
(and (string? value) (re-matches text-direction-re value))
(u/display-not-valid :direction value)
(not (r/check-permission plugin-id "content:write")) :direction
(u/display-not-valid :direction "Plugin doesn't have 'content:write' permission") {:this true
:get
(fn [self]
(let [range-data
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :direction) u/mixed-value)))
:set
(fn [_ value]
(cond
(and (string? value) (re-matches text-direction-re value))
(u/display-not-valid :direction value)
:else (not (r/check-permission plugin-id "content:write"))
(st/emit! (dwt/update-text-range id start end {:direction value}))))} (u/display-not-valid :direction "Plugin doesn't have 'content:write' permission")
{:name "align" :else
:get #(let [range-data (st/emit! (dwt/update-text-range id start end {:direction value}))))}
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :text-align) mixed-value))
:set
(fn [_ value]
(cond
(and (string? value) (re-matches text-align-re value))
(u/display-not-valid :align value)
(not (r/check-permission plugin-id "content:write")) :align
(u/display-not-valid :align "Plugin doesn't have 'content:write' permission") {:this true
:get
(fn [self]
(let [range-data
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :text-align) u/mixed-value)))
:set
(fn [_ value]
(cond
(and (string? value) (re-matches text-align-re value))
(u/display-not-valid :align value)
:else (not (r/check-permission plugin-id "content:write"))
(st/emit! (dwt/update-text-range id start end {:text-align value}))))} (u/display-not-valid :align "Plugin doesn't have 'content:write' permission")
{:name "fills" :else
:get #(let [range-data (st/emit! (dwt/update-text-range id start end {:text-align value}))))}
(-> % u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :fills) mixed-value format/format-fills))
:set
(fn [_ value]
(let [value (parser/parse-fills value)]
(cond
(not (sm/validate [:vector ::cts/fill] value))
(u/display-not-valid :fills value)
(not (r/check-permission plugin-id "content:write")) :fills
(u/display-not-valid :fills "Plugin doesn't have 'content:write' permission") {:this true
:get
(fn [self]
(let [range-data
(-> self u/proxy->shape :content (txt/content-range->text+styles start end))]
(->> range-data (map :fills) u/mixed-value format/format-fills)))
:set
(fn [_ value]
(let [value (parser/parse-fills value)]
(cond
(not (sm/validate [:vector ::cts/fill] value))
(u/display-not-valid :fills value)
:else (not (r/check-permission plugin-id "content:write"))
(st/emit! (dwt/update-text-range id start end {:fills value})))))}))) (u/display-not-valid :fills "Plugin doesn't have 'content:write' permission")
:else
(st/emit! (dwt/update-text-range id start end {:fills value})))))}
:applyTypography
(fn [typography]
(let [typography (u/proxy->library-typography typography)
attrs (-> typography
(assoc :typography-ref-file file-id)
(assoc :typography-ref-id (:id typography))
(dissoc :id :name))]
(st/emit! (dwt/update-text-range id start end attrs))))))
(defn add-text-props (defn add-text-props
[shape-proxy plugin-id] [shape-proxy plugin-id]

View file

@ -41,23 +41,28 @@
(defn current-user-proxy? [p] (defn current-user-proxy? [p]
(instance? CurrentUserProxy p)) (obj/type-of? p "CurrentUserProxy"))
(defn current-user-proxy (defn current-user-proxy
[plugin-id session-id] [plugin-id session-id]
(-> (CurrentUserProxy. plugin-id) (-> (obj/reify {:name "CurrentUserProxy"}
:$plugin {:enumerable false :get (fn [] plugin-id)})
(add-session-properties session-id))) (add-session-properties session-id)))
(defn active-user-proxy? [p] (defn active-user-proxy? [p]
(instance? ActiveUserProxy p)) (obj/type-of? p "ActiveUserProxy"))
(defn active-user-proxy (defn active-user-proxy
[plugin-id session-id] [plugin-id session-id]
(-> (ActiveUserProxy. plugin-id) (-> (obj/reify {:name "ActiveUserProxy"}
(add-session-properties session-id) :$plugin {:enumerable false :get (fn [] plugin-id)}
(crc/add-properties!
{:name "position" :get (fn [_] (-> (u/locate-presence session-id) :point format/format-point))} :position
{:name "zoom" :get (fn [_] (-> (u/locate-presence session-id) :zoom))}))) {:get (fn [] (-> (u/locate-presence session-id) :point format/format-point))}
:zoom
{:get (fn [] (-> (u/locate-presence session-id) :zoom))})
(add-session-properties session-id)))
(defn- add-user-properties (defn- add-user-properties
[user-proxy data] [user-proxy data]
@ -75,13 +80,14 @@
{:name "avatarUrl" {:name "avatarUrl"
:get (fn [_] (cfg/resolve-profile-photo-url data))}))) :get (fn [_] (cfg/resolve-profile-photo-url data))})))
(defn user-proxy?
[p]
(or (instance? UserProxy p)
(current-user-proxy? p)
(active-user-proxy? p)))
(defn user-proxy (defn user-proxy
[plugin-id data] [plugin-id data]
(-> (UserProxy. plugin-id) (-> (obj/reify {:name "UserProxy"}
:$plugin {:enumerable false :get (fn [] plugin-id)})
(add-user-properties data))) (add-user-properties data)))
(defn user-proxy?
[p]
(or (obj/type-of? p "UserProxy")
(current-user-proxy? p)
(active-user-proxy? p)))

View file

@ -119,26 +119,33 @@
flow-id (obj/get proxy "$id") flow-id (obj/get proxy "$id")
page (locate-page file-id page-id)] page (locate-page file-id page-id)]
(when (some? page) (when (some? page)
(d/seek #(= (:id %) flow-id) (:flows page))))) (get (:flows page) flow-id))))
(defn locate-ruler-guide
[file-id page-id ruler-id]
(let [page (locate-page file-id page-id)]
(when (some? page)
(d/seek #(= (:id %) ruler-id) (-> page :guides vals)))))
(defn proxy->ruler-guide (defn proxy->ruler-guide
[proxy] [proxy]
(let [file-id (obj/get proxy "$file") (let [file-id (obj/get proxy "$file")
page-id (obj/get proxy "$page") page-id (obj/get proxy "$page")
ruler-id (obj/get proxy "$id") ruler-id (obj/get proxy "$id")]
page (locate-page file-id page-id)] (locate-ruler-guide file-id page-id ruler-id)))
(when (some? page)
(d/seek #(= (:id %) ruler-id) (-> page :guides vals))))) (defn locate-interaction
[file-id page-id shape-id index]
(when-let [shape (locate-shape file-id page-id shape-id)]
(get-in shape [:interactions index])))
(defn proxy->interaction (defn proxy->interaction
[proxy] [proxy]
(let [file-id (obj/get proxy "$file") (let [file-id (obj/get proxy "$file")
page-id (obj/get proxy "$page") page-id (obj/get proxy "$page")
shape-id (obj/get proxy "$shape") shape-id (obj/get proxy "$shape")
index (obj/get proxy "$index") index (obj/get proxy "$index")]
shape (locate-shape file-id page-id shape-id)] (locate-interaction file-id page-id shape-id index)))
(when (some? shape)
(get-in shape [:interactions index]))))
(defn get-data (defn get-data
([self attr] ([self attr]
@ -193,3 +200,8 @@
(let [msg (dm/str "[PENPOT PLUGIN] Value not valid: " value ". Code: " code)] (let [msg (dm/str "[PENPOT PLUGIN] Value not valid: " value ". Code: " code)]
(.error js/console msg) (.error js/console msg)
(reject msg))) (reject msg)))
(defn mixed-value
[values]
(let [s (set values)]
(if (= (count s) 1) (first s) "mixed")))

View file

@ -7,7 +7,6 @@
(ns app.plugins.viewport (ns app.plugins.viewport
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.record :as crc]
[app.common.spec :as us] [app.common.spec :as us]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.workspace.viewport :as dwv] [app.main.data.workspace.viewport :as dwv]
@ -17,83 +16,81 @@
[app.plugins.utils :as u] [app.plugins.utils :as u]
[app.util.object :as obj])) [app.util.object :as obj]))
(deftype ViewportProxy [$plugin]
Object
(zoomReset [_]
(st/emit! dwz/reset-zoom))
(zoomToFitAll [_]
(st/emit! dwz/zoom-to-fit-all))
(zoomIntoView [_ shapes]
(let [ids
(->> shapes
(map (fn [v]
(if (string? v)
(uuid/uuid v)
(uuid/uuid (obj/get v "x"))))))]
(st/emit! (dwz/fit-to-shapes ids)))))
(crc/define-properties!
ViewportProxy
{:name js/Symbol.toStringTag
:get (fn [] (str "ViewportProxy"))})
(defn viewport-proxy? [p] (defn viewport-proxy? [p]
(instance? ViewportProxy p)) (obj/type-of? p "ViewportProxy"))
(defn viewport-proxy (defn viewport-proxy
[plugin-id] [plugin-id]
(crc/add-properties! (obj/reify {:name "ViewportProxy"}
(ViewportProxy. plugin-id) :$plugin {:enumerable false :get (fn [] plugin-id)}
{:name "center"
:get
(fn [_]
(let [vp (dm/get-in @st/state [:workspace-local :vbox])
x (+ (:x vp) (/ (:width vp) 2))
y (+ (:y vp) (/ (:height vp) 2))]
(.freeze js/Object #js {:x x :y y})))
:set :center
(fn [_ value] {:get
(let [new-x (obj/get value "x") (fn []
new-y (obj/get value "y")] (let [vp (dm/get-in @st/state [:workspace-local :vbox])
(cond x (+ (:x vp) (/ (:width vp) 2))
(not (us/safe-number? new-x)) y (+ (:y vp) (/ (:height vp) 2))]
(u/display-not-valid :center-x new-x) (.freeze js/Object #js {:x x :y y})))
(not (us/safe-number? new-y)) :set
(u/display-not-valid :center-y new-y) (fn [value]
(let [new-x (obj/get value "x")
new-y (obj/get value "y")]
(cond
(not (us/safe-number? new-x))
(u/display-not-valid :center-x new-x)
:else (not (us/safe-number? new-y))
(let [vb (dm/get-in @st/state [:workspace-local :vbox]) (u/display-not-valid :center-y new-y)
old-x (+ (:x vb) (/ (:width vb) 2))
old-y (+ (:y vb) (/ (:height vb) 2))
delta-x (- new-x old-x)
delta-y (- new-y old-y)
to-position
{:x #(+ % delta-x)
:y #(+ % delta-y)}]
(st/emit! (dwv/update-viewport-position to-position))))))}
{:name "zoom" :else
:get (let [vb (dm/get-in @st/state [:workspace-local :vbox])
(fn [_] old-x (+ (:x vb) (/ (:width vb) 2))
(dm/get-in @st/state [:workspace-local :zoom])) old-y (+ (:y vb) (/ (:height vb) 2))
:set delta-x (- new-x old-x)
(fn [_ value] delta-y (- new-y old-y)
(cond to-position
(not (us/safe-number? value)) {:x #(+ % delta-x)
(u/display-not-valid :zoom value) :y #(+ % delta-y)}]
(st/emit! (dwv/update-viewport-position to-position))))))}
:else :zoom
(let [z (dm/get-in @st/state [:workspace-local :zoom])] {:get
(st/emit! (dwz/set-zoom (/ value z))))))} (fn []
(dm/get-in @st/state [:workspace-local :zoom]))
{:name "bounds" :set
:get (fn [value]
(fn [_] (cond
(let [vbox (dm/get-in @st/state [:workspace-local :vbox])] (not (us/safe-number? value))
(.freeze js/Object (format/format-bounds vbox))))})) (u/display-not-valid :zoom value)
:else
(let [z (dm/get-in @st/state [:workspace-local :zoom])]
(st/emit! (dwz/set-zoom (/ value z))))))}
:bounds
{:get
(fn []
(let [vbox (dm/get-in @st/state [:workspace-local :vbox])]
(.freeze js/Object (format/format-bounds vbox))))}
:zoomReset
(fn []
(st/emit! dwz/reset-zoom))
:zoomToFitAll
(fn []
(st/emit! dwz/zoom-to-fit-all))
:zoomIntoView
(fn [shapes]
(let [ids
(->> shapes
(map (fn [v]
(if (string? v)
(uuid/uuid v)
(uuid/uuid (obj/get v "x"))))))]
(st/emit! (dwz/fit-to-shapes ids))))))

View file

@ -173,7 +173,7 @@
(let [definition (first params)] (let [definition (first params)]
(if (some? definition) (if (some? definition)
(let [definition (if (map? definition) (let [definition (if (map? definition)
(assoc definition :name (name ckey) :this false) (c/merge {:this false} (assoc definition :name (name ckey)))
(-> {:enumerable false} (-> {:enumerable false}
(c/merge (meta definition)) (c/merge (meta definition))
(assoc :name (name ckey)) (assoc :name (name ckey))
@ -210,6 +210,16 @@
:else :else
(throw (ex-info "invalid params" {})))))) (throw (ex-info "invalid params" {}))))))
#?(:cljs
(def type-symbol
(js/Symbol.for "penpot.reify:type")))
#?(:cljs
(defn type-of?
[o t]
(let [o (get o type-symbol)]
(= o t))))
(defmacro reify (defmacro reify
"A domain specific variation of reify that creates anonymous objects "A domain specific variation of reify that creates anonymous objects
on demand with the ability to assign protocol implementations and on demand with the ability to assign protocol implementations and
@ -221,6 +231,10 @@
(add-properties! ~obj-sym (add-properties! ~obj-sym
~@(when-let [tname (:name tmeta)] ~@(when-let [tname (:name tmeta)]
[`{:name ~'js/Symbol.toStringTag [`{:name ~'js/Symbol.toStringTag
:this false
:enumerable false
:get (fn [] ~tname)}
`{:name type-symbol
:this false :this false
:enumerable false :enumerable false
:get (fn [] ~tname)}]) :get (fn [] ~tname)}])
@ -230,6 +244,4 @@
~@(mapcat (fn [[k v]] (cons k v)) definitions)) ~@(mapcat (fn [[k v]] (cons k v)) definitions))
obj-sym)] obj-sym)]
(cljs.core/specify! ~obj-sym (cljs.core/specify! ~obj-sym)))))
cljs.core/IMeta
(~'-meta [_#] ~tmeta))))))