diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 1591f3299..39ad120c7 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -553,32 +553,6 @@ (assoc :zoom zoom) (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 @@ -1414,119 +1388,16 @@ (dwc/add-shape shape) (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 [image] (ptk/reify ::paste-bin-impl ptk/WatchEvent (watch [_ state stream] - (let [response-sb (rx/subject) - file-id (get-in state [:workspace-file :id]) + (let [file-id (get-in state [:workspace-file :id]) params {:file-id file-id - :local? true :data [image]}] - (rx/concat (rx/of (dwp/upload-media-objects - (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)))))) + (rx/of (dwp/upload-media-workspace params @ms/mouse-position)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Interactions @@ -1642,8 +1513,10 @@ (d/export dwp/fetch-shared-files) (d/export dwp/link-file-to-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 dwc/image-uploaded) ;; Selection diff --git a/frontend/src/app/main/data/workspace/common.cljs b/frontend/src/app/main/data/workspace/common.cljs index 2d27d17c8..c9cbee1a5 100644 --- a/frontend/src/app/main/data/workspace/common.cljs +++ b/frontend/src/app/main/data/workspace/common.cljs @@ -21,6 +21,7 @@ [beicon.core :as rx] [cljs.spec.alpha :as s] [clojure.set :as set] + [cuerdas.core :as str] [potok.core :as ptk])) ;; Change this to :info :debug or :trace to debug this module @@ -601,3 +602,123 @@ :index index :shapes [shape-id]})))] (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))))))) diff --git a/frontend/src/app/main/data/workspace/persistence.cljs b/frontend/src/app/main/data/workspace/persistence.cljs index 75e489b2c..df53e05b7 100644 --- a/frontend/src/app/main/data/workspace/persistence.cljs +++ b/frontend/src/app/main/data/workspace/persistence.cljs @@ -21,6 +21,7 @@ [app.main.data.media :as di] [app.main.data.messages :as dm] [app.main.data.workspace.common :as dwc] + [app.main.data.workspace.libraries :as dwl] [app.main.repo :as rp] [app.main.store :as st] [app.util.i18n :as i18n :refer [tr]] @@ -31,7 +32,8 @@ [app.util.avatars :as avatars] [beicon.core :as rx] [cljs.spec.alpha :as s] - [potok.core :as ptk])) + [potok.core :as ptk] + [app.main.store :as st])) (declare persist-changes) (declare shapes-changes-persisted) @@ -372,6 +374,89 @@ (->> (http/send! {:method :get :uri uri}) (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 [{:keys [file-id local? data name uris mtype svg-as-images] :as params}] (us/assert ::upload-media-objects params) @@ -380,113 +465,52 @@ (watch [_ state stream] (let [{:keys [on-image on-svg on-error] :or {on-image identity - 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))))] + on-svg identity}} (meta params)] (rx/concat (rx/of (dm/show {:content (tr "media.loading") :type :info :timeout nil :tag :media-loading})) - (->> image-stream - (rx/catch (fn [error] - (cond - (= (:code error) :media-type-not-allowed) - (rx/of (dm/error (tr "errors.media-type-not-allowed"))) + (->> (if (seq uris) + ;; Media objects is a list of URL's pointing to the path + (upload-uris file-id local? name uris mtype on-image on-svg) + ;; Media objects are blob of data to be upload + (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) - (rx/of (dm/error (tr "errors.media-type-mismatch"))) +(defn upload-media-asset [params] + (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) - (rx/of (dm/error (:hint error))) +(defn upload-media-workspace + [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 (s/def ::object-id ::us/uuid) (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 - [{:keys [file-id local? object-id] :as params}] + [{:keys [file-id object-id] :as params}] (us/assert ::clone-media-objects-params params) (ptk/reify ::clone-media-objects ptk/WatchEvent @@ -494,7 +518,7 @@ (let [{:keys [on-success on-error] :or {on-success identity on-error identity}} (meta params) - params {:is-local local? + params {:is-local true :file-id file-id :id object-id}] diff --git a/frontend/src/app/main/exports.cljs b/frontend/src/app/main/exports.cljs index 70fce4c10..5b3e5dcdb 100644 --- a/frontend/src/app/main/exports.cljs +++ b/frontend/src/app/main/exports.cljs @@ -99,10 +99,10 @@ (when (and shape (not (:hidden shape))) (let [shape (-> (gsh/transform-shape shape) (gsh/translate-to-frame frame)) - opts #js {:shape shape}] - (if (and (= :svg-raw (:type shape)) - (not= :svg (get-in shape [:content :tag]))) - [:> svg-raw-wrapper {:shape shape :frame frame}] + opts #js {:shape shape} + svg-element? (and (= :svg-raw (:type shape)) + (not= :svg (get-in shape [:content :tag])))] + (if-not svg-element? [:> shape-container {:shape shape} (case (:type shape) :text [:> text/text-shape opts] @@ -113,7 +113,10 @@ :frame [:> frame-wrapper {:shape shape}] :group [:> group-wrapper {:shape shape :frame frame}] :svg-raw [:> svg-raw-wrapper {:shape shape :frame frame}] - nil)])))))) + nil)] + + ;; Don't wrap svg elements inside a 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}}] (str/fmt "%s %s %s %s" x y width height)) diff --git a/frontend/src/app/main/ui/handoff/render.cljs b/frontend/src/app/main/ui/handoff/render.cljs index 3f2d5dea4..a039dc336 100644 --- a/frontend/src/app/main/ui/handoff/render.cljs +++ b/frontend/src/app/main/ui/handoff/render.cljs @@ -62,13 +62,11 @@ [props] (let [shape (unchecked-get props "shape") 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)) - (not= :svg (get-in shape [:content :tag]))) - [:& component {:shape shape - :frame frame - :childs childs}] + (if-not svg-element? [:> shape-container {:shape shape :on-mouse-enter (handle-hover-shape shape true) :on-mouse-leave (handle-hover-shape shape false) @@ -76,7 +74,12 @@ [:& component {:shape shape :frame frame :childs childs - :is-child-selected? true}]])))) + :is-child-selected? true}]] + + ;; Don't wrap svg elements inside a otherwise some can break + [:& component {:shape shape + :frame frame + :childs childs}])))) (defn frame-container-factory [objects] diff --git a/frontend/src/app/main/ui/viewer/shapes.cljs b/frontend/src/app/main/ui/viewer/shapes.cljs index b6d43c7e6..f01080e64 100644 --- a/frontend/src/app/main/ui/viewer/shapes.cljs +++ b/frontend/src/app/main/ui/viewer/shapes.cljs @@ -55,13 +55,12 @@ on-mouse-down (mf/use-callback (mf/deps shape) - #(on-mouse-down % shape))] + #(on-mouse-down % shape)) - (if (and (= :svg-raw (:type shape)) - (not= :svg (get-in shape [:content :tag]))) - [:& component {:shape shape - :frame frame - :childs childs}] + svg-element? (and (= :svg-raw (:type shape)) + (not= :svg (get-in shape [:content :tag])))] + + (if-not svg-element? [:> shape-container {:shape shape :on-mouse-down on-mouse-down :cursor (when (seq (:interactions shape)) "pointer")} @@ -77,7 +76,12 @@ :fill "#31EFB8" :stroke "#31EFB8" :stroke-width 1 - :fill-opacity 0.2}])])))) + :fill-opacity 0.2}])] + + ;; Don't wrap svg elements inside a otherwise some can break + [:& component {:shape shape + :frame frame + :childs childs}])))) (defn frame-wrapper [shape-container show-interactions?] @@ -187,8 +191,7 @@ :image [:> image-wrapper opts] :circle [:> circle-wrapper opts] :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/wrap [mf/memo]} diff --git a/frontend/src/app/main/ui/workspace/left_toolbar.cljs b/frontend/src/app/main/ui/workspace/left_toolbar.cljs index 4f57c9b2d..9685e102b 100644 --- a/frontend/src/app/main/ui/workspace/left_toolbar.cljs +++ b/frontend/src/app/main/ui/workspace/left_toolbar.cljs @@ -9,16 +9,17 @@ (ns app.main.ui.workspace.left-toolbar (:require - [rumext.alpha :as mf] + [app.common.geom.point :as gpt] [app.common.media :as cm] - [app.main.refs :as refs] [app.main.data.workspace :as dw] + [app.main.refs :as refs] [app.main.store :as st] [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.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/wrap [mf/memo]} @@ -29,26 +30,13 @@ on-click (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 (mf/use-callback (mf/deps file) (fn [blobs] - (st/emit! (dw/upload-media-objects - (with-meta {:file-id (:id file) - :local? true - :data (seq blobs)} - {:on-image handle-image-upload - :on-svg handle-svg-upload})))))] + (let [params {:file-id (:id file) + :data (seq blobs)}] + (st/emit! (dw/upload-media-workspace params (gpt/point 0 0))))))] [:li.tooltip.tooltip-right {:alt (tr "workspace.toolbar.image") diff --git a/frontend/src/app/main/ui/workspace/shapes.cljs b/frontend/src/app/main/ui/workspace/shapes.cljs index 92e1d7563..ebdd1a4e3 100644 --- a/frontend/src/app/main/ui/workspace/shapes.cljs +++ b/frontend/src/app/main/ui/workspace/shapes.cljs @@ -82,31 +82,35 @@ alt? (hooks/use-rxsub ms/keyboard-alt) 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 (or ghost? (not moving?)) (not (:hidden shape))) - (if (and (= (:type shape) :svg-raw) - (not= :svg (get-in shape [:content :tag]))) - ;; When we don't want to add a wrapper to internal raw svg elements - [:> svg-raw-wrapper opts] - [:g.shape-wrapper {:style {:cursor (if alt? cur/duplicate nil)}} - (case (:type shape) - :path [:> path/path-wrapper opts] - :text [:> text/text-wrapper opts] - :group [:> group-wrapper opts] - :rect [:> rect-wrapper opts] - :image [:> image-wrapper opts] - :circle [:> circle-wrapper opts] - :svg-raw [:> svg-raw-wrapper opts] + [:* + (if-not svg-element? + [:g.shape-wrapper {:style {:cursor (if alt? cur/duplicate nil)}} + (case (:type shape) + :path [:> path/path-wrapper opts] + :text [:> text/text-wrapper opts] + :group [:> group-wrapper opts] + :rect [:> rect-wrapper opts] + :image [:> image-wrapper opts] + :circle [:> circle-wrapper opts] + :svg-raw [:> svg-raw-wrapper opts] - ;; Only used when drawing a new frame. - :frame [:> frame-wrapper {:shape shape}] - nil) + ;; Only used when drawing a new frame. + :frame [:> frame-wrapper {:shape shape}] - (when (debug? :bounding-boxes) - [:& bounding-box {:shape shape :frame frame}])])))) + nil)] + + ;; Don't wrap svg elements inside a 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 svg-raw-wrapper (svg-raw/svg-raw-wrapper-factory shape-wrapper)) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index 4490d25c6..760851967 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -164,12 +164,9 @@ (mf/use-callback (mf/deps file-id) (fn [blobs] - (let [params (with-meta {:file-id file-id - :local? false - :data (seq blobs) - :svg-as-images true} - {:on-image on-media-uploaded})] - (st/emit! (dw/upload-media-objects params))))) + (let [params {:file-id file-id + :data (seq blobs)}] + (st/emit! (dw/upload-media-asset params))))) on-delete (mf/use-callback diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 03f25c234..288ef617b 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -456,12 +456,7 @@ on-image-uploaded (mf/use-callback (fn [image {:keys [x y]}] - (st/emit! (dw/image-upload image x y)))) - - on-svg-uploaded - (mf/use-callback - (fn [image {:keys [x y]}] - (st/emit! (dw/svg-upload image x y)))) + (st/emit! (dw/image-uploaded image x y)))) on-drop (mf/use-callback @@ -497,31 +492,23 @@ lines (str/lines data) urls (filter #(and (not (str/blank? %)) (not (str/starts-with? % "#"))) - lines)] - (st/emit! - (dw/upload-media-objects - (with-meta {:file-id (:id file) - :local? true - :uris urls} - {:on-image #(on-image-uploaded % viewport-coord) - :on-svg #(on-svg-uploaded % viewport-coord)})))) + lines) + params {:file-id (:id file) + :uris urls}] + (st/emit! (dw/upload-media-workspace params viewport-coord))) ;; 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")) - (let [path (cfg/resolve-file-media {:id asset-id})] - (st/emit! - (dw/upload-media-objects - (with-meta {:file-id (:id file) - :local? true - :uris [path] - :name asset-name - :mtype asset-type} - {:on-svg #(on-svg-uploaded % viewport-coord)})))) + (let [path (cfg/resolve-file-media {:id asset-id}) + params {:file-id (:id file) + :uris [path] + :name asset-name + :mtype asset-type}] + (st/emit! (dw/upload-media-workspace params viewport-coord))) ;; Will trigger when the user drags an image from the assets SVG (dnd/has-type? event "text/asset-id") (let [params {:file-id (:id file) - :local? true :object-id asset-id :name asset-name}] (st/emit! (dw/clone-media-object @@ -534,12 +521,8 @@ :else (let [files (dnd/get-files event) params {:file-id (:id file) - :local? true :data (seq files)}] - (st/emit! (dw/upload-media-objects - (with-meta params - {:on-image #(on-image-uploaded % viewport-coord) - :on-svg #(on-svg-uploaded % viewport-coord)})))))))) + (st/emit! (dw/upload-media-workspace params viewport-coord))))))) on-paste (mf/use-callback