diff --git a/backend/src/uxbox/http/ws.clj b/backend/src/uxbox/http/ws.clj index 03017c669..9772814e9 100644 --- a/backend/src/uxbox/http/ws.clj +++ b/backend/src/uxbox/http/ws.clj @@ -17,21 +17,33 @@ [ring.middleware.keyword-params :refer [wrap-keyword-params]] [ring.middleware.params :refer [wrap-params]] [uxbox.common.spec :as us] + [uxbox.db :as db] [uxbox.http.session :refer [wrap-auth]] [uxbox.services.notifications :as nf])) (s/def ::file-id ::us/uuid) (s/def ::session-id ::us/uuid) + (s/def ::websocket-params (s/keys :req-un [::file-id ::session-id])) (defn websocket [{:keys [profile-id] :as req}] (let [params (us/conform ::websocket-params (:params req)) - params (assoc params :profile-id profile-id)] - (if profile-id - (nf/websocket params) - {:error {:code 403 :message "Authentication required"}}))) + file (db/get-by-id db/pool :file (:file-id params)) + params (assoc params + :profile-id profile-id + :file file)] + + (cond + (not profile-id) + {:error {:code 403 :message "Authentication required"}} + + (not file) + {:error {:code 404 :message "File does not exists"}} + + :else + (nf/websocket params)))) (def handler (-> websocket diff --git a/backend/src/uxbox/services/notifications.clj b/backend/src/uxbox/services/notifications.clj index 4c8073870..69b241397 100644 --- a/backend/src/uxbox/services/notifications.clj +++ b/backend/src/uxbox/services/notifications.clj @@ -2,6 +2,9 @@ ;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; file, You can obtain one at http://mozilla.org/MPL/2.0/. ;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; ;; Copyright (c) 2020 UXBOX Labs SL (ns uxbox.services.notifications diff --git a/backend/src/uxbox/util/transit.clj b/backend/src/uxbox/util/transit.clj index c1f5618f7..e1937326d 100644 --- a/backend/src/uxbox/util/transit.clj +++ b/backend/src/uxbox/util/transit.clj @@ -32,9 +32,7 @@ (fn [v] (into {} v)))) (def point-read-handler - (t/read-handler - (fn [value] - (gpt/map->Point value)))) + (t/read-handler gpt/map->Point)) (def matrix-write-handler (t/write-handler @@ -42,9 +40,7 @@ (fn [v] (into {} v)))) (def matrix-read-handler - (t/read-handler - (fn [value] - (gmt/map->Matrix value)))) + (t/read-handler gmt/map->Matrix)) (def +read-handlers+ (assoc dt/+read-handlers+ diff --git a/backend/tests/uxbox/tests/helpers.clj b/backend/tests/uxbox/tests/helpers.clj index bd5f1eb3a..56a8d5203 100644 --- a/backend/tests/uxbox/tests/helpers.clj +++ b/backend/tests/uxbox/tests/helpers.clj @@ -19,6 +19,7 @@ [uxbox.services.mutations.colors :as colors] [uxbox.fixtures :as fixtures] [uxbox.migrations] + [uxbox.images] [uxbox.media] [uxbox.db :as db] [uxbox.util.blob :as blob] @@ -44,6 +45,7 @@ #'uxbox.db/pool #'uxbox.redis/client #'uxbox.redis/conn + #'uxbox.images/semaphore #'uxbox.services.init/query-services #'uxbox.services.init/mutation-services #'uxbox.migrations/migrations diff --git a/backend/tests/uxbox/tests/test_common_pages.clj b/backend/tests/uxbox/tests/test_common_pages.clj index 3c79c8184..ebe465b1a 100644 --- a/backend/tests/uxbox/tests/test_common_pages.clj +++ b/backend/tests/uxbox/tests/test_common_pages.clj @@ -247,7 +247,7 @@ (t/is (= [id3 id1 id2] (get-in res [:objects uuid/zero :shapes]))))) )) -(t/deftest process-changes-move-objects +(t/deftest process-change-move-objects (let [frame-a-id (uuid/custom 1) frame-b-id (uuid/custom 2) group-a-id (uuid/custom 3) @@ -391,10 +391,10 @@ (t/is (= data res)))))) -(t/deftest process-changes-move-objects-3 +(t/deftest process-change-move-objects-regression (let [shape-2-id (uuid/custom 1 2) shape-3-id (uuid/custom 1 3) - frame-id (uuid/custom 1 1) + frame-id (uuid/custom 1 1) changes [{:type :add-obj :id frame-id :frame-id uuid/zero @@ -426,7 +426,7 @@ (get-in data [:objects shape-3-id :frame-id]))))))) -(t/deftest process-changes-move-objects-2 +(t/deftest process-change-move-objects-2 (let [shape-1-id (uuid/custom 1 1) shape-2-id (uuid/custom 1 2) shape-3-id (uuid/custom 1 3) diff --git a/backend/tests/uxbox/tests/test_services_files.clj b/backend/tests/uxbox/tests/test_services_files.clj index 470159973..7cea2f01e 100644 --- a/backend/tests/uxbox/tests/test_services_files.clj +++ b/backend/tests/uxbox/tests/test_services_files.clj @@ -189,7 +189,7 @@ (t/is (= image-id (:id result))) (t/is (= "testfile" (:name result))) (t/is (= "image/jpeg" (:mtype result))) - (t/is (= "image/webp" (:thumb-mtype result)))) + (t/is (= "image/jpeg" (:thumb-mtype result)))) (let [data2 {::sm/type :import-image-to-file :image-id image-id diff --git a/backend/tests/uxbox/tests/test_services_images.clj b/backend/tests/uxbox/tests/test_services_images.clj index 5d6e8721f..92fa7e15a 100644 --- a/backend/tests/uxbox/tests/test_services_images.clj +++ b/backend/tests/uxbox/tests/test_services_images.clj @@ -128,7 +128,7 @@ (t/is (= image-id (get-in out [:result :id]))) (t/is (= "testfile" (get-in out [:result :name]))) (t/is (= "image/jpeg" (get-in out [:result :mtype]))) - (t/is (= "image/webp" (get-in out [:result :thumb-mtype]))) + (t/is (= "image/jpeg" (get-in out [:result :thumb-mtype]))) (t/is (= 800 (get-in out [:result :width]))) (t/is (= 800 (get-in out [:result :height]))) @@ -148,7 +148,7 @@ (t/is (= image-id (get-in out [:result 0 :id]))) (t/is (= "testfile" (get-in out [:result 0 :name]))) (t/is (= "image/jpeg" (get-in out [:result 0 :mtype]))) - (t/is (= "image/webp" (get-in out [:result 0 :thumb-mtype]))) + (t/is (= "image/jpeg" (get-in out [:result 0 :thumb-mtype]))) (t/is (= 800 (get-in out [:result 0 :width]))) (t/is (= 800 (get-in out [:result 0 :height]))) @@ -167,7 +167,7 @@ (t/is (= image-id (get-in out [:result :id]))) (t/is (= "testfile" (get-in out [:result :name]))) (t/is (= "image/jpeg" (get-in out [:result :mtype]))) - (t/is (= "image/webp" (get-in out [:result :thumb-mtype]))) + (t/is (= "image/jpeg" (get-in out [:result :thumb-mtype]))) (t/is (= 800 (get-in out [:result :width]))) (t/is (= 800 (get-in out [:result :height]))) diff --git a/common/uxbox/common/geom/shapes.cljc b/common/uxbox/common/geom/shapes.cljc index a2b205bef..8f63639af 100644 --- a/common/uxbox/common/geom/shapes.cljc +++ b/common/uxbox/common/geom/shapes.cljc @@ -48,11 +48,7 @@ (update :y inc-y))))] (-> shape (update :x inc-x) - (update :x1 inc-x) - (update :x2 inc-x) (update :y inc-y) - (update :y1 inc-y) - (update :y2 inc-y) (update-in [:selrect :x] inc-x) (update-in [:selrect :x1] inc-x) (update-in [:selrect :x2] inc-x) @@ -197,11 +193,10 @@ (us/assert number? width) (us/assert number? height) (-> shape - (assoc :width width - :height height - :x2 (+ (:x1 shape) width) - :y2 (+ (:y1 shape) height)) - (update :selrect (nilf #(resize % width height))))) + (assoc :width width :height height) + (update :selrect (fn [shape] + (assoc :x2 (+ (:x1 shape) width) + :y2 (+ (:y1 shape) height)))))) ;; --- Setup (Initialize) @@ -232,14 +227,11 @@ (defn- setup-image [{:keys [metadata] :as shape} {:keys [x y width height] :as props}] - (assoc shape - :x x - :y y - :width width - :height height - :proportion (/ (:width metadata) - (:height metadata)) - :proportion-lock true)) + (-> (setup-rect shape props) + (assoc + :proportion (/ (:width metadata) + (:height metadata)) + :proportion-lock true))) ;; --- Coerce to Rect-like shape. @@ -247,30 +239,16 @@ (declare group->rect-shape) (declare rect->rect-shape) +;; TODO: completly remove + (defn shape->rect-shape "Coerce shape to rect like shape." + [{:keys [type] :as shape}] (case type (:curve :path) (path->rect-shape shape) (rect->rect-shape shape))) -(defn shapes->rect-shape - [shapes] - (let [shapes (mapv shape->rect-shape shapes) - minx (transduce (map :x1) min ##Inf shapes) - miny (transduce (map :y1) min ##Inf shapes) - maxx (transduce (map :x2) max ##-Inf shapes) - maxy (transduce (map :y2) max ##-Inf shapes)] - {:x1 minx - :y1 miny - :x2 maxx - :y2 maxy - :x minx - :y miny - :width (- maxx minx) - :height (- maxy miny) - :type :rect})) - ;; -- Points (declare transform-shape-point) @@ -603,10 +581,10 @@ (defn pad-selrec ([selrect] (pad-selrec selrect 1)) - ([selrec size] + ([selrect size] (let [inc #(+ % size) dec #(- % size)] - (-> selrec + (-> selrect (update :x dec) (update :y dec) (update :x1 dec) @@ -728,10 +706,6 @@ (-> rect-shape (update :x check) (update :y check) - (update :x1 check) - (update :y1 check) - (update :x2 check) - (update :y2 check) (update :width (comp to-positive check)) (update :height (comp to-positive check))))) diff --git a/common/uxbox/common/pages.cljc b/common/uxbox/common/pages.cljc index 9d3b38f52..5516592ab 100644 --- a/common/uxbox/common/pages.cljc +++ b/common/uxbox/common/pages.cljc @@ -440,9 +440,16 @@ (defmethod process-change :del-obj [data {:keys [id] :as change}] (when-let [{:keys [frame-id shapes] :as obj} (get-in data [:objects id])] - (let [data (update data :objects dissoc id)] + (let [objects (:objects data) + parent-id (get-parent id objects) + parent (get objects parent-id) + data (update data :objects dissoc id)] (cond-> data - (contains? (:objects data) frame-id) + (and (not= parent-id frame-id) + (= :group (:type parent))) + (update-in [:objects parent-id :shapes] (fn [s] (filterv #(not= % id) s))) + + (contains? objects frame-id) (update-in [:objects frame-id :shapes] (fn [s] (filterv #(not= % id) s))) (seq shapes) ; Recursive delete all dependend objects diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index 366345735..a869298a9 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -659,18 +659,27 @@ ptk/WatchEvent (watch [_ state stream] (let [page-id (:current-page-id state) - session-id (:session-id state) objects (get-in state [:workspace-data page-id :objects]) cpindex (cp/calculate-child-parent-map objects) del-change #(array-map :type :del-obj :id %) + get-empty-parents + (fn get-empty-parents [id] + (let [parent (get objects (get cpindex id))] + (if (and (= :group (:type parent)) + (= 1 (count (:shapes parent)))) + (lazy-seq (cons (:id parent) + (get-empty-parents (:id parent)))) + nil))) + rchanges (reduce (fn [res id] (let [chd (cp/get-children id objects)] (into res (d/concat (mapv del-change (reverse chd)) - [(del-change id)])))) + [(del-change id)] + (map del-change (get-empty-parents id)))))) [] ids) @@ -1200,8 +1209,8 @@ :width (:width selection-rect) :height (:height selection-rect)}) -(def create-group - (ptk/reify ::create-group +(def group-selected + (ptk/reify ::group-selected ptk/WatchEvent (watch [_ state stream] (let [id (uuid/next) @@ -1209,21 +1218,23 @@ (when (not-empty selected) (let [page-id (get-in state [:workspace-page :id]) objects (get-in state [:workspace-data page-id :objects]) + selected-objects (map (partial get objects) selected) - selection-rect (geom/selection-rect selected-objects) + selrect (geom/selection-rect selected-objects) frame-id (-> selected-objects first :frame-id) - group-shape (group-shape id frame-id selected selection-rect) - frame-children (get-in objects [frame-id :shapes]) - index-frame (->> frame-children - (map-indexed vector) - (filter #(selected (second %))) - (ffirst)) + group (-> (group-shape id frame-id selected selrect) + (geom/setup selrect)) + + index (->> (get-in objects [frame-id :shapes]) + (map-indexed vector) + (filter #(selected (second %))) + (ffirst)) rchanges [{:type :add-obj :id id :frame-id frame-id - :obj group-shape - :index index-frame} + :obj group + :index index} {:type :mov-objects :parent-id id :shapes (vec selected)}] @@ -1235,8 +1246,8 @@ (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}) (dws/select-shapes #{id})))))))) -(def remove-group - (ptk/reify ::remove-group +(def ungroup-selected + (ptk/reify ::ungroup-selected ptk/WatchEvent (watch [_ state stream] (let [page-id (:current-page-id state) @@ -1405,8 +1416,8 @@ "ctrl+shift+'" #(st/emit! (toggle-layout-flag :snap-grid)) "+" #(st/emit! (increase-zoom nil)) "-" #(st/emit! (decrease-zoom nil)) - "g" #(st/emit! create-group) - "shift+g" #(st/emit! remove-group) + "g" #(st/emit! group-selected) + "shift+g" #(st/emit! ungroup-selected) "shift+0" #(st/emit! reset-zoom) "shift+1" #(st/emit! zoom-to-fit-all) "shift+2" #(st/emit! zoom-to-selected-shape) diff --git a/frontend/src/uxbox/main/data/workspace/drawing.cljs b/frontend/src/uxbox/main/data/workspace/drawing.cljs index 7e1cf7dee..803d12180 100644 --- a/frontend/src/uxbox/main/data/workspace/drawing.cljs +++ b/frontend/src/uxbox/main/data/workspace/drawing.cljs @@ -67,14 +67,13 @@ (letfn [(resize-shape [{:keys [x y width height] :as shape} point lock? point-snap] (let [;; The new shape behaves like a resize on the bottom-right corner initial (gpt/point (+ x width) (+ y height)) - shape' (geom/shape->rect-shape shape) - shapev (gpt/point (:width shape') (:height shape')) - deltav (gpt/to-vec initial point-snap) - scalev (gpt/divide (gpt/add shapev deltav) shapev) - scalev (if lock? - (let [v (max (:x scalev) (:y scalev))] - (gpt/point v v)) - scalev)] + shapev (gpt/point width height) + deltav (gpt/to-vec initial point-snap) + scalev (gpt/divide (gpt/add shapev deltav) shapev) + scalev (if lock? + (let [v (max (:x scalev) (:y scalev))] + (gpt/point v v)) + scalev)] (-> shape (assoc-in [:modifiers :resize-vector] scalev) (assoc-in [:modifiers :resize-origin] (gpt/point x y)) @@ -87,45 +86,46 @@ ptk/WatchEvent (watch [_ state stream] (let [{:keys [flags]} (:workspace-local state) + stoper? #(or (ms/mouse-up? %) (= % :interrupt)) - stoper (rx/filter stoper? stream) + stoper (rx/filter stoper? stream) initial @ms/mouse-position page-id (get state :current-page-id) objects (get-in state [:workspace-data page-id :objects]) - layout (get state :workspace-layout) + layout (get state :workspace-layout) - frames (->> objects - vals - (filter (comp #{:frame} :type)) - (remove #(= (:id %) uuid/zero) )) - - frame-id (or (->> frames - (filter #(geom/has-point? % initial)) - first - :id) - uuid/zero) + frames (cp/select-frames objects) + fid (or (->> frames + (filter #(geom/has-point? % initial)) + first + :id) + uuid/zero) shape (-> state (get-in [:workspace-local :drawing]) (geom/setup {:x (:x initial) :y (:y initial) :width 1 :height 1}) - (assoc :frame-id frame-id) + (assoc :frame-id fid) (assoc ::initialized? true))] (rx/concat + ;; Add shape to drawing state (rx/of #(assoc-in state [:workspace-local :drawing] shape)) + ;; Initial SNAP (->> (snap/closest-snap-point page-id [shape] layout initial) (rx/map (fn [{:keys [x y]}] - #(-> % - (assoc-in [:workspace-local :drawing :x] x) - (assoc-in [:workspace-local :drawing :y] y))))) + #(update-in % [:workspace-local :drawing] assoc :x x :y y)))) (->> ms/mouse-position (rx/with-latest vector ms/mouse-position-ctrl) - (rx/switch-map (fn [[point :as current]] - (->> (snap/closest-snap-point page-id [shape] layout point) - (rx/map #(conj current %))))) - (rx/map (fn [[pt ctrl? point-snap]] #(update-drawing % pt ctrl? point-snap))) + (rx/switch-map + (fn [[point :as current]] + (->> (snap/closest-snap-point page-id [shape] layout point) + (rx/map #(conj current %))))) + (rx/map + (fn [[pt ctrl? point-snap]] + #(update-drawing % pt ctrl? point-snap))) + (rx/take-until stoper)) (rx/of handle-finish-drawing))))))) diff --git a/frontend/src/uxbox/main/exports.cljs b/frontend/src/uxbox/main/exports.cljs index b5eb81780..6802e84ad 100644 --- a/frontend/src/uxbox/main/exports.cljs +++ b/frontend/src/uxbox/main/exports.cljs @@ -40,7 +40,7 @@ (defn- calculate-dimensions [{:keys [objects] :as data} vport] (let [shapes (cp/select-toplevel-shapes objects {:include-frames? true})] - (->> (geom/shapes->rect-shape shapes) + (->> (geom/selection-rect shapes) (geom/adjust-to-viewport vport) (geom/fix-invalid-rect-values)))) diff --git a/frontend/src/uxbox/main/ui/workspace/context_menu.cljs b/frontend/src/uxbox/main/ui/workspace/context_menu.cljs index 16445a6ba..a626334ee 100644 --- a/frontend/src/uxbox/main/ui/workspace/context_menu.cljs +++ b/frontend/src/uxbox/main/ui/workspace/context_menu.cljs @@ -58,8 +58,8 @@ do-hide-shape #(st/emit! (dw/recursive-assign id :hidden true)) do-lock-shape #(st/emit! (dw/recursive-assign id :blocked true)) do-unlock-shape #(st/emit! (dw/recursive-assign id :blocked false)) - do-create-group #(st/emit! dw/create-group) - do-remove-group #(st/emit! dw/remove-group)] + do-create-group #(st/emit! dw/group-selected) + do-remove-group #(st/emit! dw/ungroup-selected)] [:* [:& menu-entry {:title "Copy" :shortcut "Ctrl + c" diff --git a/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs b/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs index f81609f9b..ad8d0176a 100644 --- a/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/uxbox/main/ui/workspace/shapes/frame.cljs @@ -43,20 +43,6 @@ (recur (first ids) (rest ids)) false)))))) -(defn use-mouse-over - [{:keys [id] :as shape}] - (mf/use-callback - (mf/deps shape) - (fn [] - (st/emit! (dws/change-hover-state id true))))) - -(defn use-mouse-out - [{:keys [id] :as shape}] - (mf/use-callback - (mf/deps shape) - (fn [] - (st/emit! (dws/change-hover-state id false))))) - (defn frame-wrapper-factory [shape-wrapper] (let [frame-shape (frame/frame-shape shape-wrapper)] @@ -93,7 +79,19 @@ (fn [event] (dom/prevent-default event) (st/emit! dw/deselect-all - (dw/select-shape (:id shape)))))] + (dw/select-shape (:id shape))))) + + on-mouse-over + (mf/use-callback + (mf/deps (:id shape)) + (fn [] + (st/emit! (dws/change-hover-state (:id shape) true)))) + + on-mouse-out + (mf/use-callback + (mf/deps (:id shape)) + (fn [] + (st/emit! (dws/change-hover-state (:id shape) false))))] (when-not (:hidden shape) [:g {:class (when selected? "selected") @@ -115,8 +113,8 @@ ")") ;; User may also select the frame with single click in the label :on-click on-double-click - :on-mouse-over (use-mouse-over shape) - :on-mouse-out (use-mouse-out shape)} + :on-mouse-over on-mouse-over + :on-mouse-out on-mouse-out} (:name shape)] [:* [:& frame-shape diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs index 224a66b17..a26ade504 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs @@ -153,9 +153,9 @@ (fn [side {:keys [id] :as data}] (if (= side :center) (st/emit! (dw/relocate-shape id (:id item) 0)) - (let [to-index (if (= side :top) (inc index) index) + (let [to-index (if (= side :top) (inc index) index) parent-id (cp/get-parent (:id item) objects)] - (st/emit! (dw/relocate-shape id parent-id to-index))))) + (st/emit! (dw/relocate-shape id parent-id to-index))))) on-hold (fn [] diff --git a/frontend/src/uxbox/util/debug.cljs b/frontend/src/uxbox/util/debug.cljs index a6eae4df4..d997c3e5b 100644 --- a/frontend/src/uxbox/util/debug.cljs +++ b/frontend/src/uxbox/util/debug.cljs @@ -1,7 +1,7 @@ (ns uxbox.util.debug "Debugging utils") -(def debug-options #{:bounding-boxes :group :events :rotation-handler :resize-handler :selection-center #_:simple-selection }) +(def debug-options #{:bounding-boxes :group :events :rotation-handler :resize-handler :selection-center #_:simple-selection}) (defonce ^:dynamic *debug* (atom #{})) diff --git a/frontend/src/uxbox/worker/selection.cljs b/frontend/src/uxbox/worker/selection.cljs index e4ae865e8..b75dc7f22 100644 --- a/frontend/src/uxbox/worker/selection.cljs +++ b/frontend/src/uxbox/worker/selection.cljs @@ -65,7 +65,7 @@ [objects] (let [shapes (->> (cp/select-toplevel-shapes objects {:include-frames? true}) (map #(merge % (select-keys % [:x :y :width :height])))) - bounds (geom/shapes->rect-shape shapes) + bounds (geom/selection-rect shapes) bounds #js {:x (:x bounds) :y (:y bounds) :width (:width bounds)