diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 5c5bd3401..e957d1608 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -78,7 +78,6 @@ [beicon.core :as rx] [cljs.spec.alpha :as s] [cuerdas.core :as str] - [linked.core :as lks] [potok.core :as ptk])) (def default-workspace-local {:zoom 1}) @@ -220,7 +219,7 @@ (rx/map (fn [data] (assoc file :data data)))))) (rx/reduce conj []) (rx/map libraries-fetched))) - (rx/of (workspace-initialized))) + (rx/of (with-meta (workspace-initialized) {:file-id id}))) (rx/take-until stoper)))))) (defn- libraries-fetched @@ -369,7 +368,10 @@ ptk/WatchEvent (watch [_ state _] - (let [pindex (-> state :workspace-data :pages-index)] + ;; NOTE: there are cases between files navigation when this + ;; event is emmited but the page-index is still not loaded, so + ;; we only need to proceed when page-index is properly loaded + (when-let [pindex (-> state :workspace-data :pages-index)] (if (contains? pindex page-id) (rx/of (preload-data-uris page-id) (dwth/watch-state-changes) @@ -593,6 +595,7 @@ (defn start-rename-shape + "Start shape renaming process" [id] (dm/assert! (uuid? id)) (ptk/reify ::start-rename-shape @@ -601,14 +604,33 @@ (assoc-in state [:workspace-local :shape-for-rename] id)))) (defn end-rename-shape - [] - (ptk/reify ::end-rename-shape - ptk/UpdateEvent - (update [_ state] - (update state :workspace-local dissoc :shape-for-rename)))) + "End the ongoing shape rename process" + ([] (end-rename-shape nil)) + ([name] + (ptk/reify ::end-rename-shape + ptk/WatchEvent + (watch [_ state _] + (prn "end-rename-shape" name (string? name) (not (str/blank? name))) + (when-let [shape-id (dm/get-in state [:workspace-local :shape-for-rename])] + (prn "end-rename-shape" shape-id) + (let [shape (wsh/lookup-shape state shape-id)] + (rx/concat + ;; Remove rename state from workspace local state + (rx/of #(update % :workspace-local dissoc :shape-for-rename)) + + ;; Rename the shape if string is not empty/blank + (when (and (string? name) (not (str/blank? name))) + (rx/of (update-shape shape-id {:name name}))) + + ;; Update the component in case if shape is a main instance + (when (:main-instance? shape) + (when-let [component-id (:component-id shape)] + (rx/of (dwl/rename-component component-id name))))))))))) + ;; --- Update Selected Shapes attrs + (defn update-selected-shapes [attrs] (dm/assert! (cts/shape-attrs? attrs)) @@ -1173,27 +1195,53 @@ (update state :workspace-assets dissoc :selected)))))) (defn go-to-main-instance - [page-id shape-id] - (dm/assert! (uuid? page-id)) - (dm/assert! (uuid? shape-id)) + [file-id component-id] + (dm/assert! + "expected uuid type for `file-id` parameter (nilable)" + (or (nil? file-id) + (uuid? file-id))) + + (dm/assert! + "expected uuid type for `component-id` parameter" + (uuid? component-id)) + (ptk/reify ::go-to-main-instance ptk/WatchEvent (watch [_ state stream] - (let [current-page-id (:current-page-id state)] - (if (= page-id current-page-id) - (rx/of (dws/select-shapes (lks/set shape-id)) - dwz/zoom-to-selected-shape) - (let [project-id (:current-project-id state) - file-id (:current-file-id state) - pparams {:file-id file-id :project-id project-id} - qparams {:page-id page-id}] - (rx/merge - (rx/of (rt/nav :workspace pparams qparams)) - (->> stream - (rx/filter (ptk/type? ::dwv/page-loaded)) - (rx/take 1) - (rx/mapcat #(rx/of (dws/select-shapes (lks/set shape-id)) - dwz/zoom-to-selected-shape)))))))))) + (let [current-file-id (:current-file-id state) + current-page-id (:current-page-id state) + current-project-id (:current-project-id state) + file-id (or file-id current-file-id) + + redirect-to + (fn [file-id page-id] + (let [pparams {:file-id file-id :project-id current-project-id} + qparams {:page-id page-id}] + (rx/merge + (rx/of (rt/nav :workspace pparams qparams)) + (->> stream + (rx/filter (ptk/type? ::workspace-initialized)) + (rx/map meta) + (rx/filter #(= file-id (:file-id %))) + (rx/take 1) + (rx/observe-on :async) + (rx/map #(go-to-main-instance file-id component-id))))))] + + (if (= file-id current-file-id) + (let [component (dm/get-in state [:workspace-data :components component-id]) + page-id (:main-instance-page component)] + + (when (some? page-id) + (if (= page-id current-page-id) + (let [shape-id (:main-instance-id component)] + (rx/of (dws/select-shapes (d/ordered-set shape-id)) + dwz/zoom-to-selected-shape)) + (redirect-to current-page-id page-id)))) + + (let [component (dm/get-in state [:workspace-libraries file-id :data :components component-id])] + (some->> (:main-instance-page component) + (redirect-to file-id)))))))) + (defn go-to-component [component-id] diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 5620783f3..29f40d1c2 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -335,6 +335,8 @@ ;; NOTE: we need to ensure the component exists, ;; because there are small possibilities of race ;; conditions with component deletion. + ;; + ;; FIXME: this race conditon should be handled in pcb/update-component (when component (cond-> component :always @@ -343,7 +345,7 @@ (not components-v2) (update :objects - ;; Give the same name to the root shape + ;; Give the same name to the root shape #(assoc-in % [id :name] name))))) changes (-> (pcb/empty-changes it) @@ -353,9 +355,19 @@ (rx/of (dch/commit-changes changes))))))) (defn rename-component-and-main-instance - [component-id shape-id name page-id] - (st/emit! (rename-component component-id name) - (dch/update-shapes [shape-id] #(merge % {:name name}) {:page-id page-id :stack-undo? true}))) + [component-id name] + (ptk/reify ::rename-component-and-main-instance + ptk/WatchEvent + (watch [_ state _] + (when-let [component (dm/get-in state [:workspace-data :components component-id])] + (let [shape-id (:main-instance-id component) + page-id (:main-instance-page component)] + (rx/concat + (rx/of (rename-component component-id name)) + + ;; NOTE: only when components-v2 is enabled + (when (and shape-id page-id) + (rx/of (dch/update-shapes [shape-id] #(assoc % :name name) {:page-id page-id :stack-undo? true}))))))))) (defn duplicate-component "Create a new component copied from the one with the given id." diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 6d2b12cf4..70b9e935e 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -104,9 +104,12 @@ (def workspace-drawing (l/derived :workspace-drawing st/state)) +;; FIXME: define it as function, because in some situations this +;; current check is not enought for true readiness (def workspace-ready? (l/derived (fn [state] (and (:workspace-ready? state) + (:workspace-data state) (:current-file-id state) (:current-project-id state))) st/state)) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index 1a7a7e4cf..7c0872ed9 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -415,13 +415,10 @@ on-component-double-click (mf/use-fn - (mf/deps component selected) + (mf/deps file-id component-id) (fn [event] (dom/stop-propagation event) - (let [main-instance-id (:main-instance-id component) - main-instance-page (:main-instance-page component)] - (when (and main-instance-id main-instance-page) ;; Only when :components-v2 is enabled - (st/emit! (dw/go-to-main-instance main-instance-page main-instance-id)))))) + (st/emit! (dw/go-to-main-instance file-id component-id)))) on-drop (mf/use-fn @@ -605,8 +602,12 @@ on-asset-click on-assets-delete on-clear-selection open-status-ref]}] (let [input-ref (mf/use-ref nil) - state (mf/use-state {:renaming nil - :component-id nil}) + + state* (mf/use-state {}) + state (deref state*) + + current-component-id (:component-id state) + renaming? (:renaming state) open-groups-ref (mf/with-memo [open-status-ref] (-> (l/in [:groups :components]) @@ -647,50 +648,43 @@ on-duplicate (mf/use-fn - (mf/deps @state) + (mf/deps current-component-id selected) (fn [] - (let [undo-id (js/Symbol)] - (if (empty? selected) - (st/emit! (dwl/duplicate-component file-id (:component-id @state))) - (do - (st/emit! (dwu/start-undo-transaction undo-id)) - (apply st/emit! (map (partial dwl/duplicate-component file-id) selected)) - (st/emit! (dwu/commit-undo-transaction undo-id))))))) + (if (empty? selected) + (st/emit! (dwl/duplicate-component file-id current-component-id)) + (let [undo-id (js/Symbol)] + (st/emit! (dwu/start-undo-transaction undo-id)) + (run! st/emit! (map (partial dwl/duplicate-component file-id) selected)) + (st/emit! (dwu/commit-undo-transaction undo-id)))))) on-delete (mf/use-fn - (mf/deps @state file-id multi-components? multi-assets?) + (mf/deps current-component-id file-id multi-components? multi-assets? on-assets-delete) (fn [] (let [undo-id (js/Symbol)] (if (or multi-components? multi-assets?) (on-assets-delete) (st/emit! (dwu/start-undo-transaction undo-id) - (dwl/delete-component {:id (:component-id @state)}) - (dwl/sync-file file-id file-id :components (:component-id @state)) + (dwl/delete-component {:id current-component-id}) + (dwl/sync-file file-id file-id :components current-component-id) (dwu/commit-undo-transaction undo-id)))))) + on-close-menu + (mf/use-fn #(swap! menu-state close-context-menu)) + on-rename - (mf/use-fn - (fn [] - (swap! state (fn [state] - (assoc state :renaming (:component-id state)))))) + (mf/use-fn #(swap! state* assoc :renaming true)) + + cancel-rename + (mf/use-fn #(swap! state* dissoc :renaming)) do-rename (mf/use-fn - (mf/deps @state) + (mf/deps current-component-id) (fn [new-name] - (let [component-id (:renaming @state) - component (dm/get-in file [:components component-id]) - main-instance-id (:main-instance-id component) - main-instance-page (:main-instance-page component)] - - (dwl/rename-component-and-main-instance component-id main-instance-id new-name main-instance-page) - (swap! state assoc :renaming nil)))) - - cancel-rename - (mf/use-fn - (fn [] - (swap! state assoc :renaming nil))) + (swap! state* dissoc :renaming) + (st/emit! + (dwl/rename-component-and-main-instance current-component-id new-name)))) on-context-menu (mf/use-fn @@ -701,17 +695,13 @@ (when (and local? (not read-only?)) (when-not (contains? selected component-id) (on-clear-selection)) - (swap! state assoc :component-id component-id) - (swap! menu-state open-context-menu pos))))) - on-close-menu - (mf/use-fn - (fn [] - (swap! menu-state close-context-menu))) + (swap! state* assoc :component-id component-id) + (swap! menu-state open-context-menu pos))))) create-group (mf/use-fn - (mf/deps components selected on-clear-selection) + (mf/deps current-component-id components selected on-clear-selection) (fn [group-name] (on-clear-selection) (let [undo-id (js/Symbol)] @@ -720,7 +710,7 @@ (->> components (filter #(if multi-components? (contains? selected (:id %)) - (= (:component-id @state) (:id %)))) + (= current-component-id (:id %)))) (map #(dwl/rename-component (:id %) (add-group % group-name))))) @@ -780,17 +770,10 @@ on-show-main (mf/use-fn - (mf/deps @state components) + (mf/deps current-component-id file-id) (fn [event] (dom/stop-propagation event) - (let [component-id (:component-id @state) - component (->> components - (filter #(= (:id %) component-id)) - first) - main-instance-id (:main-instance-id component) - main-instance-page (:main-instance-page component)] - (when (and main-instance-id main-instance-page) ;; Only when :components-v2 is enabled - (st/emit! (dw/go-to-main-instance main-instance-page main-instance-id)))))) + (st/emit! (dw/go-to-main-instance file-id current-component-id)))) on-asset-click (mf/use-fn (mf/deps groups on-asset-click) (partial on-asset-click groups))] @@ -815,7 +798,7 @@ :prefix "" :groups groups :open-groups open-groups - :renaming (:renaming @state) + :renaming (when ^boolean renaming? current-component-id) :listing-thumbs? listing-thumbs? :selected selected :on-asset-click on-asset-click diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_name.cljs b/frontend/src/app/main/ui/workspace/sidebar/layer_name.cljs index 26b03c5b8..b7d47067d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layer_name.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layer_name.cljs @@ -8,7 +8,6 @@ (:require-macros [app.main.style :refer [css]]) (:require [app.main.data.workspace :as dw] - [app.main.data.workspace.libraries :as dwl] [app.main.store :as st] [app.main.ui.context :as ctx] [app.util.dom :as dom] @@ -19,6 +18,7 @@ (def shape-for-rename-ref (l/derived (l/in [:workspace-local :shape-for-rename]) st/state)) + (mf/defc layer-name [{:keys [shape on-start-edit disabled-double-click on-stop-edit name-ref depth parent-size selected? type-comp type-frame hidden] :as props}] (let [local (mf/use-state {}) @@ -28,27 +28,25 @@ start-edit (fn [] (when (not disabled-double-click) (on-start-edit) - (swap! local assoc :edition true))) + (swap! local assoc :edition true) + (st/emit! (dw/start-rename-shape (:id shape))))) accept-edit (fn [] (let [name-input (mf/ref-val name-ref) - name (str/trim (dom/get-value name-input)) - main-instance? (:main-instance? shape)] + name (str/trim (dom/get-value name-input))] (on-stop-edit) (swap! local assoc :edition false) - (st/emit! (dw/end-rename-shape)) - (when-not (str/empty? name) - (if main-instance? - (dwl/rename-component-and-main-instance (:component-id shape) (:id shape) name nil) - (st/emit! (dw/update-shape (:id shape) {:name name})))))) + (st/emit! (dw/end-rename-shape name)))) + cancel-edit (fn [] (on-stop-edit) (swap! local assoc :edition false) - (st/emit! (dw/end-rename-shape))) + (st/emit! (dw/end-rename-shape nil))) on-key-down (fn [event] (when (kbd/enter? event) (accept-edit)) (when (kbd/esc? event) (cancel-edit))) + space-for-icons 110 parent-size (str (- parent-size space-for-icons) "px")] @@ -87,4 +85,4 @@ :ref name-ref :on-double-click start-edit} (:name shape "") - (when (seq (:touched shape)) " *")]))) \ No newline at end of file + (when (seq (:touched shape)) " *")]))) diff --git a/frontend/src/app/main/ui/workspace/viewport/viewport_ref.cljs b/frontend/src/app/main/ui/workspace/viewport/viewport_ref.cljs index 32bd7fccf..41c379d02 100644 --- a/frontend/src/app/main/ui/workspace/viewport/viewport_ref.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/viewport_ref.cljs @@ -49,7 +49,7 @@ (defn point->viewport [pt] - (let [zoom (dm/get-in @st/state [:workspace-local :zoom])] + (let [zoom (dm/get-in @st/state [:workspace-local :zoom] 1)] (when (and (some? @viewport-ref) (some? @viewport-brect)) (let [vbox (.. ^js @viewport-ref -viewBox -baseVal)