diff --git a/CHANGES.md b/CHANGES.md index a27dd67be..6f016b14a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,7 +17,19 @@ - Fix broken profile and profile options form. - Fix problem with mask and flip [#715](https://github.com/penpot/penpot/issues/715) - +- Fix problem with rotated blur [Taiga #1370](https://tree.taiga.io/project/penpot/issue/1370) +- Disables buttons in view mode for users without permissions [Taiga #1328](https://tree.taiga.io/project/penpot/issue/1328) +- Fix issue when undo after changing the artboard of a shape [Taiga #1304](https://tree.taiga.io/project/penpot/issue/1304) +- Fix problem with system shortcuts and application [#737](https://github.com/penpot/penpot/issues/737) +- Fix issue with typographies panel cannot be collapsed [#707](https://github.com/penpot/penpot/issues/707) +- Fix problem with middle mouse button press moving the canvas when not moving mouse [#717](https://github.com/penpot/penpot/issues/717) +- Fix problem with masks interactions outside bounds [#718](https://github.com/penpot/penpot/issues/718) +- Fix issues with Alt key in distance measurement [#672](https://github.com/penpot/penpot/issues/672) +- Fix problem with rotation degree input [#741](https://github.com/penpot/penpot/issues/741) +- Fix problem with resolved comments [Taiga #1406](https://tree.taiga.io/project/penpot/issue/1406) +- Fix problem with comments styles on dashboard [Taiga #1405](https://tree.taiga.io/project/penpot/issue/1405) +- Fix problem with default square grid [Taiga #1344](https://tree.taiga.io/project/penpot/issue/1344) +- Fix error with the "Navigate to" button on prototypes [Taiga #1268](https://tree.taiga.io/project/penpot/issue/1268) ### :heart: Community contributions by (Thank you!) diff --git a/backend/src/app/rpc/mutations/comments.clj b/backend/src/app/rpc/mutations/comments.clj index 6df145155..3008b8733 100644 --- a/backend/src/app/rpc/mutations/comments.clj +++ b/backend/src/app/rpc/mutations/comments.clj @@ -146,14 +146,14 @@ (db/with-atomic [conn pool] (let [thread (db/get-by-id conn :comment-thread id {:for-update true})] (when-not thread - (ex/raise :type :not-found) + (ex/raise :type :not-found)) (files/check-read-permissions! conn profile-id (:file-id thread)) (db/update! conn :comment-thread {:is-resolved is-resolved} {:id id}) - nil)))) + nil))) ;; --- Mutation: Add Comment diff --git a/common/app/common/pages.cljc b/common/app/common/pages.cljc index aa54c710a..1b82300f7 100644 --- a/common/app/common/pages.cljc +++ b/common/app/common/pages.cljc @@ -62,6 +62,7 @@ (d/export helpers/touched-group?) (d/export helpers/get-base-shape) (d/export helpers/is-parent?) +(d/export helpers/get-index-in-parent) ;; Process changes (d/export changes/process-changes) diff --git a/common/app/common/pages/helpers.cljc b/common/app/common/pages/helpers.cljc index e630801da..6dc4ccbf1 100644 --- a/common/app/common/pages/helpers.cljc +++ b/common/app/common/pages/helpers.cljc @@ -401,3 +401,12 @@ (recur (get objects (first pending)) (conj done (:id current)) (concat (rest pending) (:shapes current)))))) + +(defn get-index-in-parent + "Retrieves the index in the parent" + [objects shape-id] + (let [shape (get objects shape-id) + parent (get objects (:parent-id shape)) + [parent-idx _] (d/seek (fn [[idx child-id]] (= child-id shape-id)) + (d/enumerate (:shapes parent)))] + parent-idx)) diff --git a/frontend/resources/styles/main/partials/comments.scss b/frontend/resources/styles/main/partials/comments.scss index bec73296e..05679f849 100644 --- a/frontend/resources/styles/main/partials/comments.scss +++ b/frontend/resources/styles/main/partials/comments.scss @@ -246,6 +246,7 @@ pointer-events: auto; .thread-groups { + hr { border: 0; height: 1px; @@ -424,16 +425,18 @@ } } - .thread-group { - .section-title { - color: $color-black; - } + .thread-groups { + max-height: calc(30rem - 40px); + overflow: auto; - .threads { - max-height: 25rem; - overflow: auto; + hr { + background-color: $color-gray-10; } } + + .thread-group .section-title { + color: $color-black; + } .comment { .author .name .fullname { diff --git a/frontend/src/app/main.cljs b/frontend/src/app/main.cljs index 23ae18fb4..0ad0b2437 100644 --- a/frontend/src/app/main.cljs +++ b/frontend/src/app/main.cljs @@ -82,7 +82,8 @@ (st/emit! (rt/initialize-router ui/routes) (rt/initialize-history on-navigate)) - (st/emit! (udu/fetch-profile)) + (st/emit! (udu/fetch-profile) + (udu/fetch-user-teams)) (mf/mount (mf/element ui/app) (dom/get-element "app")) (mf/mount (mf/element modal) (dom/get-element "modal"))) diff --git a/frontend/src/app/main/data/comments.cljs b/frontend/src/app/main/data/comments.cljs index dd8107898..a9136069c 100644 --- a/frontend/src/app/main/data/comments.cljs +++ b/frontend/src/app/main/data/comments.cljs @@ -327,9 +327,7 @@ (let [{:keys [show mode open]} cstate] (cond->> threads (= :pending show) - (filter (fn [item] - (or (not (:is-resolved item)) - (= (:id item) open)))) + (filter (comp not :is-resolved)) (= :yours mode) (filter #(contains? (:participants %) (:id profile)))))) diff --git a/frontend/src/app/main/data/shortcuts.cljs b/frontend/src/app/main/data/shortcuts.cljs index 03800e992..f8ff70f3b 100644 --- a/frontend/src/app/main/data/shortcuts.cljs +++ b/frontend/src/app/main/data/shortcuts.cljs @@ -39,13 +39,23 @@ (str "command+" shortcut) (str "ctrl+" shortcut))) +(defn a-mod + "Adds the alt/option modifier to a shortcuts depending on the + operating system for the user" + [shortcut] + (str "alt+" shortcut)) + +(defn ca-mod + [shortcut] + (c-mod (a-mod shortcut))) + (defn bind-shortcuts [shortcuts bind-fn cb-fn] - (doseq [[key {:keys [command disabled fn]}] shortcuts] + (doseq [[key {:keys [command disabled fn type]}] shortcuts] (when-not disabled (if (vector? command) (doseq [cmd (seq command)] - (bind-fn cmd (cb-fn key fn))) - (bind-fn command (cb-fn key fn)))))) + (bind-fn cmd (cb-fn key fn) type)) + (bind-fn command (cb-fn key fn) type))))) (defn meta [key] (str @@ -61,9 +71,19 @@ "Shift+") key)) +(defn alt [key] + (str + (if (cfg/check-platform? :macos) + mac-option + "Alt+") + key)) + (defn meta-shift [key] (-> key meta shift)) +(defn meta-alt [key] + (-> key meta alt)) + (defn supr [] (if (cfg/check-platform? :macos) mac-delete diff --git a/frontend/src/app/main/data/users.cljs b/frontend/src/app/main/data/users.cljs index b1250d86e..9b724a420 100644 --- a/frontend/src/app/main/data/users.cljs +++ b/frontend/src/app/main/data/users.cljs @@ -55,7 +55,10 @@ (ptk/reify ::profile-fetched ptk/UpdateEvent (update [_ state] - (assoc state :profile data)) + (-> state + (assoc :profile data) + ;; Safeguard if the profile is loaded after teams + (assoc-in [:profile :teams] (get-in state [:profile :teams])))) ptk/EffectEvent (effect [_ state stream] @@ -203,4 +206,23 @@ (->> (rp/query :team-users {:team-id team-id}) (rx/map #(partial fetched %))))))) +(defn user-teams-fetched [data] + (ptk/reify ::user-teams-fetched + ptk/UpdateEvent + (update [_ state] + (let [teams (->> data + (group-by :id) + (d/mapm #(first %2)))] + (assoc-in state [:profile :teams] teams))))) + +(defn fetch-user-teams [] + (ptk/reify ::fetch-user-teams + ptk/WatchEvent + (watch [_ state s] + (->> (rp/query! :teams) + (rx/map user-teams-fetched) + (rx/catch (fn [error] + (if (= (:type error) :not-found) + (rx/of (rt/nav :auth-login)) + (rx/empty)))))))) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 3c4243ee0..8ea1041d4 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1726,6 +1726,13 @@ :data [image]}] (rx/of (dwp/upload-media-workspace params @ms/mouse-position)))))) +(defn toggle-distances-display [value] + (ptk/reify ::toggle-distances-display + + ptk/UpdateEvent + (update [_ state] + (assoc-in state [:workspace-local :show-distances?] value)))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Interactions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/frontend/src/app/main/data/workspace/grid.cljs b/frontend/src/app/main/data/workspace/grid.cljs index 0b8a9bd82..92a71120c 100644 --- a/frontend/src/app/main/data/workspace/grid.cljs +++ b/frontend/src/app/main/data/workspace/grid.cljs @@ -45,7 +45,7 @@ ptk/WatchEvent (watch [_ state stream] (let [page-id (:current-page-id state) - data (get-in state [:workspace-data page-id]) + data (get-in state [:workspace-data :pages-index page-id]) params (or (get-in data [:options :saved-grids :square]) (:square default-grid-params)) grid {:type :square diff --git a/frontend/src/app/main/data/workspace/shortcuts.cljs b/frontend/src/app/main/data/workspace/shortcuts.cljs index cefc68be7..f9ca932b4 100644 --- a/frontend/src/app/main/data/workspace/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/shortcuts.cljs @@ -44,20 +44,20 @@ (rx/empty)))))) (def shortcuts - {:toggle-layers {:tooltip (ds/meta "L") - :command (ds/c-mod "l") + {:toggle-layers {:tooltip (ds/alt "L") + :command (ds/a-mod "l") :fn #(st/emit! (dw/go-to-layout :layers))} - :toggle-assets {:tooltip (ds/meta "I") - :command (ds/c-mod "i") + :toggle-assets {:tooltip (ds/alt "I") + :command (ds/a-mod "i") :fn #(st/emit! (dw/go-to-layout :assets))} - :toggle-history {:tooltip (ds/meta "H") - :command (ds/c-mod "h") + :toggle-history {:tooltip (ds/alt "H") + :command (ds/a-mod "h") :fn #(st/emit! (dw/go-to-layout :document-history))} - :toggle-palette {:tooltip (ds/meta "P") - :command (ds/c-mod "p") + :toggle-palette {:tooltip (ds/alt "P") + :command (ds/a-mod "p") :fn #(st/emit! (dw/toggle-layout-flags :colorpalette))} :toggle-rules {:tooltip (ds/meta-shift "R") @@ -260,6 +260,16 @@ :start-editing {:tooltip (ds/enter) :command "enter" :fn #(st/emit! (dw/start-editing-selected))} + + :start-measure {:tooltip (ds/alt "") + :command ["alt" "."] + :type "keydown" + :fn #(st/emit! (dw/toggle-distances-display true))} + + :stop-measure {:tooltip (ds/alt "") + :command ["alt" "."] + :type "keyup" + :fn #(st/emit! (dw/toggle-distances-display false))} }) (defn get-tooltip [shortcut] diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 57be6ef0b..636391406 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -249,14 +249,15 @@ :parent-id frame-id :shapes (mapv :id moving-shapes)}] - moving-shapes-by-frame-id (group-by :frame-id moving-shapes) - uch (->> moving-shapes-by-frame-id - (mapv (fn [[frame-id shapes]] + uch (->> moving-shapes + (reverse) + (mapv (fn [shape] {:type :mov-objects :page-id page-id - :parent-id frame-id - :shapes (mapv :id shapes)})))] + :parent-id (:parent-id shape) + :index (cp/get-index-in-parent objects (:id shape)) + :shapes [(:id shape)]})))] (when-not (empty? rch) (rx/of dwc/pop-undo-into-transaction diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index f2bf529bb..b2ed9e41f 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -69,7 +69,8 @@ :transform :hover :modifiers - :selrect]) + :selrect + :show-distances?]) workspace-local =)) (def selected-zoom diff --git a/frontend/src/app/main/streams.cljs b/frontend/src/app/main/streams.cljs index 969bae465..223462ded 100644 --- a/frontend/src/app/main/streams.cljs +++ b/frontend/src/app/main/streams.cljs @@ -143,28 +143,3 @@ (rx/dedupe))] (rx/subscribe-with ob sub) sub)) - -(defn mouse-position-deltas - [current] - (->> (rx/concat (rx/of current) - (rx/sample 10 mouse-position)) - (rx/buffer 2 1) - (rx/map (fn [[old new]] - (gpt/subtract new old))))) - - -(defonce mouse-position-delta - (let [sub (rx/behavior-subject nil) - ob (->> st/stream - (rx/filter pointer-event?) - (rx/filter #(= :delta (:source %))) - (rx/map :pt))] - (rx/subscribe-with ob sub) - sub)) - -(defonce viewport-scroll - (let [sub (rx/behavior-subject nil) - sob (->> (rx/filter scroll-event? st/stream) - (rx/map :point))] - (rx/subscribe-with sob sub) - sub)) diff --git a/frontend/src/app/main/ui/components/numeric_input.cljs b/frontend/src/app/main/ui/components/numeric_input.cljs index a8161d7d9..11cc46021 100644 --- a/frontend/src/app/main/ui/components/numeric_input.cljs +++ b/frontend/src/app/main/ui/components/numeric_input.cljs @@ -78,10 +78,10 @@ (dom/set-value! (dom/get-target event) new-value)) (and wrap-value? (num? max-val) (num? min-val) (= value max-val) up?) - (dom/set-value! (dom/get-target event) min-val) + (dom/set-value! (dom/get-target event) (dec min-val)) (and wrap-value? (num? min-val) (num? max-val) (= value min-val) down?) - (dom/set-value! (dom/get-target event) max-val)))))) + (dom/set-value! (dom/get-target event) (inc max-val))))))) handle-key-down (mf/use-callback diff --git a/frontend/src/app/main/ui/shapes/filters.cljs b/frontend/src/app/main/ui/shapes/filters.cljs index f530dc169..463ece89d 100644 --- a/frontend/src/app/main/ui/shapes/filters.cljs +++ b/frontend/src/app/main/ui/shapes/filters.cljs @@ -9,12 +9,13 @@ (ns app.main.ui.shapes.filters (:require - [rumext.alpha :as mf] - [cuerdas.core :as str] - [app.util.color :as color] [app.common.data :as d] + [app.common.geom.shapes :as gsh] [app.common.math :as mth] - [app.common.uuid :as uuid])) + [app.common.uuid :as uuid] + [app.util.color :as color] + [cuerdas.core :as str] + [rumext.alpha :as mf])) (defn get-filter-id [] (str "filter_" (uuid/next))) @@ -137,7 +138,7 @@ (filter #(= :drop-shadow (:type %))) (map (partial filter-bounds shape) )) ;; We add the selrect so the minimum size will be the selrect - filter-bounds (conj filter-bounds (:selrect shape)) + filter-bounds (conj filter-bounds (-> shape :points gsh/points->selrect)) x1 (apply min (map :x1 filter-bounds)) y1 (apply min (map :y1 filter-bounds)) x2 (apply max (map :x2 filter-bounds)) diff --git a/frontend/src/app/main/ui/shapes/group.cljs b/frontend/src/app/main/ui/shapes/group.cljs index 991ee92ca..c355b30d5 100644 --- a/frontend/src/app/main/ui/shapes/group.cljs +++ b/frontend/src/app/main/ui/shapes/group.cljs @@ -12,7 +12,7 @@ [app.util.object :as obj] [rumext.alpha :as mf] [app.main.ui.shapes.attrs :as attrs] - [app.main.ui.shapes.mask :refer [mask-str mask-factory]])) + [app.main.ui.shapes.mask :refer [mask-str clip-str mask-factory]])) (defn group-shape [shape-wrapper] @@ -35,6 +35,7 @@ props (-> (attrs/extract-style-attrs shape) (obj/merge! #js {:pointerEvents pointer-events + :clipPath (when (and mask (not expand-mask)) (clip-str mask)) :mask (when (and mask (not expand-mask)) (mask-str mask))}))] [:> :g props diff --git a/frontend/src/app/main/ui/shapes/mask.cljs b/frontend/src/app/main/ui/shapes/mask.cljs index fab611fd3..07ccb7cf9 100644 --- a/frontend/src/app/main/ui/shapes/mask.cljs +++ b/frontend/src/app/main/ui/shapes/mask.cljs @@ -10,18 +10,25 @@ (ns app.main.ui.shapes.mask (:require [rumext.alpha :as mf] - [cuerdas.core :as str])) + [cuerdas.core :as str] + [app.common.geom.shapes :as gsh])) (defn mask-str [mask] (str/fmt "url(#%s)" (str (:id mask) "-mask"))) +(defn clip-str [mask] + (str/fmt "url(#%s)" (str (:id mask) "-clip"))) + (defn mask-factory [shape-wrapper] (mf/fnc mask-shape {::mf/wrap-props false} [props] (let [frame (unchecked-get props "frame") - mask (unchecked-get props "mask")] + mask (unchecked-get props "mask") + mask' (-> mask + (gsh/transform-shape) + (gsh/translate-to-frame frame))] [:defs [:filter {:id (str (:id mask) "-filter")} [:feFlood {:flood-color "white" @@ -30,8 +37,16 @@ :in2 "SourceGraphic" :operator "in" :result "comp"}]] + ;; Clip path is necesary so the elements inside the mask won't affect + ;; the events outside. Clip hides the elements but mask doesn't (like display vs visibility) + ;; we cannot use clips instead of mask because clips can only be simple shapes + [:clipPath {:id (str (:id mask) "-clip")} + [:polyline {:points (->> (:points mask') + (map #(str (:x %) "," (:y %))) + (str/join " "))}]] [:mask {:id (str (:id mask) "-mask")} [:g {:filter (str/fmt "url(#%s)" (str (:id mask) "-filter"))} - [:& shape-wrapper {:frame frame :shape (-> mask - (dissoc :shadow :blur))}]]]]))) + [:& shape-wrapper {:frame frame + :shape (-> mask + (dissoc :shadow :blur))}]]]]))) diff --git a/frontend/src/app/main/ui/viewer/header.cljs b/frontend/src/app/main/ui/viewer/header.cljs index bb87dce0c..02e89ecdd 100644 --- a/frontend/src/app/main/ui/viewer/header.cljs +++ b/frontend/src/app/main/ui/viewer/header.cljs @@ -191,6 +191,11 @@ profile (mf/deref refs/profile) anonymous? (= uuid/zero (:id profile)) + team-id (get-in data [:project :team-id]) + + has-permission? (and (not anonymous?) + (contains? (:teams profile) team-id)) + project-id (get-in data [:project :id]) file-id (get-in data [:file :id]) page-id (get-in data [:page :id]) @@ -219,7 +224,9 @@ [:header.viewer-header [:div.main-icon - [:a {:on-click on-goback} i/logo-icon]] + [:a {:on-click on-goback + ;; If the user doesn't have permission we disable the link + :style {:pointer-events (when-not has-permission? "none")}} i/logo-icon]] [:div.sitemap-zone {:alt (t locale "viewer.header.sitemap") :on-click on-click} @@ -238,7 +245,7 @@ :alt "View mode"} i/play] - (when-not anonymous? + (when has-permission? [:button.mode-zone-button.tooltip.tooltip-bottom {:on-click #(navigate :comments) :class (dom/classnames :active (= section :comments)) @@ -257,11 +264,11 @@ :comments [:& comments-menu {:locale locale}] nil) - (when-not anonymous? + (when has-permission? [:& share-link {:token (:token data) :page (:page data)}]) - (when-not anonymous? + (when has-permission? [:a.btn-text-basic.btn-small {:on-click on-edit} (t locale "viewer.header.edit-page")]) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index 665261936..5d0446a04 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -491,7 +491,7 @@ [:div.asset-group [:div.group-title {:class (when (not open?) "closed")} - [:span {:on-click (st/emitf (dwl/set-assets-box-open file-id :typography (not open?)))} + [:span {:on-click (st/emitf (dwl/set-assets-box-open file-id :typographies (not open?)))} i/arrow-slide (t locale "workspace.assets.typography")] [:span.num-assets (str "\u00A0(") (count typographies) ")"] ;; Unicode 00A0 is non-breaking space (when local? diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs index 1b8033a8d..6e47ee95c 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs @@ -37,7 +37,8 @@ show-frames-dropdown? (mf/use-state false) on-set-blur #(reset! show-frames-dropdown? false) - on-navigate #(st/emit! (dw/select-shapes (d/ordered-set (:id destination)))) + on-navigate #(when destination + (st/emit! (dw/select-shapes (d/ordered-set (:id destination))))) on-select-destination (fn [dest] @@ -77,4 +78,5 @@ [:li {:key (:id frame) :on-click #(on-select-destination (:id frame))} (:name frame)]))]]] - [:span.navigate-icon {on-click on-navigate} i/navigate]]]]))) + [:span.navigate-icon {:style {:visibility (when (not destination) "hidden")} + :on-click on-navigate} i/navigate]]]]))) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index a8c27ee33..eeaaa55d1 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -131,18 +131,21 @@ (defn- handle-viewport-positioning [viewport-ref] (let [node (mf/ref-val viewport-ref) - stoper (rx/filter #(= ::finish-positioning %) st/stream) + stoper (rx/filter #(= ::finish-positioning %) st/stream)] - stream (->> ms/mouse-position-delta - (rx/take-until stoper))] (st/emit! dw/start-pan) - (rx/subscribe stream - (fn [delta] + + (->> st/stream + (rx/filter ms/pointer-event?) + (rx/filter #(= :delta (:source %))) + (rx/map :pt) + (rx/take-until stoper) + (rx/subs (fn [delta] (let [zoom (gpt/point @refs/selected-zoom) delta (gpt/divide delta zoom)] (st/emit! (dw/update-viewport-position {:x #(- % (:x delta)) - :y #(- % (:y delta))}))))))) + :y #(- % (:y delta))})))))))) ;; --- Viewport @@ -347,7 +350,8 @@ transform hover modifiers - selrect]} local + selrect + show-distances?]} local page-id (mf/use-ctx ctx/current-page-id) @@ -794,7 +798,7 @@ [:& selection-handlers {:selected selected :zoom zoom :edition edition - :show-distances (and (not transform) @alt?) + :show-distances (and (not transform) show-distances?) :disable-handlers (or drawing-tool edition)}]) (when (= (count selected) 1)