This commit is contained in:
Andrés Moya 2026-06-12 16:41:47 +02:00
parent fbaf8ac63e
commit e7e7b2a4b3
8 changed files with 144 additions and 131 deletions

View File

@ -1051,8 +1051,9 @@
(defmethod process-change :set-token-theme-status
[data {:keys [id status]}]
(-> (ctf/ensure-tokens-lib data)
(ctf/update-token-status ctos/set-theme-status id status)))
(let [data' (ctf/ensure-tokens-lib data)]
(-> data'
(ctf/update-token-status ctos/set-theme-status (ctf/get-tokens-lib data') id status))))
(defmethod process-change :set-active-token-themes
[data {:keys [theme-paths]}]

View File

@ -376,31 +376,3 @@
;; FIXME: this should be precalculated ?
(defn is-reference? [token]
(str/includes? (:value token) "{"))
;; Tokens status
(defn make-token-status-from-lib
"Make a TokenStatus from a TokensLib, activating the themes and sets
marked as active in the library (to migrate from legacy files)."
[tokens-lib]
(assert (ctob/tokens-lib? tokens-lib) "expected valid tokens-lib")
(let [active-themes (into #{}
(comp (map :id)
(filter #(not= % ctob/hidden-theme-id)))
(ctob/get-active-themes tokens-lib))
active-sets (into #{}
(comp (map #(ctob/get-set-by-name tokens-lib %))
(map ctob/get-id))
(ctob/get-active-themes-set-names tokens-lib))]
(ctos/make-token-status :active-themes active-themes
:active-sets active-sets)))
(defn set-theme-status
[token-status tokens-lib theme-id status]
(assert (ctos/token-status? token-status) "expected valid token-status")
(assert (ctob/tokens-lib? tokens-lib) "expected valid tokens-lib")
(assert (uuid? theme-id) "expected valid theme-id")
(assert (boolean? status) "expected boolean status")
(if (ctob/get-theme tokens-lib theme-id)
(ctos/set-theme-status token-status theme-id status)
token-status))

View File

@ -11,7 +11,6 @@
[app.common.features :as cfeat]
[app.common.files.defaults :refer [version]]
[app.common.files.helpers :as cfh]
[app.common.files.tokens :as cfo]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.geom.shapes.tree-seq :as gsts]
@ -204,7 +203,7 @@
[file-data]
(if (and (some? (:tokens-lib file-data)) (nil? (:token-status file-data)))
;; TODO: remove this when we deprecate old-style files without token-status
(assoc file-data :token-status (cfo/make-token-status-from-lib (:tokens-lib file-data)))
(assoc file-data :token-status (ctos/make-token-status-from-lib (:tokens-lib file-data)))
(-> file-data
(update :tokens-lib #(or % (ctob/make-tokens-lib)))
(update :token-status #(or % (ctos/make-token-status))))))
@ -327,7 +326,7 @@
[file-data]
(if (and (some? (:tokens-lib file-data)) (nil? (:token-status file-data)))
;; TODO: remove this when we deprecate old-style files without token-status
(cfo/make-token-status-from-lib (:tokens-lib file-data))
(ctos/make-token-status-from-lib (:tokens-lib file-data))
(:token-status file-data)))
(defn update-tokens-lib

View File

@ -11,30 +11,33 @@
[app.common.schema :as sm]
[app.common.schema.generators :as sg]
[app.common.transit :as t]
[app.common.types.tokens-lib :as ctob]
[clojure.core.protocols :as cp]
[clojure.datafy :refer [datafy]]
[clojure.pprint :as pp]))
[clojure.pprint :as pp]
[clojure.set :as set]))
;; TokenStatus datatype contains the status of the active themes and sets
;; in a tokens library.
(defprotocol ITokenStatus
(activate-theme [_ theme-id] "Add a theme uuid to active themes")
(deactivate-theme [_ theme-id] "Remove a theme uuid from active themes")
(set-theme-status [_ theme-id status] "Add or remove a theme uuid to active themes")
(theme-active? [_ theme-id] "Check if a theme uuid is active")
(activate-theme [_ tokens-lib theme-id] "Activate a theme and deactivate other themes in the same group. Update active sets.")
(deactivate-theme [_ tokens-lib theme-id] "Deactivate a theme and update active sets")
(set-theme-status [_ tokens-lib theme-id status] "Set the activation status of a theme")
(theme-active? [_ theme-id] "Check if a theme is active")
(active-themes-count [_] "Return the number of active themes")
(activate-set [_ set-id] "Add a set uuid to active sets")
(deactivate-set [_ set-id] "Remove a set uuid from active sets")
(toggle-set-active [_ set-id] "Toggle a set uuid in active sets")
(set-active? [_ set-id] "Check if a set uuid is active")
(activate-set [_ set-id] "Add a set to active sets")
(deactivate-set [_ set-id] "Remove a set from active sets")
(toggle-set-active [_ set-id] "Toggle a set in active sets")
(set-active? [_ set-id] "Check if a set is active")
(active-set-count [_] "Return the number of active sets"))
(deftype TokenStatus [active-themes active-sets]
(declare calculate-active-sets)
(deftype TokenStatus [active-theme-ids active-set-ids]
cp/Datafiable
(datafy [_]
{:active-themes active-themes
:active-sets active-sets})
{:active-theme-ids active-theme-ids
:active-set-ids active-set-ids})
#?@(:clj
[c.json/JSONWriter
@ -42,54 +45,77 @@
(c.json/-write (datafy this) writter options))])
ITokenStatus
(activate-theme [_ theme-id]
(activate-theme [this tokens-lib theme-id]
(assert (ctob/tokens-lib? tokens-lib) "expected valid tokens-lib")
(assert (uuid? theme-id) "expected valid theme-id")
(TokenStatus. (conj active-themes theme-id) active-sets))
(if-not (theme-active? this theme-id)
(if-let [theme (ctob/get-theme tokens-lib theme-id)]
(let [group-themes (ctob/get-themes-in-group tokens-lib (:group theme))
active-theme-ids' (-> (set/difference active-theme-ids group-themes)
(conj theme-id))]
(TokenStatus. active-theme-ids'
(calculate-active-sets active-theme-ids' tokens-lib)))
this)
this))
(deactivate-theme [_ theme-id]
(deactivate-theme [this tokens-lib theme-id]
(assert (ctob/tokens-lib? tokens-lib) "expected valid tokens-lib")
(assert (uuid? theme-id) "expected valid theme-id")
(TokenStatus. (disj active-themes theme-id) active-sets))
(if (theme-active? this theme-id)
(let [active-theme-ids' (disj active-theme-ids theme-id)]
(TokenStatus. active-theme-ids'
(calculate-active-sets active-theme-ids' tokens-lib)))
this))
(set-theme-status [this theme-id status]
(set-theme-status [this tokens-lib theme-id status]
(assert (ctob/tokens-lib? tokens-lib) "expected valid tokens-lib")
(assert (uuid? theme-id) "expected valid theme-id")
(assert (boolean? status) "expected boolean status")
(if status
(activate-theme this theme-id)
(deactivate-theme this theme-id)))
(activate-theme this tokens-lib theme-id)
(deactivate-theme this tokens-lib theme-id)))
(theme-active? [_ theme-id]
(assert (uuid? theme-id) "expected valid theme-id")
(contains? active-themes theme-id))
(contains? active-theme-ids theme-id))
(active-themes-count [_]
(count active-themes))
(count active-theme-ids))
(activate-set [_ set-id]
(assert (uuid? set-id) "expected valid set-id")
(TokenStatus. active-themes (conj active-sets set-id)))
(TokenStatus. active-theme-ids (conj active-set-ids set-id)))
(deactivate-set [_ set-id]
(assert (uuid? set-id) "expected valid set-id")
(TokenStatus. active-themes (disj active-sets set-id)))
(TokenStatus. active-theme-ids (disj active-set-ids set-id)))
(toggle-set-active [this set-id]
(assert (uuid? set-id) "expected valid set-id")
(if (contains? active-sets set-id)
(if (contains? active-set-ids set-id)
(deactivate-set this set-id)
(activate-set this set-id)))
(set-active? [_ set-id]
(assert (uuid? set-id) "expected valid set-id")
(contains? active-sets set-id))
(contains? active-set-ids set-id))
(active-set-count [_]
(count active-sets)))
(count active-set-ids)))
(defn- calculate-active-sets
[active-theme-ids tokens-lib]
(let [active-themes (map #(ctob/get-theme tokens-lib %) active-theme-ids) ;; OJOOOOOOOOOOOOOOOOOOOOO
active-set-names (reduce set/union #{} (map :sets active-themes))
active-sets (map #(ctob/get-set-by-name tokens-lib %) active-set-names)
active-set-ids (into #{} (map ctob/get-id) active-sets)]
active-set-ids))
;; === Helper & Predicate ===
(defn map->TokenStatus
[{:keys [active-themes active-sets]}]
(TokenStatus. active-themes active-sets))
[{:keys [active-theme-ids active-set-ids]}]
(TokenStatus. active-theme-ids active-set-ids))
(defn token-status?
[o]
@ -101,8 +127,8 @@
(def schema:token-status-attrs
[:map {:title "TokenStatus"}
[:active-themes [:set {:gen/max 5} ::sm/uuid]]
[:active-sets [:set {:gen/max 5} ::sm/uuid]]])
[:active-theme-ids [:set {:gen/max 5} ::sm/uuid]]
[:active-set-ids [:set {:gen/max 5} ::sm/uuid]]])
(def schema:token-status
[:and {:gen/gen (->> (sg/generator schema:token-status-attrs)
@ -120,11 +146,27 @@
(defn make-token-status
[& {:as attrs}]
(-> attrs
(update :active-themes #(or % #{}))
(update :active-sets #(or % #{}))
(update :active-theme-ids #(or % #{}))
(update :active-set-ids #(or % #{}))
(check-token-status-attrs)
(map->TokenStatus)))
(defn make-token-status-from-lib
"Make a TokenStatus from a TokensLib, activating the themes and sets
marked as active in the library (to migrate from legacy files)."
[tokens-lib]
(assert (ctob/tokens-lib? tokens-lib) "expected valid tokens-lib")
(let [active-theme-ids (into #{}
(comp (map :id)
(filter #(not= % ctob/hidden-theme-id)))
(ctob/get-active-themes tokens-lib))
active-set-ids (into #{}
(comp (map #(ctob/get-set-by-name tokens-lib %))
(map ctob/get-id))
(ctob/get-active-themes-set-names tokens-lib))]
(make-token-status :active-theme-ids active-theme-ids
:active-set-ids active-set-ids)))
;; === Pretty-print for debugging ===
(defmethod pp/simple-dispatch TokenStatus [^TokenStatus obj]

View File

@ -780,6 +780,7 @@
(get-theme-tree [_] "get a nested tree of all themes in the library")
(get-theme-tree-no-hidden [_] "get a nested tree of all themes in the library except the hidden theme")
(get-themes [_] "get an ordered sequence of all themes in the library")
(get-themes-in-group [_ group] "get an ordered sequence of the themes in the group")
(get-theme [_ id] "get one theme looking for id")
(get-theme-by-name [_ group name] "get one theme looking for group and name")
(get-theme-groups [_] "get a sequence of group names by order")
@ -1206,6 +1207,10 @@ Will return a value that matches this schema:
(->> (tree-seq d/ordered-map? vals themes)
(filter (partial instance? TokenTheme))))
(get-themes-in-group [_ group]
(->> (get themes group)
(map (comp get-id val))))
(theme-count [this]
(count (get-themes this)))

View File

@ -7,11 +7,6 @@
(ns common-tests.files.tokens-test
(:require
[app.common.files.tokens :as cfo]
[app.common.test-helpers.files :as thf]
[app.common.test-helpers.ids-map :as thi]
[app.common.test-helpers.tokens :as tht]
[app.common.types.token-status :as ctos]
[app.common.types.tokens-lib :as ctob]
[clojure.test :as t]))
(t/deftest test-parse-token-value
@ -85,42 +80,3 @@
(t/is (nil? (cfo/shapes-token-applied? {:name "a"} [{:applied-tokens {:x "a"}}
{:applied-tokens {:x "a"}}]
#{:y})))))
;; Make TokenStatus from a TokensLib (to migrate from legacy files)
(t/deftest make-token-status-from-tokens-lib
(let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a)
:name "set-a"))
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-b)
:name "set-b"))
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-c)
:name "set-c"))
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-d)
:name "set-d"))
(ctob/add-theme (ctob/make-token-theme :id (thi/new-id! :theme-1)
:name "theme-1"
:sets #{"set-a" "set-b"}))
(ctob/add-theme (ctob/make-token-theme :id (thi/new-id! :theme-2)
:name "theme-2"
:sets #{"set-b"}))
(ctob/add-theme (ctob/make-token-theme :id (thi/new-id! :theme-3)
:name "theme-3"
:sets #{"set-c" "set-d"}))
(ctob/set-active-themes #{"/theme-1" "/theme-2"}))
token-status (cfo/make-token-status-from-lib tokens-lib)]
(t/is (ctos/token-status? token-status))
(t/is (ctos/check-token-status token-status))
(t/is (= (ctos/active-themes-count token-status) 2))
(t/is (ctos/theme-active? token-status (thi/id :theme-1)))
(t/is (ctos/theme-active? token-status (thi/id :theme-2)))
(t/is (= (ctos/active-set-count token-status) 2))
(t/is (ctos/set-active? token-status (thi/id :set-a)))
(t/is (ctos/set-active? token-status (thi/id :set-b)))))
(t/deftest set-theme-status
(t/testing "setting the status of a theme gets it activated or deactivated"
(let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-theme (ctob/make-token-theme :id (thi/new-id! :theme1) :name "theme")))
token-status (ctos/make-token-status)
token-status' (cfo/set-theme-status token-status tokens-lib (thi/id :theme1) true)]
(t/is (ctos/theme-active? token-status' (thi/id :theme1))))))

View File

@ -94,6 +94,7 @@
#(ctf/update-token-status
%
ctos/activate-theme
(tht/get-tokens-lib file)
(thi/id :theme1)))
token-status' (tht/get-token-status file')]
(t/is (= 1 (ctos/active-themes-count token-status')))

View File

@ -19,8 +19,8 @@
(t/deftest make-token-status
(let [theme-id (uuid/next)
set-id (uuid/next)
status (ctos/make-token-status :active-themes #{theme-id}
:active-sets #{set-id})]
status (ctos/make-token-status :active-theme-ids #{theme-id}
:active-set-ids #{set-id})]
(t/is (ctos/token-status? status))
(t/is (ctos/check-token-status status))
(t/is (= (ctos/active-themes-count status) 1))
@ -39,33 +39,70 @@
(t/testing "non-set for active-themes"
(t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception)
#"expected valid params for token-status"
(ctos/make-token-status :active-themes []))))
(ctos/make-token-status :active-theme-ids []))))
(t/testing "non-uuid in active-sets"
(t/is (thrown-with-msg? #?(:cljs js/Error :clj Exception)
#"expected valid params for token-status"
(ctos/make-token-status :active-sets #{"not-a-uuid"})))))
(ctos/make-token-status :active-set-ids #{"not-a-uuid"})))))
;; Make TokenStatus from a TokensLib (to migrate from legacy files)
(t/deftest make-token-status-from-tokens-lib
(let [tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-a)
:name "set-a"))
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-b)
:name "set-b"))
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-c)
:name "set-c"))
(ctob/add-set (ctob/make-token-set :id (thi/new-id! :set-d)
:name "set-d"))
(ctob/add-theme (ctob/make-token-theme :id (thi/new-id! :theme-1)
:name "theme-1"
:sets #{"set-a" "set-b"}))
(ctob/add-theme (ctob/make-token-theme :id (thi/new-id! :theme-2)
:name "theme-2"
:sets #{"set-b"}))
(ctob/add-theme (ctob/make-token-theme :id (thi/new-id! :theme-3)
:name "theme-3"
:sets #{"set-c" "set-d"}))
(ctob/set-active-themes #{"/theme-1" "/theme-2"}))
token-status (ctos/make-token-status-from-lib tokens-lib)]
(t/is (ctos/token-status? token-status))
(t/is (ctos/check-token-status token-status))
(t/is (= (ctos/active-themes-count token-status) 2))
(t/is (ctos/theme-active? token-status (thi/id :theme-1)))
(t/is (ctos/theme-active? token-status (thi/id :theme-2)))
(t/is (= (ctos/active-set-count token-status) 2))
(t/is (ctos/set-active? token-status (thi/id :set-a)))
(t/is (ctos/set-active? token-status (thi/id :set-b)))))
(t/deftest activate-theme
(let [theme-id (uuid/next)
tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-theme (ctob/make-token-theme :id theme-id :name "theme")))
status (ctos/make-token-status)
status' (ctos/activate-theme status theme-id)]
status' (ctos/activate-theme status tokens-lib theme-id)]
(t/is (not (ctos/theme-active? status theme-id)))
(t/is (ctos/theme-active? status' theme-id))
(t/is (= (ctos/active-themes-count status') 1))))
(t/deftest deactivate-theme
(let [theme-id (uuid/next)
status (ctos/make-token-status :active-themes #{theme-id})
status' (ctos/deactivate-theme status theme-id)]
tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-theme (ctob/make-token-theme :id theme-id :name "theme")))
status (ctos/make-token-status :active-theme-ids #{theme-id})
status' (ctos/deactivate-theme status tokens-lib theme-id)]
(t/is (ctos/theme-active? status theme-id))
(t/is (not (ctos/theme-active? status' theme-id)))
(t/is (= (ctos/active-themes-count status') 0))))
(t/deftest set-theme-status
(let [theme-id (uuid/next)
tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-theme (ctob/make-token-theme :id theme-id :name "theme")))
status (ctos/make-token-status)
status' (ctos/set-theme-status status theme-id true)
status'' (ctos/set-theme-status status' theme-id false)]
status' (ctos/set-theme-status status tokens-lib theme-id true)
status'' (ctos/set-theme-status status' tokens-lib theme-id false)]
(t/is (ctos/theme-active? status' theme-id))
(t/is (not (ctos/theme-active? status'' theme-id)))
(t/is (= (ctos/active-themes-count status') 1))
@ -81,7 +118,7 @@
(t/deftest deactivate-set
(let [set-id (uuid/next)
status (ctos/make-token-status :active-sets #{set-id})
status (ctos/make-token-status :active-set-ids #{set-id})
status' (ctos/deactivate-set status set-id)]
(t/is (ctos/set-active? status set-id))
(t/is (not (ctos/set-active? status' set-id)))
@ -100,19 +137,19 @@
(t/deftest datafy-token-status
(let [theme-id (uuid/next)
set-id (uuid/next)
status (ctos/make-token-status :active-themes #{theme-id}
:active-sets #{set-id})
status (ctos/make-token-status :active-theme-ids #{theme-id}
:active-set-ids #{set-id})
result (datafy status)]
(t/is (map? result))
(t/is (not (ctos/token-status? result)))
(t/is (= (:active-themes result) #{theme-id}))
(t/is (= (:active-sets result) #{set-id}))))
(t/is (= (:active-theme-ids result) #{theme-id}))
(t/is (= (:active-set-ids result) #{set-id}))))
(t/deftest transit-serialization
(let [theme-id (uuid/next)
set-id (uuid/next)
status (ctos/make-token-status :active-themes #{theme-id}
:active-sets #{set-id})
status (ctos/make-token-status :active-theme-ids #{theme-id}
:active-set-ids #{set-id})
encoded (tr/encode-str status)
status' (tr/decode-str encoded)]
(t/is (ctos/token-status? status'))
@ -122,8 +159,8 @@
(t/deftest fressian-serialization
(let [theme-id (uuid/next)
set-id (uuid/next)
status (ctos/make-token-status :active-themes #{theme-id}
:active-sets #{set-id})
status (ctos/make-token-status :active-theme-ids #{theme-id}
:active-set-ids #{set-id})
encoded (fres/encode status)
status' (fres/decode encoded)]
(t/is (ctos/token-status? status'))
@ -133,10 +170,10 @@
(t/deftest json-serialization
(let [theme-id (uuid/next)
set-id (uuid/next)
status (ctos/make-token-status :active-themes #{theme-id}
:active-sets #{set-id})
status (ctos/make-token-status :active-theme-ids #{theme-id}
:active-set-ids #{set-id})
json-str (json/write-str status)
parsed (json/read-str json-str :key-fn keyword)]
(t/is (map? parsed))
(t/is (= [(str theme-id)] (:active-themes parsed)))
(t/is (= [(str set-id)] (:active-sets parsed))))))
(t/is (= [(str theme-id)] (:active-theme-ids parsed)))
(t/is (= [(str set-id)] (:active-set-ids parsed))))))