mirror of
https://github.com/penpot/penpot.git
synced 2026-05-09 10:08:54 +00:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
6f6a750373
25
CHANGES.md
25
CHANGES.md
@ -20,7 +20,9 @@
|
||||
- Add layer opacity and blend mode to shapes [Taiga #937](https://tree.taiga.io/project/penpot/us/937)
|
||||
- Add more chinese translations [#726](https://github.com/penpot/penpot/pull/726)
|
||||
- Add native support for text-direction (RTL, LTR & auto).
|
||||
- Add several enhancements in shape selection [Taiga #1195](https://tree.taiga.io/project/penpot/us/1195)
|
||||
- Add thumbnail in memory caching mechanism.
|
||||
- Add turkish translation strings [#759](https://github.com/penpot/penpot/pull/759), [#794](https://github.com/penpot/penpot/pull/794)
|
||||
- Duplicate and move files and projects [Taiga #267](https://tree.taiga.io/project/penpot/us/267)
|
||||
- Hide viewer navbar on fullscreen [Taiga 1375](https://tree.taiga.io/project/penpot/us/1375)
|
||||
- Import SVG will create Penpot's shapes [Taiga #1006](https://tree.taiga.io/project/penpot/us/1066)
|
||||
@ -30,18 +32,22 @@
|
||||
- Rename artboard with double click on the title [Taiga #1392](https://tree.taiga.io/project/penpot/us/1392)
|
||||
- Replace Slate-Editor with DraftJS [Taiga #1346](https://tree.taiga.io/project/penpot/us/1346)
|
||||
- Set proper page title [Taiga #1377](https://tree.taiga.io/project/penpot/us/1377)
|
||||
- Several enhancements in shape selection [Taiga #1195](https://tree.taiga.io/project/penpot/us/1195)
|
||||
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Disables buttons in view mode for users without permissions [Taiga #1328](https://tree.taiga.io/project/penpot/issue/1328)
|
||||
- Disable buttons in view mode for users without permissions [Taiga #1328](https://tree.taiga.io/project/penpot/issue/1328)
|
||||
- Fix broken profile and profile options form.
|
||||
- Fix calculate size of some animated gifs [Taiga #1487](https://tree.taiga.io/project/penpot/issue/1487)
|
||||
- Fix error with the "Navigate to" button on prototypes [Taiga #1268](https://tree.taiga.io/project/penpot/issue/1268)
|
||||
- Fix issue when undo after changing the artboard of a shape [Taiga #1304](https://tree.taiga.io/project/penpot/issue/1304)
|
||||
- Fix issue with typographies panel cannot be collapsed [#707](https://github.com/penpot/penpot/issues/707)
|
||||
- Fix issue with Alt key in distance measurement [#672](https://github.com/penpot/penpot/issues/672)
|
||||
- Fix issue with blending modes in masks [Taiga #1476](https://tree.taiga.io/project/penpot/issue/1476)
|
||||
- Fix issue with blocked shapes [Taiga #1480](https://tree.taiga.io/project/penpot/issue/1480)
|
||||
- Fix issue with comments styles on dashboard [Taiga #1405](https://tree.taiga.io/project/penpot/issue/1405)
|
||||
- Fix issue with default square grid [Taiga #1344](https://tree.taiga.io/project/penpot/issue/1344)
|
||||
- Fix issue with enter key shortcut [#775](https://github.com/penpot/penpot/issues/775)
|
||||
- Fix issue with enter to edit paths [Taiga #1481](https://tree.taiga.io/project/penpot/issue/1481)
|
||||
- Fix issue with mask and flip [#715](https://github.com/penpot/penpot/issues/715)
|
||||
- Fix issue with masks interactions outside bounds [#718](https://github.com/penpot/penpot/issues/718)
|
||||
- Fix issue with middle mouse button press moving the canvas when not moving mouse [#717](https://github.com/penpot/penpot/issues/717)
|
||||
@ -49,12 +55,12 @@
|
||||
- Fix issue with rotated blur [Taiga #1370](https://tree.taiga.io/project/penpot/issue/1370)
|
||||
- Fix issue with rotation degree input [#741](https://github.com/penpot/penpot/issues/741)
|
||||
- Fix issue with system shortcuts and application [#737](https://github.com/penpot/penpot/issues/737)
|
||||
- Fix problem with enter key shortcut [#775](https://github.com/penpot/penpot/issues/775)
|
||||
- Updates worksans font [#744](https://github.com/penpot/penpot/issues/744)
|
||||
- Fix problem with team management in dashboard [Taiga #1475](https://tree.taiga.io/project/penpot/issue/1475)
|
||||
- Fix problem with blending modes in masks [Taiga #1476](https://tree.taiga.io/project/penpot/issue/1476)
|
||||
- Fix issue with team management in dashboard [Taiga #1475](https://tree.taiga.io/project/penpot/issue/1475)
|
||||
- Fix issue with typographies panel cannot be collapsed [#707](https://github.com/penpot/penpot/issues/707)
|
||||
- Fix text selection in comments [#745](https://github.com/penpot/penpot/issues/745)
|
||||
- Fix problem with blocked shapes [Taiga #1480](https://tree.taiga.io/project/penpot/issue/1480)
|
||||
- Update Work-Sans font [#744](https://github.com/penpot/penpot/issues/744)
|
||||
|
||||
>>>>>>> origin/staging
|
||||
|
||||
### :arrow_up: Deps updates
|
||||
|
||||
@ -66,7 +72,8 @@
|
||||
|
||||
- iblueer [#726](https://github.com/penpot/penpot/pull/726)
|
||||
- gizembln [#759](https://github.com/penpot/penpot/pull/759)
|
||||
- girafic [#759](https://github.com/penpot/penpot/pull/748)
|
||||
- girafic [#748](https://github.com/penpot/penpot/pull/748)
|
||||
- mbrksntrk [#794](https://github.com/penpot/penpot/pull/794)
|
||||
|
||||
|
||||
## 1.3.0-alpha
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
(ns app.http.errors
|
||||
"A errors handling for the http server."
|
||||
(:require
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.util.log4j :refer [update-thread-context!]]
|
||||
[clojure.tools.logging :as log]
|
||||
@ -86,15 +87,54 @@
|
||||
|
||||
(defmethod handle-exception :default
|
||||
[error request]
|
||||
(let [cdata (get-error-context request error)]
|
||||
(let [edata (ex-data error)]
|
||||
;; NOTE: this is a special case for the idle-in-transaction error;
|
||||
;; when it happens, the connection is automatically closed and
|
||||
;; next-jdbc combines the two errors in a single ex-info. We only
|
||||
;; need the :handling error, because the :rollback error will be
|
||||
;; always "connection closed".
|
||||
(if (and (ex/exception? (:rollback edata))
|
||||
(ex/exception? (:handling edata)))
|
||||
(handle-exception (:handling edata) request)
|
||||
(let [cdata (get-error-context request error)]
|
||||
(update-thread-context! cdata)
|
||||
(log/errorf error "internal error: %s (id: %s)"
|
||||
(ex-message error)
|
||||
(str (:id cdata)))
|
||||
{:status 500
|
||||
:body {:type :server-error
|
||||
:hint (ex-message error)
|
||||
:data edata}}))))
|
||||
|
||||
(defmethod handle-exception org.postgresql.util.PSQLException
|
||||
[error request]
|
||||
(let [cdata (get-error-context request error)
|
||||
state (.getSQLState ^java.sql.SQLException error)]
|
||||
|
||||
(update-thread-context! cdata)
|
||||
(log/errorf error "internal error: %s (id: %s)"
|
||||
(log/errorf error "PSQL Exception: %s (id: %s, state: %s)"
|
||||
(ex-message error)
|
||||
(str (:id cdata)))
|
||||
{:status 500
|
||||
:body {:type :server-error
|
||||
:hint (ex-message error)
|
||||
:data (ex-data error)}}))
|
||||
(str (:id cdata))
|
||||
state)
|
||||
|
||||
(cond
|
||||
(= state "57014")
|
||||
{:status 504
|
||||
:body {:type :server-timeout
|
||||
:code :statement-timeout
|
||||
:hint (ex-message error)}}
|
||||
|
||||
(= state "25P03")
|
||||
{:status 504
|
||||
:body {:type :server-timeout
|
||||
:code :idle-in-transaction-timeout
|
||||
:hint (ex-message error)}}
|
||||
|
||||
:else
|
||||
{:status 500
|
||||
:body {:type :server-timeout
|
||||
:hint (ex-message error)
|
||||
:state state}})))
|
||||
|
||||
(defn handle
|
||||
[error req]
|
||||
|
||||
@ -157,8 +157,11 @@
|
||||
:code :media-type-mismatch
|
||||
:hint (str "Seems like you are uploading a file whose content does not match the extension."
|
||||
"Expected: " mtype ". Got: " mtype')))
|
||||
{:width (.getImageWidth instance)
|
||||
:height (.getImageHeight instance)
|
||||
;; For an animated GIF, getImageWidth/Height returns the delta size of one frame (if no frame given
|
||||
;; it returns size of the last one), whereas getPageWidth/Height always return the full size of
|
||||
;; any frame.
|
||||
{:width (.getPageWidth instance)
|
||||
:height (.getPageHeight instance)
|
||||
:mtype mtype}))))
|
||||
|
||||
(defmethod process :default
|
||||
@ -185,8 +188,9 @@
|
||||
;; --- Utility functions
|
||||
|
||||
(defn validate-media-type
|
||||
[media-type]
|
||||
(when-not (cm/valid-media-types media-type)
|
||||
(ex/raise :type :validation
|
||||
:code :media-type-not-allowed
|
||||
:hint "Seems like you are uploading an invalid media object")))
|
||||
([mtype] (validate-media-type mtype cm/valid-media-types))
|
||||
([mtype allowed]
|
||||
(when-not (contains? allowed mtype)
|
||||
(ex/raise :type :validation
|
||||
:code :media-type-not-allowed
|
||||
:hint "Seems like you are uploading an invalid media object"))))
|
||||
|
||||
@ -84,10 +84,24 @@
|
||||
(d/without-nils)
|
||||
(blob/encode))))))
|
||||
|
||||
(def sql:retrieve-used-libraries
|
||||
"select flr.*
|
||||
from file_library_rel as flr
|
||||
inner join file as l on (flr.library_file_id = l.id)
|
||||
where flr.file_id = ?
|
||||
and l.deleted_at is null")
|
||||
|
||||
(def sql:retrieve-used-media-objects
|
||||
"select fmo.*
|
||||
from file_media_object as fmo
|
||||
inner join storage_object as o on (fmo.media_id = o.id)
|
||||
where fmo.file_id = ?
|
||||
and o.deleted_at is null")
|
||||
|
||||
(defn duplicate-file
|
||||
[conn {:keys [profile-id file index project-id name]} {:keys [reset-shared-flag] :as opts}]
|
||||
(let [flibs (db/query conn :file-library-rel {:file-id (:id file)})
|
||||
fmeds (db/query conn :file-media-object {:file-id (:id file)})
|
||||
(let [flibs (db/exec! conn [sql:retrieve-used-libraries (:id file)])
|
||||
fmeds (db/exec! conn [sql:retrieve-used-media-objects (:id file)])
|
||||
|
||||
;; memo uniform creation/modification date
|
||||
now (dt/now)
|
||||
@ -185,7 +199,8 @@
|
||||
(defn duplicate-project
|
||||
[conn {:keys [profile-id project name] :as params}]
|
||||
(let [files (db/query conn :file
|
||||
{:project-id (:id project)}
|
||||
{:project-id (:id project)
|
||||
:deleted-at nil}
|
||||
{:columns [:id]})
|
||||
|
||||
project (cond-> project
|
||||
|
||||
@ -386,8 +386,8 @@
|
||||
|
||||
(sv/defmethod ::update-profile-photo
|
||||
[{:keys [pool storage] :as cfg} {:keys [profile-id file] :as params}]
|
||||
(media/validate-media-type (:content-type file))
|
||||
(db/with-atomic [conn pool]
|
||||
(media/validate-media-type (:content-type file) #{"image/jpeg" "image/png" "image/webp"})
|
||||
(let [profile (db/get-by-id conn :profile profile-id)
|
||||
_ (media/run cfg {:cmd :info :input {:path (:tempfile file)
|
||||
:mtype (:content-type file)}})
|
||||
|
||||
@ -255,9 +255,10 @@
|
||||
|
||||
(sv/defmethod ::update-team-photo
|
||||
[{:keys [pool storage] :as cfg} {:keys [profile-id file team-id] :as params}]
|
||||
(media/validate-media-type (:content-type file))
|
||||
(db/with-atomic [conn pool]
|
||||
(teams/check-edition-permissions! conn profile-id team-id)
|
||||
(media/validate-media-type (:content-type file) #{"image/jpeg" "image/png" "image/webp"})
|
||||
|
||||
(let [team (teams/retrieve-team conn profile-id team-id)
|
||||
_ (media/run cfg {:cmd :info :input {:path (:tempfile file)
|
||||
:mtype (:content-type file)}})
|
||||
|
||||
@ -145,6 +145,10 @@
|
||||
:name (str "file" i)}
|
||||
params))))
|
||||
|
||||
(defn mark-file-deleted*
|
||||
([params] (mark-file-deleted* *pool* params))
|
||||
([conn {:keys [id] :as params}]
|
||||
(#'files/mark-file-deleted conn {:id id})))
|
||||
|
||||
(defn create-team*
|
||||
([i params] (create-team* *pool* i params))
|
||||
@ -160,7 +164,6 @@
|
||||
:role :owner})
|
||||
team)))
|
||||
|
||||
|
||||
(defn create-file-media-object*
|
||||
([params] (create-file-media-object* *pool* params))
|
||||
([conn {:keys [name width height mtype file-id is-local media-id]
|
||||
|
||||
@ -73,7 +73,7 @@
|
||||
(let [[item :as rows] (db/query th/*pool* :file-media-object {:file-id (:id result)})]
|
||||
(t/is (= 1 (count rows)))
|
||||
|
||||
;; Checj that bot items have different ids
|
||||
;; Check that both items have different ids
|
||||
(t/is (not= (:id item) (:id mobj)))
|
||||
|
||||
;; check that both file-media-objects points to the same storage object.
|
||||
@ -92,6 +92,67 @@
|
||||
|
||||
))))
|
||||
|
||||
(t/deftest duplicate-file-with-deleted-rels
|
||||
(let [storage (:app.storage/storage th/*system*)
|
||||
sobject (sto/put-object storage {:content (sto/content "content")
|
||||
:content-type "text/plain"
|
||||
:other "data"})
|
||||
profile (th/create-profile* 1 {:is-active true})
|
||||
project (th/create-project* 1 {:team-id (:default-team-id profile)
|
||||
:profile-id (:id profile)})
|
||||
file1 (th/create-file* 1 {:profile-id (:id profile)
|
||||
:project-id (:id project)})
|
||||
file2 (th/create-file* 2 {:profile-id (:id profile)
|
||||
:project-id (:id project)
|
||||
:is-shared true})
|
||||
|
||||
libl (th/link-file-to-library* {:file-id (:id file1)
|
||||
:library-id (:id file2)})
|
||||
|
||||
mobj (th/create-file-media-object* {:file-id (:id file1)
|
||||
:is-local false
|
||||
:media-id (:id sobject)})
|
||||
|
||||
_ (th/mark-file-deleted* {:id (:id file2)})
|
||||
_ (sto/del-object storage (:id sobject))]
|
||||
|
||||
(th/update-file*
|
||||
{:file-id (:id file1)
|
||||
:profile-id (:id profile)
|
||||
:changes [{:type :add-media
|
||||
:object (select-keys mobj [:id :width :height :mtype :name])}]})
|
||||
|
||||
(let [data {::th/type :duplicate-file
|
||||
:profile-id (:id profile)
|
||||
:file-id (:id file1)
|
||||
:name "file 1 (copy)"}
|
||||
out (th/mutation! data)]
|
||||
|
||||
;; (th/print-result! out)
|
||||
|
||||
;; Check tha tresult is correct
|
||||
(t/is (nil? (:error out)))
|
||||
(let [result (:result out)]
|
||||
|
||||
;; Check that the returned result is a file but has different id
|
||||
;; and different name.
|
||||
(t/is (= "file 1 (copy)" (:name result)))
|
||||
(t/is (not= (:id file1) (:id result)))
|
||||
|
||||
;; Check that the deleted library is not duplicated
|
||||
(let [[item :as rows] (db/query th/*pool* :file-library-rel {:file-id (:id result)})]
|
||||
(t/is (= 0 (count rows))))
|
||||
|
||||
;; Check that the new file has no media objects
|
||||
(let [[item :as rows] (db/query th/*pool* :file-media-object {:file-id (:id result)})]
|
||||
(t/is (= 0 (count rows))))
|
||||
|
||||
;; Check the total number of files
|
||||
(let [rows (db/query th/*pool* :file {:project-id (:id project)})]
|
||||
(t/is (= 3 (count rows))))
|
||||
|
||||
))))
|
||||
|
||||
(t/deftest duplicate-project
|
||||
(let [storage (:app.storage/storage th/*system*)
|
||||
sobject (sto/put-object storage {:content (sto/content "content")
|
||||
@ -162,6 +223,77 @@
|
||||
|
||||
)))))
|
||||
|
||||
(t/deftest duplicate-project-with-deleted-files
|
||||
(let [storage (:app.storage/storage th/*system*)
|
||||
sobject (sto/put-object storage {:content (sto/content "content")
|
||||
:content-type "text/plain"
|
||||
:other "data"})
|
||||
profile (th/create-profile* 1 {:is-active true})
|
||||
project (th/create-project* 1 {:team-id (:default-team-id profile)
|
||||
:profile-id (:id profile)})
|
||||
file1 (th/create-file* 1 {:profile-id (:id profile)
|
||||
:project-id (:id project)})
|
||||
file2 (th/create-file* 2 {:profile-id (:id profile)
|
||||
:project-id (:id project)
|
||||
:is-shared true})
|
||||
|
||||
libl (th/link-file-to-library* {:file-id (:id file1)
|
||||
:library-id (:id file2)})
|
||||
mobj (th/create-file-media-object* {:file-id (:id file1)
|
||||
:is-local false
|
||||
:media-id (:id sobject)})]
|
||||
|
||||
(th/update-file*
|
||||
{:file-id (:id file1)
|
||||
:profile-id (:id profile)
|
||||
:changes [{:type :add-media
|
||||
:object (select-keys mobj [:id :width :height :mtype :name])}]})
|
||||
|
||||
(th/mark-file-deleted* {:id (:id file1)})
|
||||
|
||||
(let [data {::th/type :duplicate-project
|
||||
:profile-id (:id profile)
|
||||
:project-id (:id project)
|
||||
:name "project 1 (copy)"}
|
||||
out (th/mutation! data)]
|
||||
|
||||
;; Check tha tresult is correct
|
||||
(t/is (nil? (:error out)))
|
||||
|
||||
(let [result (:result out)]
|
||||
;; Check that they are the same project but different id and name
|
||||
(t/is (= "project 1 (copy)" (:name result)))
|
||||
(t/is (not= (:id project) (:id result)))
|
||||
|
||||
;; Check the total number of projects (previously is 2, now is 3)
|
||||
(let [rows (db/query th/*pool* :project {:team-id (:default-team-id profile)})]
|
||||
(t/is (= 3 (count rows))))
|
||||
|
||||
;; Check that the new project has only the second file
|
||||
(let [p1-files (db/query th/*pool* :file
|
||||
{:project-id (:id project)}
|
||||
{:order-by [:name]})
|
||||
p2-files (db/query th/*pool* :file
|
||||
{:project-id (:id result)}
|
||||
{:order-by [:name]})]
|
||||
(t/is (= (count (rest p1-files))
|
||||
(count p2-files)))
|
||||
|
||||
;; check that the both files are equivalent
|
||||
(doseq [[fa fb] (map vector (rest p1-files) p2-files)]
|
||||
(t/is (not= (:id fa) (:id fb)))
|
||||
(t/is (= (:name fa) (:name fb)))
|
||||
|
||||
(when (= (:id fa) (:id file1))
|
||||
(t/is (false? (b/equals? (:data fa)
|
||||
(:data fb)))))
|
||||
|
||||
(when (= (:id fa) (:id file2))
|
||||
(t/is (false? (b/equals? (:data fa)
|
||||
(:data fb))))))
|
||||
|
||||
)))))
|
||||
|
||||
(t/deftest move-file-on-same-team
|
||||
(let [profile (th/create-profile* 1 {:is-active true})
|
||||
team (th/create-team* 1 {:profile-id (:id profile)})
|
||||
|
||||
@ -13,27 +13,42 @@
|
||||
[app.common.data :as d]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(def version-re #"^(([A-Za-z]+)\-?)?(\d+\.\d+\.\d+)(\-?((alpha|prealpha|beta|rc)(\d+)?))?(\-?(\d+))?(\-?(\w+))$")
|
||||
(def version-re #"^(([A-Za-z]+)\-?)?((\d+)\.(\d+)\.(\d+))(\-?((alpha|prealpha|beta|rc|dev)(\d+)?))?(\-?(\d+))?(\-?g(\w+))$")
|
||||
|
||||
(defn parse
|
||||
[data]
|
||||
(cond
|
||||
(= data "%version%")
|
||||
{:full "develop"
|
||||
:base "develop"
|
||||
:branch "develop"
|
||||
:base "0.0.0"
|
||||
:main "0.0"
|
||||
:major "0"
|
||||
:minor "0"
|
||||
:patch "0"
|
||||
:modifier nil
|
||||
:commit nil
|
||||
:commit-hash nil}
|
||||
|
||||
(string? data)
|
||||
(let [result (re-find version-re data)]
|
||||
(let [result (re-find version-re data)
|
||||
major (get result 4)
|
||||
minor (get result 5)
|
||||
patch (get result 6)
|
||||
base (get result 3)
|
||||
main (str/fmt "%s.%s" major minor)
|
||||
branch (get result 2)]
|
||||
|
||||
{:full data
|
||||
:base (get result 3)
|
||||
:branch (get result 2)
|
||||
:modifier (get result 5)
|
||||
:commit (get result 9)
|
||||
:commit-hash (get result 11)})
|
||||
:base base
|
||||
:main main
|
||||
:major major
|
||||
:minor minor
|
||||
:patch patch
|
||||
:branch branch
|
||||
:modifier (get result 8)
|
||||
:commit (get result 12)
|
||||
:commit-hash (get result 14)})
|
||||
|
||||
:else nil))
|
||||
|
||||
|
||||
@ -36,6 +36,26 @@
|
||||
[data]
|
||||
(xml/js2xml (clj->js data)))
|
||||
|
||||
(defn ^boolean element?
|
||||
[item]
|
||||
(and (map? item)
|
||||
(= "element" (get item "type"))))
|
||||
|
||||
(defn ^boolean group-element?
|
||||
[item]
|
||||
(and (element? item)
|
||||
(= "g" (get item "name"))))
|
||||
|
||||
(defn ^boolean shape-element?
|
||||
[item]
|
||||
(and (element? item)
|
||||
(str/starts-with? (get-in item ["attributes" "id"]) "shape-")))
|
||||
|
||||
(defn ^boolean foreign-object-element?
|
||||
[item]
|
||||
(and (element? item)
|
||||
(= "foreignObject" (get item "name"))))
|
||||
|
||||
(defn ^boolean empty-defs-element?
|
||||
[item]
|
||||
(and (= (get item "name") "defs")
|
||||
@ -50,10 +70,6 @@
|
||||
(nil? d)
|
||||
(str/empty? d)))))
|
||||
|
||||
(defn ^boolean foreign-object-element?
|
||||
[item]
|
||||
(and (map? item)
|
||||
(= "foreignObject" (get item "name"))))
|
||||
|
||||
(defn flatten-toplevel-svg-elements
|
||||
"Flattens XML data structure if two nested top-side SVG elements found."
|
||||
@ -78,8 +94,14 @@
|
||||
|
||||
(process-element [item xform]
|
||||
(let [item (d/update-when item "elements" #(into [] xform %))]
|
||||
(if (str/starts-with? (get-in item ["attributes" "id"]) "shape-")
|
||||
(assoc item "elements" (get-in item ["elements" 0 "elements"]))
|
||||
(if (shape-element? item)
|
||||
(update item "elements"
|
||||
(fn [elements]
|
||||
;; flatten content of a shape element
|
||||
(into [] (mapcat (fn [item]
|
||||
(if (group-element? item)
|
||||
(get item "elements")
|
||||
[item]))) elements)))
|
||||
item)))]
|
||||
|
||||
(let [xform (comp (remove empty-defs-element?)
|
||||
@ -90,9 +112,7 @@
|
||||
(flatten-toplevel-svg-elements)
|
||||
(walk/prewalk (fn [item]
|
||||
(cond-> item
|
||||
(and (map? item)
|
||||
(string? (get item "name"))
|
||||
(= "element" (get item "type")))
|
||||
(element? item)
|
||||
(process-element xform))))
|
||||
(clj->xml)))))
|
||||
|
||||
@ -234,8 +254,14 @@
|
||||
(p/let [dom (bwr/select page "#screenshot")
|
||||
xmldata (bwr/eval! dom (fn [elem] (.-outerHTML ^js elem)))
|
||||
nodes (process-text-nodes page)
|
||||
nodes (d/index-by :id nodes)]
|
||||
(replace-text-nodes xmldata nodes)))
|
||||
nodes (d/index-by :id nodes)
|
||||
result (replace-text-nodes xmldata nodes)]
|
||||
;; (println "------- ORIGIN:")
|
||||
;; (cljs.pprint/pprint (xml->clj xmldata))
|
||||
;; (println "------- RESULT:")
|
||||
;; (cljs.pprint/pprint (xml->clj result))
|
||||
;; (println "-------")
|
||||
result))
|
||||
|
||||
(render-in-page [page {:keys [uri cookie] :as rctx}]
|
||||
(p/do!
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -274,7 +274,7 @@
|
||||
|
||||
(defn update-team-photo
|
||||
[{:keys [file team-id] :as params}]
|
||||
(us/assert ::di/js-file file)
|
||||
(us/assert ::di/file file)
|
||||
(us/assert ::us/uuid team-id)
|
||||
(ptk/reify ::update-team-photo
|
||||
ptk/WatchEvent
|
||||
|
||||
@ -1015,9 +1015,13 @@
|
||||
{:keys [id type shapes]} (get objects (first selected))]
|
||||
|
||||
(case type
|
||||
(:text :path)
|
||||
:text
|
||||
(rx/of (dwc/start-edition-mode id))
|
||||
|
||||
:path
|
||||
(rx/of (dwc/start-edition-mode id)
|
||||
(dwdp/start-path-edit id))
|
||||
|
||||
:group
|
||||
(rx/of (dwc/select-shapes (into (d/ordered-set) [(last shapes)])))
|
||||
|
||||
|
||||
@ -517,33 +517,6 @@
|
||||
objects (lookup-page-objects state page-id)]
|
||||
(rx/of (expand-all-parents ids objects))))))
|
||||
|
||||
;; --- Start shape "edition mode"
|
||||
(defn stop-path-edit []
|
||||
(ptk/reify ::stop-path-edit
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (get-in state [:workspace-local :edition])]
|
||||
(update state :workspace-local dissoc :edit-path id)))))
|
||||
|
||||
(defn start-path-edit
|
||||
[id]
|
||||
(ptk/reify ::start-path-edit
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
;; Only edit if the object has been created
|
||||
(if-let [id (get-in state [:workspace-local :edition])]
|
||||
(assoc-in state [:workspace-local :edit-path id] {:edit-mode :move
|
||||
:selected #{}
|
||||
:snap-toggled true})
|
||||
state))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(->> stream
|
||||
(rx/filter #(= % :interrupt))
|
||||
(rx/take 1)
|
||||
(rx/map #(stop-path-edit))))))
|
||||
|
||||
(declare clear-edition-mode)
|
||||
|
||||
(defn start-edition-mode
|
||||
@ -562,15 +535,11 @@
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [objects (lookup-page-objects state)
|
||||
path? (= :path (get-in objects [id :type]))]
|
||||
(rx/merge
|
||||
(when path?
|
||||
(rx/of (start-path-edit id)))
|
||||
(->> stream
|
||||
(rx/filter interrupt?)
|
||||
(rx/take 1)
|
||||
(rx/map (constantly clear-edition-mode))))))))
|
||||
(let [objects (lookup-page-objects state)]
|
||||
(->> stream
|
||||
(rx/filter interrupt?)
|
||||
(rx/take 1)
|
||||
(rx/map (constantly clear-edition-mode)))))))
|
||||
|
||||
(def clear-edition-mode
|
||||
(ptk/reify ::clear-edition-mode
|
||||
|
||||
@ -180,11 +180,7 @@
|
||||
(or (= (ptk/type event) ::finish-path)
|
||||
(= (ptk/type event) :esc-pressed)
|
||||
(= event :interrupt) ;; ESC
|
||||
(and (ms/mouse-double-click? event))
|
||||
(and (ms/keyboard-event? event)
|
||||
(= type :down)
|
||||
;; TODO: Enter now finish path but can finish drawing/editing as well
|
||||
(= enter-keycode (:key event)))))
|
||||
(and (ms/mouse-double-click? event))))
|
||||
|
||||
(defn generate-path-changes [page-id shape old-content new-content]
|
||||
(us/verify ::content old-content)
|
||||
@ -572,11 +568,13 @@
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [id (get-in state [:workspace-local :edition])
|
||||
shape (get-in state (get-path state))
|
||||
page-id (:current-page-id state)
|
||||
old-content (get-in state [:workspace-local :edit-path id :old-content])
|
||||
[rch uch] (generate-path-changes page-id shape old-content (:content shape))]
|
||||
(rx/of (dwc/commit-changes rch uch {:commit-local? true}))))))
|
||||
old-content (get-in state [:workspace-local :edit-path id :old-content])]
|
||||
(if (some? old-content)
|
||||
(let [shape (get-in state (get-path state))
|
||||
page-id (:current-page-id state)
|
||||
[rch uch] (generate-path-changes page-id shape old-content (:content shape))]
|
||||
(rx/of (dwc/commit-changes rch uch {:commit-local? true})))
|
||||
(rx/empty))))))
|
||||
|
||||
(declare start-draw-mode)
|
||||
(defn check-changed-content []
|
||||
@ -829,3 +827,37 @@
|
||||
(rx/take 1)
|
||||
(rx/observe-on :async)
|
||||
(rx/map #(handle-new-shape-result shape-id))))))))
|
||||
|
||||
(defn stop-path-edit []
|
||||
(ptk/reify ::stop-path-edit
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [id (get-in state [:workspace-local :edition])]
|
||||
(update state :workspace-local dissoc :edit-path id)))))
|
||||
|
||||
(defn start-path-edit
|
||||
[id]
|
||||
(ptk/reify ::start-path-edit
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [edit-path (get-in state [:workspace-local :edit-path id])]
|
||||
|
||||
(cond-> state
|
||||
(or (not edit-path) (= :draw (:edit-mode edit-path)))
|
||||
(assoc-in [:workspace-local :edit-path id] {:edit-mode :move
|
||||
:selected #{}
|
||||
:snap-toggled true})
|
||||
|
||||
(and (some? edit-path) (= :move (:edit-mode edit-path)))
|
||||
(assoc-in [:workspace-local :edit-path id :edit-mode] :draw))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [mode (get-in state [:workspace-local :edit-path id :edit-mode])]
|
||||
(rx/concat
|
||||
(rx/of (change-edit-mode mode))
|
||||
(->> stream
|
||||
(rx/take-until (->> stream (rx/filter (ptk/type? ::start-path-edit))))
|
||||
(rx/filter #(= % :interrupt))
|
||||
(rx/take 1)
|
||||
(rx/map #(stop-path-edit))))))))
|
||||
|
||||
@ -95,7 +95,8 @@
|
||||
{::mf/wrap-props false
|
||||
::mf/forward-ref true}
|
||||
[props ref]
|
||||
(let [{:keys [id x y width height content grow-type] :as shape} (obj/get props "shape")
|
||||
(let [{:keys [id x y width height content] :as shape} (obj/get props "shape")
|
||||
grow-type (obj/get props "grow-type") ;; This is only needed in workspace
|
||||
embed-fonts? (mf/use-ctx muc/embed-ctx)
|
||||
;; We add 8px to add a padding for the exporter
|
||||
;; width (+ width 8)
|
||||
|
||||
@ -101,7 +101,7 @@
|
||||
(.observe observer paragraph-node)
|
||||
#(.disconnect observer)))))
|
||||
|
||||
[:& text/text-shape {:ref text-ref-cb :shape shape}]))
|
||||
[:& text/text-shape {:ref text-ref-cb :shape shape :grow-type (:grow-type shape)}]))
|
||||
|
||||
(mf/defc text-wrapper
|
||||
{::mf/wrap-props false}
|
||||
|
||||
@ -92,7 +92,7 @@
|
||||
|
||||
on-click (actions/on-click hover selected)
|
||||
on-context-menu (actions/on-context-menu hover)
|
||||
on-double-click (actions/on-double-click hover hover-ids objects)
|
||||
on-double-click (actions/on-double-click hover hover-ids drawing-path? objects)
|
||||
on-drag-enter (actions/on-drag-enter)
|
||||
on-drag-over (actions/on-drag-over)
|
||||
on-drop (actions/on-drop file viewport-ref zoom)
|
||||
|
||||
@ -135,9 +135,9 @@
|
||||
(st/emit! (dw/select-shape (:id @hover))))))))
|
||||
|
||||
(defn on-double-click
|
||||
[hover hover-ids objects]
|
||||
[hover hover-ids drawing-path? objects]
|
||||
(mf/use-callback
|
||||
(mf/deps @hover @hover-ids)
|
||||
(mf/deps @hover @hover-ids drawing-path?)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(let [ctrl? (kbd/ctrl? event)
|
||||
@ -153,7 +153,7 @@
|
||||
|
||||
(st/emit! (ms/->MouseEvent :double-click ctrl? shift? alt?))
|
||||
|
||||
(when shape
|
||||
(when (and (not drawing-path?) shape)
|
||||
(cond frame?
|
||||
(st/emit! (dw/select-shape id shift?))
|
||||
|
||||
@ -164,7 +164,8 @@
|
||||
(st/emit! (dw/select-shape (:id selected))))
|
||||
|
||||
(or text? path?)
|
||||
(st/emit! (dw/start-edition-mode id))
|
||||
(st/emit! (dw/select-shape id)
|
||||
(dw/start-editing-selected))
|
||||
|
||||
:else
|
||||
;; Do nothing
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user