♻️ Refactor page options data structure

This commit is contained in:
Andrey Antukh 2024-09-03 11:59:33 +02:00 committed by Alonso Torres
parent 2dea0b52ed
commit 4ad4057878
41 changed files with 659 additions and 525 deletions

View file

@ -741,46 +741,36 @@
(defn add-guide
[file guide]
(let [guide (cond-> guide
(nil? (:id guide))
(assoc :id (uuid/next)))
page-id (:current-page-id file)
old-guides (or (dm/get-in file [:data :pages-index page-id :options :guides]) {})
new-guides (assoc old-guides (:id guide) guide)]
page-id (:current-page-id file)]
(-> file
(commit-change
{:type :set-option
{:type :set-guide
:page-id page-id
:option :guides
:value new-guides})
:id (:id guide)
:params guide})
(assoc :last-id (:id guide)))))
(defn delete-guide
[file id]
(let [page-id (:current-page-id file)
old-guides (or (dm/get-in file [:data :pages-index page-id :options :guides]) {})
new-guides (dissoc old-guides id)]
(-> file
(commit-change
{:type :set-option
:page-id page-id
:option :guides
:value new-guides}))))
(let [page-id (:current-page-id file)]
(commit-change file
{:type :set-guide
:page-id page-id
:id id
:params nil})))
(defn update-guide
[file guide]
(let [page-id (:current-page-id file)
old-guides (or (dm/get-in file [:data :pages-index page-id :options :guides]) {})
new-guides (assoc old-guides (:id guide) guide)]
(-> file
(commit-change
{:type :set-option
:page-id page-id
:option :guides
:value new-guides}))))
(let [page-id (:current-page-id file)]
(commit-change file
{:type :set-guide
:page-id page-id
:id (:id guide)
:params guide})))
(defn strip-image-extension [filename]
(let [image-extensions-re #"(\.png)|(\.jpg)|(\.jpeg)|(\.webp)|(\.gif)|(\.svg)$"]

View file

@ -10,21 +10,25 @@
[app.common.data.macros :as dm]
[app.common.exceptions :as ex]
[app.common.files.helpers :as cfh]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.schema :as sm]
[app.common.schema.desc-native :as smd]
[app.common.schema.generators :as sg]
[app.common.types.color :as ctc]
[app.common.types.colors-list :as ctcl]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn]
[app.common.types.file :as ctf]
[app.common.types.grid :as ctg]
[app.common.types.page :as ctp]
[app.common.types.pages-list :as ctpl]
[app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctst]
[app.common.types.typographies-list :as ctyl]
[app.common.types.typography :as ctt]
[app.common.uuid :as uuid]
[clojure.set :as set]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -60,6 +64,44 @@
[:type [:= :set-remote-synced]]
[:remote-synced {:optional true} [:maybe :boolean]]]]])
(defn- set-default-grid-change-generator
[]
(->> (sg/elements #{:square :column :row})
(sg/mcat (fn [type]
(sg/fmap (fn [params]
{:page-id (uuid/next)
:type :mod-grid
:grid-type type
:params params})
(case type
:square (sg/generator ctg/schema:square-params)
:column (sg/generator ctg/schema:column-params)
:row (sg/generator ctg/schema:column-params)))))))
(def schema:set-default-grid-change
[:multi {:decode/json #(update % :grid-type keyword)
:gen/gen (set-default-grid-change-generator)
:dispatch :grid-type
::smd/simplified true}
[:square
[:map
[:page-id ::sm/uuid]
[:grid-type [:and :keyword [:= :square]]]
[:params [:maybe ctg/schema:square-params]]]]
[:column
[:map
[:page-id ::sm/uuid]
[:grid-type [:and :keyword [:= :column]]]
[:params [:maybe ctg/schema:column-params]]]]
[:row
[:map
[:page-id ::sm/uuid]
[:grid-type [:and :keyword [:= :row]]]
[:params [:maybe ctg/schema:column-params]]]]])
(def schema:change
[:schema
[:multi {:dispatch :type
@ -67,13 +109,18 @@
:decode/json #(update % :type keyword)
::smd/simplified true}
[:set-option
[:map {:title "SetOptionChange"}
[:type [:= :set-option]]
;; DEPRECATED: remove before 2.3 release
;;
;; Is still there for not cause error when event is received
[:map {:title "SetOptionChange"}]]
[:set-comment-thread-position
[:map
[:comment-thread-id ::sm/uuid]
[:page-id ::sm/uuid]
[:option [:union
[:keyword]
[:vector {:gen/max 10} :keyword]]]
[:value :any]]]
[:frame-id [:maybe ::sm/uuid]]
[:position [:maybe ::gpt/point]]]]
[:add-obj
[:map {:title "AddObjChange"}
@ -103,6 +150,22 @@
[:component-id {:optional true} ::sm/uuid]
[:ignore-touched {:optional true} :boolean]]]
[:set-guide
[:map {:title "SetGuideChange"}
[:page-id ::sm/uuid]
[:id ::sm/uuid]
[:params [:maybe ::ctp/guide]]]]
[:set-flow
[:map {:title "SetFlowChange"}
[:page-id ::sm/uuid]
[:id ::sm/uuid]
[:params [:maybe ::ctp/flow]]]]
;; Change used for all crud operation on persisted grid options on
;; the page.
[:set-default-grid schema:set-default-grid-change]
[:fix-obj
[:map {:title "FixObjChange"}
[:type [:= :fix-obj]]
@ -143,7 +206,10 @@
[:map {:title "ModPageChange"}
[:type [:= :mod-page]]
[:id ::sm/uuid]
[:name :string]]]
;; All props are optional, background can be nil because is the
;; way to remove already set background
[:background {:optional true} [:maybe ::ctc/rgb-color]]
[:name {:optional true} :string]]]
[:mod-plugin-data
[:map {:title "ModPagePluginData"}
@ -356,14 +422,49 @@
#?(:clj (validate-shapes! data result items))
result))))
;; DEPRECATED: remove before 2.3 release
(defmethod process-change :set-option
[data {:keys [page-id option value]}]
[data _]
data)
;; --- Comment Threads
(defmethod process-change :set-comment-thread-position
[data {:keys [page-id comment-thread-id position frame-id]}]
(d/update-in-when data [:pages-index page-id]
(fn [data]
(let [path (if (seqable? option) option [option])]
(if value
(assoc-in data (into [:options] path) value)
(assoc data :options (d/dissoc-in (:options data) path)))))))
(fn [page]
(if (and position frame-id)
(update page :comment-thread-positions assoc
comment-thread-id {:frame-id frame-id
:position position})
(update page :comment-thread-positions dissoc
comment-thread-id)))))
;; --- Guides
(defmethod process-change :set-guide
[data {:keys [page-id id params]}]
(if (nil? params)
(d/update-in-when data [:pages-index page-id] update :guides dissoc id)
(d/update-in-when data [:pages-index page-id] update :guides assoc id params)))
;; --- Flows
(defmethod process-change :set-flow
[data {:keys [page-id id params]}]
(if (nil? params)
(d/update-in-when data [:pages-index page-id] update :flows dissoc id)
(d/update-in-when data [:pages-index page-id] update :flows assoc id params)))
;; --- Grids
(defmethod process-change :set-default-grid
[data {:keys [page-id grid-type params]}]
(if (nil? params)
(d/update-in-when data [:pages-index page-id] update :default-grids dissoc grid-type)
(d/update-in-when data [:pages-index page-id] update :default-grids assoc grid-type params)))
;; --- Shape / Obj
(defmethod process-change :add-obj
[data {:keys [id obj page-id component-id frame-id parent-id index ignore-touched]}]
@ -603,8 +704,20 @@
(ctpl/add-page data page)))
(defmethod process-change :mod-page
[data {:keys [id name]}]
(d/update-in-when data [:pages-index id] assoc :name name))
[data {:keys [id] :as params}]
(d/update-in-when data [:pages-index id]
(fn [page]
(let [name (get params :name)
bg (get params :background :not-found)]
(cond-> page
(string? name)
(assoc :name name)
(string? bg)
(assoc :background bg)
(nil? bg)
(dissoc :background))))))
(defmethod process-change :mod-plugin-data
[data {:keys [object-type object-id page-id namespace key value]}]
@ -660,6 +773,7 @@
[data _]
data)
;; -- Media
(defmethod process-change :add-media

View file

@ -135,12 +135,6 @@
(or (contains? (meta changes) ::page-id)
(contains? (meta changes) ::component-id))))
(defn- assert-page!
[changes]
(dm/assert!
"Call (with-page) before using this function"
(contains? (meta changes) ::page)))
(defn- assert-objects!
[changes]
(dm/assert!
@ -195,11 +189,30 @@
(apply-changes-local)))
(defn mod-page
[changes page new-name]
(-> changes
(update :redo-changes conj {:type :mod-page :id (:id page) :name new-name})
(update :undo-changes conj {:type :mod-page :id (:id page) :name (:name page)})
(apply-changes-local)))
([changes options]
(let [page (::page (meta changes))]
(mod-page changes page options)))
([changes page {:keys [name background]}]
(let [change {:type :mod-page :id (:id page)}
redo (cond-> change
(some? name)
(assoc :name name)
(some? background)
(assoc :background background))
undo (cond-> change
(some? name)
(assoc :name (:name page))
(some? background)
(assoc :background (:background page)))]
(-> changes
(update :redo-changes conj redo)
(update :undo-changes conj undo)
(apply-changes-local)))))
(defn mod-plugin-data
([changes namespace key value]
@ -246,42 +259,77 @@
(update :undo-changes conj {:type :mov-page :id page-id :index prev-index})
(apply-changes-local)))
(defn set-page-option
[changes option-key option-val]
(assert-page! changes)
(defn set-guide
[changes id guide]
(let [page-id (::page-id (meta changes))
page (::page (meta changes))
old-val (get-in page [:options option-key])]
page (::page (meta changes))
old-val (dm/get-in page [:guides id])]
(-> changes
(update :redo-changes conj {:type :set-option
(update :redo-changes conj {:type :set-guide
:page-id page-id
:option option-key
:value option-val})
(update :undo-changes conj {:type :set-option
:id id
:params guide})
(update :undo-changes conj {:type :set-guide
:page-id page-id
:option option-key
:value old-val})
(apply-changes-local))))
(defn update-page-option
[changes option-key update-fn & args]
(assert-page! changes)
:id id
:params old-val}))))
(defn set-flow
[changes id flow]
(let [page-id (::page-id (meta changes))
page (::page (meta changes))
old-val (get-in page [:options option-key])
new-val (apply update-fn old-val args)]
page (::page (meta changes))
old-val (dm/get-in page [:flows id])
(-> changes
(update :redo-changes conj {:type :set-option
:page-id page-id
:option option-key
:value new-val})
(update :undo-changes conj {:type :set-option
:page-id page-id
:option option-key
:value old-val})
(apply-changes-local))))
changes (-> changes
(update :redo-changes conj {:type :set-flow
:page-id page-id
:id id
:params flow})
(update :undo-changes conj {:type :set-flow
:page-id page-id
:id id
:params old-val}))]
;; FIXME: not sure if we need this
(apply-changes-local changes)))
(defn set-comment-thread-position
[changes {:keys [id frame-id position] :as thread}]
(let [page-id (::page-id (meta changes))
page (::page (meta changes))
old-val (dm/get-in page [:comment-thread-positions id])
changes (-> changes
(update :redo-changes conj {:type :set-comment-thread-position
:comment-thread-id id
:page-id page-id
:frame-id frame-id
:position position})
(update :undo-changes conj {:type :set-comment-thread-position
:page-id page-id
:comment-thread-id id
:frame-id (:frame-id old-val)
:position (:position old-val)}))]
;; FIXME: not sure if we need this
(apply-changes-local changes)))
(defn set-default-grid
[changes type params]
(let [page-id (::page-id (meta changes))
page (::page (meta changes))
old-val (dm/get-in page [:grids type])
changes (update changes :redo-changes conj {:type :set-default-grid
:page-id page-id
:grid-type type
:params params})
changes (update changes :undo-changes conj {:type :set-default-grid
:page-id page-id
:grid-type type
:params old-val})]
;; FIXME: not sure if we need this
(apply-changes-local changes)))
;; Shape tree changes

View file

@ -6,4 +6,4 @@
(ns app.common.files.defaults)
(def version 51)
(def version 52)

View file

@ -1017,6 +1017,30 @@
(into {} (filter #(-> % val valid-color?) colors)))]
(update data :colors update-colors)))
(defn migrate-up-52
"This migration moves page options to the page level"
[data]
(let [update-page
(fn [{:keys [options] :as page}]
(cond-> page
(and (some? (:saved-grids options))
(not (contains? page :default-grids)))
(assoc :default-grids (:saved-grids options))
(and (some? (:flows options))
(not (contains? page :flows)))
(assoc :flows (:flows options))
(and (some? (:guides options))
(not (contains? page :guides)))
(assoc :guides (:guides options))
(and (some? (:comment-threads-position options))
(not (contains? page :comment-thread-positions)))
(assoc :comment-thread-positions (:comment-threads-position options))))]
(update data :pages-index d/update-vals update-page)))
(def migrations
"A vector of all applicable migrations"
[{:id 2 :migrate-up migrate-up-2}
@ -1059,4 +1083,5 @@
{:id 48 :migrate-up migrate-up-48}
{:id 49 :migrate-up migrate-up-49}
{:id 50 :migrate-up migrate-up-50}
{:id 51 :migrate-up migrate-up-51}])
{:id 51 :migrate-up migrate-up-51}
{:id 52 :migrate-up migrate-up-52}])

View file

@ -15,10 +15,10 @@
[old-page page check-attrs]
(let [old-objects (get old-page :objects)
old-guides (or (get-in old-page [:options :guides]) [])
old-guides (or (get old-page :guides) [])
new-objects (get page :objects)
new-guides (or (get-in page [:options :guides]) [])
new-guides (or (get page :guides) [])
changed-object?
(fn [id]

View file

@ -1947,54 +1947,54 @@
(defn generate-duplicate-flows
[changes shapes page ids-map]
(let [flows (-> page :options :flows)
unames (volatile! (into #{} (map :name flows)))
frames-with-flow (->> shapes
(filter #(= (:type %) :frame))
(filter #(some? (ctp/get-frame-flow flows (:id %)))))]
(if-not (empty? frames-with-flow)
(let [update-flows (fn [flows]
(reduce
(fn [flows frame]
(let [name (cfh/generate-unique-name @unames "Flow 1")
_ (vswap! unames conj name)
new-flow {:id (uuid/next)
:name name
:starting-frame (get ids-map (:id frame))}]
(ctp/add-flow flows new-flow)))
flows
frames-with-flow))]
(pcb/update-page-option changes :flows update-flows))
changes)))
(let [flows (get page :flows)
unames (volatile! (cfh/get-used-names (vals flows)))
has-flow? (partial ctp/get-frame-flow flows)]
(reduce (fn [changes frame-id]
(let [name (cfh/generate-unique-name @unames "Flow 1")
frame-id (get ids-map frame-id)
flow-id (uuid/next)
new-flow {:id flow-id
:name name
:starting-frame frame-id}]
(vswap! unames conj name)
(pcb/set-flow changes flow-id new-flow)))
changes
(->> shapes
(filter cfh/frame-shape?)
(map :id)
(filter has-flow?)))))
(defn generate-duplicate-guides
[changes shapes page ids-map delta]
(let [guides (get-in page [:options :guides])
frames (->> shapes (filter cfh/frame-shape?))
(let [guides (get page :guides)
frames (filter cfh/frame-shape? shapes)]
new-guides
(reduce
(fn [g frame]
(let [new-id (ids-map (:id frame))
new-frame (-> frame (gsh/move delta))
;; FIXME: this can be implemented efficiently just indexing guides
;; by frame-id instead of iterate over all guides all the time
new-guides
(->> guides
(vals)
(filter #(= (:frame-id %) (:id frame)))
(map #(-> %
(assoc :id (uuid/next))
(assoc :frame-id new-id)
(assoc :position (if (= (:axis %) :x)
(+ (:position %) (- (:x new-frame) (:x frame)))
(+ (:position %) (- (:y new-frame) (:y frame))))))))]
(cond-> g
(not-empty new-guides)
(conj (into {} (map (juxt :id identity) new-guides))))))
guides
frames)]
(-> (pcb/with-page changes page)
(pcb/set-page-option :guides new-guides))))
(reduce (fn [changes frame]
(let [new-id (get ids-map (:id frame))
new-frame (gsh/move frame delta)]
(reduce-kv (fn [changes _ guide]
(if (= (:id frame) (:frame-id guide))
(let [guide-id (uuid/next)
position (if (= (:axis guide) :x)
(+ (:position guide) (- (:x new-frame) (:x frame)))
(+ (:position guide) (- (:y new-frame) (:y frame))))
guide {:id guide-id
:frame-id new-id
:position position}]
(pcb/set-guide changes guide-id guide))
changes))
changes
guides)))
(pcb/with-page changes page)
frames)))
(defn generate-duplicate-component-change
[changes objects page component-root parent-id frame-id delta libraries library-data]

View file

@ -7,13 +7,11 @@
(ns app.common.logic.shapes
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.changes-builder :as pcb]
[app.common.files.helpers :as cfh]
[app.common.geom.shapes :as gsh]
[app.common.types.component :as ctk]
[app.common.types.container :as ctn]
[app.common.types.page :as ctp]
[app.common.types.shape.interactions :as ctsi]
[app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid]))
@ -85,7 +83,9 @@
(pcb/with-page page)
(pcb/with-objects objects)
(pcb/with-library-data file))
lookup (d/getf objects)
groups-to-unmask
(reduce (fn [group-ids id]
;; When the shape to delete is the mask of a masked group,
@ -110,23 +110,14 @@
interactions)))
(vals objects))
ids-set (set ids-to-delete)
guides-to-remove
(->> (dm/get-in page [:options :guides])
(vals)
(filter #(contains? ids-set (:frame-id %)))
(map :id))
changes
(reduce (fn [changes {:keys [id] :as flow}]
(if (contains? ids-to-delete (:starting-frame flow))
(pcb/set-flow changes id nil)
changes))
changes
(:flows page))
guides
(->> guides-to-remove
(reduce dissoc (dm/get-in page [:options :guides])))
starting-flows
(filter (fn [flow]
;; If any of the deleted is a frame that starts a flow,
;; this must be deleted, too.
(contains? ids-to-delete (:starting-frame flow)))
(-> page :options :flows))
all-parents
(reduce (fn [res id]
@ -172,8 +163,18 @@
(into ids-to-delete all-children))
[])
changes (-> changes
(pcb/set-page-option :guides guides))
ids-set (set ids-to-delete)
guides-to-delete
(->> (:guides page)
(vals)
(filter #(contains? ids-set (:frame-id %)))
(map :id))
changes (reduce (fn [changes guide-id]
(pcb/set-flow changes guide-id nil))
changes
guides-to-delete)
changes (reduce (fn [changes component-id]
;; It's important to delete the component before the main instance, because we
@ -181,6 +182,7 @@
(pcb/delete-component changes component-id (:id page)))
changes
components-to-delete)
changes (-> changes
(generate-update-shape-flags ids-to-hide objects {:hidden true})
(pcb/remove-objects all-children {:ignore-touched true})
@ -197,11 +199,7 @@
(into []
(remove #(and (ctsi/has-destination %)
(contains? ids-to-delete (:destination %))))
interactions)))))
(cond-> (seq starting-flows)
(pcb/update-page-option :flows (fn [flows]
(->> (map :id starting-flows)
(reduce ctp/remove-flow flows))))))]
interactions))))))]
[all-parents changes]))

View file

@ -6,6 +6,7 @@
(ns app.common.types.grid
(:require
[app.common.colors :as clr]
[app.common.schema :as sm]
[app.common.types.color :as ctc]))
@ -54,7 +55,7 @@
[:display :boolean]
[:params schema:square-params]]]])
(def schema:saved-grids
(def schema:default-grids
[:map {:title "PageGrid"}
[:square {:optional true} ::square-params]
[:row {:optional true} ::column-params]
@ -63,4 +64,24 @@
(sm/register! ::square-params schema:square-params)
(sm/register! ::column-params schema:column-params)
(sm/register! ::grid schema:grid)
(sm/register! ::saved-grids schema:saved-grids)
(sm/register! ::default-grids schema:default-grids)
(def ^:private default-square-params
{:size 16
:color {:color clr/info
:opacity 0.4}})
(def ^:private default-layout-params
{:size 12
:type :stretch
:item-length nil
:gutter 8
:margin 0
:color {:color clr/default-layout
:opacity 0.1}})
(def default-grid-params
{:square default-square-params
:column default-layout-params
:row default-layout-params})

View file

@ -7,6 +7,7 @@
(ns app.common.types.page
(:require
[app.common.data :as d]
[app.common.geom.point :as-alias gpt]
[app.common.schema :as sm]
[app.common.types.color :as-alias ctc]
[app.common.types.grid :as ctg]
@ -24,38 +25,56 @@
[:name :string]
[:starting-frame ::sm/uuid]])
(def schema:flows
[:map-of {:gen/max 2} ::sm/uuid schema:flow])
(def schema:guide
[:map {:title "Guide"}
[:id ::sm/uuid]
[:axis [::sm/one-of #{:x :y}]]
[:position ::sm/safe-number]
;; FIXME: remove maybe?
[:frame-id {:optional true} [:maybe ::sm/uuid]]])
(def schema:guides
[:map-of {:gen/max 2} ::sm/uuid schema:guide])
(def schema:objects
[:map-of {:gen/max 5} ::sm/uuid ::cts/shape])
(def schema:comment-thread-position
[:map {:title "CommentThreadPosition"}
[:frame-id ::sm/uuid]
[:position ::gpt/point]])
(def schema:page
[:map {:title "FilePage"}
[:id ::sm/uuid]
[:name :string]
[:objects
[:map-of {:gen/max 5} ::sm/uuid ::cts/shape]]
[:objects schema:objects]
[:default-grids {:optional true} ::ctg/default-grids]
[:flows {:optional true} schema:flows]
[:guides {:optional true} schema:guides]
[:plugin-data {:optional true} ::ctpg/plugin-data]
[:background {:optional true} ::ctc/rgb-color]
[:comment-thread-positions {:optional true}
[:map-of ::sm/uuid schema:comment-thread-position]]
[:options
[:map {:title "PageOptions"}
[:background {:optional true} ::ctc/rgb-color]
[:saved-grids {:optional true} ::ctg/saved-grids]
[:flows {:optional true}
[:vector {:gen/max 2} schema:flow]]
[:guides {:optional true}
[:map-of {:gen/max 2} ::sm/uuid schema:guide]]
[:plugin-data {:optional true} ::ctpg/plugin-data]]]])
;; DEPERECATED: remove after 2.3 release
[:map {:title "PageOptions"}]]])
(sm/register! ::page schema:page)
(sm/register! ::guide schema:guide)
(sm/register! ::flow schema:flow)
(def check-page-guide!
(sm/check-fn ::guide))
(def valid-guide?
(sm/lazy-validator schema:guide))
;; FIXME: convert to validator
(def check-page!
(sm/check-fn ::page))
(sm/check-fn schema:page))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; INIT & HELPERS
@ -80,25 +99,6 @@
(assoc :id (or id (uuid/next)))
(assoc :name (or name "Page 1"))))
;; --- Helpers for flow
(defn rename-flow
[flow name]
(assoc flow :name name))
(defn add-flow
[flows flow]
(conj (or flows []) flow))
(defn remove-flow
[flows flow-id]
(d/removev #(= (:id %) flow-id) flows))
(defn update-flow
[flows flow-id update-fn]
(let [index (d/index-of-pred flows #(= (:id %) flow-id))]
(update flows index update-fn)))
(defn get-frame-flow
[flows frame-id]
(d/seek #(= (:starting-frame %) frame-id) flows))
(d/seek #(= (:starting-frame %) frame-id) (vals flows)))

View file

@ -19,71 +19,6 @@
(binding [ffeat/*current* #{"components/v2"}]
(ctf/make-file-data file-id page-id)))
(t/deftest process-change-set-option
(let [file-id (uuid/custom 2 2)
page-id (uuid/custom 1 1)
data (make-file-data file-id page-id)]
(t/testing "Sets option single"
(let [chg {:type :set-option
:page-id page-id
:option :test
:value "test"}
res (ch/process-changes data [chg])]
(t/is (= "test" (get-in res [:pages-index page-id :options :test])))))
(t/testing "Sets option nested"
(let [chgs [{:type :set-option
:page-id page-id
:option [:values :test :a]
:value "a"}
{:type :set-option
:page-id page-id
:option [:values :test :b]
:value "b"}]
res (ch/process-changes data chgs)]
(t/is (= {:a "a" :b "b"}
(get-in res [:pages-index page-id :options :values :test])))))
(t/testing "Remove option single"
(let [chg {:type :set-option
:page-id page-id
:option :test
:value nil}
res (ch/process-changes data [chg])]
(t/is (empty? (keys (get-in res [:pages-index page-id :options]))))))
(t/testing "Remove option nested 1"
(let [chgs [{:type :set-option
:page-id page-id
:option [:values :test :a]
:value "a"}
{:type :set-option
:page-id page-id
:option [:values :test :b]
:value "b"}
{:type :set-option
:page-id page-id
:option [:values :test]
:value nil}]
res (ch/process-changes data chgs)]
(t/is (empty? (keys (get-in res [:pages-index page-id :options]))))))
(t/testing "Remove option nested 2"
(let [chgs [{:type :set-option
:option [:values :test1 :a]
:page-id page-id
:value "a"}
{:type :set-option
:option [:values :test2 :b]
:page-id page-id
:value "b"}
{:type :set-option
:page-id page-id
:option [:values :test2]
:value nil}]
res (ch/process-changes data chgs)]
(t/is (= [:test1] (keys (get-in res [:pages-index page-id :options :values]))))))))
(t/deftest process-change-add-obj
(let [file-id (uuid/custom 2 2)
page-id (uuid/custom 1 1)