diff --git a/frontend/src/app/main/data/workspace/colors.cljs b/frontend/src/app/main/data/workspace/colors.cljs index 9150e651a5..9594da29e8 100644 --- a/frontend/src/app/main/data/workspace/colors.cljs +++ b/frontend/src/app/main/data/workspace/colors.cljs @@ -683,18 +683,23 @@ (ptk/reify ::change-color-in-selected ptk/WatchEvent (watch [_ _ _] - (let [undo-id (js/Symbol)] + (let [undo-id (js/Symbol) + {text-ops true style-ops false} (group-by #(= :content (:prop %)) operations) + style-groups (group-by (juxt :prop :index) style-ops)] (rx/concat (rx/of (dwu/start-undo-transaction undo-id)) - (->> (rx/from operations) - (rx/map (fn [{:keys [shape-id index] :as operation}] - (case (:prop operation) - :fill (change-fill [shape-id] new-color index) - :stroke (change-stroke-color [shape-id] new-color index) - :shadow (change-shadow [shape-id] new-color index) - :content (dwt/update-text-with-function - shape-id - (partial change-text-color old-color new-color index)))))) + (->> (rx/from style-groups) + (rx/map (fn [[[prop index] operations]] + (let [ids (mapv :shape-id operations)] + (case prop + :fill (change-fill ids new-color index) + :stroke (change-stroke-color ids new-color index) + :shadow (change-shadow ids new-color index)))))) + (->> (rx/from text-ops) + (rx/map (fn [{:keys [shape-id index]}] + (dwt/update-text-with-function + shape-id + (partial change-text-color old-color new-color index))))) (rx/of (dwu/commit-undo-transaction undo-id))))))) (defn apply-color-from-palette diff --git a/frontend/test/frontend_tests/data/workspace_colors_test.cljs b/frontend/test/frontend_tests/data/workspace_colors_test.cljs index 2ae0150078..5f5b25fc1e 100644 --- a/frontend/test/frontend_tests/data/workspace_colors_test.cljs +++ b/frontend/test/frontend_tests/data/workspace_colors_test.cljs @@ -8,7 +8,8 @@ (:require [app.common.uuid :as uuid] [app.main.data.workspace.colors :as dwc] - [clojure.test :as t] + [cljs.test :as t :include-macros true] + [frontend-tests.helpers.async :as async] [potok.v2.core :as ptk])) (t/deftest build-change-fill-event @@ -82,3 +83,26 @@ :current-color {:color "#ff0000" :opacity 1}}} event (dwc/update-colorpicker-color {} true)] (t/is (some? (ptk/watch event valid-state nil))))) + +(t/deftest ^:async color-change-updates-the-shapes-of-a-property-in-one-event + ;; Changing a color in a selection of many shapes must not commit once per + ;; shape: each commit makes the page objects a new map, and everything that + ;; derives from them runs again. + (let [[a b c d e] (repeatedly 5 uuid/next) + color {:color "#ff0000" :opacity 1} + old-color {:color "#000000" :opacity 1} + operations [{:prop :fill :shape-id a :index 0} + {:prop :fill :shape-id b :index 0} + {:prop :fill :shape-id c :index 1} + {:prop :stroke :shape-id a :index 0} + {:prop :stroke :shape-id d :index 0} + {:prop :stroke :shape-id e :index 0}] + types (atom [])] + (await (async/observe (ptk/watch (dwc/change-color-in-selected operations color old-color) {} nil) + :on-next #(swap! types conj (ptk/type %)))) + + (t/is (= {:app.main.data.workspace.undo/start-undo-transaction 1 + :app.main.data.workspace.colors/change-fill 2 + :app.main.data.workspace.colors/change-stroke-color 1 + :app.main.data.workspace.undo/commit-undo-transaction 1} + (frequencies @types)))))