0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-10 09:08:31 -05:00

Merge branch 'hiru-remove-graphics' into develop

This commit is contained in:
Andrey Antukh 2022-11-14 14:44:55 +01:00
commit 58dbe21544
8 changed files with 405 additions and 164 deletions

View file

@ -388,12 +388,14 @@
(setup-rect-selrect)))
(defn- setup-image
[{:keys [metadata] :as shape} props]
[shape props]
(let [metadata (or (:metadata shape) (:metadata props))]
(-> (setup-rect shape props)
(assoc
:metadata metadata
:proportion (/ (:width metadata)
(:height metadata))
:proportion-lock true)))
:proportion-lock true))))
(defn setup-shape
"A function that initializes the geometric data of
@ -409,7 +411,9 @@
(defn make-shape
"Make a non group shape, ready to use."
[type geom-props attrs]
(-> (make-minimal-shape type)
(-> (if-not (= type :group)
(make-minimal-shape type)
(make-minimal-group uuid/zero geom-props (:name attrs)))
(setup-shape geom-props)
(merge attrs)))

View file

@ -1848,6 +1848,21 @@
}
}
.remove-graphics-dialog {
.close {
border: 1px solid $color-gray-30;
background: $color-canvas;
border-radius: 3px;
padding: 0.5rem 1rem;
cursor: pointer;
margin-right: 8px;
&:hover {
background: $color-gray-20;
}
}
}
//- LOGIN
.login-register {
background-color: $color-white;

View file

@ -13,11 +13,16 @@
[app.common.geom.point :as gpt]
[app.common.geom.proportions :as gpr]
[app.common.geom.shapes :as gsh]
[app.common.geom.shapes.rect :as gpsr]
[app.common.logging :as log]
[app.common.pages.changes-builder :as pcb]
[app.common.pages.helpers :as cph]
[app.common.spec :as us]
[app.common.text :as txt]
[app.common.transit :as t]
[app.common.types.container :as ctn]
[app.common.types.file :as ctf]
[app.common.types.pages-list :as ctpl]
[app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctst]
[app.common.uuid :as uuid]
@ -25,6 +30,7 @@
[app.main.data.comments :as dcm]
[app.main.data.events :as ev]
[app.main.data.messages :as msg]
[app.main.data.modal :as modal]
[app.main.data.users :as du]
[app.main.data.workspace.bool :as dwb]
[app.main.data.workspace.changes :as dch]
@ -49,17 +55,19 @@
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.svg-upload :as svg]
[app.main.data.workspace.thumbnails :as dwth]
[app.main.data.workspace.transforms :as dwt]
[app.main.data.workspace.undo :as dwu]
[app.main.data.workspace.viewport :as dwv]
[app.main.data.workspace.zoom :as dwz]
[app.main.features :as features]
[app.main.repo :as rp]
[app.main.streams :as ms]
[app.util.dom :as dom]
[app.util.globals :as ug]
[app.util.http :as http]
[app.util.i18n :as i18n]
[app.util.i18n :as i18n :refer [tr]]
[app.util.router :as rt]
[app.util.timers :as tm]
[app.util.webapi :as wapi]
@ -78,6 +86,7 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(declare file-initialized)
(declare remove-graphics)
;; --- Initialize Workspace
@ -170,15 +179,20 @@
:current-file-comments-users (d/index-by :id file-comments-users)))
ptk/WatchEvent
(watch [_ _ _]
(watch [_ state _]
(let [file-id (:id file)
ignore-until (:ignore-sync-until file)
some-graphics? (some? (-> file :data :media))
needs-update? (some #(and (> (:modified-at %) (:synced-at %))
(or (not ignore-until)
(> (:modified-at %) ignore-until)))
libraries)]
libraries)
components-v2 (features/active-feature? state :components-v2)]
(rx/merge
(rx/of (fbc/fix-bool-contents))
(if (and some-graphics? components-v2)
(rx/of (remove-graphics (:id file) (:name file)))
(rx/empty))
(if needs-update?
(rx/of (dwl/notify-sync-file file-id))
(rx/empty)))))))
@ -1241,7 +1255,7 @@
(catch :default e
(let [data (ex-data e)]
(if (:not-implemented data)
(rx/of (msg/warn (i18n/tr "errors.clipboard-not-implemented")))
(rx/of (msg/warn (tr "errors.clipboard-not-implemented")))
(js/console.error "ERROR" e))))))))
(defn paste-from-event
@ -1588,6 +1602,163 @@
(dwu/commit-undo-transaction))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Remove graphics
;; TODO: this should be deprecated and removed together with components-v2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- initialize-remove-graphics
[total]
(ptk/reify ::initialize-remove-graphics
ptk/UpdateEvent
(update [_ state]
(assoc state :remove-graphics {:total total
:current nil}))))
(defn- update-remove-graphics
[current]
(ptk/reify ::update-remove-graphics
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:remove-graphics :current] current))))
(defn- complete-remove-graphics
[]
(ptk/reify ::complete-remove-graphics
ptk/UpdateEvent
(update [_ state]
(dissoc state :remove-graphics))))
(defn- create-shapes-svg
[file-id objects pos media-obj]
(let [path (cfg/resolve-file-media media-obj)
upload-images
(fn [svg-data]
(->> (svg/upload-images svg-data file-id)
(rx/map #(assoc svg-data :image-data %))))
process-svg
(fn [svg-data]
(let [[shape children]
(svg/create-svg-shapes svg-data pos objects uuid/zero #{} false)]
[shape children]))]
(->> (http/send! {:method :get :uri path :mode :no-cors})
(rx/map :body)
(rx/map #(vector (:name media-obj) %))
(rx/merge-map dwm/svg->clj)
(rx/merge-map upload-images)
(rx/map process-svg)
(rx/catch ; When error downloading media-obj, skip it and continue with next one
#(log/error :hint "error downloading file"
:path path
:name (:name media-obj)
:cause %)))))
(defn- create-shapes-img
[pos {:keys [name width height id mtype] :as media-obj}]
(let [group-shape (cts/make-shape :group
{:x (:x pos)
:y (:y pos)
:width width
:height height}
{:name name
:frame-id uuid/zero
:parent-id uuid/zero})
img-shape (cts/make-shape :image
{:x (:x pos)
:y (:y pos)
:width width
:height height
:metadata {:id id
:width width
:height height
:mtype mtype}}
{:name name
:frame-id uuid/zero
:parent-id (:id group-shape)})]
(rx/of [group-shape [img-shape]])))
(defn- remove-graphic
[it file-data page [index [media-obj pos]]]
(let [process-shapes
(fn [[shape children]]
(let [page' (reduce #(ctst/add-shape (:id %2) %2 %1 uuid/zero (:parent-id %2) nil false)
page
(cons shape children))
shape' (ctn/get-shape page' (:id shape))
path (cph/merge-path-item (tr "workspace.assets.graphics") (:path media-obj))
[component-shape component-shapes updated-shapes]
(ctn/make-component-shape shape' (:objects page') (:id file-data) true)
changes (-> (pcb/empty-changes it)
(pcb/set-save-undo? false)
(pcb/with-page page')
(pcb/with-objects (:objects page'))
(pcb/with-library-data file-data)
(pcb/delete-media (:id media-obj))
(pcb/add-objects (cons shape children))
(pcb/add-component (:id component-shape)
path
(:name media-obj)
component-shapes
updated-shapes
(:id shape)
(:id page)))]
(dch/commit-changes changes)))
shapes (if (= (:mtype media-obj) "image/svg+xml")
(create-shapes-svg (:id file-data) (:objects page) pos media-obj)
(create-shapes-img pos media-obj))]
(rx/concat
(rx/of (update-remove-graphics index))
(rx/map process-shapes shapes))))
(defn- remove-graphics
[file-id file-name]
(ptk/reify ::remove-graphics
ptk/WatchEvent
(watch [it state _]
(let [file-data (wsh/get-file state file-id)
grid-gap 50
[file-data' page-id start-pos]
(ctf/get-or-add-library-page file-data grid-gap)
new-page? (nil? (ctpl/get-page file-data page-id))
page (ctpl/get-page file-data' page-id)
media (vals (:media file-data'))
media-points
(map #(assoc % :points (gpsr/rect->points {:x 0
:y 0
:width (:width %)
:height (:height %)}))
media)
shape-grid
(ctst/generate-shape-grid media-points start-pos grid-gap)]
(rx/concat
(rx/of (modal/show {:type :remove-graphics-dialog :file-name file-name})
(initialize-remove-graphics (count media)))
(when new-page?
(rx/of (dch/commit-changes (-> (pcb/empty-changes it)
(pcb/set-save-undo? false)
(pcb/add-page (:id page) page)))))
(rx/mapcat (partial remove-graphic it file-data' page)
(rx/from (d/enumerate (d/zip media shape-grid))))
(rx/of (modal/hide)
(complete-remove-graphics)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Exports
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -17,8 +17,6 @@
[app.main.store :as st]
[app.util.http :as http]
[app.util.i18n :refer [tr]]
[app.util.svg :as usvg]
[app.util.webapi :as wapi]
[beicon.core :as rx]
[cljs.spec.alpha :as s]
[cuerdas.core :as str]
@ -35,27 +33,6 @@
(catch :default _err
(rx/throw {:type :svg-parser}))))
(defn extract-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 data-uri->blob
[data-uri]
(let [[mtype b64-data] (str/split data-uri ";base64,")
mtype (subs mtype (inc (str/index-of mtype ":")))
decoded (.atob js/window b64-data)
size (.-length ^js decoded)
content (js/Uint8Array. size)]
(doseq [i (range 0 size)]
(aset content i (.charCodeAt decoded i)))
(wapi/create-blob content mtype)))
;; TODO: rename to bitmap-image-uploaded
(defn image-uploaded
[image {:keys [x y]}]
@ -82,26 +59,8 @@
;; Once the SVG is uploaded, we need to extract all the bitmap
;; images and upload them separately, then proceed to create
;; all shapes.
(->> (rx/from (usvg/collect-images svg-data))
(rx/map (fn [uri]
(merge
{:file-id file-id
:is-local true}
(if (str/starts-with? uri "data:")
{:name "image"
:content (data-uri->blob uri)}
{:name (extract-name uri)
:url uri}))))
(rx/mapcat (fn [uri-data]
(->> (rp/mutation! (if (contains? uri-data :content)
:upload-file-media-object
:create-file-media-object-from-url) uri-data)
;; When the image uploaded fail we skip the shape
;; returning `nil` will afterward not create the shape.
(rx/catch #(rx/of nil))
(rx/map #(vector (:url uri-data) %)))))
(rx/reduce (fn [acc [url image]] (assoc acc url image)) {})
(rx/map #(svg/create-svg-shapes (assoc svg-data :image-data %) position))))))
(->> (svg/upload-images svg-data file-id)
(rx/map #(svg/add-svg-shapes (assoc svg-data :image-data %) position))))))
(defn- process-uris
[{:keys [file-id local? name uris mtype on-image on-svg]}]
@ -112,13 +71,13 @@
(prepare [uri]
{:file-id file-id
:is-local local?
:name (or name (extract-name uri))
:name (or name (svg/extract-name uri))
:url uri})
(fetch-svg [name uri]
(->> (http/send! {:method :get :uri uri :mode :no-cors})
(rx/map #(vector
(or name (extract-name uri))
(or name (svg/extract-name uri))
(:body %)))))]
(rx/merge
@ -234,6 +193,7 @@
(rx/catch handle-error)
(rx/finalize #(st/emit! (dm/hide-tag :media-loading)))))))))
;; Deprecated in components-v2
(defn upload-media-asset
[params]
(let [params (assoc params

View file

@ -20,9 +20,11 @@
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.repo :as rp]
[app.util.color :as uc]
[app.util.path.parser :as upp]
[app.util.svg :as usvg]
[app.util.webapi :as wapi]
[beicon.core :as rx]
[cuerdas.core :as str]
[potok.core :as ptk]))
@ -411,37 +413,78 @@
(mapv #(usvg/inherit-attributes attrs %)))]
[shape children]))))))
(defn add-svg-child-changes [page-id objects selected frame-id parent-id svg-data [unames changes] [index data]]
(let [[shape children] (parse-svg-element frame-id svg-data data unames)]
(if (some? shape)
(let [shape-id (:id shape)
(defn create-svg-children
[objects selected frame-id parent-id svg-data [unames children] [_index svg-element]]
(let [[new-shape new-children] (parse-svg-element frame-id svg-data svg-element unames)]
(if (some? new-shape)
(let [shape-id (:id new-shape)
new-shape (dwsh/make-new-shape shape objects selected)
changes (-> changes
(pcb/add-object new-shape)
(pcb/change-parent parent-id [new-shape] index))
new-shape' (-> (dwsh/make-new-shape new-shape objects selected)
(assoc :parent-id parent-id))
unames (conj unames (:name new-shape))
children (conj children new-shape')
unames (conj unames (:name new-shape'))
reducer-fn (partial add-svg-child-changes page-id objects selected frame-id shape-id svg-data)]
(reduce reducer-fn [unames changes] (d/enumerate children)))
reducer-fn (partial create-svg-children objects selected frame-id shape-id svg-data)]
[unames changes])))
(reduce reducer-fn [unames children] (d/enumerate new-children)))
[unames children])))
(defn data-uri->blob
[data-uri]
(let [[mtype b64-data] (str/split data-uri ";base64,")
mtype (subs mtype (inc (str/index-of mtype ":")))
decoded (.atob js/window b64-data)
size (.-length ^js decoded)
content (js/Uint8Array. size)]
(doseq [i (range 0 size)]
(aset content i (.charCodeAt decoded i)))
(wapi/create-blob content mtype)))
(defn extract-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 upload-images
"Extract all bitmap images inside the svg data, and upload them, associated to the file.
Return a map {<url> <image-data>}."
[svg-data file-id]
(->> (rx/from (usvg/collect-images svg-data))
(rx/map (fn [uri]
(merge
{:file-id file-id
:is-local true}
(if (str/starts-with? uri "data:")
{:name "image"
:content (data-uri->blob uri)}
{:name (extract-name uri)
:url uri}))))
(rx/mapcat (fn [uri-data]
(->> (rp/mutation! (if (contains? uri-data :content)
:upload-file-media-object
:create-file-media-object-from-url) uri-data)
;; When the image uploaded fail we skip the shape
;; returning `nil` will afterward not create the shape.
(rx/catch #(rx/of nil))
(rx/map #(vector (:url uri-data) %)))))
(rx/reduce (fn [acc [url image]] (assoc acc url image)) {})))
(defn create-svg-shapes
[svg-data {:keys [x y] :as position}]
(ptk/reify ::create-svg-shapes
ptk/WatchEvent
(watch [it state _]
[svg-data {:keys [x y] :as position} objects frame-id selected center?]
(try
(let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
frame-id (ctst/top-nested-frame objects position)
selected (wsh/lookup-selected state)
[vb-x vb-y vb-width vb-height] (svg-dimensions svg-data)
x (- x vb-x (/ vb-width 2))
y (- y vb-y (/ vb-height 2))
(let [[vb-x vb-y vb-width vb-height] (svg-dimensions svg-data)
x (if center?
(- x vb-x (/ vb-width 2))
x)
y (if center?
(- y vb-y (/ vb-height 2))
y)
unames (ctst/retrieve-used-names objects)
@ -482,22 +525,50 @@
(assoc :defs def-nodes)
(assoc :content (into [base-background-shape] (:content svg-data))))
;; Creates the root shape
;; Create the root shape
new-shape (dwsh/make-new-shape root-shape objects selected)
root-attrs (-> (:attrs svg-data)
(usvg/format-styles))
[_ new-children]
(reduce (partial create-svg-children objects selected frame-id root-id svg-data)
[unames []]
(d/enumerate (->> (:content svg-data)
(mapv #(usvg/inherit-attributes root-attrs %)))))]
[new-shape new-children])
(catch :default e
(.error js/console "Error SVG" e)
(rx/throw {:type :svg-parser
:data e}))))
(defn add-svg-shapes
[svg-data position]
(ptk/reify ::add-svg-shapes
ptk/WatchEvent
(watch [it state _]
(let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
frame-id (ctst/top-nested-frame objects position)
selected (wsh/lookup-selected state)
[new-shape new-children]
(create-svg-shapes svg-data position objects frame-id selected true)
changes (-> (pcb/empty-changes it page-id)
(pcb/with-objects objects)
(pcb/add-object new-shape))
root-attrs (-> (:attrs svg-data)
(usvg/format-styles))
changes
(reduce (fn [changes [index new-child]]
(-> changes
(pcb/add-object new-child)
(pcb/change-parent (:parent-id new-child) [new-child] index)))
changes
(d/enumerate new-children))
;; Reduce the children to create the changes to add the children shapes
[_ changes]
(reduce (partial add-svg-child-changes page-id objects selected frame-id root-id svg-data)
[unames changes]
(d/enumerate (->> (:content svg-data)
(mapv #(usvg/inherit-attributes root-attrs %)))))
changes (pcb/resize-parents changes
(->> changes
:redo-changes
@ -507,9 +578,5 @@
vec))]
(rx/of (dch/commit-changes changes)
(dws/select-shapes (d/ordered-set root-id))))
(dws/select-shapes (d/ordered-set (:id new-shape))))))))
(catch :default e
(.error js/console "Error SVG" e)
(rx/throw {:type :svg-parser
:data e}))))))

View file

@ -300,7 +300,6 @@
[ids]
(l/derived #(select-keys % ids) workspace-modifiers))
(def workspace-modifiers-with-objects
(l/derived
(fn [state]
@ -361,6 +360,10 @@
(def workspace-focus-selected
(l/derived :workspace-focus-selected st/state))
;; Remove this when deprecating components-v2
(def remove-graphics
(l/derived :remove-graphics st/state))
;; ---- Viewer refs
(defn lookup-viewer-objects-by-id

View file

@ -9,6 +9,7 @@
[app.common.colors :as clr]
[app.common.data.macros :as dm]
[app.main.data.messages :as msg]
[app.main.data.modal :as modal]
[app.main.data.workspace :as dw]
[app.main.data.workspace.persistence :as dwp]
[app.main.features :as features]
@ -168,3 +169,19 @@
:layout layout}]
[:& workspace-loader])]]]]]]))
(mf/defc remove-graphics-dialog
{::mf/register modal/components
::mf/register-as :remove-graphics-dialog}
[{:keys [] :as ctx}]
(let [remove-state (mf/deref refs/remove-graphics)
close #(modal/hide!)]
[:div.modal-overlay
[:div.modal-container.remove-graphics-dialog
[:div.modal-header
[:div.modal-header-title
[:h2 (str "Updating " (:file-name ctx) "...")]]
[:div.modal-close-button
{:on-click close} i/close]]
[:div.modal-content
[:p (str "Converting " (:current remove-state) " / " (:total remove-state))]]]]))

View file

@ -759,9 +759,7 @@
(mf/use-fn
(mf/deps object selected-objects item-ref on-drag-start)
(fn [event]
(on-asset-drag-start event object selected-objects item-ref :graphics on-drag-start)))
]
(on-asset-drag-start event object selected-objects item-ref :graphics on-drag-start)))]
[:div {:ref item-ref
:class-name (dom/classnames
@ -919,6 +917,8 @@
groups (group-assets objects reverse-sort?)
components-v2 (mf/use-ctx ctx/components-v2)
add-graphic
(mf/use-fn
(fn []
@ -1051,12 +1051,13 @@
:open? open?}
(when local?
[:& asset-section-block {:role :title-button}
(when-not components-v2
[:div.assets-button {:on-click add-graphic}
i/plus
[:& file-uploader {:accept cm/str-image-types
:multi true
:ref input-ref
:on-selected on-file-selected}]]])
:on-selected on-file-selected}]])])
[:& asset-section-block {:role :content}
[:& graphics-group {:file-id file-id
@ -1921,6 +1922,8 @@
(count (:colors selected-assets))
(count (:typographies selected-assets)))
components-v2 (mf/use-ctx ctx/components-v2)
toggle-open #(st/emit! (dwl/set-assets-box-open (:id file) :library (not open?)))
url (rt/resolve router :workspace
@ -2053,7 +2056,8 @@
show-graphics? (and (or (= (:box filters) :all)
(= (:box filters) :graphics))
(or (> (count media) 0)
(str/empty? (:term filters))))
(and (str/empty? (:term filters))
(not components-v2))))
show-colors? (and (or (= (:box filters) :all)
(= (:box filters) :colors))
(or (> (count colors) 0)