♻️ Refactor files upload effects

This commit is contained in:
alonso.torres 2021-01-07 19:02:18 +01:00
parent 802f19453d
commit 27a85ce0da
10 changed files with 318 additions and 319 deletions

View file

@ -553,32 +553,6 @@
(assoc :zoom zoom) (assoc :zoom zoom)
(update :vbox merge srect))))))))))) (update :vbox merge srect)))))))))))
;; --- Add shape to Workspace
(defn- viewport-center
[state]
(let [{:keys [x y width height]} (get-in state [:workspace-local :vbox])]
[(+ x (/ width 2)) (+ y (/ height 2))]))
(defn create-and-add-shape
[type frame-x frame-y data]
(ptk/reify ::create-and-add-shape
ptk/WatchEvent
(watch [_ state stream]
(let [{:keys [width height]} data
[vbc-x vbc-y] (viewport-center state)
x (:x data (- vbc-x (/ width 2)))
y (:y data (- vbc-y (/ height 2)))
page-id (:current-page-id state)
frame-id (-> (dwc/lookup-page-objects state page-id)
(cp/frame-id-by-position {:x frame-x :y frame-y}))
shape (-> (cp/make-minimal-shape type)
(merge data)
(merge {:x x :y y})
(assoc :frame-id frame-id)
(gsh/setup-selrect))]
(rx/of (dwc/add-shape shape))))))
;; --- Update Shape Attrs ;; --- Update Shape Attrs
@ -1414,119 +1388,16 @@
(dwc/add-shape shape) (dwc/add-shape shape)
(dwc/commit-undo-transaction)))))) (dwc/commit-undo-transaction))))))
(defn image-upload [image x y]
(ptk/reify ::add-image
ptk/WatchEvent
(watch [_ state stream]
(let [{:keys [name width height id mtype]} image
shape {:name name
:width width
:height height
:x (- x (/ width 2))
:y (- y (/ height 2))
:metadata {:width width
:height height
:mtype mtype
:id id}}]
(rx/of (create-and-add-shape :image x y shape))))))
(defn- svg-dimensions [data]
(let [width (get-in data [:attrs :width] 100)
height (get-in data [:attrs :height] 100)
viewbox (get-in data [:attrs :viewBox] (str "0 0 " width " " height))
[_ _ width-str height-str] (str/split viewbox " ")
width (d/parse-integer width-str)
height (d/parse-integer height-str)]
[width height]))
(defn svg-upload [data x y]
(ptk/reify ::svg-upload
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
frame-id (cp/frame-id-by-position objects {:x x :y y})
[width height] (svg-dimensions data)
x (- x (/ width 2))
y (- y (/ height 2))
create-svg-raw
(fn [{:keys [tag] :as data} unames root-id]
(let [base (cond (string? tag) tag
(keyword? tag) (name tag)
(nil? tag) "node"
:else (str tag))]
(-> {:id (uuid/next)
:type :svg-raw
:name (dwc/generate-unique-name unames (str "svg-" base))
:frame-id frame-id
;; For svg children we set its coordinates as the root of the svg
:width width
:height height
:x x
:y y
:content data
:root-id root-id}
(gsh/setup-selrect))))
add-svg-child
(fn add-svg-child [parent-id root-id [unames [rchs uchs]] [index {:keys [content] :as data}]]
(let [shape (create-svg-raw data unames root-id)
shape-id (:id shape)
[rch1 uch1] (dwc/add-shape-changes page-id shape)
;; Mov-objects won't have undo because we "delete" the object in the undo of the
;; previous operation
rch2 [{:type :mov-objects
:parent-id parent-id
:frame-id frame-id
:page-id page-id
:index index
:shapes [shape-id]}]
;; Careful! the undo changes are concatenated reversed (we undo in reverse order
changes [(d/concat rchs rch1 rch2) (d/concat uch1 uchs)]
unames (conj unames (:name shape))]
(reduce (partial add-svg-child shape-id root-id) [unames changes] (d/enumerate (:content data)))))
unames (dwc/retrieve-used-names objects)
svg-name (->> (str/replace (:name data) ".svg" "")
(dwc/generate-unique-name unames))
root-shape (create-svg-raw data unames nil)
root-shape (-> root-shape
(assoc :name svg-name))
root-id (:id root-shape)
changes (dwc/add-shape-changes page-id root-shape)
[_ [rchanges uchanges]] (reduce (partial add-svg-child root-id root-id) [unames changes] (d/enumerate (:content data)))]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
(dwc/select-shapes (d/ordered-set root-id)))
))))
(defn- paste-image (defn- paste-image
[image] [image]
(ptk/reify ::paste-bin-impl (ptk/reify ::paste-bin-impl
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [response-sb (rx/subject) (let [file-id (get-in state [:workspace-file :id])
file-id (get-in state [:workspace-file :id])
params {:file-id file-id params {:file-id file-id
:local? true
:data [image]}] :data [image]}]
(rx/concat (rx/of (dwp/upload-media-objects (rx/of (dwp/upload-media-workspace params @ms/mouse-position))))))
(with-meta params
{:on-image
#(let [{:keys [x y]} @ms/mouse-position]
(rx/push! response-sb (image-upload % x y)))
:on-svg
#(let [{:keys [x y]} @ms/mouse-position]
(rx/push! response-sb (svg-upload % x y)))})))
(rx/take 1 response-sb))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Interactions ;; Interactions
@ -1642,8 +1513,10 @@
(d/export dwp/fetch-shared-files) (d/export dwp/fetch-shared-files)
(d/export dwp/link-file-to-library) (d/export dwp/link-file-to-library)
(d/export dwp/unlink-file-from-library) (d/export dwp/unlink-file-from-library)
(d/export dwp/upload-media-objects) (d/export dwp/upload-media-asset)
(d/export dwp/upload-media-workspace)
(d/export dwp/clone-media-object) (d/export dwp/clone-media-object)
(d/export dwc/image-uploaded)
;; Selection ;; Selection

View file

@ -21,6 +21,7 @@
[beicon.core :as rx] [beicon.core :as rx]
[cljs.spec.alpha :as s] [cljs.spec.alpha :as s]
[clojure.set :as set] [clojure.set :as set]
[cuerdas.core :as str]
[potok.core :as ptk])) [potok.core :as ptk]))
;; Change this to :info :debug or :trace to debug this module ;; Change this to :info :debug or :trace to debug this module
@ -601,3 +602,123 @@
:index index :index index
:shapes [shape-id]})))] :shapes [shape-id]})))]
(rx/of (commit-changes rchanges uchanges {:commit-local? true})))))) (rx/of (commit-changes rchanges uchanges {:commit-local? true}))))))
;; --- Add shape to Workspace
(defn- viewport-center
[state]
(let [{:keys [x y width height]} (get-in state [:workspace-local :vbox])]
[(+ x (/ width 2)) (+ y (/ height 2))]))
(defn create-and-add-shape
[type frame-x frame-y data]
(ptk/reify ::create-and-add-shape
ptk/WatchEvent
(watch [_ state stream]
(let [{:keys [width height]} data
[vbc-x vbc-y] (viewport-center state)
x (:x data (- vbc-x (/ width 2)))
y (:y data (- vbc-y (/ height 2)))
page-id (:current-page-id state)
frame-id (-> (lookup-page-objects state page-id)
(cp/frame-id-by-position {:x frame-x :y frame-y}))
shape (-> (cp/make-minimal-shape type)
(merge data)
(merge {:x x :y y})
(assoc :frame-id frame-id)
(gsh/setup-selrect))]
(rx/of (add-shape shape))))))
(defn image-uploaded [image x y]
(ptk/reify ::image-uploaded
ptk/WatchEvent
(watch [_ state stream]
(let [{:keys [name width height id mtype]} image
shape {:name name
:width width
:height height
:x (- x (/ width 2))
:y (- y (/ height 2))
:metadata {:width width
:height height
:mtype mtype
:id id}}]
(rx/of (create-and-add-shape :image x y shape))))))
(defn- svg-dimensions [data]
(let [width (get-in data [:attrs :width] 100)
height (get-in data [:attrs :height] 100)
viewbox (get-in data [:attrs :viewBox] (str "0 0 " width " " height))
[_ _ width-str height-str] (str/split viewbox " ")
width (d/parse-integer width-str)
height (d/parse-integer height-str)]
[width height]))
(defn svg-uploaded [data x y]
(ptk/reify ::svg-uploaded
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
objects (lookup-page-objects state page-id)
frame-id (cp/frame-id-by-position objects {:x x :y y})
[width height] (svg-dimensions data)
x (- x (/ width 2))
y (- y (/ height 2))
create-svg-raw
(fn [{:keys [tag] :as data} unames root-id]
(let [base (cond (string? tag) tag
(keyword? tag) (name tag)
(nil? tag) "node"
:else (str tag))]
(-> {:id (uuid/next)
:type :svg-raw
:name (generate-unique-name unames (str "svg-" base))
:frame-id frame-id
;; For svg children we set its coordinates as the root of the svg
:width width
:height height
:x x
:y y
:content data
:root-id root-id}
(gsh/setup-selrect))))
add-svg-child
(fn add-svg-child [parent-id root-id [unames [rchs uchs]] [index {:keys [content] :as data}]]
(let [shape (create-svg-raw data unames root-id)
shape-id (:id shape)
[rch1 uch1] (add-shape-changes page-id shape)
;; Mov-objects won't have undo because we "delete" the object in the undo of the
;; previous operation
rch2 [{:type :mov-objects
:parent-id parent-id
:frame-id frame-id
:page-id page-id
:index index
:shapes [shape-id]}]
;; Careful! the undo changes are concatenated reversed (we undo in reverse order
changes [(d/concat rchs rch1 rch2) (d/concat uch1 uchs)]
unames (conj unames (:name shape))]
(reduce (partial add-svg-child shape-id root-id) [unames changes] (d/enumerate (:content data)))))
unames (retrieve-used-names objects)
svg-name (->> (str/replace (:name data) ".svg" "")
(generate-unique-name unames))
root-shape (create-svg-raw data unames nil)
root-shape (-> root-shape
(assoc :name svg-name))
root-id (:id root-shape)
changes (add-shape-changes page-id root-shape)
[_ [rchanges uchanges]] (reduce (partial add-svg-child root-id root-id) [unames changes] (d/enumerate (:content data)))]
(rx/of (commit-changes rchanges uchanges {:commit-local? true})
(select-shapes (d/ordered-set root-id)))))))

View file

@ -21,6 +21,7 @@
[app.main.data.media :as di] [app.main.data.media :as di]
[app.main.data.messages :as dm] [app.main.data.messages :as dm]
[app.main.data.workspace.common :as dwc] [app.main.data.workspace.common :as dwc]
[app.main.data.workspace.libraries :as dwl]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.store :as st] [app.main.store :as st]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
@ -31,7 +32,8 @@
[app.util.avatars :as avatars] [app.util.avatars :as avatars]
[beicon.core :as rx] [beicon.core :as rx]
[cljs.spec.alpha :as s] [cljs.spec.alpha :as s]
[potok.core :as ptk])) [potok.core :as ptk]
[app.main.store :as st]))
(declare persist-changes) (declare persist-changes)
(declare shapes-changes-persisted) (declare shapes-changes-persisted)
@ -372,6 +374,89 @@
(->> (http/send! {:method :get :uri uri}) (->> (http/send! {:method :get :uri uri})
(rx/map :body))) (rx/map :body)))
(defn url-name [url]
(let [query-idx (str/last-index-of url "?")
url (if (> query-idx 0) (subs url 0 query-idx) url)
filename (->> (str/split url "/") (last))
ext-idx (str/last-index-of filename ".")]
(if (> ext-idx 0) (subs filename 0 ext-idx) filename)))
(defn- handle-upload-error [on-error stream]
(->> stream
(rx/catch
(fn [error]
(cond
(= (:code error) :media-type-not-allowed)
(rx/of (dm/error (tr "errors.media-type-not-allowed")))
(= (:code error) :media-type-mismatch)
(rx/of (dm/error (tr "errors.media-type-mismatch")))
(= (:code error) :unable-to-optimize)
(rx/of (dm/error (:hint error)))
(fn? on-error)
(do
(on-error error)
(rx/empty))
:else
(rx/throw error))))))
(defn- upload-uris [file-id local? name uris mtype on-image on-svg]
(letfn [(svg-url? [url]
(or (and mtype (= mtype "image/svg+xml"))
(str/ends-with? url ".svg")))
(prepare-uri [uri]
{:file-id file-id
:is-local local?
:name (or name (url-name uri))
:url uri})]
(rx/merge
(->> (rx/from uris)
(rx/filter (comp not svg-url?))
(rx/map prepare-uri)
(rx/mapcat #(rp/mutation! :create-file-media-object-from-url %))
(rx/do on-image))
(->> (rx/from uris)
(rx/filter svg-url?)
(rx/merge-map fetch-svg)
(rx/merge-map parse-svg)
(rx/with-latest vector uris)
(rx/map #(assoc (first %) :name (or name (url-name (second %)))))
(rx/do on-svg)))))
(defn- upload-data [file-id local? name data force-media on-image on-svg]
(let [svg-blob? (fn [blob]
(and (not force-media)
(= (.-type blob) "image/svg+xml")))
prepare-file
(fn [blob]
(let [name (or name (if (di/file? blob) (.-name blob) "blob"))]
{:file-id file-id
:name name
:is-local local?
:content blob}))
file-stream (->> (rx/from data)
(rx/map di/validate-file))]
(rx/merge
(->> file-stream
(rx/filter (comp not svg-blob?))
(rx/map prepare-file)
(rx/mapcat #(rp/mutation! :upload-file-media-object %))
(rx/do on-image))
(->> file-stream
(rx/filter svg-blob?)
(rx/merge-map #(.text %))
(rx/merge-map parse-svg)
(rx/with-latest vector file-stream)
(rx/map #(assoc (first %) :name (.-name (second %))))
(rx/do on-svg)))))
(defn upload-media-objects (defn upload-media-objects
[{:keys [file-id local? data name uris mtype svg-as-images] :as params}] [{:keys [file-id local? data name uris mtype svg-as-images] :as params}]
(us/assert ::upload-media-objects params) (us/assert ::upload-media-objects params)
@ -380,113 +465,52 @@
(watch [_ state stream] (watch [_ state stream]
(let [{:keys [on-image on-svg on-error] (let [{:keys [on-image on-svg on-error]
:or {on-image identity :or {on-image identity
on-svg identity}} (meta params) on-svg identity}} (meta params)]
svg? (fn [blob]
(= (.-type blob) "image/svg+xml"))
svg-url? (fn [url]
(or (and mtype (= mtype "image/svg+xml"))
(str/ends-with? url ".svg")))
url-name (fn [url]
(let [query-idx (str/last-index-of url "?")
url (if (> query-idx 0) (subs url 0 query-idx) url)
filename (->> (str/split url "/") (last))
ext-idx (str/last-index-of filename ".")]
(if (> ext-idx 0) (subs filename 0 ext-idx) filename)))
prepare-file
(fn [blob]
(let [name (or name (if (di/file? blob) (.-name blob) "blob"))]
{:name name
:file-id file-id
:content blob
:is-local local?}))
prepare-uri
(fn [uri]
{:file-id file-id
:is-local local?
:url uri
:name (or name (url-name uri))})
file-stream
(when data
(->> (rx/from data)
(rx/map di/validate-file)))
image-stream
(cond
(seq uris)
(rx/merge
(->> (rx/from uris)
(rx/filter (comp not svg-url?))
(rx/map prepare-uri)
(rx/mapcat #(rp/mutation! :create-file-media-object-from-url %))
(rx/do on-image))
(->> (rx/from uris)
(rx/filter svg-url?)
(rx/merge-map fetch-svg)
(rx/merge-map parse-svg)
(rx/with-latest vector uris)
(rx/map #(assoc (first %) :name (or name (url-name (second %)))))
(rx/do on-svg)))
:else
(rx/merge
(->> file-stream
(rx/filter #(or svg-as-images (not (svg? %))))
(rx/map prepare-file)
(rx/mapcat #(rp/mutation! :upload-file-media-object %))
(rx/do on-image))
(->> file-stream
(rx/filter #(and (not svg-as-images) (svg? %)))
(rx/merge-map #(.text %))
(rx/merge-map parse-svg)
(rx/with-latest vector file-stream)
(rx/map #(assoc (first %) :name (.-name (second %))))
(rx/do on-svg))))]
(rx/concat (rx/concat
(rx/of (dm/show {:content (tr "media.loading") (rx/of (dm/show {:content (tr "media.loading")
:type :info :type :info
:timeout nil :timeout nil
:tag :media-loading})) :tag :media-loading}))
(->> image-stream (->> (if (seq uris)
(rx/catch (fn [error] ;; Media objects is a list of URL's pointing to the path
(cond (upload-uris file-id local? name uris mtype on-image on-svg)
(= (:code error) :media-type-not-allowed) ;; Media objects are blob of data to be upload
(rx/of (dm/error (tr "errors.media-type-not-allowed"))) (upload-data file-id local? name data svg-as-images on-image on-svg))
;; Every stream has its own sideffect. We need to ignore the result
(rx/ignore)
(handle-upload-error on-error)
(rx/finalize (st/emitf (dm/hide-tag :media-loading)))))))))
(= (:code error) :media-type-mismatch) (defn upload-media-asset [params]
(rx/of (dm/error (tr "errors.media-type-mismatch"))) (let [params (-> params
(assoc :svg-as-images true)
(assoc :local? false)
(with-meta {:on-image #(st/emit! (dwl/add-media %))}))]
(upload-media-objects params)))
(= (:code error) :unable-to-optimize) (defn upload-media-workspace
(rx/of (dm/error (:hint error))) [params position]
(let [{:keys [x y]} position
params (-> params
(assoc :local? true)
(with-meta
{:on-image
#(st/emit! (dwc/image-uploaded % x y))
:on-svg
#(st/emit! (dwc/svg-uploaded % x y))}))]
(upload-media-objects params)))
(fn? on-error)
(do
(on-error error)
(rx/empty))
:else
(rx/throw error))))
(rx/finalize (fn []
(st/emit! (dm/hide-tag :media-loading))))))))))
;; --- Upload File Media objects ;; --- Upload File Media objects
(s/def ::object-id ::us/uuid) (s/def ::object-id ::us/uuid)
(s/def ::clone-media-objects-params (s/def ::clone-media-objects-params
(s/keys :req-un [::file-id ::local? ::object-id])) (s/keys :req-un [::file-id ::object-id]))
(defn clone-media-object (defn clone-media-object
[{:keys [file-id local? object-id] :as params}] [{:keys [file-id object-id] :as params}]
(us/assert ::clone-media-objects-params params) (us/assert ::clone-media-objects-params params)
(ptk/reify ::clone-media-objects (ptk/reify ::clone-media-objects
ptk/WatchEvent ptk/WatchEvent
@ -494,7 +518,7 @@
(let [{:keys [on-success on-error] (let [{:keys [on-success on-error]
:or {on-success identity :or {on-success identity
on-error identity}} (meta params) on-error identity}} (meta params)
params {:is-local local? params {:is-local true
:file-id file-id :file-id file-id
:id object-id}] :id object-id}]

View file

@ -99,10 +99,10 @@
(when (and shape (not (:hidden shape))) (when (and shape (not (:hidden shape)))
(let [shape (-> (gsh/transform-shape shape) (let [shape (-> (gsh/transform-shape shape)
(gsh/translate-to-frame frame)) (gsh/translate-to-frame frame))
opts #js {:shape shape}] opts #js {:shape shape}
(if (and (= :svg-raw (:type shape)) svg-element? (and (= :svg-raw (:type shape))
(not= :svg (get-in shape [:content :tag]))) (not= :svg (get-in shape [:content :tag])))]
[:> svg-raw-wrapper {:shape shape :frame frame}] (if-not svg-element?
[:> shape-container {:shape shape} [:> shape-container {:shape shape}
(case (:type shape) (case (:type shape)
:text [:> text/text-shape opts] :text [:> text/text-shape opts]
@ -113,7 +113,10 @@
:frame [:> frame-wrapper {:shape shape}] :frame [:> frame-wrapper {:shape shape}]
:group [:> group-wrapper {:shape shape :frame frame}] :group [:> group-wrapper {:shape shape :frame frame}]
:svg-raw [:> svg-raw-wrapper {:shape shape :frame frame}] :svg-raw [:> svg-raw-wrapper {:shape shape :frame frame}]
nil)])))))) nil)]
;; Don't wrap svg elements inside a <g> otherwise some can break
[:> svg-raw-wrapper {:shape shape :frame frame}]))))))
(defn get-viewbox [{:keys [x y width height] :or {x 0 y 0 width 100 height 100}}] (defn get-viewbox [{:keys [x y width height] :or {x 0 y 0 width 100 height 100}}]
(str/fmt "%s %s %s %s" x y width height)) (str/fmt "%s %s %s %s" x y width height))

View file

@ -62,13 +62,11 @@
[props] [props]
(let [shape (unchecked-get props "shape") (let [shape (unchecked-get props "shape")
childs (unchecked-get props "childs") childs (unchecked-get props "childs")
frame (unchecked-get props "frame")] frame (unchecked-get props "frame")
svg-element? (and (= :svg-raw (:type shape))
(not= :svg (get-in shape [:content :tag])))]
(if (and (= :svg-raw (:type shape)) (if-not svg-element?
(not= :svg (get-in shape [:content :tag])))
[:& component {:shape shape
:frame frame
:childs childs}]
[:> shape-container {:shape shape [:> shape-container {:shape shape
:on-mouse-enter (handle-hover-shape shape true) :on-mouse-enter (handle-hover-shape shape true)
:on-mouse-leave (handle-hover-shape shape false) :on-mouse-leave (handle-hover-shape shape false)
@ -76,7 +74,12 @@
[:& component {:shape shape [:& component {:shape shape
:frame frame :frame frame
:childs childs :childs childs
:is-child-selected? true}]])))) :is-child-selected? true}]]
;; Don't wrap svg elements inside a <g> otherwise some can break
[:& component {:shape shape
:frame frame
:childs childs}]))))
(defn frame-container-factory (defn frame-container-factory
[objects] [objects]

View file

@ -55,13 +55,12 @@
on-mouse-down (mf/use-callback on-mouse-down (mf/use-callback
(mf/deps shape) (mf/deps shape)
#(on-mouse-down % shape))] #(on-mouse-down % shape))
(if (and (= :svg-raw (:type shape)) svg-element? (and (= :svg-raw (:type shape))
(not= :svg (get-in shape [:content :tag]))) (not= :svg (get-in shape [:content :tag])))]
[:& component {:shape shape
:frame frame (if-not svg-element?
:childs childs}]
[:> shape-container {:shape shape [:> shape-container {:shape shape
:on-mouse-down on-mouse-down :on-mouse-down on-mouse-down
:cursor (when (seq (:interactions shape)) "pointer")} :cursor (when (seq (:interactions shape)) "pointer")}
@ -77,7 +76,12 @@
:fill "#31EFB8" :fill "#31EFB8"
:stroke "#31EFB8" :stroke "#31EFB8"
:stroke-width 1 :stroke-width 1
:fill-opacity 0.2}])])))) :fill-opacity 0.2}])]
;; Don't wrap svg elements inside a <g> otherwise some can break
[:& component {:shape shape
:frame frame
:childs childs}]))))
(defn frame-wrapper (defn frame-wrapper
[shape-container show-interactions?] [shape-container show-interactions?]
@ -187,8 +191,7 @@
:image [:> image-wrapper opts] :image [:> image-wrapper opts]
:circle [:> circle-wrapper opts] :circle [:> circle-wrapper opts]
:group [:> group-container {:shape shape :frame frame}] :group [:> group-container {:shape shape :frame frame}]
:svg-raw [:> svg-raw-container {:shape shape :frame frame}] :svg-raw [:> svg-raw-container {:shape shape :frame frame}])))))))
)))))))
(mf/defc frame-svg (mf/defc frame-svg
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}

View file

@ -9,16 +9,17 @@
(ns app.main.ui.workspace.left-toolbar (ns app.main.ui.workspace.left-toolbar
(:require (:require
[rumext.alpha :as mf] [app.common.geom.point :as gpt]
[app.common.media :as cm] [app.common.media :as cm]
[app.main.refs :as refs]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.file-uploader :refer [file-uploader]] [app.main.ui.components.file-uploader :refer [file-uploader]]
[app.util.object :as obj] [app.main.ui.icons :as i]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.main.ui.icons :as i])) [app.util.object :as obj]
[rumext.alpha :as mf]))
(mf/defc image-upload (mf/defc image-upload
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}
@ -29,26 +30,13 @@
on-click on-click
(mf/use-callback #(dom/click (mf/ref-val ref))) (mf/use-callback #(dom/click (mf/ref-val ref)))
handle-image-upload
(mf/use-callback
(fn [image]
(st/emit! (dw/image-upload image 0 0))))
handle-svg-upload
(mf/use-callback
(fn [svg]
(st/emit! (dw/svg-upload svg 0 0))))
on-files-selected on-files-selected
(mf/use-callback (mf/use-callback
(mf/deps file) (mf/deps file)
(fn [blobs] (fn [blobs]
(st/emit! (dw/upload-media-objects (let [params {:file-id (:id file)
(with-meta {:file-id (:id file) :data (seq blobs)}]
:local? true (st/emit! (dw/upload-media-workspace params (gpt/point 0 0))))))]
:data (seq blobs)}
{:on-image handle-image-upload
:on-svg handle-svg-upload})))))]
[:li.tooltip.tooltip-right [:li.tooltip.tooltip-right
{:alt (tr "workspace.toolbar.image") {:alt (tr "workspace.toolbar.image")

View file

@ -82,31 +82,35 @@
alt? (hooks/use-rxsub ms/keyboard-alt) alt? (hooks/use-rxsub ms/keyboard-alt)
moving-iref (mf/use-memo (mf/deps (:id shape)) (make-is-moving-ref (:id shape))) moving-iref (mf/use-memo (mf/deps (:id shape)) (make-is-moving-ref (:id shape)))
moving? (mf/deref moving-iref)] moving? (mf/deref moving-iref)
svg-element? (and (= (:type shape) :svg-raw)
(not= :svg (get-in shape [:content :tag])))]
(when (and shape (when (and shape
(or ghost? (not moving?)) (or ghost? (not moving?))
(not (:hidden shape))) (not (:hidden shape)))
(if (and (= (:type shape) :svg-raw) [:*
(not= :svg (get-in shape [:content :tag]))) (if-not svg-element?
;; When we don't want to add a wrapper to internal raw svg elements [:g.shape-wrapper {:style {:cursor (if alt? cur/duplicate nil)}}
[:> svg-raw-wrapper opts] (case (:type shape)
[:g.shape-wrapper {:style {:cursor (if alt? cur/duplicate nil)}} :path [:> path/path-wrapper opts]
(case (:type shape) :text [:> text/text-wrapper opts]
:path [:> path/path-wrapper opts] :group [:> group-wrapper opts]
:text [:> text/text-wrapper opts] :rect [:> rect-wrapper opts]
:group [:> group-wrapper opts] :image [:> image-wrapper opts]
:rect [:> rect-wrapper opts] :circle [:> circle-wrapper opts]
:image [:> image-wrapper opts] :svg-raw [:> svg-raw-wrapper opts]
:circle [:> circle-wrapper opts]
:svg-raw [:> svg-raw-wrapper opts]
;; Only used when drawing a new frame. ;; Only used when drawing a new frame.
:frame [:> frame-wrapper {:shape shape}] :frame [:> frame-wrapper {:shape shape}]
nil)
(when (debug? :bounding-boxes) nil)]
[:& bounding-box {:shape shape :frame frame}])]))))
;; Don't wrap svg elements inside a <g> otherwise some can break
[:> svg-raw-wrapper opts])
(when (debug? :bounding-boxes)
[:& bounding-box {:shape shape :frame frame}])])))
(def group-wrapper (group/group-wrapper-factory shape-wrapper)) (def group-wrapper (group/group-wrapper-factory shape-wrapper))
(def svg-raw-wrapper (svg-raw/svg-raw-wrapper-factory shape-wrapper)) (def svg-raw-wrapper (svg-raw/svg-raw-wrapper-factory shape-wrapper))

View file

@ -164,12 +164,9 @@
(mf/use-callback (mf/use-callback
(mf/deps file-id) (mf/deps file-id)
(fn [blobs] (fn [blobs]
(let [params (with-meta {:file-id file-id (let [params {:file-id file-id
:local? false :data (seq blobs)}]
:data (seq blobs) (st/emit! (dw/upload-media-asset params)))))
:svg-as-images true}
{:on-image on-media-uploaded})]
(st/emit! (dw/upload-media-objects params)))))
on-delete on-delete
(mf/use-callback (mf/use-callback

View file

@ -456,12 +456,7 @@
on-image-uploaded on-image-uploaded
(mf/use-callback (mf/use-callback
(fn [image {:keys [x y]}] (fn [image {:keys [x y]}]
(st/emit! (dw/image-upload image x y)))) (st/emit! (dw/image-uploaded image x y))))
on-svg-uploaded
(mf/use-callback
(fn [image {:keys [x y]}]
(st/emit! (dw/svg-upload image x y))))
on-drop on-drop
(mf/use-callback (mf/use-callback
@ -497,31 +492,23 @@
lines (str/lines data) lines (str/lines data)
urls (filter #(and (not (str/blank? %)) urls (filter #(and (not (str/blank? %))
(not (str/starts-with? % "#"))) (not (str/starts-with? % "#")))
lines)] lines)
(st/emit! params {:file-id (:id file)
(dw/upload-media-objects :uris urls}]
(with-meta {:file-id (:id file) (st/emit! (dw/upload-media-workspace params viewport-coord)))
:local? true
:uris urls}
{:on-image #(on-image-uploaded % viewport-coord)
:on-svg #(on-svg-uploaded % viewport-coord)}))))
;; Will trigger when the user drags an SVG asset from the assets panel ;; Will trigger when the user drags an SVG asset from the assets panel
(and (dnd/has-type? event "text/asset-id") (= asset-type "image/svg+xml")) (and (dnd/has-type? event "text/asset-id") (= asset-type "image/svg+xml"))
(let [path (cfg/resolve-file-media {:id asset-id})] (let [path (cfg/resolve-file-media {:id asset-id})
(st/emit! params {:file-id (:id file)
(dw/upload-media-objects :uris [path]
(with-meta {:file-id (:id file) :name asset-name
:local? true :mtype asset-type}]
:uris [path] (st/emit! (dw/upload-media-workspace params viewport-coord)))
:name asset-name
:mtype asset-type}
{:on-svg #(on-svg-uploaded % viewport-coord)}))))
;; Will trigger when the user drags an image from the assets SVG ;; Will trigger when the user drags an image from the assets SVG
(dnd/has-type? event "text/asset-id") (dnd/has-type? event "text/asset-id")
(let [params {:file-id (:id file) (let [params {:file-id (:id file)
:local? true
:object-id asset-id :object-id asset-id
:name asset-name}] :name asset-name}]
(st/emit! (dw/clone-media-object (st/emit! (dw/clone-media-object
@ -534,12 +521,8 @@
:else :else
(let [files (dnd/get-files event) (let [files (dnd/get-files event)
params {:file-id (:id file) params {:file-id (:id file)
:local? true
:data (seq files)}] :data (seq files)}]
(st/emit! (dw/upload-media-objects (st/emit! (dw/upload-media-workspace params viewport-coord)))))))
(with-meta params
{:on-image #(on-image-uploaded % viewport-coord)
:on-svg #(on-svg-uploaded % viewport-coord)}))))))))
on-paste on-paste
(mf/use-callback (mf/use-callback