mirror of
https://github.com/penpot/penpot.git
synced 2025-04-11 22:41:23 -05:00
✨ Add storage namespacing
Allows separate global properties from user specific properties
This commit is contained in:
parent
042b3a71d8
commit
c8caca77a3
14 changed files with 144 additions and 85 deletions
|
@ -15,7 +15,7 @@
|
|||
[app.util.http :as http]
|
||||
[app.util.i18n :as i18n]
|
||||
[app.util.object :as obj]
|
||||
[app.util.storage :refer [storage]]
|
||||
[app.util.storage :as storage]
|
||||
[app.util.time :as dt]
|
||||
[beicon.v2.core :as rx]
|
||||
[beicon.v2.operators :as rxo]
|
||||
|
@ -170,7 +170,7 @@
|
|||
(let [session (atom nil)
|
||||
stopper (rx/filter (ptk/type? ::initialize) stream)
|
||||
buffer (atom #queue [])
|
||||
profile (->> (rx/from-atom storage {:emit-current-value? true})
|
||||
profile (->> (rx/from-atom storage/user {:emit-current-value? true})
|
||||
(rx/map :profile)
|
||||
(rx/map :id)
|
||||
(rx/pipe (rxo/distinct-contiguous)))]
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
[app.main.repo :as rp]
|
||||
[app.main.store :as st]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.storage :refer [storage]]
|
||||
[app.util.storage :as storage]
|
||||
[app.util.webapi :as wa]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -335,8 +335,9 @@
|
|||
(assoc-in state [:workspace-data :recent-fonts] most-recent-fonts)))
|
||||
ptk/EffectEvent
|
||||
(effect [_ state _]
|
||||
(let [most-recent-fonts (get-in state [:workspace-data :recent-fonts])]
|
||||
(swap! storage assoc ::recent-fonts most-recent-fonts)))))
|
||||
(let [most-recent-fonts (get-in state [:workspace-data :recent-fonts])]
|
||||
;; FIXME: this should be prefixed by team
|
||||
(swap! storage/user assoc ::recent-fonts most-recent-fonts)))))
|
||||
|
||||
(defn load-recent-fonts
|
||||
[fonts]
|
||||
|
@ -344,7 +345,7 @@
|
|||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [fonts-map (d/index-by :id fonts)
|
||||
saved-recent-fonts (->> (::recent-fonts @storage)
|
||||
saved-recent-fonts (->> (::recent-fonts storage/user)
|
||||
(keep #(get fonts-map (:id %)))
|
||||
(into #{}))]
|
||||
(assoc-in state [:workspace-data :recent-fonts] saved-recent-fonts)))))
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
[app.plugins.register :as register]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.router :as rt]
|
||||
[app.util.storage :as s]
|
||||
[app.util.storage :as storage]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
|
@ -52,14 +52,14 @@
|
|||
|
||||
(defn get-current-team-id
|
||||
[profile]
|
||||
(let [team-id (::current-team-id @s/storage)]
|
||||
(let [team-id (::current-team-id storage/user)]
|
||||
(or team-id (:default-team-id profile))))
|
||||
|
||||
(defn set-current-team!
|
||||
[team-id]
|
||||
(if (nil? team-id)
|
||||
(swap! s/storage dissoc ::current-team-id)
|
||||
(swap! s/storage assoc ::current-team-id team-id)))
|
||||
(swap! storage/user dissoc ::current-team-id)
|
||||
(swap! storage/user assoc ::current-team-id team-id)))
|
||||
|
||||
;; --- EVENT: fetch-teams
|
||||
|
||||
|
@ -79,9 +79,9 @@
|
|||
;; if not, dissoc it from storage.
|
||||
|
||||
(let [ids (into #{} (map :id) teams)]
|
||||
(when-let [ctid (::current-team-id @s/storage)]
|
||||
(when-let [ctid (::current-team-id storage/user)]
|
||||
(when-not (contains? ids ctid)
|
||||
(swap! s/storage dissoc ::current-team-id)))))))
|
||||
(swap! storage/user dissoc ::current-team-id)))))))
|
||||
|
||||
(defn fetch-teams
|
||||
[]
|
||||
|
@ -132,10 +132,10 @@
|
|||
(effect [_ state _]
|
||||
(let [profile (:profile state)
|
||||
email (:email profile)
|
||||
previous-profile (:profile @s/storage)
|
||||
previous-profile (:profile storage/user)
|
||||
previous-email (:email previous-profile)]
|
||||
(when profile
|
||||
(swap! s/storage assoc :profile profile)
|
||||
(swap! storage/user assoc :profile profile)
|
||||
(i18n/set-locale! (:lang profile))
|
||||
(when (not= previous-email email)
|
||||
(set-current-team! nil))
|
||||
|
@ -336,7 +336,7 @@
|
|||
ptk/EffectEvent
|
||||
(effect [_ _ _]
|
||||
;; We prefer to keek some stuff in the storage like the current-team-id and the profile
|
||||
(swap! s/storage (constantly {}))))))
|
||||
(swap! storage/user (constantly {}))))))
|
||||
|
||||
(defn logout
|
||||
([] (logout {}))
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
[app.util.http :as http]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.router :as rt]
|
||||
[app.util.storage :refer [storage]]
|
||||
[app.util.storage :as storage]
|
||||
[app.util.timers :as tm]
|
||||
[app.util.webapi :as wapi]
|
||||
[beicon.v2.core :as rx]
|
||||
|
@ -336,7 +336,7 @@
|
|||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc state
|
||||
:recent-colors (:recent-colors @storage)
|
||||
:recent-colors (:recent-colors storage/user)
|
||||
:workspace-ready? false
|
||||
:current-file-id file-id
|
||||
:current-project-id project-id
|
||||
|
|
|
@ -7,22 +7,22 @@
|
|||
(ns app.main.data.workspace.assets
|
||||
"Workspace assets management events and helpers."
|
||||
(:require
|
||||
[app.util.storage :refer [storage]]))
|
||||
[app.util.storage :as storage]))
|
||||
|
||||
(defn get-current-assets-ordering
|
||||
[]
|
||||
(let [ordering (::ordering @storage)]
|
||||
(let [ordering (::ordering storage/user)]
|
||||
(or ordering :asc)))
|
||||
|
||||
(defn set-current-assets-ordering!
|
||||
[ordering]
|
||||
(swap! storage assoc ::ordering ordering))
|
||||
(swap! storage/user assoc ::ordering ordering))
|
||||
|
||||
(defn get-current-assets-list-style
|
||||
[]
|
||||
(let [list-style (::list-style @storage)]
|
||||
(let [list-style (::list-style storage/user)]
|
||||
(or list-style :thumbs)))
|
||||
|
||||
(defn set-current-assets-list-style!
|
||||
[list-style]
|
||||
(swap! storage assoc ::list-style list-style))
|
||||
(swap! storage/user assoc ::list-style list-style))
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.util.color :as uc]
|
||||
[app.util.storage :refer [storage]]
|
||||
[app.util.storage :as storage]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
@ -718,9 +718,9 @@
|
|||
|
||||
(defn get-active-color-tab
|
||||
[]
|
||||
(let [tab (::tab @storage)]
|
||||
(let [tab (::tab storage/user)]
|
||||
(or tab :ramp)))
|
||||
|
||||
(defn set-active-color-tab!
|
||||
[tab]
|
||||
(swap! storage assoc ::tab tab))
|
||||
(swap! storage/user assoc ::tab tab))
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.data.events :as ev]
|
||||
[app.util.storage :refer [storage]]
|
||||
[app.util.storage :as storage]
|
||||
[clojure.set :as set]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
|
@ -144,7 +144,7 @@
|
|||
stored in Storage."
|
||||
[layout]
|
||||
(reduce (fn [layout [flag key]]
|
||||
(condp = (get @storage key ::none)
|
||||
(condp = (get storage/user key ::none)
|
||||
::none layout
|
||||
false (disj layout flag)
|
||||
true (conj layout flag)))
|
||||
|
@ -155,7 +155,7 @@
|
|||
"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))))
|
||||
(swap! storage/user assoc key (contains? layout flag))))
|
||||
|
||||
(def layout-state-persistence-mapping
|
||||
"A mapping of keys that need to be persisted from `:workspace-global` into Storage."
|
||||
|
@ -167,7 +167,7 @@
|
|||
props that are previously persisted in the Storage."
|
||||
[state]
|
||||
(reduce (fn [state [key skey]]
|
||||
(let [val (get @storage skey ::none)]
|
||||
(let [val (get storage/user skey ::none)]
|
||||
(if (= val ::none)
|
||||
state
|
||||
(assoc state key val))))
|
||||
|
@ -181,7 +181,7 @@
|
|||
(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)))))
|
||||
(swap! storage/user dissoc skey)
|
||||
(swap! storage/user assoc skey val)))))
|
||||
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
[app.util.color :as uc]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.router :as rt]
|
||||
[app.util.storage :as s]
|
||||
[app.util.storage :as storage]
|
||||
[app.util.time :as dt]
|
||||
[beicon.v2.core :as rx]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -147,7 +147,7 @@
|
|||
ptk/EffectEvent
|
||||
(effect [_ state _]
|
||||
(let [recent-colors (:recent-colors state)]
|
||||
(swap! s/storage assoc :recent-colors recent-colors)))))
|
||||
(swap! storage/user assoc :recent-colors recent-colors)))))
|
||||
|
||||
(def clear-color-for-rename
|
||||
(ptk/reify ::clear-color-for-rename
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
[app.main.ui.icons :as i]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.router :as rt]
|
||||
[app.util.storage :as sto]
|
||||
[app.util.storage :as storage]
|
||||
[beicon.v2.core :as rx]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
|
@ -198,7 +198,7 @@
|
|||
|
||||
:else
|
||||
(do
|
||||
(swap! sto/storage assoc ::email (:email params))
|
||||
(swap! storage/user assoc ::email (:email params))
|
||||
(st/emit! (rt/nav :auth-register-success)))))))
|
||||
|
||||
on-error
|
||||
|
@ -264,7 +264,7 @@
|
|||
(mf/defc register-success-page
|
||||
{::mf/props :obj}
|
||||
[{:keys [params]}]
|
||||
(let [email (or (:email params) (::email @sto/storage))]
|
||||
(let [email (or (:email params) (::email storage/user))]
|
||||
[:div {:class (stl/css :auth-form-wrapper :register-success)}
|
||||
(when-not (:hide-logo params)
|
||||
[:h1 {:class (stl/css :logo-container)}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
[app.main.store :as st]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.dom.dnd :as dnd]
|
||||
[app.util.storage :refer [storage]]
|
||||
[app.util.storage :as storage]
|
||||
[app.util.timers :as ts]
|
||||
[app.util.webapi :as wapi]
|
||||
[beicon.v2.core :as rx]
|
||||
|
@ -294,7 +294,7 @@
|
|||
`key` for new values."
|
||||
[key default]
|
||||
(let [id (mf/use-id)
|
||||
state* (mf/use-state #(get @storage key default))
|
||||
state* (mf/use-state #(get storage/user key default))
|
||||
state (deref state*)
|
||||
stream (mf/with-memo [id]
|
||||
(->> mbc/stream
|
||||
|
@ -304,7 +304,7 @@
|
|||
|
||||
(mf/with-effect [state key id]
|
||||
(mbc/emit! id key state)
|
||||
(swap! storage assoc key state))
|
||||
(swap! storage/user assoc key state))
|
||||
|
||||
(use-stream stream (partial reset! state*))
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.storage :refer [storage]]
|
||||
[app.util.storage :as storage]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(log/set-level! :warn)
|
||||
|
@ -23,7 +23,7 @@
|
|||
|
||||
(defn- get-initial-state
|
||||
[initial file-id key]
|
||||
(let [saved (dm/get-in @storage [::state file-id key])]
|
||||
(let [saved (dm/get-in storage/user [::state file-id key])]
|
||||
(d/nilv saved initial)))
|
||||
|
||||
(defn- update-persistent-state
|
||||
|
@ -81,7 +81,7 @@
|
|||
start-size (mf/ref-val start-size-ref)
|
||||
new-size (-> (+ start-size delta) (max min-val) (min max-val))]
|
||||
(reset! current-size* new-size)
|
||||
(swap! storage update-persistent-state file-id key new-size)))))
|
||||
(swap! storage/user update-persistent-state file-id key new-size)))))
|
||||
|
||||
set-size
|
||||
(mf/use-fn
|
||||
|
@ -89,7 +89,7 @@
|
|||
(fn [new-size]
|
||||
(let [new-size (mth/clamp new-size min-val max-val)]
|
||||
(reset! current-size* new-size)
|
||||
(swap! storage update-persistent-state file-id key new-size))))]
|
||||
(swap! storage/user update-persistent-state file-id key new-size))))]
|
||||
|
||||
(mf/with-effect [on-change-size current-size]
|
||||
(when on-change-size
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
[app.common.logging :as log]
|
||||
[app.config :as cfg]
|
||||
[app.util.globals :as globals]
|
||||
[app.util.storage :refer [storage]]
|
||||
[app.util.storage :as storage]
|
||||
[cuerdas.core :as str]
|
||||
[goog.object :as gobj]
|
||||
[okulary.core :as l]
|
||||
|
@ -76,7 +76,7 @@
|
|||
cfg/default-language))))
|
||||
|
||||
(defonce translations #js {})
|
||||
(defonce locale (l/atom (or (get @storage ::locale)
|
||||
(defonce locale (l/atom (or (get storage/global ::locale)
|
||||
(autodetect))))
|
||||
|
||||
(defn init!
|
||||
|
@ -93,7 +93,7 @@
|
|||
(if (or (nil? lname)
|
||||
(str/empty? lname))
|
||||
(let [lname (autodetect)]
|
||||
(swap! storage dissoc ::locale)
|
||||
(swap! storage/global dissoc ::locale)
|
||||
(reset! locale lname))
|
||||
(let [supported (into #{} (map :value supported-locales))
|
||||
lname (loop [locales (seq (parse-locale lname))]
|
||||
|
@ -102,7 +102,7 @@
|
|||
locale
|
||||
(recur (rest locales)))
|
||||
cfg/default-language))]
|
||||
(swap! storage assoc ::locale lname)
|
||||
(swap! storage/global assoc ::locale lname)
|
||||
(reset! locale lname))))
|
||||
|
||||
(deftype C [val]
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
[app.common.transit :as t]
|
||||
[app.util.functions :as fns]
|
||||
[app.util.globals :as g]
|
||||
[cuerdas.core :as str]))
|
||||
[cuerdas.core :as str]
|
||||
[okulary.util :as ou]))
|
||||
|
||||
;; Using ex/ignoring because can receive a DOMException like this when
|
||||
;; importing the code as a library: Failed to read the 'localStorage'
|
||||
|
@ -19,26 +20,27 @@
|
|||
(ex/ignoring (unchecked-get g/global "localStorage")))
|
||||
|
||||
(defn- encode-key
|
||||
[k]
|
||||
[prefix k]
|
||||
(assert (keyword? k) "key must be keyword")
|
||||
(let [kns (namespace k)
|
||||
kn (name k)]
|
||||
(str "penpot:" kns "/" kn)))
|
||||
(str prefix ":" kns "/" kn)))
|
||||
|
||||
(defn- decode-key
|
||||
[k]
|
||||
(when (str/starts-with? k "penpot:")
|
||||
(let [k (subs k 7)]
|
||||
[prefix k]
|
||||
(when (str/starts-with? k prefix)
|
||||
(let [l (+ (count prefix) 1)
|
||||
k (subs k l)]
|
||||
(if (str/starts-with? k "/")
|
||||
(keyword (subs k 1))
|
||||
(let [[kns kn] (str/split k "/" 2)]
|
||||
(keyword kns kn))))))
|
||||
|
||||
(defn- lookup-by-index
|
||||
[result index]
|
||||
[prefix result index]
|
||||
(try
|
||||
(let [key (.key ^js local-storage index)
|
||||
key' (decode-key key)]
|
||||
key' (decode-key prefix key)]
|
||||
(if key'
|
||||
(let [val (.getItem ^js local-storage key)]
|
||||
(assoc! result key' (t/decode-str val)))
|
||||
|
@ -46,39 +48,95 @@
|
|||
(catch :default _
|
||||
result)))
|
||||
|
||||
(defn- load
|
||||
[]
|
||||
(when (some? local-storage)
|
||||
(defn- load-data
|
||||
[prefix]
|
||||
(if (some? local-storage)
|
||||
(let [length (.-length ^js local-storage)]
|
||||
(loop [index 0
|
||||
result (transient {})]
|
||||
(if (< index length)
|
||||
(recur (inc index)
|
||||
(lookup-by-index result index))
|
||||
(persistent! result))))))
|
||||
(lookup-by-index prefix result index))
|
||||
(persistent! result))))
|
||||
{}))
|
||||
|
||||
(defonce ^:private latest-state (load))
|
||||
(defn create-storage
|
||||
[prefix]
|
||||
(let [initial (load-data prefix)
|
||||
curr-data #js {:content initial}
|
||||
last-data #js {:content initial}
|
||||
watches (js/Map.)
|
||||
|
||||
(defn- on-change*
|
||||
[curr-state]
|
||||
(let [prev-state latest-state]
|
||||
(try
|
||||
(run! (fn [key]
|
||||
(let [prev-val (get prev-state key)
|
||||
curr-val (get curr-state key)]
|
||||
(when-not (identical? curr-val prev-val)
|
||||
(if (some? curr-val)
|
||||
(.setItem ^js local-storage (encode-key key) (t/encode-str curr-val))
|
||||
(.removeItem ^js local-storage (encode-key key))))))
|
||||
(into #{} (concat (keys curr-state)
|
||||
(keys prev-state))))
|
||||
(finally
|
||||
(set! latest-state curr-state)))))
|
||||
on-change*
|
||||
(fn [curr-state]
|
||||
(let [prev-state (unchecked-get last-data "content")]
|
||||
(try
|
||||
(run! (fn [key]
|
||||
(let [prev-val (get prev-state key)
|
||||
curr-val (get curr-state key)]
|
||||
(when-not (identical? curr-val prev-val)
|
||||
(if (some? curr-val)
|
||||
(.setItem ^js local-storage (encode-key prefix key) (t/encode-str curr-val))
|
||||
(.removeItem ^js local-storage (encode-key prefix key))))))
|
||||
(into #{} (concat (keys curr-state)
|
||||
(keys prev-state))))
|
||||
(finally
|
||||
(unchecked-set last-data "content" curr-state)))))
|
||||
|
||||
(defonce on-change
|
||||
(fns/debounce on-change* 2000))
|
||||
on-change
|
||||
(fns/debounce on-change* 2000)]
|
||||
|
||||
(defonce storage (atom latest-state))
|
||||
(add-watch storage :persistence
|
||||
(fn [_ _ _ curr-state]
|
||||
(on-change curr-state)))
|
||||
(reify
|
||||
IAtom
|
||||
|
||||
IDeref
|
||||
(-deref [_] (unchecked-get curr-data "content"))
|
||||
|
||||
ILookup
|
||||
(-lookup [coll k]
|
||||
(-lookup coll k nil))
|
||||
(-lookup [_ k not-found]
|
||||
(let [state (unchecked-get curr-data "content")]
|
||||
(-lookup state k not-found)))
|
||||
|
||||
IReset
|
||||
(-reset! [self newval]
|
||||
(let [oldval (unchecked-get curr-data "content")]
|
||||
(unchecked-set curr-data "content" newval)
|
||||
(on-change newval)
|
||||
(when (> (.-size watches) 0)
|
||||
(-notify-watches self oldval newval))
|
||||
newval))
|
||||
|
||||
ISwap
|
||||
(-swap! [self f]
|
||||
(let [state (unchecked-get curr-data "content")]
|
||||
(-reset! self (f state))))
|
||||
(-swap! [self f x]
|
||||
(let [state (unchecked-get curr-data "content")]
|
||||
(-reset! self (f state x))))
|
||||
(-swap! [self f x y]
|
||||
(let [state (unchecked-get curr-data "content")]
|
||||
(-reset! self (f state x y))))
|
||||
(-swap! [self f x y more]
|
||||
(let [state (unchecked-get curr-data "content")]
|
||||
(-reset! self (apply f state x y more))))
|
||||
|
||||
IWatchable
|
||||
(-notify-watches [self oldval newval]
|
||||
(ou/doiter
|
||||
(.entries watches)
|
||||
(fn [n]
|
||||
(let [f (aget n 1)
|
||||
k (aget n 0)]
|
||||
(f k self oldval newval)))))
|
||||
|
||||
(-add-watch [self key f]
|
||||
(.set watches key f)
|
||||
self)
|
||||
|
||||
(-remove-watch [_ key]
|
||||
(.delete watches key)))))
|
||||
|
||||
(defonce global (create-storage "penpot-global"))
|
||||
(defonce user (create-storage "penpot-user"))
|
||||
|
|
|
@ -10,11 +10,11 @@
|
|||
(:require
|
||||
[app.config :as cfg]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.storage :refer [storage]]
|
||||
[app.util.storage :as storage]
|
||||
[beicon.v2.core :as rx]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defonce theme (get @storage ::theme cfg/default-theme))
|
||||
(defonce theme (get storage/global ::theme cfg/default-theme))
|
||||
(defonce theme-sub (rx/subject))
|
||||
(defonce themes #js {})
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
|||
(when (not= theme v)
|
||||
(when-some [el (dom/get-element "theme")]
|
||||
(set! (.-href el) (str "css/main-" v ".css")))
|
||||
(swap! storage assoc ::theme v)
|
||||
(swap! storage/global assoc ::theme v)
|
||||
(set! theme v)
|
||||
(rx/push! theme-sub v)))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue