Merge pull request #1263 from penpot/bugfixes

Bugfixes
This commit is contained in:
Andrey Antukh 2021-10-07 13:50:52 +02:00 committed by GitHub
commit 59d44c41e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 343 additions and 148 deletions

View file

@ -77,7 +77,8 @@
(watch [_ _ _]
(->> (rp/mutation :create-comment-thread params)
(rx/mapcat #(rp/query :comment-thread {:file-id (:file-id %) :id (:id %)}))
(rx/map #(partial created %)))))))
(rx/map #(partial created %))
(rx/catch #(rx/throw {:type :comment-error})))))))
(defn update-comment-thread-status
[{:keys [id] :as thread}]
@ -87,7 +88,8 @@
(watch [_ _ _]
(let [done #(d/update-in-when % [:comment-threads id] assoc :count-unread-comments 0)]
(->> (rp/mutation :update-comment-thread-status {:id id})
(rx/map (constantly done)))))))
(rx/map (constantly done))
(rx/catch #(rx/throw {:type :comment-error})))))))
(defn update-comment-thread
@ -104,6 +106,7 @@
ptk/WatchEvent
(watch [_ _ _]
(->> (rp/mutation :update-comment-thread {:id id :is-resolved is-resolved})
(rx/catch #(rx/throw {:type :comment-error}))
(rx/ignore)))))
@ -118,7 +121,8 @@
(watch [_ _ _]
(rx/concat
(->> (rp/mutation :add-comment {:thread-id (:id thread) :content content})
(rx/map #(partial created %)))
(rx/map #(partial created %))
(rx/catch #(rx/throw {:type :comment-error})))
(rx/of (refresh-comment-thread thread)))))))
(defn update-comment
@ -132,6 +136,7 @@
ptk/WatchEvent
(watch [_ _ _]
(->> (rp/mutation :update-comment {:id id :content content})
(rx/catch #(rx/throw {:type :comment-error}))
(rx/ignore)))))
(defn delete-comment-thread
@ -147,6 +152,7 @@
ptk/WatchEvent
(watch [_ _ _]
(->> (rp/mutation :delete-comment-thread {:id id})
(rx/catch #(rx/throw {:type :comment-error}))
(rx/ignore)))))
(defn delete-comment
@ -160,6 +166,7 @@
ptk/WatchEvent
(watch [_ _ _]
(->> (rp/mutation :delete-comment {:id id})
(rx/catch #(rx/throw {:type :comment-error}))
(rx/ignore)))))
(defn refresh-comment-thread
@ -171,7 +178,8 @@
ptk/WatchEvent
(watch [_ _ _]
(->> (rp/query :comment-thread {:file-id file-id :id id})
(rx/map #(partial fetched %)))))))
(rx/map #(partial fetched %))
(rx/catch #(rx/throw {:type :comment-error})))))))
(defn retrieve-comment-threads
[file-id]
@ -182,7 +190,8 @@
ptk/WatchEvent
(watch [_ _ _]
(->> (rp/query :comment-threads {:file-id file-id})
(rx/map #(partial fetched %)))))))
(rx/map #(partial fetched %))
(rx/catch #(rx/throw {:type :comment-error})))))))
(defn retrieve-comments
[thread-id]
@ -193,7 +202,8 @@
ptk/WatchEvent
(watch [_ _ _]
(->> (rp/query :comments {:thread-id thread-id})
(rx/map #(partial fetched %)))))))
(rx/map #(partial fetched %))
(rx/catch #(rx/throw {:type :comment-error})))))))
(defn retrieve-unread-comment-threads
"A event used mainly in dashboard for retrieve all unread threads of a team."
@ -204,7 +214,8 @@
(watch [_ _ _]
(let [fetched #(assoc %2 :comment-threads (d/index-by :id %1))]
(->> (rp/query :unread-comment-threads {:team-id team-id})
(rx/map #(partial fetched %)))))))
(rx/map #(partial fetched %))
(rx/catch #(rx/throw {:type :comment-error})))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -1324,10 +1324,33 @@
(ptk/reify ::show-context-menu
ptk/UpdateEvent
(update [_ state]
(let [mdata (cond-> params
(some? shape)
(assoc :selected
(wsh/lookup-selected state)))]
(let [selected (wsh/lookup-selected state)
objects (wsh/lookup-page-objects state)
selected-with-children
(into []
(mapcat #(cp/get-object-with-children % objects))
selected)
head (get objects (first selected))
first-not-group-like?
(and (= (count selected) 1)
(not (contains? #{:group :bool} (:type head))))
has-invalid-shapes? (->> selected-with-children
(some (comp #{:frame :text} :type)))
disable-booleans? (or (empty? selected) has-invalid-shapes? first-not-group-like?)
disable-flatten? (or (empty? selected) has-invalid-shapes?)
mdata
(-> params
(assoc :disable-booleans? disable-booleans?)
(assoc :disable-flatten? disable-flatten?)
(cond-> (some? shape)
(assoc :selected selected)))]
(assoc-in state [:workspace-local :context-menu] mdata)))))
(defn show-shape-context-menu

View file

@ -19,6 +19,7 @@
[app.main.data.workspace.groups :as dwg]
[app.main.data.workspace.libraries-helpers :as dwlh]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
[app.main.repo :as rp]
[app.main.store :as st]
[app.util.i18n :refer [tr]]
@ -134,10 +135,12 @@
:color color}
uchg {:type :mod-color
:color prev}]
(rx/of (dch/commit-changes {:redo-changes [rchg]
(rx/of (dwu/start-undo-transaction)
(dch/commit-changes {:redo-changes [rchg]
:undo-changes [uchg]
:origin it})
(sync-file (:current-file-id state) file-id))))))
(sync-file (:current-file-id state) file-id)
(dwu/commit-undo-transaction))))))
(defn delete-color
[{:keys [id] :as params}]
@ -244,10 +247,12 @@
:typography typography}
uchg {:type :mod-typography
:typography prev}]
(rx/of (dch/commit-changes {:redo-changes [rchg]
(rx/of (dwu/start-undo-transaction)
(dch/commit-changes {:redo-changes [rchg]
:undo-changes [uchg]
:origin it})
(sync-file (:current-file-id state) file-id))))))
(sync-file (:current-file-id state) file-id)
(dwu/commit-undo-transaction))))))
(defn delete-typography
[id]

View file

@ -420,13 +420,13 @@
(gpt/point (+ (:width obj) 50) 0)
(gpt/point 0 0))
(let [obj-original (get objects id-original)
obj-duplicated (get objects id-duplicated)
distance (gpt/subtract (gpt/point obj-duplicated)
(gpt/point obj-original))
new-pos (gpt/add (gpt/point obj-duplicated) distance)
delta (gpt/subtract new-pos (gpt/point obj))]
delta))))
(let [pt-original (-> (get objects id-original) :selrect gpt/point)
pt-duplicated (-> (get objects id-duplicated) :selrect gpt/point)
pt-obj (-> obj :selrect gpt/point)
distance (gpt/subtract pt-duplicated pt-original)
new-pos (gpt/add pt-duplicated distance)]
(gpt/subtract new-pos pt-obj)))))
(defn duplicate-selected [move-delta?]
(ptk/reify ::duplicate-selected

View file

@ -180,12 +180,10 @@
shape (get objects id)
merge-fn (fn [node attrs]
(reduce-kv (fn [node k v]
(if (= (get node k) v)
(dissoc node k)
(assoc node k v)))
node
attrs))
(reduce-kv
(fn [node k v] (assoc node k v))
node
attrs))
update-fn #(update-shape % txt/is-paragraph-node? merge-fn attrs)
shape-ids (cond (= (:type shape) :text) [id]

View file

@ -94,6 +94,14 @@
:type :error
:timeout 3000}))))
(defmethod ptk/handle-error :comment-error
[_]
(ts/schedule
(st/emitf
(dm/show {:content "There was an error with the comment"
:type :error
:timeout 3000}))))
;; This is a pure frontend error that can be caused by an active
;; assertion (assertion that is preserved on production builds). From
;; the user perspective this should be treated as internal error.

View file

@ -152,12 +152,14 @@
:render-object
(do
(let [file-id (uuid (get-in route [:path-params :file-id]))
page-id (uuid (get-in route [:path-params :page-id]))
object-id (uuid (get-in route [:path-params :object-id]))]
(let [file-id (uuid (get-in route [:path-params :file-id]))
page-id (uuid (get-in route [:path-params :page-id]))
object-id (uuid (get-in route [:path-params :object-id]))
render-texts (get-in route [:query-params :render-texts])]
[:& render/render-object {:file-id file-id
:page-id page-id
:object-id object-id}]))
:object-id object-id
:render-texts? (and (some? render-texts) (= render-texts "true"))}]))
:render-sprite
(do

View file

@ -210,8 +210,13 @@
[:div.fields-row
[:& fm/input {:name :accept-terms-and-privacy
:class "check-primary"
:label (tr "auth.terms-privacy-agreement")
:type "checkbox"}]]
:type "checkbox"}
[:span
(tr "auth.terms-privacy-agreement")
[:div
[:a {:href "https://penpot.app/terms.html" :target "_blank"} (tr "auth.terms-of-service")]
[:span ",\u00A0"]
[:a {:href "https://penpot.app/privacy.html" :target "_blank"} (tr "auth.privacy-policy")]]]]]
;; (when (contains? @cf/flags :newsletter-registration-check)
;; [:div.fields-row

View file

@ -302,21 +302,22 @@
(when-let [node (mf/ref-val ref)]
(.scrollIntoViewIfNeeded ^js node))))
[:div.thread-content
{:style {:top (str pos-y "px")
:left (str pos-x "px")}
:on-click dom/stop-propagation}
(when (some? comment)
[:div.thread-content
{:style {:top (str pos-y "px")
:left (str pos-x "px")}
:on-click dom/stop-propagation}
[:div.comments
[:& comment-item {:comment comment
:users users
:thread thread}]
(for [item (rest comments)]
[:*
[:hr]
[:& comment-item {:comment item :users users}]])
[:div {:ref ref}]]
[:& reply-form {:thread thread}]]))
[:div.comments
[:& comment-item {:comment comment
:users users
:thread thread}]
(for [item (rest comments)]
[:*
[:hr]
[:& comment-item {:comment item :users users}]])
[:div {:ref ref}]]
[:& reply-form {:thread thread}]])))
(mf/defc thread-bubble
{::mf/wrap [mf/memo]}

View file

@ -19,7 +19,7 @@
(def use-form fm/use-form)
(mf/defc input
[{:keys [label help-icon disabled form hint trim] :as props}]
[{:keys [label help-icon disabled form hint trim children] :as props}]
(let [input-type (get props :type "text")
input-name (get props :name)
more-classes (get props :class)
@ -82,7 +82,7 @@
(swap! form assoc-in [:touched input-name] true)))
props (-> props
(dissoc :help-icon :form :trim)
(dissoc :help-icon :form :trim :children)
(assoc :id (name input-name)
:value value
:auto-focus auto-focus?
@ -97,7 +97,13 @@
{:class klass}
[:*
[:> :input props]
[:label {:for (name input-name)} label]
(cond
(some? label)
[:label {:for (name input-name)} label]
(some? children)
[:label {:for (name input-name)} children])
(when help-icon'
[:div.help-icon
{:style {:cursor "pointer"}

View file

@ -25,9 +25,26 @@
[cuerdas.core :as str]
[rumext.alpha :as mf]))
(defn bounds
[object objects]
(if (= :group (:type object))
(let [children-bounds
(into []
(comp (map #(get objects %))
(map #(bounds % objects)))
(:shapes object))]
(gsh/join-rects children-bounds))
(let [padding (filters/calculate-padding object)]
(-> (filters/get-filters-bounds object)
(update :x - padding)
(update :y - padding)
(update :width + (* 2 padding))
(update :height + (* 2 padding))))))
(mf/defc object-svg
{::mf/wrap [mf/memo]}
[{:keys [objects object-id zoom] :or {zoom 1} :as props}]
[{:keys [objects object-id zoom render-texts?] :or {zoom 1} :as props}]
(let [object (get objects object-id)
frame-id (if (= :frame (:type object))
(:id object)
@ -47,20 +64,10 @@
objects (reduce updt-fn objects mod-ids)
object (get objects object-id)
;; We need to get the shadows/blurs paddings to create the viewbox properly
{:keys [x y width height]} (filters/get-filters-bounds object)
{:keys [x y width height] :as bs} (bounds object objects)
[_ _ width height :as coords] (->> [x y width height] (map #(* % zoom)))
x (* x zoom)
y (* y zoom)
width (* width zoom)
height (* height zoom)
padding (* (filters/calculate-padding object) zoom)
vbox (str/join " " [(- x padding)
(- y padding)
(+ width padding padding)
(+ height padding padding)])
vbox (str/join " " coords)
frame-wrapper
(mf/use-memo
@ -76,18 +83,22 @@
(mf/use-memo
(mf/deps objects)
#(exports/shape-wrapper-factory objects))
]
text-shapes
(->> objects
(filter (fn [[_ shape]] (= :text (:type shape))))
(mapv second))]
(mf/use-effect
(mf/deps width height)
#(dom/set-page-style {:size (str (mth/ceil (+ width padding padding)) "px "
(mth/ceil (+ height padding padding)) "px")}))
#(dom/set-page-style {:size (str (mth/ceil width) "px "
(mth/ceil height) "px")}))
[:& (mf/provider embed/context) {:value true}
[:svg {:id "screenshot"
:view-box vbox
:width (+ width padding padding)
:height (+ height padding padding)
:width width
:height height
:version "1.1"
:xmlns "http://www.w3.org/2000/svg"
:xmlnsXlink "http://www.w3.org/1999/xlink"
@ -100,7 +111,19 @@
:frame [:& frame-wrapper {:shape object :view-box vbox}]
:group [:> shape-container {:shape object}
[:& group-wrapper {:shape object}]]
[:& shape-wrapper {:shape object}])]]))
[:& shape-wrapper {:shape object}])]
;; Auxiliary SVG for rendering text-shapes
(when render-texts?
(for [object text-shapes]
[:svg {:id (str "screenshot-text-" (:id object))
:view-box (str "0 0 " (:width object) " " (:height object))
:width (:width object)
:height (:height object)
:version "1.1"
:xmlns "http://www.w3.org/2000/svg"
:xmlnsXlink "http://www.w3.org/1999/xlink"}
[:& shape-wrapper {:shape (-> object (assoc :x 0 :y 0))}]]))]))
(defn- adapt-root-frame
[objects object-id]
@ -120,7 +143,7 @@
;; backend entry point for download only the data of single page.
(mf/defc render-object
[{:keys [file-id page-id object-id] :as props}]
[{:keys [file-id page-id object-id render-texts?] :as props}]
(let [objects (mf/use-state nil)]
(mf/use-effect
(mf/deps file-id page-id object-id)
@ -140,6 +163,7 @@
(when @objects
[:& object-svg {:objects @objects
:object-id object-id
:render-texts? render-texts?
:zoom 1}])))
(mf/defc render-sprite

View file

@ -60,8 +60,8 @@
:viewBox "0 0 3 6"
:refX "2"
:refY "3"
:markerWidth "3"
:markerHeight "6"
:markerWidth "8.5"
:markerHeight "8.5"
:orient "auto-start-reverse"
:fill stroke-color
:fillOpacity stroke-opacity}
@ -72,8 +72,8 @@
:viewBox "0 0 3 6"
:refX "2"
:refY "3"
:markerWidth "3"
:markerHeight "6"
:markerWidth "8.5"
:markerHeight "8.5"
:orient "auto-start-reverse"
:fill stroke-color
:fillOpacity stroke-opacity}
@ -82,10 +82,10 @@
(when (or (= cap-start :square-marker) (= cap-end :square-marker))
[:marker {:id (str marker-id-prefix "-square-marker")
:viewBox "0 0 6 6"
:refX "5"
:refX "3"
:refY "3"
:markerWidth "6"
:markerHeight "6"
:markerWidth "4.2426" ;; diagonal length of a 3x3 square
:markerHeight "4.2426"
:orient "auto-start-reverse"
:fill stroke-color
:fillOpacity stroke-opacity}
@ -94,10 +94,10 @@
(when (or (= cap-start :circle-marker) (= cap-end :circle-marker))
[:marker {:id (str marker-id-prefix "-circle-marker")
:viewBox "0 0 6 6"
:refX "5"
:refX "3"
:refY "3"
:markerWidth "6"
:markerHeight "6"
:markerWidth "4"
:markerHeight "4"
:orient "auto-start-reverse"
:fill stroke-color
:fillOpacity stroke-opacity}
@ -106,7 +106,7 @@
(when (or (= cap-start :diamond-marker) (= cap-end :diamond-marker))
[:marker {:id (str marker-id-prefix "-diamond-marker")
:viewBox "0 0 6 6"
:refX "5"
:refX "3"
:refY "3"
:markerWidth "6"
:markerHeight "6"

View file

@ -18,7 +18,8 @@
(let [valign (:vertical-align node "top")
width (some-> (:width shape) (+ 1))
base #js {:height (or (:height shape) "100%")
:width (or width "100%")}]
:width (or width "100%")
:fontFamily "sourcesanspro"}]
(cond-> base
(= valign "top") (obj/set! "justifyContent" "flex-start")
(= valign "center") (obj/set! "justifyContent" "center")
@ -40,6 +41,7 @@
:justifyContent "inherit"
:minHeight (when-not (or auto-width? auto-height?) "100%")
:minWidth (when-not auto-width? "100%")
:marginRight "1px"
:verticalAlign "top"}))
(defn generate-paragraph-styles

View file

@ -27,15 +27,27 @@
:v (mf/use-ref nil)
:alpha (mf/use-ref nil)}
setup-hex-color
(fn [hex]
(let [[r g b] (uc/hex->rgb hex)
[h s v] (uc/hex->hsv hex)]
(on-change {:hex hex
:h h :s s :v v
:r r :g g :b b})))
on-change-hex
(fn [e]
(let [val (-> e dom/get-target-val parse-hex)]
(when (uc/hex? val)
(let [[r g b] (uc/hex->rgb val)
[h s v] (uc/hex->hsv hex)]
(on-change {:hex val
:h h :s s :v v
:r r :g g :b b})))))
(setup-hex-color val))))
on-blur-hex
(fn [e]
(let [val (-> e dom/get-target-val)
val (cond
(uc/color? val) (uc/parse-color val)
(uc/hex? (parse-hex val)) (parse-hex val))]
(when (some? val)
(setup-hex-color val))))
on-change-property
(fn [property max-value]
@ -81,9 +93,10 @@
[:div.color-values
{:class (when disable-opacity "disable-opacity")}
[:input {:id "hex-value"
:ref (:hex refs)
:default-value hex
:on-change on-change-hex}]
:ref (:hex refs)
:default-value hex
:on-change on-change-hex
:on-blur on-blur-hex}]
(if (= type :rgb)
[:*

View file

@ -87,15 +87,15 @@
(mf/defc shape-context-menu
[{:keys [mdata] :as props}]
(let [{:keys [id] :as shape} (:shape mdata)
selected (:selected mdata)
(let [{:keys [shape selected disable-booleans? disable-flatten?]} mdata
{:keys [id type]} shape
single? (= (count selected) 1)
multiple? (> (count selected) 1)
editable-shape? (#{:group :text :path} (:type shape))
editable-shape? (#{:group :text :path} type)
is-group? (and (some? shape) (= :group (:type shape)))
is-bool? (and (some? shape) (= :bool (:type shape)))
is-group? (and (some? shape) (= :group type))
is-bool? (and (some? shape) (= :bool type))
options (mf/deref refs/workspace-page-options)
flows (:flows options)
@ -235,10 +235,12 @@
:shortcut (sc/get-tooltip :start-editing)
:on-click do-start-editing}])
[:& menu-entry {:title (tr "workspace.shape.menu.transform-to-path")
:on-click do-transform-to-path}]
(when-not disable-flatten?
[:& menu-entry {:title (tr "workspace.shape.menu.transform-to-path")
:on-click do-transform-to-path}])
(when (or multiple? (and single? (or is-group? is-bool?)))
(when (and (not disable-booleans?)
(or multiple? (and single? (or is-group? is-bool?))))
[:& menu-entry {:title (tr "workspace.shape.menu.path")}
[:& menu-entry {:title (tr "workspace.shape.menu.union")
:shortcut (sc/get-tooltip :boolean-union)
@ -253,7 +255,7 @@
:shortcut (sc/get-tooltip :boolean-exclude)
:on-click (set-bool :exclude)}]
(when (and single? is-bool?)
(when (and single? is-bool? (not disable-flatten?))
[:*
[:& menu-separator]
[:& menu-entry {:title (tr "workspace.shape.menu.flatten")
@ -279,9 +281,7 @@
[:& menu-entry {:title (tr "workspace.shape.menu.delete-flow-start")
:on-click (do-remove-flow flow)}])))
(when (and (or (nil? (:shape-ref shape))
(> (count selected) 1))
(not= (:type shape) :frame))
(when (not= (:type shape) :frame)
[:*
[:& menu-separator]
[:& menu-entry {:title (tr "workspace.shape.menu.create-component")

View file

@ -174,7 +174,7 @@
handle-return
(mf/use-callback
(fn [_ state]
(let [style (ted/get-editor-current-inline-styles state)
(let [style (ted/get-editor-current-block-data state)
state (-> (ted/insert-text state "\n" style)
(handle-change))]
(st/emit! (dwt/update-editor-state shape state)))

View file

@ -230,7 +230,7 @@
on-fold-group
(mf/use-callback
(mf/deps group-open?)
(mf/deps file-id box path group-open?)
(fn [event]
(dom/stop-propagation event)
(st/emit! (dwl/set-assets-group-open file-id

View file

@ -17,19 +17,22 @@
(mf/defc booleans-options
[]
(let [selected (mf/deref refs/selected-objects)
selected-with-children (mf/deref refs/selected-objects-with-children)
disabled-bool-btns
(or (empty? selected)
(and (<= (count selected) 1)
(not (contains? #{:group :bool} (:type (first selected))))))
has-invalid-shapes? (->> selected-with-children
(some (comp #{:frame :text} :type)))
disabled-flatten
(empty? selected)
first-not-group-like?
(and (= (count selected) 1)
(not (contains? #{:group :bool} (:type (first selected)))))
disabled-bool-btns (or (empty? selected) has-invalid-shapes? first-not-group-like?)
disabled-flatten (or (empty? selected) has-invalid-shapes?)
head (first selected)
is-group? (and (some? head) (= :group (:type head)))
is-bool? (and (some? head) (= :bool (:type head)))
head-bool-type (and (some? head) (:bool-type head))
head-bool-type (and (some? head) is-bool? (:bool-type head))
set-bool
(fn [bool-type]

View file

@ -73,7 +73,7 @@
:group (tr "workspace.options.group-stroke")
(tr "workspace.options.stroke"))
show-options (not= (:stroke-style values :none) :none)
show-options (not= (or (:stroke-style values) :none) :none)
show-caps (and show-caps
(not (#{:inner :outer} (:stroke-alignment values))))
@ -141,7 +141,10 @@
target (dom/get-current-target event)
rect (dom/get-bounding-rect target)
top (+ (:bottom rect) 5)
top (if (< (+ (:bottom rect) 320) (:height window-size))
(+ (:bottom rect) 5)
(- (:height window-size) 325))
left (if (< (+ (:left rect) 200) (:width window-size))
(:left rect)
(- (:width window-size) 205))]

View file

@ -74,7 +74,8 @@
(defn filter-fonts
[{:keys [term backends]} fonts]
(let [xform (cond-> (map identity)
(let [term (str/lower term)
xform (cond-> (map identity)
(seq term)
(comp (filter #(str/includes? (str/lower (:name %)) term)))
@ -175,7 +176,7 @@
[:div.font-selector
[:div.font-selector-dropdown
[:header
[:input {:placeholder "Search font"
[:input {:placeholder (tr "workspace.options.search-font")
:value (:term @state)
:ref input
:spell-check false

View file

@ -197,7 +197,7 @@
[:& use/export-page {:options options}]
[:& (mf/provider use/include-metadata-ctx) {:value true}
[:& (mf/provider use/include-metadata-ctx) {:value false}
[:& (mf/provider embed/context) {:value true}
;; Render root shape
[:& shapes/root-shape {:key page-id