From 464a686c04139d4667416fe5c999f83952330b92 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 13 Apr 2021 09:46:36 +0200 Subject: [PATCH 1/6] :bug: Fix incorrect handling of user lang selection. That causes double loading of the http resources in some circumstances. --- frontend/src/app/main.cljs | 14 +++++++------- frontend/src/app/main/ui/viewer.cljs | 16 ++++++++-------- frontend/src/app/util/i18n.cljs | 1 + 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/frontend/src/app/main.cljs b/frontend/src/app/main.cljs index 2126bead2..3df6cd88d 100644 --- a/frontend/src/app/main.cljs +++ b/frontend/src/app/main.cljs @@ -79,20 +79,20 @@ (defn init-ui [] - (st/emit! (rt/initialize-router ui/routes) - (rt/initialize-history on-navigate)) - - (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"))) + (mf/mount (mf/element modal) (dom/get-element "modal"))) (defn ^:export init [] (i18n/init! cfg/translations) (theme/init! cfg/themes) (st/init) - (init-ui)) + (init-ui) + + (st/emit! (rt/initialize-router ui/routes) + (rt/initialize-history on-navigate) + (udu/fetch-profile) + (udu/fetch-user-teams))) (defn reinit [] diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index 89fdbc91c..0c651fb49 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -265,19 +265,19 @@ (mf/defc viewer-page [{:keys [file-id page-id index token section] :as props}] - - (mf/use-effect - (mf/deps file-id page-id token) - (st/emitf (dv/initialize props))) - (let [data (mf/deref refs/viewer-data) state (mf/deref refs/viewer-local)] + (mf/use-effect + (mf/deps file-id page-id token) + (fn [] + (prn "viewer-page$use-effect") + (st/emit! (dv/initialize props)))) + (mf/use-effect (mf/deps (:file data)) - #(when (:file data) - (dom/set-html-title (tr "title.viewer" - (get-in data [:file :name]))))) + #(when-let [name (get-in data [:file :name])] + (dom/set-html-title (tr "title.viewer" name)))) (when (and data state) [:& viewer-content diff --git a/frontend/src/app/util/i18n.cljs b/frontend/src/app/util/i18n.cljs index 15d00312f..20a70ad47 100644 --- a/frontend/src/app/util/i18n.cljs +++ b/frontend/src/app/util/i18n.cljs @@ -73,6 +73,7 @@ (swap! storage assoc ::locale lang) (reset! locale lang)) (do + (swap! storage dissoc ::locale) (reset! locale (autodetect))))) (defn reset-locale From 99bcf0484a81cdd3d20a9b1a418c581e8f2388b3 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 13 Apr 2021 11:57:52 +0200 Subject: [PATCH 2/6] :bug: Fix race conditions on profile and teams loading. --- frontend/src/app/main.cljs | 5 +- frontend/src/app/main/data/auth.cljs | 8 +-- frontend/src/app/main/data/dashboard.cljs | 4 +- frontend/src/app/main/data/users.cljs | 63 +++++++++++-------- frontend/src/app/main/refs.cljs | 3 + .../src/app/main/ui/dashboard/sidebar.cljs | 10 +-- frontend/src/app/main/ui/viewer.cljs | 1 - frontend/src/app/main/ui/viewer/header.cljs | 6 +- 8 files changed, 51 insertions(+), 49 deletions(-) diff --git a/frontend/src/app/main.cljs b/frontend/src/app/main.cljs index 3df6cd88d..a7fe96920 100644 --- a/frontend/src/app/main.cljs +++ b/frontend/src/app/main.cljs @@ -14,7 +14,7 @@ [app.config :as cfg] [app.main.data.auth :as da] [app.main.data.messages :as dm] - [app.main.data.users :as udu] + [app.main.data.users :as du] [app.main.repo :as rp] [app.main.store :as st] [app.main.ui :as ui] @@ -91,8 +91,7 @@ (st/emit! (rt/initialize-router ui/routes) (rt/initialize-history on-navigate) - (udu/fetch-profile) - (udu/fetch-user-teams))) + (du/fetch-profile-and-teams))) (defn reinit [] diff --git a/frontend/src/app/main/data/auth.cljs b/frontend/src/app/main/data/auth.cljs index 972f66362..8955fa194 100644 --- a/frontend/src/app/main/data/auth.cljs +++ b/frontend/src/app/main/data/auth.cljs @@ -47,10 +47,10 @@ (watch [this state stream] (let [team-id (current-team-id profile) props (:props profile)] - (rx/merge - (rx/of (du/profile-fetched profile) - (rt/nav' :dashboard-projects {:team-id team-id})) - + (rx/concat + (rx/of (du/profile-fetched profile)) + (rx/of (du/fetch-teams)) + (rx/of (rt/nav' :dashboard-projects {:team-id team-id})) (when-not (:onboarding-viewed props) (->> (rx/of (modal/show {:type :onboarding})) (rx/delay 1000)))))))) diff --git a/frontend/src/app/main/data/dashboard.cljs b/frontend/src/app/main/data/dashboard.cljs index ad216b03f..760e5b8f6 100644 --- a/frontend/src/app/main/data/dashboard.cljs +++ b/frontend/src/app/main/data/dashboard.cljs @@ -65,8 +65,6 @@ ;; Data Fetching ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; --- Fetch Team - (defn fetch-team [{:keys [id] :as params}] (letfn [(fetched [team state] @@ -117,7 +115,7 @@ (defn fetch-bundle [{:keys [id] :as params}] (us/assert ::us/uuid id) - (ptk/reify ::fetch-team + (ptk/reify ::fetch-bundle ptk/WatchEvent (watch [_ state stream] (let [profile (:profile state)] diff --git a/frontend/src/app/main/data/users.cljs b/frontend/src/app/main/data/users.cljs index bb15a0526..35a862df3 100644 --- a/frontend/src/app/main/data/users.cljs +++ b/frontend/src/app/main/data/users.cljs @@ -12,6 +12,7 @@ [app.config :as cf] [app.common.data :as d] [app.common.spec :as us] + [app.common.uuid :as uuid] [app.main.data.media :as di] [app.main.data.messages :as dm] [app.main.repo :as rp] @@ -47,19 +48,29 @@ ::lang ::theme])) -;; --- Profile Fetched +(defn fetch-teams + [] + (letfn [(on-fetched [state data] + (let [teams (d/index-by :id data)] + (assoc state :teams teams)))] + (ptk/reify ::fetch-teams + ptk/WatchEvent + (watch [_ state s] + (->> (rp/query! :teams) + (rx/map (fn [data] #(on-fetched % data)))))))) (defn profile-fetched [{:keys [fullname id] :as data}] (us/verify ::profile data) (ptk/reify ::profile-fetched + IDeref + (-deref [_] data) + ptk/UpdateEvent (update [_ state] (-> state (assoc :profile-id id) - (assoc :profile data) - ;; Safeguard if the profile is loaded after teams - (assoc-in [:profile :teams] (get-in state [:profile :teams])))) + (assoc :profile data))) ptk/EffectEvent (effect [_ state stream] @@ -73,12 +84,31 @@ (defn fetch-profile [] - (reify + (ptk/reify ::fetch-profile ptk/WatchEvent - (watch [_ state s] + (watch [_ state stream] (->> (rp/query! :profile) (rx/map profile-fetched))))) +(defn fetch-profile-and-teams + "Event used mainly on application bootstrap; it fetches the profile + and if and only if the fetched profile corresponds to an + authenticated user; proceed to fetch teams." + [] + (ptk/reify ::fetch-profile-and-teams + ptk/WatchEvent + (watch [_ state stream] + (rx/merge + (rx/of (fetch-profile)) + (->> stream + (rx/filter (ptk/type? ::profile-fetched)) + (rx/take 1) + (rx/map deref) + (rx/mapcat (fn [profile] + (if (= uuid/zero (:id profile)) + (rx/empty) + (rx/of (fetch-teams)))))))))) + ;; --- Update Profile (defn update-profile @@ -204,24 +234,3 @@ (watch [_ state stream] (->> (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/refs.cljs b/frontend/src/app/main/refs.cljs index e7bc1e5d6..43af0686a 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -32,6 +32,9 @@ (def profile (l/derived :profile st/state)) +(def teams + (l/derived :teams st/state)) + (def exception (l/derived :exception st/state)) diff --git a/frontend/src/app/main/ui/dashboard/sidebar.cljs b/frontend/src/app/main/ui/dashboard/sidebar.cljs index 9ef53c900..de53bedac 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/app/main/ui/dashboard/sidebar.cljs @@ -205,7 +205,7 @@ (mf/defc teams-selector-dropdown [{:keys [team profile locale] :as props}] (let [show-dropdown? (mf/use-state false) - teams (mf/use-state []) + teams (mf/deref refs/teams) on-create-clicked (mf/use-callback @@ -217,12 +217,6 @@ (da/set-current-team! team-id) (st/emit! (rt/nav :dashboard-projects {:team-id team-id}))))] - (mf/use-layout-effect - (mf/deps (:id team)) - (fn [] - (->> (rp/query! :teams) - (rx/subs #(reset! teams %))))) - [:ul.dropdown.teams-dropdown [:li.title (t locale "dashboard.switch-team")] [:hr] @@ -230,7 +224,7 @@ [:span.team-icon i/logo-icon] [:span.team-text (t locale "dashboard.your-penpot")]] - (for [team (remove :is-default @teams)] + (for [team (remove :is-default (vals teams))] [:* {:key (:id team)} [:li.team-name {:on-click (partial team-selected (:id team))} [:span.team-icon diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index 0c651fb49..d76418cf4 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -271,7 +271,6 @@ (mf/use-effect (mf/deps file-id page-id token) (fn [] - (prn "viewer-page$use-effect") (st/emit! (dv/initialize props)))) (mf/use-effect diff --git a/frontend/src/app/main/ui/viewer/header.cljs b/frontend/src/app/main/ui/viewer/header.cljs index e2eeff8a5..39eb8b84e 100644 --- a/frontend/src/app/main/ui/viewer/header.cljs +++ b/frontend/src/app/main/ui/viewer/header.cljs @@ -188,12 +188,12 @@ total (count frames) locale (mf/deref i18n/locale) profile (mf/deref refs/profile) - anonymous? (= uuid/zero (:id profile)) + teams (mf/deref refs/teams) team-id (get-in data [:project :team-id]) - has-permission? (and (not anonymous?) - (contains? (:teams profile) team-id)) + has-permission? (and (not= uuid/zero (:id profile)) + (contains? teams team-id)) project-id (get-in data [:project :id]) file-id (get-in data [:file :id]) From 239ec1252940cf90efd447986f8b5129f57b3643 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 13 Apr 2021 12:12:56 +0200 Subject: [PATCH 3/6] :bug: Fix usability issue on team invitation dialog. Happens when user select (autocompletion), writes or pastes an email that starts and/or ends with a trailing spaces. The new spec allows tailing spaces but conforms to a valid email address without trailing spaces. --- common/app/common/spec.cljc | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/common/app/common/spec.cljc b/common/app/common/spec.cljc index 907395b40..81fc5195d 100644 --- a/common/app/common/spec.cljc +++ b/common/app/common/spec.cljc @@ -25,9 +25,6 @@ ;; --- Constants -(def email-rx - #"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$") - (def uuid-rx #"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") @@ -86,12 +83,6 @@ v ::s/invalid)) -(defn- email-conformer - [v] - (if (and (string? v) (re-matches email-rx v)) - v - ::s/invalid)) - (defn keyword-conformer [v] (cond @@ -109,7 +100,6 @@ (s/def ::keyword (s/conformer keyword-conformer name)) (s/def ::inst inst?) (s/def ::string string?) -(s/def ::email (s/conformer email-conformer str)) (s/def ::color (s/conformer color-conformer str)) (s/def ::uuid (s/conformer uuid-conformer str)) (s/def ::boolean (s/conformer boolean-conformer boolean-unformer)) @@ -134,6 +124,18 @@ (>= % min-safe-int) (<= % max-safe-int))) + +;; --- SPEC: email + +(let [re #"[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+" + cfn (fn [v] + (if (string? v) + (if-let [matches (re-seq re v)] + (first matches) + (do ::s/invalid)) + ::s/invalid))] + (s/def ::email (s/conformer cfn str))) + ;; --- Macros (defn spec-assert* From 94ccc013d7a9b874cbb472947c1c04885a3345a0 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 13 Apr 2021 13:10:51 +0200 Subject: [PATCH 4/6] :bug: Fix unexpected console errors on removing shape. Caused because in some instances selected shapes set will contain an id that is already removed from object. This is a tipical race condition. --- frontend/src/app/main/ui/measurements.cljs | 5 +++-- frontend/src/app/main/ui/workspace/viewport.cljs | 8 ++++++-- frontend/src/app/main/ui/workspace/viewport/outline.cljs | 9 +++++---- frontend/src/app/util/webapi.cljs | 4 +--- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/main/ui/measurements.cljs b/frontend/src/app/main/ui/measurements.cljs index b5b9521f9..baa197178 100644 --- a/frontend/src/app/main/ui/measurements.cljs +++ b/frontend/src/app/main/ui/measurements.cljs @@ -233,7 +233,8 @@ :stroke-width (/ select-guide-width zoom) :stroke-dasharray (/ select-guide-dasharray zoom)}}])]) -(mf/defc measurement [{:keys [bounds frame selected-shapes hover-shape zoom]}] +(mf/defc measurement + [{:keys [bounds frame selected-shapes hover-shape zoom]}] (let [selected-ids (into #{} (map :id) selected-shapes) selected-selrect (gsh/selection-rect selected-shapes) hover-selrect (:selrect hover-shape) @@ -244,7 +245,7 @@ [:g.measurement-feedback {:pointer-events "none"} [:& selection-guides {:selrect selected-selrect :bounds bounds :zoom zoom}] [:& size-display {:selrect selected-selrect :zoom zoom}] - + (if (or (not hover-shape) (not hover-selected-shape?)) (when frame [:g.hover-shapes diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index ca4bdb610..309a976e3 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -88,7 +88,11 @@ zoom (d/check-num zoom 1) drawing-tool (:tool drawing) drawing-obj (:object drawing) - selected-shapes (->> selected (mapv #(get objects %))) + + selected-shapes (into [] + (comp (map #(get objects %)) + (filter some?)) + selected) selected-frames (into #{} (map :frame-id) selected-shapes) ;; Only when we have all the selected shapes in one frame @@ -292,7 +296,7 @@ {:page-id page-id}]) [:& widgets/viewport-actions] - + (when show-prototypes? [:& interactions/interactions {:selected selected}]) diff --git a/frontend/src/app/main/ui/workspace/viewport/outline.cljs b/frontend/src/app/main/ui/workspace/viewport/outline.cljs index b84ef8322..84a7b4571 100644 --- a/frontend/src/app/main/ui/workspace/viewport/outline.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/outline.cljs @@ -68,9 +68,9 @@ ::mf/wrap [#(mf/memo' % (mf/check-props ["shapes" "zoom"]))]} [props] (let [shapes (obj/get props "shapes") - zoom (obj/get props "zoom") - color (if (or (> (count shapes) 1) (nil? (:shape-ref (first shapes)))) - "#31EFB8" "#00E0FF")] + zoom (obj/get props "zoom") + color (if (or (> (count shapes) 1) (nil? (:shape-ref (first shapes)))) + "#31EFB8" "#00E0FF")] (for [shape shapes] [:& outline {:key (str "outline-" (:id shape)) :shape (gsh/transform-shape shape) @@ -97,7 +97,8 @@ shapes (->> outlines-ids (filter #(not= edition %)) (map #(get objects %)) - (filterv show-outline?))] + (filterv show-outline?) + (filter some?))] [:g.outlines {:display (when (some? transform) "none")} [:& shape-outlines-render {:shapes shapes diff --git a/frontend/src/app/util/webapi.cljs b/frontend/src/app/util/webapi.cljs index f3ce1da2f..57bf1c9bf 100644 --- a/frontend/src/app/util/webapi.cljs +++ b/frontend/src/app/util/webapi.cljs @@ -90,9 +90,7 @@ (let [target (.-target ^js event)] (when (and (not (.-isContentEditable target)) ;; ignore when pasting into (not= (.-tagName target) "INPUT")) ;; an editable control - (-> ^js event - (.getBrowserEvent) - (.-clipboardData))))) + (.. ^js event getBrowserEvent -clipboardData)))) (defn extract-text [clipboard-data] From 162b0cfa6c1bfbf3a338222de9fdca172d759194 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 13 Apr 2021 12:14:40 +0200 Subject: [PATCH 5/6] :bug: Fixes issue when parsing exponential numbers in paths (backport). --- frontend/src/app/util/geom/path.cljs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/util/geom/path.cljs b/frontend/src/app/util/geom/path.cljs index 206fc5b6c..9fad1cec7 100644 --- a/frontend/src/app/util/geom/path.cljs +++ b/frontend/src/app/util/geom/path.cljs @@ -31,11 +31,11 @@ (into [] (impl-simplify/simplify points tolerance true))))) ;; -(def commands-regex #"(?i)[a-z][^a-z]*") +(def commands-regex #"(?i)[mzlhvcsqta][^mzlhvcsqta]*") ;; Matches numbers for path values allows values like... -.01, 10, +12.22 ;; 0 and 1 are special because can refer to flags -(def num-regex #"[+-]?(\d+(\.\d+)?|\.\d+)") +(def num-regex #"[+-]?(\d+(\.\d+)?|\.\d+)(e[+-]?\d+)?") (def flag-regex #"[01]") @@ -373,14 +373,15 @@ (reduce simplify-command [[start] start-pos start-pos start-pos start-pos]) (first)))) -(defn path->content [string] - (let [clean-string (-> string - (str/trim) - ;; Change "commas" for spaces - (str/replace #"," " ") - ;; Remove all consecutive spaces - (str/replace #"\s+" " ")) - commands (re-seq commands-regex clean-string)] +(defn path->content [path-str] + (let [clean-path-str + (-> path-str + (str/trim) + ;; Change "commas" for spaces + (str/replace #"," " ") + ;; Remove all consecutive spaces + (str/replace #"\s+" " ")) + commands (re-seq commands-regex clean-path-str)] (-> (mapcat parse-command commands) (simplify-commands)))) From 6161911ff14b7d88631488d62c742ac110e80bfe Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 13 Apr 2021 13:46:23 +0200 Subject: [PATCH 6/6] :bug: Fixes measurement on root frame --- frontend/src/app/main/ui/measurements.cljs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/main/ui/measurements.cljs b/frontend/src/app/main/ui/measurements.cljs index baa197178..69bb881e8 100644 --- a/frontend/src/app/main/ui/measurements.cljs +++ b/frontend/src/app/main/ui/measurements.cljs @@ -9,14 +9,15 @@ (ns app.main.ui.measurements (:require - [rumext.alpha :as mf] + [app.common.data :as d] + [app.common.geom.point :as gpt] + [app.common.geom.shapes :as gsh] + [app.common.math :as mth] + [app.common.uuid :as uuid] + [app.main.store :as st] [cuerdas.core :as str] [okulary.core :as l] - [app.common.data :as d] - [app.common.math :as mth] - [app.common.geom.shapes :as gsh] - [app.common.geom.point :as gpt] - [app.main.store :as st])) + [rumext.alpha :as mf])) ;; ------------------------------------------------ ;; CONSTANTS @@ -247,7 +248,7 @@ [:& size-display {:selrect selected-selrect :zoom zoom}] (if (or (not hover-shape) (not hover-selected-shape?)) - (when frame + (when (and frame (not= uuid/zero (:id frame))) [:g.hover-shapes [:& distance-display {:from (:selrect frame) :to selected-selrect