From 19f4faa03f926c8b32f9a5c3da97531116367cf6 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 7 Mar 2022 23:23:10 +0100 Subject: [PATCH] :recycle: Refactor workspace layout initialization and persistence --- frontend/src/app/main/data/workspace.cljs | 169 ++++------------- .../src/app/main/data/workspace/colors.cljs | 29 ++- .../src/app/main/data/workspace/layout.cljs | 171 ++++++++++++++++++ .../src/app/main/data/workspace/texts.cljs | 4 +- 4 files changed, 229 insertions(+), 144 deletions(-) create mode 100644 frontend/src/app/main/data/workspace/layout.cljs diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index da0d014b0..22433b94c 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -34,6 +34,7 @@ [app.main.data.workspace.guides :as dwgu] [app.main.data.workspace.interactions :as dwi] [app.main.data.workspace.layers :as dwly] + [app.main.data.workspace.layout :as layout] [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.notifications :as dwn] [app.main.data.workspace.path :as dwdp] @@ -53,12 +54,10 @@ [app.util.http :as http] [app.util.i18n :as i18n] [app.util.router :as rt] - [app.util.storage :refer [storage]] [app.util.timers :as tm] [app.util.webapi :as wapi] [beicon.core :as rx] [cljs.spec.alpha :as s] - [clojure.set :as set] [cuerdas.core :as str] [potok.core :as ptk])) @@ -74,69 +73,9 @@ ;; --- Initialize Workspace -(s/def ::layout-flag - #{:sitemap - :layers - :comments - :assets - :document-history - :colorpalette - :element-options - :rules - :display-grid - :snap-grid - :scale-text - :dynamic-alignment - :display-artboard-names - :snap-guides}) - -(s/def ::layout-flags (s/coll-of ::layout-flag)) - -(def default-workspace-layout - #{:sitemap - :layers - :element-options - :rules - :display-grid - :snap-grid - :dynamic-alignment - :display-artboard-names - :snap-guides}) - -(def layout-presets - {:assets - {:del #{:sitemap :layers :document-history} - :add #{:assets}} - - :document-history - {:del #{:assets :layers :sitemap} - :add #{:document-history}} - - :layers - {:del #{:document-history :assets} - :add #{:sitemap :layers}}}) - -(s/def ::options-mode #{:design :prototype}) - -(def default-workspace-global - {:options-mode :design}) - (def default-workspace-local {:zoom 1}) -(defn ensure-layout - [lname] - (ptk/reify ::ensure-layout - ptk/UpdateEvent - (update [_ state] - (update state :workspace-layout - (fn [stored] - (let [todel (get-in layout-presets [lname :del] #{}) - toadd (get-in layout-presets [lname :add] #{})] - (-> stored - (set/difference todel) - (set/union toadd)))))))) - (defn initialize [lname] (us/verify (s/nilable ::us/keyword) lname) @@ -144,14 +83,14 @@ ptk/UpdateEvent (update [_ state] (-> state - (update :workspace-layout #(or % default-workspace-layout)) - (update :workspace-global #(or % default-workspace-global)))) + (update :workspace-layout #(or % layout/default-layout)) + (update :workspace-global #(or % layout/default-global)))) ptk/WatchEvent (watch [_ _ _] - (if (and lname (contains? layout-presets lname)) - (rx/of (ensure-layout lname)) - (rx/of (ensure-layout :layers)))))) + (if (and lname (contains? layout/presets lname)) + (rx/of (layout/ensure-layout lname)) + (rx/of (layout/ensure-layout :layers)))))) (defn initialize-file [project-id file-id] @@ -247,7 +186,6 @@ (rx/observe-on :async)))))) (declare go-to-page) -(declare load-flag) (defn initialize-page [page-id] @@ -272,12 +210,8 @@ (assoc :current-page-id id) (assoc :trimmed-page (dm/select-keys page [:id :name])) (assoc :workspace-local local) - (update :workspace-layout - #(if (load-flag :colorpalette false) - (conj % :colorpalette) - (disj % :colorpalette))) - (assoc-in [:workspace-local :selected-palette] (load-flag :selected-palette :recent)) - (assoc-in [:workspace-local :selected-palette-colorpicker] (load-flag :selected-palette-colorpicker :recent)) + (update :workspace-layout layout/load-layout-flags) + (update :workspace-global layout/load-layout-state) (update :workspace-global assoc :background-color (-> page :options :background)) (update-in [:route :params :query] assoc :page-id (dm/str id)))) state)))) @@ -395,76 +329,39 @@ (->> (rp/mutation :rename-file params) (rx/ignore)))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Local storage Flags Manipulation -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(def storeable-layout-flags #{:colorpalette}) -(def storeable-workspace-local-flags #{:selected-palette :selected-palette-colorpicker}) - -(defn store-layout-flags! - [state flags] - (doseq [item (filter storeable-layout-flags flags)] - (swap! storage assoc item (contains? (:workspace-layout state) item)))) - -(defn store-workspace-local-flag! - [flag value] - (when (contains? storeable-workspace-local-flags flag) - (swap! storage assoc flag value))) - -(defn load-flag - [flag default] - (or (flag @storage) default)) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Workspace State Manipulation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; --- Toggle layout flag +;; --- Layout Flags + +(dm/export layout/toggle-layout-flag) +(dm/export layout/remove-layout-flag) + +;; --- Nudge + +(defn update-nudge + [{:keys [big small] :as params}] + (ptk/reify ::update-nudge + IDeref + (-deref [_] (d/without-nils params)) -(defn toggle-layout-flags - [& flags] - (ptk/reify ::toggle-layout-flags ptk/UpdateEvent (update [_ state] - (let [new-state (update state :workspace-layout - (fn [stored] - (reduce (fn [flags flag] - (if (contains? flags flag) - (disj flags flag) - (conj flags flag))) - stored - (d/concat-set flags))))] - (store-layout-flags! new-state flags) - new-state)))) + (update-in state [:profile :props :nudge] + (fn [nudge] + (cond-> nudge + (number? big) (assoc :big big) + (number? small) (assoc :small small))))) -(defn remove-layout-flags - [& flags] - (ptk/reify ::remove-layout-flags - ptk/UpdateEvent - (update [_ state] - (let [new-state (update state :workspace-layout - (fn [stored] - (reduce disj stored (d/concat-set flags))))] - (store-layout-flags! (:workspace-layout new-state) flags) - new-state)))) - -;; --- Set workspace flag - -(defn set-workspace-local-flag! - [state flag value] - (store-workspace-local-flag! flag value) - (assoc-in state [:workspace-local flag] value)) + ptk/WatchEvent + (watch [_ state _] + (let [nudge (get-in state [:profile :props :nudge])] + (rx/of (du/update-profile-props {:nudge nudge})))))) ;; --- Set element options mode -(defn set-options-mode - [mode] - (us/assert ::options-mode mode) - (ptk/reify ::set-options-mode - ptk/UpdateEvent - (update [_ state] - (assoc-in state [:workspace-global :options-mode] mode)))) +(dm/export layout/set-options-mode) ;; --- Tooltip @@ -1100,8 +997,8 @@ (defn go-to-layout [layout] - (us/verify ::layout-flag layout) - (ptk/reify ::set-workspace-layout + (us/verify ::layout/flag layout) + (ptk/reify ::go-to-layout IDeref (-deref [_] {:layout layout}) @@ -1154,7 +1051,7 @@ (defn go-to-component [component-id] - (ptk/reify ::set-workspace-layout-component + (ptk/reify ::go-to-component IDeref (-deref [_] {:layout :assets}) diff --git a/frontend/src/app/main/data/workspace/colors.cljs b/frontend/src/app/main/data/workspace/colors.cljs index 11c6559ce..5e132559c 100644 --- a/frontend/src/app/main/data/workspace/colors.cljs +++ b/frontend/src/app/main/data/workspace/colors.cljs @@ -9,8 +9,8 @@ [app.common.colors :as clr] [app.common.data :as d] [app.main.data.modal :as md] - [app.main.data.workspace :as dw] [app.main.data.workspace.changes :as dch] + [app.main.data.workspace.layout :as layout] [app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.texts :as dwt] [app.main.repo :as rp] @@ -46,7 +46,12 @@ (ptk/reify ::change-palette-selected ptk/UpdateEvent (update [_ state] - (dw/set-workspace-local-flag! state :selected-palette selected)))) + (assoc-in state [:workspace-global :selected-palette] selected)) + + ptk/EffectEvent + (effect [_ state _] + (let [wglobal (:workspace-global state)] + (layout/persist-layout-state! wglobal))))) (defn change-palette-selected-colorpicker "Change the library used by the color picker" @@ -54,7 +59,12 @@ (ptk/reify ::change-palette-selected-colorpicker ptk/UpdateEvent (update [_ state] - (dw/set-workspace-local-flag! state :selected-palette-colorpicker selected)))) + (assoc-in state [:workspace-global :selected-palette-colorpicker] selected)) + + ptk/EffectEvent + (effect [_ state _] + (let [wglobal (:workspace-global state)] + (layout/persist-layout-state! wglobal))))) (defn show-palette "Show the palette tool and change the library it uses" @@ -62,9 +72,16 @@ (ptk/reify ::show-palette ptk/UpdateEvent (update [_ state] - (-> state - (update :workspace-layout conj :colorpalette) - (dw/set-workspace-local-flag! :selected-palette selected))))) + (assoc-in state [:workspace-global :selected-palette] selected)) + + ptk/WatchEvent + (watch [_ _ _] + (rx/of (layout/toggle-layout-flag :colorpalette :force? true))) + + ptk/EffectEvent + (effect [_ state _] + (let [wglobal (:workspace-global state)] + (layout/persist-layout-state! wglobal))))) (defn start-picker [] diff --git a/frontend/src/app/main/data/workspace/layout.cljs b/frontend/src/app/main/data/workspace/layout.cljs new file mode 100644 index 000000000..9693a1030 --- /dev/null +++ b/frontend/src/app/main/data/workspace/layout.cljs @@ -0,0 +1,171 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; 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/. +;; +;; Copyright (c) UXBOX Labs SL + +(ns app.main.data.workspace.layout + "Workspace layout management events and helpers." + (:require + [app.common.spec :as us] + [app.util.storage :refer [storage]] + [cljs.spec.alpha :as s] + [clojure.set :as set] + [potok.core :as ptk])) + +(s/def ::flag + #{:sitemap + :layers + :comments + :assets + :document-history + :colorpalette + :element-options + :rules + :display-grid + :snap-grid + :scale-text + :dynamic-alignment + :display-artboard-names + :snap-guides}) + +(def presets + {:assets + {:del #{:sitemap :layers :document-history} + :add #{:assets}} + + :document-history + {:del #{:assets :layers :sitemap} + :add #{:document-history}} + + :layers + {:del #{:document-history :assets} + :add #{:sitemap :layers}}}) + +(s/def ::options-mode #{:design :prototype}) + +(def default-layout + #{:sitemap + :layers + :element-options + :rules + :display-grid + :snap-grid + :dynamic-alignment + :display-artboard-names + :snap-guides}) + +(def default-global + {:options-mode :design}) + +(defn ensure-layout + [name] + (ptk/reify ::ensure-layout + ptk/UpdateEvent + (update [_ state] + (update state :workspace-layout + (fn [stored] + (let [todel (get-in presets [name :del] #{}) + toadd (get-in presets [name :add] #{})] + (-> stored + (set/difference todel) + (set/union toadd)))))))) + +(declare persist-layout-flags!) + +(defn toggle-layout-flag + [flag & {:keys [force?] :as opts}] + (ptk/reify ::toggle-layout-flag + IDeref + (-deref [_] {:name flag}) + + ptk/UpdateEvent + (update [_ state] + (update state :workspace-layout + (fn [flags] + (if force? + (conj flags flag) + (if (contains? flags flag) + (disj flags flag) + (conj flags flag)))))) + + ptk/EffectEvent + (effect [_ state _] + (let [flags (:workspace-layout state)] + (persist-layout-flags! flags))))) + +(defn remove-layout-flag + [flag] + (ptk/reify ::remove-layout-flag + ptk/UpdateEvent + (update [_ state] + (update state :workspace-layout + (fn [flags] + (disj flags flag)))) + + ptk/EffectEvent + (effect [_ state _] + (let [flags (:workspace-layout state)] + (persist-layout-flags! flags))))) + +(defn set-options-mode + [mode] + (us/assert ::options-mode mode) + (ptk/reify ::set-options-mode + ptk/UpdateEvent + (update [_ state] + (assoc-in state [:workspace-global :options-mode] mode)))) + +(def layout-flags-persistence-mapping + "A map of layout flags that should be persisted in local storage; the + value corresponds to the key that will be used for save the data in + storage object. It should be namespace qualified." + {:colorpalette :app.main.data.workspace/show-colorpalette? + :textpalette :app.main.data.workspace/show-textpalette?}) + +(defn load-layout-flags + "Given the current layout flags, and updates them with the data + stored in Storage." + [layout] + (reduce (fn [layout [flag key]] + (condp = (get @storage key ::none) + ::none layout + false (disj layout flag) + true (conj layout flag))) + layout + layout-flags-persistence-mapping)) + +(defn persist-layout-flags! + "Given a set of layout flags, and persist a subset of them to the Storage." + [layout] + (doseq [[flag key] layout-flags-persistence-mapping] + (swap! storage assoc key (contains? layout flag)))) + +(def layout-state-persistence-mapping + "A mapping of keys that need to be persisted from `:workspace-global` into Storage." + {:selected-palette :app.main.data.workspace/selected-palette + :selected-palette-colorpicker :app.main.data.workspace/selected-palette-colorpicker}) + +(defn load-layout-state + "Given state (the :workspace-global) and update it with layout related + props that are previously persisted in the Storage." + [state] + (reduce (fn [state [key skey]] + (let [val (get @storage skey ::none)] + (if (= val ::none) + state + (assoc state key val)))) + state + layout-state-persistence-mapping)) + +(defn persist-layout-state! + "Given state (the :workspace-global) and persists a subset of layout + related props to the Storage." + [state] + (doseq [[key skey] layout-state-persistence-mapping] + (let [val (get state key ::does-not-exist)] + (if (= val ::does-not-exist) + (swap! storage dissoc skey) + (swap! storage assoc skey val))))) + + diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index 7b0b5f4c5..58087a559 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -81,7 +81,7 @@ (update [_ state] (let [text-state (some->> content ted/import-content) attrs (d/merge txt/default-text-attrs - (get-in state [:workspace-local :defaults :font])) + (get-in state [:workspace-global :default-font])) editor (cond-> (ted/create-editor-state text-state decorator) (and (nil? content) (some? attrs)) (ted/update-editor-current-block-data attrs))] @@ -406,5 +406,5 @@ (let [multiple? (->> data vals (d/seek #(= % :multiple)))] (cond-> state (not multiple?) - (assoc-in [:workspace-local :defaults :font] data)))))) + (assoc-in [:workspace-global :default-font] data))))))