mirror of
https://github.com/penpot/penpot.git
synced 2025-05-03 04:55:54 +02:00
✨ Integrate objects-map and introduce file feature flags
This commit is contained in:
parent
69f084e1df
commit
951b3eb4fe
21 changed files with 406 additions and 264 deletions
|
@ -352,10 +352,13 @@
|
||||||
[v]
|
[v]
|
||||||
(and (pgarray? v) (= "uuid" (.getBaseTypeName ^PgArray v))))
|
(and (pgarray? v) (= "uuid" (.getBaseTypeName ^PgArray v))))
|
||||||
|
|
||||||
|
;; TODO rename to decode-pgarray-into
|
||||||
(defn decode-pgarray
|
(defn decode-pgarray
|
||||||
([v] (some->> ^PgArray v .getArray vec))
|
([v] (decode-pgarray v []))
|
||||||
([v in] (some->> ^PgArray v .getArray (into in)))
|
([v in]
|
||||||
([v in xf] (some->> ^PgArray v .getArray (into in xf))))
|
(into in (some-> ^PgArray v .getArray)))
|
||||||
|
([v in xf]
|
||||||
|
(into in xf (some-> ^PgArray v .getArray))))
|
||||||
|
|
||||||
(defn pgarray->set
|
(defn pgarray->set
|
||||||
[v]
|
[v]
|
||||||
|
|
|
@ -254,6 +254,9 @@
|
||||||
{:name "0081-add-deleted-at-index-to-file-table"
|
{:name "0081-add-deleted-at-index-to-file-table"
|
||||||
:fn (mg/resource "app/migrations/sql/0081-add-deleted-at-index-to-file-table.sql")}
|
:fn (mg/resource "app/migrations/sql/0081-add-deleted-at-index-to-file-table.sql")}
|
||||||
|
|
||||||
|
{:name "0082-add-features-column-to-file-table"
|
||||||
|
:fn (mg/resource "app/migrations/sql/0082-add-features-column-to-file-table.sql")}
|
||||||
|
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE file
|
||||||
|
ADD COLUMN features text[] DEFAULT NULL;
|
|
@ -8,6 +8,8 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
|
[app.common.files.features :as ffeat]
|
||||||
|
[app.common.logging :as l]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.common.pages.migrations :as pmg]
|
[app.common.pages.migrations :as pmg]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
|
@ -24,6 +26,7 @@
|
||||||
[app.rpc.semaphore :as rsem]
|
[app.rpc.semaphore :as rsem]
|
||||||
[app.storage.impl :as simpl]
|
[app.storage.impl :as simpl]
|
||||||
[app.util.blob :as blob]
|
[app.util.blob :as blob]
|
||||||
|
[app.util.objects-map :as omap]
|
||||||
[app.util.services :as sv]
|
[app.util.services :as sv]
|
||||||
[app.util.time :as dt]
|
[app.util.time :as dt]
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
|
@ -44,10 +47,11 @@
|
||||||
|
|
||||||
;; --- Mutation: Create File
|
;; --- Mutation: Create File
|
||||||
|
|
||||||
|
(s/def ::features ::us/set-of-strings)
|
||||||
(s/def ::is-shared ::us/boolean)
|
(s/def ::is-shared ::us/boolean)
|
||||||
(s/def ::create-file
|
(s/def ::create-file
|
||||||
(s/keys :req-un [::profile-id ::name ::project-id]
|
(s/keys :req-un [::profile-id ::name ::project-id]
|
||||||
:opt-un [::id ::is-shared ::components-v2]))
|
:opt-un [::id ::is-shared ::features ::components-v2]))
|
||||||
|
|
||||||
(sv/defmethod ::create-file
|
(sv/defmethod ::create-file
|
||||||
[{:keys [pool] :as cfg} {:keys [profile-id project-id] :as params}]
|
[{:keys [pool] :as cfg} {:keys [profile-id project-id] :as params}]
|
||||||
|
@ -68,27 +72,37 @@
|
||||||
(defn create-file
|
(defn create-file
|
||||||
[conn {:keys [id name project-id is-shared data revn
|
[conn {:keys [id name project-id is-shared data revn
|
||||||
modified-at deleted-at ignore-sync-until
|
modified-at deleted-at ignore-sync-until
|
||||||
components-v2]
|
components-v2 features]
|
||||||
:or {is-shared false revn 0}
|
:or {is-shared false revn 0}
|
||||||
:as params}]
|
:as params}]
|
||||||
(let [id (or id (:id data) (uuid/next))
|
(let [id (or id (:id data) (uuid/next))
|
||||||
data (or data (ctf/make-file-data id components-v2))
|
|
||||||
file (db/insert! conn :file
|
;; BACKWARD COMPATIBILITY with the components-v2 param
|
||||||
(d/without-nils
|
features (cond-> (or features #{})
|
||||||
{:id id
|
components-v2 (conj "components/v2"))
|
||||||
:project-id project-id
|
|
||||||
:name name
|
data (or data
|
||||||
:revn revn
|
(binding [ffeat/*current* features]
|
||||||
:is-shared is-shared
|
(ctf/make-file-data id)))
|
||||||
:data (blob/encode data)
|
|
||||||
:ignore-sync-until ignore-sync-until
|
features (db/create-array conn "text" features)
|
||||||
:modified-at modified-at
|
file (db/insert! conn :file
|
||||||
:deleted-at deleted-at}))]
|
(d/without-nils
|
||||||
|
{:id id
|
||||||
|
:project-id project-id
|
||||||
|
:name name
|
||||||
|
:revn revn
|
||||||
|
:is-shared is-shared
|
||||||
|
:data (blob/encode data)
|
||||||
|
:features features
|
||||||
|
:ignore-sync-until ignore-sync-until
|
||||||
|
:modified-at modified-at
|
||||||
|
:deleted-at deleted-at}))]
|
||||||
|
|
||||||
(->> (assoc params :file-id id :role :owner)
|
(->> (assoc params :file-id id :role :owner)
|
||||||
(create-file-role conn))
|
(create-file-role conn))
|
||||||
|
|
||||||
(assoc file :data data)))
|
(-> file files/decode-row)))
|
||||||
|
|
||||||
;; --- Mutation: Rename File
|
;; --- Mutation: Rename File
|
||||||
|
|
||||||
|
@ -309,24 +323,59 @@
|
||||||
(s/def ::update-file
|
(s/def ::update-file
|
||||||
(s/and
|
(s/and
|
||||||
(s/keys :req-un [::id ::session-id ::profile-id ::revn]
|
(s/keys :req-un [::id ::session-id ::profile-id ::revn]
|
||||||
:opt-un [::changes ::changes-with-metadata ::components-v2])
|
:opt-un [::changes ::changes-with-metadata ::components-v2 ::features])
|
||||||
(fn [o]
|
(fn [o]
|
||||||
(or (contains? o :changes)
|
(or (contains? o :changes)
|
||||||
(contains? o :changes-with-metadata)))))
|
(contains? o :changes-with-metadata)))))
|
||||||
|
|
||||||
|
(def ^:private sql:retrieve-file
|
||||||
|
"SELECT f.*, p.team_id
|
||||||
|
FROM file AS f
|
||||||
|
JOIN project AS p ON (p.id = f.project_id)
|
||||||
|
WHERE f.id = ?
|
||||||
|
AND (f.deleted_at IS NULL OR
|
||||||
|
f.deleted_at > now())
|
||||||
|
FOR KEY SHARE")
|
||||||
|
|
||||||
(sv/defmethod ::update-file
|
(sv/defmethod ::update-file
|
||||||
{::rsem/queue :update-file}
|
{::rsem/queue :update-file}
|
||||||
[{:keys [pool] :as cfg} {:keys [id profile-id] :as params}]
|
[{:keys [pool] :as cfg} {:keys [id profile-id components-v2] :as params}]
|
||||||
(db/with-atomic [conn pool]
|
(db/with-atomic [conn pool]
|
||||||
(db/xact-lock! conn id)
|
(db/xact-lock! conn id)
|
||||||
(let [{:keys [id] :as file} (db/get-by-id conn :file id {:for-key-share true})
|
(let [file (db/exec-one! conn [sql:retrieve-file id])
|
||||||
team-id (retrieve-team-id conn (:project-id file))]
|
features' (:features params #{})
|
||||||
(files/check-edition-permissions! conn profile-id id)
|
features (db/decode-pgarray (:features file) features')
|
||||||
(with-meta
|
|
||||||
(update-file (assoc cfg :conn conn)
|
;; BACKWARD COMPATIBILITY with the components-v2 parameter
|
||||||
(assoc params :file file))
|
features (cond-> features
|
||||||
{::audit/props {:project-id (:project-id file)
|
components-v2 (conj "components/v2"))
|
||||||
:team-id team-id}}))))
|
|
||||||
|
file (assoc file :features features)]
|
||||||
|
|
||||||
|
(when-not file
|
||||||
|
(ex/raise :type :not-found
|
||||||
|
:code :object-not-found
|
||||||
|
:hint (format "file with id '%s' does not exists" id)))
|
||||||
|
|
||||||
|
;; If features are specified from params and the final feature
|
||||||
|
;; set is different than the persisted one, update it on the
|
||||||
|
;; database.
|
||||||
|
(when (not= features features')
|
||||||
|
(let [features (db/create-array conn "text" features)]
|
||||||
|
(db/update! conn :file
|
||||||
|
{:features features}
|
||||||
|
{:id id})))
|
||||||
|
|
||||||
|
(binding [ffeat/*current* features
|
||||||
|
ffeat/*wrap-objects-fn* (if (features "storate/objects-map")
|
||||||
|
omap/wrap
|
||||||
|
identity)]
|
||||||
|
(files/check-edition-permissions! conn profile-id (:id file))
|
||||||
|
(with-meta
|
||||||
|
(update-file (assoc cfg :conn conn)
|
||||||
|
(assoc params :file file))
|
||||||
|
{::audit/props {:project-id (:project-id file)
|
||||||
|
:team-id (:team-id file)}})))))
|
||||||
|
|
||||||
(defn- take-snapshot?
|
(defn- take-snapshot?
|
||||||
"Defines the rule when file `data` snapshot should be saved."
|
"Defines the rule when file `data` snapshot should be saved."
|
||||||
|
@ -347,7 +396,7 @@
|
||||||
|
|
||||||
(defn- update-file
|
(defn- update-file
|
||||||
[{:keys [conn metrics] :as cfg}
|
[{:keys [conn metrics] :as cfg}
|
||||||
{:keys [file changes changes-with-metadata session-id profile-id components-v2] :as params}]
|
{:keys [file changes changes-with-metadata session-id profile-id] :as params}]
|
||||||
(when (> (:revn params)
|
(when (> (:revn params)
|
||||||
(:revn file))
|
(:revn file))
|
||||||
|
|
||||||
|
@ -378,7 +427,8 @@
|
||||||
(assoc :id (:id file))
|
(assoc :id (:id file))
|
||||||
(pmg/migrate-data))
|
(pmg/migrate-data))
|
||||||
|
|
||||||
components-v2
|
|
||||||
|
(contains? ffeat/*current* "components/v2")
|
||||||
(ctf/migrate-to-components-v2)
|
(ctf/migrate-to-components-v2)
|
||||||
|
|
||||||
:always
|
:always
|
||||||
|
@ -455,7 +505,8 @@
|
||||||
:changes changes})
|
:changes changes})
|
||||||
|
|
||||||
(when (and (:is-shared file) (seq lchanges))
|
(when (and (:is-shared file) (seq lchanges))
|
||||||
(let [team-id (retrieve-team-id conn (:project-id file))]
|
(let [team-id (or (:team-id file)
|
||||||
|
(retrieve-team-id conn (:project-id file)))]
|
||||||
;; Asynchronously publish message to the msgbus
|
;; Asynchronously publish message to the msgbus
|
||||||
(mbus/pub! msgbus
|
(mbus/pub! msgbus
|
||||||
:topic team-id
|
:topic team-id
|
||||||
|
|
|
@ -227,29 +227,34 @@
|
||||||
(d/index-by :object-id :data))))))
|
(d/index-by :object-id :data))))))
|
||||||
|
|
||||||
(defn retrieve-file
|
(defn retrieve-file
|
||||||
[{:keys [pool] :as cfg} id components-v2]
|
[{:keys [pool] :as cfg} id features]
|
||||||
(let [file (->> (db/get-by-id pool :file id)
|
(let [file (->> (db/get-by-id pool :file id)
|
||||||
(decode-row)
|
(decode-row)
|
||||||
(pmg/migrate-file))]
|
(pmg/migrate-file))]
|
||||||
|
|
||||||
(if components-v2
|
(if (contains? features "components/v2")
|
||||||
(update file :data ctf/migrate-to-components-v2)
|
(update file :data ctf/migrate-to-components-v2)
|
||||||
(if (get-in file [:data :options :components-v2])
|
(if (dm/get-in file [:data :options :components-v2])
|
||||||
(ex/raise :type :restriction
|
(ex/raise :type :restriction
|
||||||
:code :feature-disabled
|
:code :feature-disabled
|
||||||
:hint "tried to open a components-v2 file with feature disabled")
|
:hint "tried to open a components/v2 file with feature disabled")
|
||||||
file))))
|
file))))
|
||||||
|
|
||||||
|
(s/def ::features ::us/set-of-strings)
|
||||||
(s/def ::file
|
(s/def ::file
|
||||||
(s/keys :req-un [::profile-id ::id]
|
(s/keys :req-un [::profile-id ::id]
|
||||||
:opt-un [::components-v2]))
|
:opt-un [::features ::components-v2]))
|
||||||
|
|
||||||
(sv/defmethod ::file
|
(sv/defmethod ::file
|
||||||
"Retrieve a file by its ID. Only authenticated users."
|
"Retrieve a file by its ID. Only authenticated users."
|
||||||
[{:keys [pool] :as cfg} {:keys [profile-id id components-v2] :as params}]
|
[{:keys [pool] :as cfg} {:keys [profile-id id features components-v2] :as params}]
|
||||||
(let [perms (get-permissions pool profile-id id)]
|
(let [perms (get-permissions pool profile-id id)
|
||||||
|
|
||||||
|
;; BACKWARD COMPATIBILTY with the components-v2 parameter
|
||||||
|
features (cond-> (or features #{})
|
||||||
|
components-v2 (conj features "components/v2"))]
|
||||||
(check-read-permissions! perms)
|
(check-read-permissions! perms)
|
||||||
(let [file (retrieve-file cfg id components-v2)
|
(let [file (retrieve-file cfg id features)
|
||||||
thumbs (retrieve-object-thumbnails cfg id)]
|
thumbs (retrieve-object-thumbnails cfg id)]
|
||||||
(-> file
|
(-> file
|
||||||
(assoc :thumbnails thumbs)
|
(assoc :thumbnails thumbs)
|
||||||
|
@ -278,7 +283,7 @@
|
||||||
(s/def ::page
|
(s/def ::page
|
||||||
(s/and
|
(s/and
|
||||||
(s/keys :req-un [::profile-id ::file-id]
|
(s/keys :req-un [::profile-id ::file-id]
|
||||||
:opt-un [::page-id ::object-id ::components-v2])
|
:opt-un [::page-id ::object-id ::features ::components-v2])
|
||||||
(fn [obj]
|
(fn [obj]
|
||||||
(if (contains? obj :object-id)
|
(if (contains? obj :object-id)
|
||||||
(contains? obj :page-id)
|
(contains? obj :page-id)
|
||||||
|
@ -294,11 +299,15 @@
|
||||||
mandatory.
|
mandatory.
|
||||||
|
|
||||||
Mainly used for rendering purposes."
|
Mainly used for rendering purposes."
|
||||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id page-id object-id components-v2] :as props}]
|
[{:keys [pool] :as cfg} {:keys [profile-id file-id page-id object-id features components-v2] :as props}]
|
||||||
(check-read-permissions! pool profile-id file-id)
|
(check-read-permissions! pool profile-id file-id)
|
||||||
(let [file (retrieve-file cfg file-id components-v2)
|
(let [;; BACKWARD COMPATIBILTY with the components-v2 parameter
|
||||||
page-id (or page-id (-> file :data :pages first))
|
features (cond-> (or features #{})
|
||||||
page (get-in file [:data :pages-index page-id])]
|
components-v2 (conj features "components/v2"))
|
||||||
|
|
||||||
|
file (retrieve-file cfg file-id features)
|
||||||
|
page-id (or page-id (-> file :data :pages first))
|
||||||
|
page (dm/get-in file [:data :pages-index page-id])]
|
||||||
|
|
||||||
(cond-> (prune-thumbnails page)
|
(cond-> (prune-thumbnails page)
|
||||||
(uuid? object-id)
|
(uuid? object-id)
|
||||||
|
@ -384,14 +393,17 @@
|
||||||
|
|
||||||
(s/def ::file-data-for-thumbnail
|
(s/def ::file-data-for-thumbnail
|
||||||
(s/keys :req-un [::profile-id ::file-id]
|
(s/keys :req-un [::profile-id ::file-id]
|
||||||
:opt-un [::components-v2]))
|
:opt-un [::components-v2 ::features]))
|
||||||
|
|
||||||
(sv/defmethod ::file-data-for-thumbnail
|
(sv/defmethod ::file-data-for-thumbnail
|
||||||
"Retrieves the data for generate the thumbnail of the file. Used
|
"Retrieves the data for generate the thumbnail of the file. Used
|
||||||
mainly for render thumbnails on dashboard."
|
mainly for render thumbnails on dashboard."
|
||||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id components-v2] :as props}]
|
[{:keys [pool] :as cfg} {:keys [profile-id file-id features components-v2] :as props}]
|
||||||
(check-read-permissions! pool profile-id file-id)
|
(check-read-permissions! pool profile-id file-id)
|
||||||
(let [file (retrieve-file cfg file-id components-v2)]
|
(let [;; BACKWARD COMPATIBILTY with the components-v2 parameter
|
||||||
|
features (cond-> (or features #{})
|
||||||
|
components-v2 (conj features "components/v2"))
|
||||||
|
file (retrieve-file cfg file-id features)]
|
||||||
{:file-id file-id
|
{:file-id file-id
|
||||||
:revn (:revn file)
|
:revn (:revn file)
|
||||||
:page (get-file-thumbnail-data cfg file)}))
|
:page (get-file-thumbnail-data cfg file)}))
|
||||||
|
@ -567,8 +579,9 @@
|
||||||
;; --- Helpers
|
;; --- Helpers
|
||||||
|
|
||||||
(defn decode-row
|
(defn decode-row
|
||||||
[{:keys [data changes] :as row}]
|
[{:keys [data changes features] :as row}]
|
||||||
(when row
|
(when row
|
||||||
(cond-> row
|
(cond-> row
|
||||||
changes (assoc :changes (blob/decode changes))
|
features (assoc :features (db/decode-pgarray features))
|
||||||
data (assoc :data (blob/decode data)))))
|
changes (assoc :changes (blob/decode changes))
|
||||||
|
data (assoc :data (blob/decode data)))))
|
||||||
|
|
|
@ -23,8 +23,8 @@
|
||||||
(db/get-by-id pool :project id {:columns [:id :name :team-id]}))
|
(db/get-by-id pool :project id {:columns [:id :name :team-id]}))
|
||||||
|
|
||||||
(defn- retrieve-bundle
|
(defn- retrieve-bundle
|
||||||
[{:keys [pool] :as cfg} file-id profile-id components-v2]
|
[{:keys [pool] :as cfg} file-id profile-id features]
|
||||||
(p/let [file (files/retrieve-file cfg file-id components-v2)
|
(p/let [file (files/retrieve-file cfg file-id features)
|
||||||
project (retrieve-project pool (:project-id file))
|
project (retrieve-project pool (:project-id file))
|
||||||
libs (files/retrieve-file-libraries cfg false file-id)
|
libs (files/retrieve-file-libraries cfg false file-id)
|
||||||
users (comments/get-file-comments-users pool file-id profile-id)
|
users (comments/get-file-comments-users pool file-id profile-id)
|
||||||
|
@ -45,40 +45,49 @@
|
||||||
(s/def ::file-id ::us/uuid)
|
(s/def ::file-id ::us/uuid)
|
||||||
(s/def ::profile-id ::us/uuid)
|
(s/def ::profile-id ::us/uuid)
|
||||||
(s/def ::share-id ::us/uuid)
|
(s/def ::share-id ::us/uuid)
|
||||||
|
(s/def ::features ::us/set-of-strings)
|
||||||
|
|
||||||
|
;; TODO: deprecated, should be removed when version >= 1.18
|
||||||
(s/def ::components-v2 ::us/boolean)
|
(s/def ::components-v2 ::us/boolean)
|
||||||
|
|
||||||
(s/def ::view-only-bundle
|
(s/def ::view-only-bundle
|
||||||
(s/keys :req-un [::file-id] :opt-un [::profile-id ::share-id ::components-v2]))
|
(s/keys :req-un [::file-id]
|
||||||
|
:opt-un [::profile-id ::share-id ::features ::components-v2]))
|
||||||
|
|
||||||
(sv/defmethod ::view-only-bundle {:auth false}
|
(sv/defmethod ::view-only-bundle {:auth false}
|
||||||
[{:keys [pool] :as cfg} {:keys [profile-id file-id share-id components-v2] :as params}]
|
[{:keys [pool] :as cfg} {:keys [profile-id file-id share-id features components-v2] :as params}]
|
||||||
(p/let [slink (slnk/retrieve-share-link pool file-id share-id)
|
(p/let [;; BACKWARD COMPATIBILTY with the components-v2 parameter
|
||||||
|
features (cond-> (or features #{})
|
||||||
|
components-v2 (conj features "components/v2"))
|
||||||
|
|
||||||
|
slink (slnk/retrieve-share-link pool file-id share-id)
|
||||||
perms (files/get-permissions pool profile-id file-id share-id)
|
perms (files/get-permissions pool profile-id file-id share-id)
|
||||||
thumbs (files/retrieve-object-thumbnails cfg file-id)
|
thumbs (files/retrieve-object-thumbnails cfg file-id)
|
||||||
bundle (p/-> (retrieve-bundle cfg file-id profile-id components-v2)
|
bundle (p/-> (retrieve-bundle cfg file-id profile-id features)
|
||||||
(assoc :permissions perms)
|
(assoc :permissions perms)
|
||||||
(assoc-in [:file :thumbnails] thumbs))]
|
(assoc-in [:file :thumbnails] thumbs))]
|
||||||
|
|
||||||
;; When we have neither profile nor share, we just return a not
|
;; When we have neither profile nor share, we just return a not
|
||||||
;; found response to the user.
|
;; found response to the user.
|
||||||
(when (and (not profile-id)
|
(do
|
||||||
(not slink))
|
(when (and (not profile-id)
|
||||||
(ex/raise :type :not-found
|
(not slink))
|
||||||
:code :object-not-found))
|
(ex/raise :type :not-found
|
||||||
|
:code :object-not-found))
|
||||||
|
|
||||||
;; When we have only profile, we need to check read permissions
|
;; When we have only profile, we need to check read permissions
|
||||||
;; on file.
|
;; on file.
|
||||||
(when (and profile-id (not slink))
|
(when (and profile-id (not slink))
|
||||||
(files/check-read-permissions! pool profile-id file-id))
|
(files/check-read-permissions! pool profile-id file-id))
|
||||||
|
|
||||||
(cond-> bundle
|
(cond-> bundle
|
||||||
(some? slink)
|
(some? slink)
|
||||||
(assoc :share slink)
|
(assoc :share slink)
|
||||||
|
|
||||||
(and (some? slink)
|
(and (some? slink)
|
||||||
(not (contains? (:flags slink) "view-all-pages")))
|
(not (contains? (:flags slink) "view-all-pages")))
|
||||||
(update-in [:file :data] (fn [data]
|
(update-in [:file :data] (fn [data]
|
||||||
(let [allowed-pages (:pages slink)]
|
(let [allowed-pages (:pages slink)]
|
||||||
(-> data
|
(-> data
|
||||||
(update :pages (fn [pages] (filterv #(contains? allowed-pages %) pages)))
|
(update :pages (fn [pages] (filterv #(contains? allowed-pages %) pages)))
|
||||||
(update :pages-index (fn [index] (select-keys index allowed-pages))))))))))
|
(update :pages-index (fn [index] (select-keys index allowed-pages)))))))))))
|
||||||
|
|
||||||
|
|
|
@ -148,38 +148,41 @@
|
||||||
(defn create-profile*
|
(defn create-profile*
|
||||||
([i] (create-profile* *pool* i {}))
|
([i] (create-profile* *pool* i {}))
|
||||||
([i params] (create-profile* *pool* i params))
|
([i params] (create-profile* *pool* i params))
|
||||||
([conn i params]
|
([pool i params]
|
||||||
(let [params (merge {:id (mk-uuid "profile" i)
|
(let [params (merge {:id (mk-uuid "profile" i)
|
||||||
:fullname (str "Profile " i)
|
:fullname (str "Profile " i)
|
||||||
:email (str "profile" i ".test@nodomain.com")
|
:email (str "profile" i ".test@nodomain.com")
|
||||||
:password "123123"
|
:password "123123"
|
||||||
:is-demo false}
|
:is-demo false}
|
||||||
params)]
|
params)]
|
||||||
(->> params
|
(with-open [conn (db/open pool)]
|
||||||
(cmd.auth/create-profile conn)
|
(->> params
|
||||||
(cmd.auth/create-profile-relations conn)))))
|
(cmd.auth/create-profile conn)
|
||||||
|
(cmd.auth/create-profile-relations conn))))))
|
||||||
|
|
||||||
(defn create-project*
|
(defn create-project*
|
||||||
([i params] (create-project* *pool* i params))
|
([i params] (create-project* *pool* i params))
|
||||||
([conn i {:keys [profile-id team-id] :as params}]
|
([pool i {:keys [profile-id team-id] :as params}]
|
||||||
(us/assert uuid? profile-id)
|
(us/assert uuid? profile-id)
|
||||||
(us/assert uuid? team-id)
|
(us/assert uuid? team-id)
|
||||||
(->> (merge {:id (mk-uuid "project" i)
|
(with-open [conn (db/open pool)]
|
||||||
:name (str "project" i)}
|
(->> (merge {:id (mk-uuid "project" i)
|
||||||
params)
|
:name (str "project" i)}
|
||||||
(#'projects/create-project conn))))
|
params)
|
||||||
|
(#'projects/create-project conn)))))
|
||||||
|
|
||||||
(defn create-file*
|
(defn create-file*
|
||||||
([i params]
|
([i params]
|
||||||
(create-file* *pool* i params))
|
(create-file* *pool* i params))
|
||||||
([conn i {:keys [profile-id project-id] :as params}]
|
([pool i {:keys [profile-id project-id] :as params}]
|
||||||
(us/assert uuid? profile-id)
|
(us/assert uuid? profile-id)
|
||||||
(us/assert uuid? project-id)
|
(us/assert uuid? project-id)
|
||||||
(#'files/create-file conn
|
(with-open [conn (db/open pool)]
|
||||||
(merge {:id (mk-uuid "file" i)
|
(#'files/create-file conn
|
||||||
:name (str "file" i)
|
(merge {:id (mk-uuid "file" i)
|
||||||
:components-v2 true}
|
:name (str "file" i)
|
||||||
params))))
|
:components-v2 true}
|
||||||
|
params)))))
|
||||||
|
|
||||||
(defn mark-file-deleted*
|
(defn mark-file-deleted*
|
||||||
([params] (mark-file-deleted* *pool* params))
|
([params] (mark-file-deleted* *pool* params))
|
||||||
|
@ -188,85 +191,95 @@
|
||||||
|
|
||||||
(defn create-team*
|
(defn create-team*
|
||||||
([i params] (create-team* *pool* i params))
|
([i params] (create-team* *pool* i params))
|
||||||
([conn i {:keys [profile-id] :as params}]
|
([pool i {:keys [profile-id] :as params}]
|
||||||
(us/assert uuid? profile-id)
|
(us/assert uuid? profile-id)
|
||||||
(let [id (mk-uuid "team" i)]
|
(with-open [conn (db/open pool)]
|
||||||
(teams/create-team conn {:id id
|
(let [id (mk-uuid "team" i)]
|
||||||
:profile-id profile-id
|
(teams/create-team conn {:id id
|
||||||
:name (str "team" i)}))))
|
:profile-id profile-id
|
||||||
|
:name (str "team" i)})))))
|
||||||
|
|
||||||
(defn create-file-media-object*
|
(defn create-file-media-object*
|
||||||
([params] (create-file-media-object* *pool* params))
|
([params] (create-file-media-object* *pool* params))
|
||||||
([conn {:keys [name width height mtype file-id is-local media-id]
|
([pool {:keys [name width height mtype file-id is-local media-id]
|
||||||
:or {name "sample" width 100 height 100 mtype "image/svg+xml" is-local true}}]
|
:or {name "sample" width 100 height 100 mtype "image/svg+xml" is-local true}}]
|
||||||
(db/insert! conn :file-media-object
|
|
||||||
{:id (uuid/next)
|
(with-open [conn (db/open pool)]
|
||||||
:file-id file-id
|
(db/insert! conn :file-media-object
|
||||||
:is-local is-local
|
{:id (uuid/next)
|
||||||
:name name
|
:file-id file-id
|
||||||
:media-id media-id
|
:is-local is-local
|
||||||
:width width
|
:name name
|
||||||
:height height
|
:media-id media-id
|
||||||
:mtype mtype})))
|
:width width
|
||||||
|
:height height
|
||||||
|
:mtype mtype}))))
|
||||||
|
|
||||||
(defn link-file-to-library*
|
(defn link-file-to-library*
|
||||||
([params] (link-file-to-library* *pool* params))
|
([params] (link-file-to-library* *pool* params))
|
||||||
([conn {:keys [file-id library-id] :as params}]
|
([pool {:keys [file-id library-id] :as params}]
|
||||||
(#'files/link-file-to-library conn {:file-id file-id :library-id library-id})))
|
(with-open [conn (db/open pool)]
|
||||||
|
(#'files/link-file-to-library conn {:file-id file-id :library-id library-id}))))
|
||||||
|
|
||||||
(defn create-complaint-for
|
(defn create-complaint-for
|
||||||
[conn {:keys [id created-at type]}]
|
[pool {:keys [id created-at type]}]
|
||||||
(db/insert! conn :profile-complaint-report
|
(with-open [conn (db/open pool)]
|
||||||
{:profile-id id
|
(db/insert! conn :profile-complaint-report
|
||||||
:created-at (or created-at (dt/now))
|
{:profile-id id
|
||||||
:type (name type)
|
:created-at (or created-at (dt/now))
|
||||||
:content (db/tjson {})}))
|
:type (name type)
|
||||||
|
:content (db/tjson {})})))
|
||||||
|
|
||||||
(defn create-global-complaint-for
|
(defn create-global-complaint-for
|
||||||
[conn {:keys [email type created-at]}]
|
[pool {:keys [email type created-at]}]
|
||||||
(db/insert! conn :global-complaint-report
|
(with-open [conn (db/open pool)]
|
||||||
{:email email
|
(db/insert! conn :global-complaint-report
|
||||||
:type (name type)
|
{:email email
|
||||||
:created-at (or created-at (dt/now))
|
:type (name type)
|
||||||
:content (db/tjson {})}))
|
:created-at (or created-at (dt/now))
|
||||||
|
:content (db/tjson {})})))
|
||||||
|
|
||||||
(defn create-team-role*
|
(defn create-team-role*
|
||||||
([params] (create-team-role* *pool* params))
|
([params] (create-team-role* *pool* params))
|
||||||
([conn {:keys [team-id profile-id role] :or {role :owner}}]
|
([pool {:keys [team-id profile-id role] :or {role :owner}}]
|
||||||
(#'teams/create-team-role conn {:team-id team-id
|
(with-open [conn (db/open pool)]
|
||||||
:profile-id profile-id
|
(#'teams/create-team-role conn {:team-id team-id
|
||||||
:role role})))
|
:profile-id profile-id
|
||||||
|
:role role}))))
|
||||||
|
|
||||||
(defn create-project-role*
|
(defn create-project-role*
|
||||||
([params] (create-project-role* *pool* params))
|
([params] (create-project-role* *pool* params))
|
||||||
([conn {:keys [project-id profile-id role] :or {role :owner}}]
|
([pool {:keys [project-id profile-id role] :or {role :owner}}]
|
||||||
(#'projects/create-project-role conn {:project-id project-id
|
(with-open [conn (db/open pool)]
|
||||||
:profile-id profile-id
|
(#'projects/create-project-role conn {:project-id project-id
|
||||||
:role role})))
|
:profile-id profile-id
|
||||||
|
:role role}))))
|
||||||
|
|
||||||
(defn create-file-role*
|
(defn create-file-role*
|
||||||
([params] (create-file-role* *pool* params))
|
([params] (create-file-role* *pool* params))
|
||||||
([conn {:keys [file-id profile-id role] :or {role :owner}}]
|
([pool {:keys [file-id profile-id role] :or {role :owner}}]
|
||||||
(#'files/create-file-role conn {:file-id file-id
|
(with-open [conn (db/open pool)]
|
||||||
:profile-id profile-id
|
(#'files/create-file-role conn {:file-id file-id
|
||||||
:role role})))
|
:profile-id profile-id
|
||||||
|
:role role}))))
|
||||||
|
|
||||||
(defn update-file*
|
(defn update-file*
|
||||||
([params] (update-file* *pool* params))
|
([params] (update-file* *pool* params))
|
||||||
([conn {:keys [file-id changes session-id profile-id revn]
|
([pool {:keys [file-id changes session-id profile-id revn]
|
||||||
:or {session-id (uuid/next) revn 0}}]
|
:or {session-id (uuid/next) revn 0}}]
|
||||||
(let [file (db/get-by-id conn :file file-id)
|
(with-open [conn (db/open pool)]
|
||||||
msgbus (:app.msgbus/msgbus *system*)
|
(let [file (db/get-by-id conn :file file-id)
|
||||||
metrics (:app.metrics/metrics *system*)]
|
msgbus (:app.msgbus/msgbus *system*)
|
||||||
(#'files/update-file {:conn conn
|
metrics (:app.metrics/metrics *system*)]
|
||||||
:msgbus msgbus
|
(#'files/update-file {:conn conn
|
||||||
:metrics metrics}
|
:msgbus msgbus
|
||||||
{:file file
|
:metrics metrics}
|
||||||
:revn revn
|
{:file file
|
||||||
:components-v2 true
|
:revn revn
|
||||||
:changes changes
|
:components-v2 true
|
||||||
:session-id session-id
|
:changes changes
|
||||||
:profile-id profile-id}))))
|
:session-id session-id
|
||||||
|
:profile-id profile-id})))))
|
||||||
|
|
||||||
;; --- RPC HELPERS
|
;; --- RPC HELPERS
|
||||||
|
|
||||||
|
|
10
common/src/app/common/files/features.cljc
Normal file
10
common/src/app/common/files/features.cljc
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
;; 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) KALEIDOS INC
|
||||||
|
|
||||||
|
(ns app.common.files.features)
|
||||||
|
|
||||||
|
(def ^:dynamic *current* #{})
|
||||||
|
(def ^:dynamic *wrap-objects-fn* identity)
|
|
@ -347,27 +347,12 @@
|
||||||
;; -- Components
|
;; -- Components
|
||||||
|
|
||||||
(defmethod process-change :add-component
|
(defmethod process-change :add-component
|
||||||
[data {:keys [id name path main-instance-id main-instance-page shapes]}]
|
[data params]
|
||||||
(ctkl/add-component data
|
(ctkl/add-component data params))
|
||||||
id
|
|
||||||
name
|
|
||||||
path
|
|
||||||
main-instance-id
|
|
||||||
main-instance-page
|
|
||||||
shapes))
|
|
||||||
|
|
||||||
(defmethod process-change :mod-component
|
(defmethod process-change :mod-component
|
||||||
[data {:keys [id name path objects]}]
|
[data params]
|
||||||
(update-in data [:components id]
|
(ctkl/mod-component data params))
|
||||||
#(cond-> %
|
|
||||||
(some? name)
|
|
||||||
(assoc :name name)
|
|
||||||
|
|
||||||
(some? path)
|
|
||||||
(assoc :path path)
|
|
||||||
|
|
||||||
(some? objects)
|
|
||||||
(assoc :objects objects))))
|
|
||||||
|
|
||||||
(defmethod process-change :del-component
|
(defmethod process-change :del-component
|
||||||
[data {:keys [id skip-undelete?]}]
|
[data {:keys [id skip-undelete?]}]
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.files.features :as ffeat]
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
|
@ -50,10 +51,12 @@
|
||||||
|
|
||||||
(defn with-objects
|
(defn with-objects
|
||||||
[changes objects]
|
[changes objects]
|
||||||
(let [file-data (-> (ctf/make-file-data (uuid/next) uuid/zero true)
|
(let [fdata (binding [ffeat/*current* #{"components/v2"}]
|
||||||
(assoc-in [:pages-index uuid/zero :objects] objects))]
|
(ctf/make-file-data (uuid/next) uuid/zero))
|
||||||
(vary-meta changes assoc ::file-data file-data
|
fdata (assoc-in fdata [:pages-index uuid/zero :objects] objects)]
|
||||||
::applied-changes-count 0)))
|
(vary-meta changes assoc
|
||||||
|
::file-data fdata
|
||||||
|
::applied-changes-count 0)))
|
||||||
|
|
||||||
(defn with-library-data
|
(defn with-library-data
|
||||||
[changes data]
|
[changes data]
|
||||||
|
@ -268,7 +271,7 @@
|
||||||
:page-id (::page-id (meta changes))
|
:page-id (::page-id (meta changes))
|
||||||
:parent-id (:parent-id shape)
|
:parent-id (:parent-id shape)
|
||||||
:shapes [(:id shape)]
|
:shapes [(:id shape)]
|
||||||
:index idx})))]
|
:index idx})))]
|
||||||
|
|
||||||
(-> changes
|
(-> changes
|
||||||
(update :redo-changes conj set-parent-change)
|
(update :redo-changes conj set-parent-change)
|
||||||
|
@ -592,7 +595,7 @@
|
||||||
:main-instance-page main-instance-page
|
:main-instance-page main-instance-page
|
||||||
:shapes new-shapes})
|
:shapes new-shapes})
|
||||||
(into (map mk-change) updated-shapes))))
|
(into (map mk-change) updated-shapes))))
|
||||||
(update :undo-changes
|
(update :undo-changes
|
||||||
(fn [undo-changes]
|
(fn [undo-changes]
|
||||||
(-> undo-changes
|
(-> undo-changes
|
||||||
(d/preconj {:type :del-component
|
(d/preconj {:type :del-component
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
(defn instance-root?
|
(defn instance-root?
|
||||||
[shape]
|
[shape]
|
||||||
(some? (:component-id shape)))
|
(some? (:component-id shape)))
|
||||||
|
|
||||||
(defn instance-of?
|
(defn instance-of?
|
||||||
[shape file-id component-id]
|
[shape file-id component-id]
|
||||||
(and (some? (:component-id shape))
|
(and (some? (:component-id shape))
|
||||||
|
|
|
@ -2,30 +2,52 @@
|
||||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
;; 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/.
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) UXBOX Labs SL
|
;; Copyright (c) KELEIDOS INC
|
||||||
|
|
||||||
(ns app.common.types.components-list
|
(ns app.common.types.components-list
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]))
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.files.features :as feat]))
|
||||||
|
|
||||||
(defn components-seq
|
(defn components-seq
|
||||||
[file-data]
|
[file-data]
|
||||||
(vals (:components file-data)))
|
(vals (:components file-data)))
|
||||||
|
|
||||||
(defn add-component
|
(defn add-component
|
||||||
[file-data id name path main-instance-id main-instance-page shapes]
|
[file-data {:keys [id name path main-instance-id main-instance-page shapes]}]
|
||||||
(let [components-v2 (get-in file-data [:options :components-v2])]
|
(let [components-v2 (dm/get-in file-data [:options :components-v2])
|
||||||
|
wrap-object-fn feat/*wrap-objects-fn*]
|
||||||
(cond-> file-data
|
(cond-> file-data
|
||||||
:always
|
:always
|
||||||
(assoc-in [:components id]
|
(assoc-in [:components id]
|
||||||
{:id id
|
{:id id
|
||||||
:name name
|
:name name
|
||||||
:path path
|
:path path
|
||||||
:objects (d/index-by :id shapes)})
|
:objects (->> shapes
|
||||||
|
(d/index-by :id)
|
||||||
|
(wrap-object-fn))})
|
||||||
|
|
||||||
components-v2
|
components-v2
|
||||||
(update-in [:components id] assoc :main-instance-id main-instance-id
|
(update-in [:components id] assoc
|
||||||
:main-instance-page main-instance-page))))
|
:main-instance-id main-instance-id
|
||||||
|
:main-instance-page main-instance-page))))
|
||||||
|
|
||||||
|
(defn mod-component
|
||||||
|
[file-data {:keys [id name path objects]}]
|
||||||
|
(let [wrap-objects-fn feat/*wrap-objects-fn*]
|
||||||
|
(update-in file-data [:components id]
|
||||||
|
(fn [component]
|
||||||
|
(let [objects (some-> objects wrap-objects-fn)]
|
||||||
|
(cond-> component
|
||||||
|
(some? name)
|
||||||
|
(assoc :name name)
|
||||||
|
|
||||||
|
(some? path)
|
||||||
|
(assoc :path path)
|
||||||
|
|
||||||
|
(some? objects)
|
||||||
|
(assoc :objects objects)))))))
|
||||||
|
|
||||||
(defn get-component
|
(defn get-component
|
||||||
[file-data component-id]
|
[file-data component-id]
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
(ns app.common.types.container
|
(ns app.common.types.container
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
|
@ -41,8 +42,8 @@
|
||||||
(us/assert uuid? id)
|
(us/assert uuid? id)
|
||||||
|
|
||||||
(-> (if (= type :page)
|
(-> (if (= type :page)
|
||||||
(get-in file [:pages-index id])
|
(dm/get-in file [:pages-index id])
|
||||||
(get-in file [:components id]))
|
(dm/get-in file [:components id]))
|
||||||
(assoc :type type)))
|
(assoc :type type)))
|
||||||
|
|
||||||
(defn get-shape
|
(defn get-shape
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
(ns app.common.types.file
|
(ns app.common.types.file
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.files.features :as ffeat]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.pages.common :refer [file-version]]
|
[app.common.pages.common :refer [file-version]]
|
||||||
|
@ -65,16 +67,16 @@
|
||||||
:pages-index {}})
|
:pages-index {}})
|
||||||
|
|
||||||
(defn make-file-data
|
(defn make-file-data
|
||||||
([file-id components-v2]
|
([file-id]
|
||||||
(make-file-data file-id (uuid/next) components-v2))
|
(make-file-data file-id (uuid/next)))
|
||||||
|
|
||||||
([file-id page-id components-v2]
|
([file-id page-id]
|
||||||
(let [page (ctp/make-empty-page page-id "Page-1")]
|
(let [page (ctp/make-empty-page page-id "Page-1")]
|
||||||
(cond-> (-> empty-file-data
|
(cond-> (-> empty-file-data
|
||||||
(assoc :id file-id)
|
(assoc :id file-id)
|
||||||
(ctpl/add-page page))
|
(ctpl/add-page page))
|
||||||
|
|
||||||
components-v2
|
(contains? ffeat/*current* "components/v2")
|
||||||
(assoc-in [:options :components-v2] true)))))
|
(assoc-in [:options :components-v2] true)))))
|
||||||
|
|
||||||
;; Helpers
|
;; Helpers
|
||||||
|
@ -108,7 +110,7 @@
|
||||||
([libraries component-id]
|
([libraries component-id]
|
||||||
(some #(ctkl/get-component (:data %) component-id) (vals libraries)))
|
(some #(ctkl/get-component (:data %) component-id) (vals libraries)))
|
||||||
([libraries library-id component-id]
|
([libraries library-id component-id]
|
||||||
(ctkl/get-component (get-in libraries [library-id :data]) component-id)))
|
(ctkl/get-component (dm/get-in libraries [library-id :data]) component-id)))
|
||||||
|
|
||||||
(defn delete-component
|
(defn delete-component
|
||||||
"Delete a component and store it to be able to be recovered later.
|
"Delete a component and store it to be able to be recovered later.
|
||||||
|
@ -118,7 +120,7 @@
|
||||||
(delete-component file-data component-id false))
|
(delete-component file-data component-id false))
|
||||||
|
|
||||||
([file-data component-id skip-undelete?]
|
([file-data component-id skip-undelete?]
|
||||||
(let [components-v2 (get-in file-data [:options :components-v2])
|
(let [components-v2 (dm/get-in file-data [:options :components-v2])
|
||||||
|
|
||||||
add-to-deleted-components
|
add-to-deleted-components
|
||||||
(fn [file-data]
|
(fn [file-data]
|
||||||
|
@ -144,12 +146,12 @@
|
||||||
(defn get-deleted-component
|
(defn get-deleted-component
|
||||||
"Retrieve a component that has been deleted but still is in the safe store."
|
"Retrieve a component that has been deleted but still is in the safe store."
|
||||||
[file-data component-id]
|
[file-data component-id]
|
||||||
(get-in file-data [:deleted-components component-id]))
|
(dm/get-in file-data [:deleted-components component-id]))
|
||||||
|
|
||||||
(defn restore-component
|
(defn restore-component
|
||||||
"Recover a deleted component and put it again in place."
|
"Recover a deleted component and put it again in place."
|
||||||
[file-data component-id]
|
[file-data component-id]
|
||||||
(let [component (-> (get-in file-data [:deleted-components component-id])
|
(let [component (-> (dm/get-in file-data [:deleted-components component-id])
|
||||||
(dissoc :main-instance-x :main-instance-y))]
|
(dissoc :main-instance-x :main-instance-y))]
|
||||||
(cond-> file-data
|
(cond-> file-data
|
||||||
(some? component)
|
(some? component)
|
||||||
|
@ -242,7 +244,7 @@
|
||||||
[file-data]
|
[file-data]
|
||||||
(let [components (ctkl/components-seq file-data)]
|
(let [components (ctkl/components-seq file-data)]
|
||||||
(if (or (empty? components)
|
(if (or (empty? components)
|
||||||
(get-in file-data [:options :components-v2]))
|
(dm/get-in file-data [:options :components-v2]))
|
||||||
(assoc-in file-data [:options :components-v2] true)
|
(assoc-in file-data [:options :components-v2] true)
|
||||||
(let [grid-gap 50
|
(let [grid-gap 50
|
||||||
|
|
||||||
|
@ -342,12 +344,12 @@
|
||||||
copy-component
|
copy-component
|
||||||
(fn [file-data]
|
(fn [file-data]
|
||||||
(ctkl/add-component file-data
|
(ctkl/add-component file-data
|
||||||
(:id component)
|
{:id (:id component)
|
||||||
(:name component)
|
:name (:name component)
|
||||||
(:path component)
|
:path (:path component)
|
||||||
(:id main-instance-shape)
|
:main-instance-id (:id main-instance-shape)
|
||||||
page-id
|
:main-instance-page page-id
|
||||||
(vals (:objects component))))
|
:shapes (vals (:objects component))}))
|
||||||
|
|
||||||
; Change all existing instances to point to the local file
|
; Change all existing instances to point to the local file
|
||||||
remap-instances
|
remap-instances
|
||||||
|
@ -500,10 +502,10 @@
|
||||||
component-file (when component-file-id (get libraries component-file-id nil))
|
component-file (when component-file-id (get libraries component-file-id nil))
|
||||||
component (when component-id
|
component (when component-id
|
||||||
(if component-file
|
(if component-file
|
||||||
(get-in component-file [:data :components component-id])
|
(dm/get-in component-file [:data :components component-id])
|
||||||
(get components component-id)))
|
(get components component-id)))
|
||||||
component-shape (when (and component (:shape-ref shape))
|
component-shape (when (and component (:shape-ref shape))
|
||||||
(get-in component [:objects (:shape-ref shape)]))]
|
(dm/get-in component [:objects (:shape-ref shape)]))]
|
||||||
(str/format " %s--> %s%s%s"
|
(str/format " %s--> %s%s%s"
|
||||||
(cond (:component-root? shape) "#"
|
(cond (:component-root? shape) "#"
|
||||||
(:component-id shape) "@"
|
(:component-id shape) "@"
|
||||||
|
@ -518,7 +520,7 @@
|
||||||
component-file-id (:component-file shape)
|
component-file-id (:component-file shape)
|
||||||
component-file (when component-file-id (get libraries component-file-id nil))
|
component-file (when component-file-id (get libraries component-file-id nil))
|
||||||
component (if component-file
|
component (if component-file
|
||||||
(get-in component-file [:data :components component-id])
|
(dm/get-in component-file [:data :components component-id])
|
||||||
(get components component-id))]
|
(get components component-id))]
|
||||||
(str/format " (%s%s)"
|
(str/format " (%s%s)"
|
||||||
(when component-file (str/format "<%s> " (:name component-file)))
|
(when component-file (str/format "<%s> " (:name component-file)))
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
(ns app.common.types.page
|
(ns app.common.types.page
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.common.files.features :as ffeat]
|
||||||
[app.common.types.page.flow :as ctpf]
|
[app.common.types.page.flow :as ctpf]
|
||||||
[app.common.types.page.grid :as ctpg]
|
[app.common.types.page.grid :as ctpg]
|
||||||
[app.common.types.page.guide :as ctpu]
|
[app.common.types.page.guide :as ctpu]
|
||||||
|
@ -48,9 +49,11 @@
|
||||||
|
|
||||||
(defn make-empty-page
|
(defn make-empty-page
|
||||||
[id name]
|
[id name]
|
||||||
(assoc empty-page-data
|
(let [wrap-fn ffeat/*wrap-objects-fn*]
|
||||||
:id id
|
(-> empty-page-data
|
||||||
:name name))
|
(assoc :id id)
|
||||||
|
(assoc :name name)
|
||||||
|
(update :objects wrap-fn))))
|
||||||
|
|
||||||
;; --- Helpers for flow
|
;; --- Helpers for flow
|
||||||
|
|
||||||
|
|
|
@ -2,32 +2,29 @@
|
||||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
;; 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/.
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) UXBOX Labs SL
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
(ns app.common.types.pages-list
|
(ns app.common.types.pages-list
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.pages.helpers :as cph]))
|
[app.common.pages.helpers :as cph]))
|
||||||
|
|
||||||
(defn get-page
|
(defn get-page
|
||||||
[file-data id]
|
[file-data id]
|
||||||
(get-in file-data [:pages-index id]))
|
(dm/get-in file-data [:pages-index id]))
|
||||||
|
|
||||||
(defn add-page
|
(defn add-page
|
||||||
[file-data page]
|
[file-data {:keys [id index] :as page}]
|
||||||
(let [index (:index page)
|
(-> file-data
|
||||||
page (dissoc page :index)
|
;; It's legitimate to add a page that is already there, for
|
||||||
|
;; example in an idempotent changes operation.
|
||||||
; It's legitimate to add a page that is already there,
|
(update :pages (fn [pages]
|
||||||
; for example in an idempotent changes operation.
|
(let [exists? (some (partial = id) pages)]
|
||||||
add-if-not-exists (fn [pages id]
|
(cond
|
||||||
(cond
|
exists? pages
|
||||||
(d/seek #(= % id) pages) pages
|
(nil? index) (conj pages id)
|
||||||
(nil? index) (conj pages id)
|
:else (cph/insert-at-index pages index [id])))))
|
||||||
:else (cph/insert-at-index pages index [id])))]
|
(update :pages-index assoc id (dissoc page :index))))
|
||||||
(-> file-data
|
|
||||||
(update :pages add-if-not-exists (:id page))
|
|
||||||
(update :pages-index assoc (:id page) page))))
|
|
||||||
|
|
||||||
(defn pages-seq
|
(defn pages-seq
|
||||||
[file-data]
|
[file-data]
|
||||||
|
@ -42,4 +39,3 @@
|
||||||
(-> file-data
|
(-> file-data
|
||||||
(update :pages (fn [pages] (filterv #(not= % page-id) pages)))
|
(update :pages (fn [pages] (filterv #(not= % page-id) pages)))
|
||||||
(update :pages-index dissoc page-id)))
|
(update :pages-index dissoc page-id)))
|
||||||
|
|
||||||
|
|
|
@ -6,16 +6,22 @@
|
||||||
|
|
||||||
(ns app.common.pages-test
|
(ns app.common.pages-test
|
||||||
(:require
|
(:require
|
||||||
[clojure.test :as t]
|
[app.common.files.features :as ffeat]
|
||||||
[clojure.pprint :refer [pprint]]
|
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.common.types.file :as ctf]
|
[app.common.types.file :as ctf]
|
||||||
[app.common.uuid :as uuid]))
|
[app.common.uuid :as uuid]
|
||||||
|
[clojure.pprint :refer [pprint]]
|
||||||
|
[clojure.test :as t]))
|
||||||
|
|
||||||
|
(defn- make-file-data
|
||||||
|
[file-id page-id]
|
||||||
|
(binding [ffeat/*current* #{"components/v2"}]
|
||||||
|
(ctf/make-file-data file-id page-id)))
|
||||||
|
|
||||||
(t/deftest process-change-set-option
|
(t/deftest process-change-set-option
|
||||||
(let [file-id (uuid/custom 2 2)
|
(let [file-id (uuid/custom 2 2)
|
||||||
page-id (uuid/custom 1 1)
|
page-id (uuid/custom 1 1)
|
||||||
data (ctf/make-file-data file-id page-id true)]
|
data (make-file-data file-id page-id)]
|
||||||
(t/testing "Sets option single"
|
(t/testing "Sets option single"
|
||||||
(let [chg {:type :set-option
|
(let [chg {:type :set-option
|
||||||
:page-id page-id
|
:page-id page-id
|
||||||
|
@ -81,7 +87,7 @@
|
||||||
(t/deftest process-change-add-obj
|
(t/deftest process-change-add-obj
|
||||||
(let [file-id (uuid/custom 2 2)
|
(let [file-id (uuid/custom 2 2)
|
||||||
page-id (uuid/custom 1 1)
|
page-id (uuid/custom 1 1)
|
||||||
data (ctf/make-file-data file-id page-id true)
|
data (make-file-data file-id page-id)
|
||||||
id-a (uuid/custom 2 1)
|
id-a (uuid/custom 2 1)
|
||||||
id-b (uuid/custom 2 2)
|
id-b (uuid/custom 2 2)
|
||||||
id-c (uuid/custom 2 3)]
|
id-c (uuid/custom 2 3)]
|
||||||
|
@ -135,7 +141,7 @@
|
||||||
(t/deftest process-change-mod-obj
|
(t/deftest process-change-mod-obj
|
||||||
(let [file-id (uuid/custom 2 2)
|
(let [file-id (uuid/custom 2 2)
|
||||||
page-id (uuid/custom 1 1)
|
page-id (uuid/custom 1 1)
|
||||||
data (ctf/make-file-data file-id page-id true)]
|
data (make-file-data file-id page-id)]
|
||||||
(t/testing "simple mod-obj"
|
(t/testing "simple mod-obj"
|
||||||
(let [chg {:type :mod-obj
|
(let [chg {:type :mod-obj
|
||||||
:page-id page-id
|
:page-id page-id
|
||||||
|
@ -162,7 +168,7 @@
|
||||||
(let [file-id (uuid/custom 2 2)
|
(let [file-id (uuid/custom 2 2)
|
||||||
page-id (uuid/custom 1 1)
|
page-id (uuid/custom 1 1)
|
||||||
id (uuid/custom 2 1)
|
id (uuid/custom 2 1)
|
||||||
data (ctf/make-file-data file-id page-id true)
|
data (make-file-data file-id page-id)
|
||||||
data (-> data
|
data (-> data
|
||||||
(assoc-in [:pages-index page-id :objects uuid/zero :shapes] [id])
|
(assoc-in [:pages-index page-id :objects uuid/zero :shapes] [id])
|
||||||
(assoc-in [:pages-index page-id :objects id]
|
(assoc-in [:pages-index page-id :objects id]
|
||||||
|
@ -206,7 +212,7 @@
|
||||||
|
|
||||||
file-id (uuid/custom 2 2)
|
file-id (uuid/custom 2 2)
|
||||||
page-id (uuid/custom 1 1)
|
page-id (uuid/custom 1 1)
|
||||||
data (ctf/make-file-data file-id page-id true)
|
data (make-file-data file-id page-id)
|
||||||
|
|
||||||
data (update-in data [:pages-index page-id :objects]
|
data (update-in data [:pages-index page-id :objects]
|
||||||
#(-> %
|
#(-> %
|
||||||
|
@ -450,7 +456,7 @@
|
||||||
:obj {:type :rect
|
:obj {:type :rect
|
||||||
:name "Shape 3"}}
|
:name "Shape 3"}}
|
||||||
]
|
]
|
||||||
data (ctf/make-file-data file-id page-id true)
|
data (make-file-data file-id page-id)
|
||||||
data (cp/process-changes data changes)]
|
data (cp/process-changes data changes)]
|
||||||
|
|
||||||
(t/testing "preserve order on multiple shape mov 1"
|
(t/testing "preserve order on multiple shape mov 1"
|
||||||
|
@ -557,7 +563,7 @@
|
||||||
:parent-id group-1-id
|
:parent-id group-1-id
|
||||||
:shapes [shape-1-id shape-2-id]}]
|
:shapes [shape-1-id shape-2-id]}]
|
||||||
|
|
||||||
data (ctf/make-file-data file-id page-id true)
|
data (make-file-data file-id page-id)
|
||||||
data (cp/process-changes data changes)]
|
data (cp/process-changes data changes)]
|
||||||
|
|
||||||
(t/testing "case 1"
|
(t/testing "case 1"
|
||||||
|
|
|
@ -6,16 +6,22 @@
|
||||||
|
|
||||||
(ns app.common.test-helpers.files
|
(ns app.common.test-helpers.files
|
||||||
(:require
|
(:require
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.files.features :as ffeat]
|
||||||
[app.common.types.components-list :as ctkl]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.types.colors-list :as ctcl]
|
[app.common.types.colors-list :as ctcl]
|
||||||
[app.common.types.container :as ctn]
|
[app.common.types.components-list :as ctkl]
|
||||||
[app.common.types.file :as ctf]
|
[app.common.types.container :as ctn]
|
||||||
[app.common.types.pages-list :as ctpl]
|
[app.common.types.file :as ctf]
|
||||||
[app.common.types.shape :as cts]
|
[app.common.types.pages-list :as ctpl]
|
||||||
[app.common.types.shape-tree :as ctst]
|
[app.common.types.shape :as cts]
|
||||||
[app.common.types.typographies-list :as ctyl]
|
[app.common.types.shape-tree :as ctst]
|
||||||
[app.common.uuid :as uuid]))
|
[app.common.types.typographies-list :as ctyl]
|
||||||
|
[app.common.uuid :as uuid]))
|
||||||
|
|
||||||
|
(defn- make-file-data
|
||||||
|
[file-id page-id]
|
||||||
|
(binding [ffeat/*current* #{"components/v2"}]
|
||||||
|
(ctf/make-file-data file-id page-id)))
|
||||||
|
|
||||||
(def ^:private idmap (atom {}))
|
(def ^:private idmap (atom {}))
|
||||||
|
|
||||||
|
@ -33,7 +39,7 @@
|
||||||
([file-id page-id props]
|
([file-id page-id props]
|
||||||
(merge {:id file-id
|
(merge {:id file-id
|
||||||
:name (get props :name "File1")
|
:name (get props :name "File1")
|
||||||
:data (ctf/make-file-data file-id page-id true)}
|
:data (make-file-data file-id page-id)}
|
||||||
props)))
|
props)))
|
||||||
|
|
||||||
(defn sample-shape
|
(defn sample-shape
|
||||||
|
@ -81,12 +87,12 @@
|
||||||
#(reduce (fn [page shape] (ctst/set-shape page shape))
|
#(reduce (fn [page shape] (ctst/set-shape page shape))
|
||||||
%
|
%
|
||||||
updated-shapes))
|
updated-shapes))
|
||||||
(ctkl/add-component (:id component-shape)
|
(ctkl/add-component {:id (:id component-shape)
|
||||||
(:name component-shape)
|
:name (:name component-shape)
|
||||||
""
|
:path ""
|
||||||
shape-id
|
:main-instance-id shape-id
|
||||||
page-id
|
:main-instance-page page-id
|
||||||
component-shapes))))))
|
:shapes component-shapes}))))))
|
||||||
|
|
||||||
(defn sample-instance
|
(defn sample-instance
|
||||||
[file label page-id library component-id]
|
[file label page-id library component-id]
|
||||||
|
|
|
@ -765,9 +765,14 @@
|
||||||
(let [{:keys [on-success on-error]
|
(let [{:keys [on-success on-error]
|
||||||
:or {on-success identity
|
:or {on-success identity
|
||||||
on-error rx/throw}} (meta params)
|
on-error rx/throw}} (meta params)
|
||||||
name (name (gensym (str (tr "dashboard.new-file-prefix") " ")))
|
|
||||||
components-v2 (features/active-feature? state :components-v2)
|
name (name (gensym (str (tr "dashboard.new-file-prefix") " ")))
|
||||||
params (assoc params :name name :components-v2 components-v2)]
|
features (cond-> #{}
|
||||||
|
(features/active-feature? state :components-v2)
|
||||||
|
(conj "components/v2"))
|
||||||
|
params (-> params
|
||||||
|
(assoc :name name)
|
||||||
|
(assoc :features features))]
|
||||||
|
|
||||||
(->> (rp/mutation! :create-file params)
|
(->> (rp/mutation! :create-file params)
|
||||||
(rx/tap on-success)
|
(rx/tap on-success)
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
(ns app.main.data.workspace.persistence
|
(ns app.main.data.workspace.persistence
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.common.logging :as log]
|
[app.common.logging :as log]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.common.pages.changes-spec :as pcs]
|
[app.common.pages.changes-spec :as pcs]
|
||||||
|
@ -137,14 +138,16 @@
|
||||||
(ptk/reify ::persist-changes
|
(ptk/reify ::persist-changes
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [components-v2 (features/active-feature? state :components-v2)
|
(let [features (cond-> #{}
|
||||||
sid (:session-id state)
|
(features/active-feature? state :components-v2)
|
||||||
file (get state :workspace-file)
|
(conj "components/v2"))
|
||||||
params {:id (:id file)
|
sid (:session-id state)
|
||||||
:revn (:revn file)
|
file (get state :workspace-file)
|
||||||
:session-id sid
|
params {:id (:id file)
|
||||||
:changes-with-metadata (into [] changes)
|
:revn (:revn file)
|
||||||
:components-v2 components-v2}]
|
:session-id sid
|
||||||
|
:changes-with-metadata (into [] changes)
|
||||||
|
:features features}]
|
||||||
|
|
||||||
(when (= file-id (:id params))
|
(when (= file-id (:id params))
|
||||||
(->> (rp/mutation :update-file params)
|
(->> (rp/mutation :update-file params)
|
||||||
|
@ -180,15 +183,17 @@
|
||||||
(ptk/reify ::persist-synchronous-changes
|
(ptk/reify ::persist-synchronous-changes
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [components-v2 (features/active-feature? state :components-v2)
|
(let [features (cond-> #{}
|
||||||
sid (:session-id state)
|
(features/active-feature? state :components-v2)
|
||||||
file (get-in state [:workspace-libraries file-id])
|
(conj "components/v2"))
|
||||||
|
sid (:session-id state)
|
||||||
|
file (dm/get-in state [:workspace-libraries file-id])
|
||||||
|
|
||||||
params {:id (:id file)
|
params {:id (:id file)
|
||||||
:revn (:revn file)
|
:revn (:revn file)
|
||||||
:session-id sid
|
:session-id sid
|
||||||
:changes changes
|
:changes changes
|
||||||
:components-v2 components-v2}]
|
:features features}]
|
||||||
|
|
||||||
(when (:id params)
|
(when (:id params)
|
||||||
(->> (rp/mutation :update-file params)
|
(->> (rp/mutation :update-file params)
|
||||||
|
@ -220,6 +225,9 @@
|
||||||
(ptk/reify ::changes-persisted
|
(ptk/reify ::changes-persisted
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
|
;; NOTE: we don't set the file features context here because
|
||||||
|
;; there are no useful context for code that need to be executed
|
||||||
|
;; on the frontend side
|
||||||
(let [changes (group-by :page-id changes)]
|
(let [changes (group-by :page-id changes)]
|
||||||
(if (= file-id (:current-file-id state))
|
(if (= file-id (:current-file-id state))
|
||||||
(-> state
|
(-> state
|
||||||
|
@ -238,7 +246,6 @@
|
||||||
(update-in [:workspace-libraries file-id :data]
|
(update-in [:workspace-libraries file-id :data]
|
||||||
cp/process-changes changes)))))))
|
cp/process-changes changes)))))))
|
||||||
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Data Fetching & Uploading
|
;; Data Fetching & Uploading
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
@ -278,8 +285,11 @@
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [share-id (-> state :viewer-local :share-id)
|
(let [share-id (-> state :viewer-local :share-id)
|
||||||
components-v2 (features/active-feature? state :components-v2)]
|
features (cond-> #{}
|
||||||
(->> (rx/zip (rp/query! :file-raw {:id file-id :components-v2 components-v2})
|
(features/active-feature? state :components-v2)
|
||||||
|
(conj "components/v2"))]
|
||||||
|
|
||||||
|
(->> (rx/zip (rp/query! :file-raw {:id file-id :features features})
|
||||||
(rp/query! :team-users {:file-id file-id})
|
(rp/query! :team-users {:file-id file-id})
|
||||||
(rp/query! :project {:id project-id})
|
(rp/query! :project {:id project-id})
|
||||||
(rp/query! :file-libraries {:file-id file-id})
|
(rp/query! :file-libraries {:file-id file-id})
|
||||||
|
|
|
@ -72,4 +72,3 @@
|
||||||
(doseq [f features-list]
|
(doseq [f features-list]
|
||||||
(when (not= f :components-v2)
|
(when (not= f :components-v2)
|
||||||
(toggle-feature! f)))))
|
(toggle-feature! f)))))
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue