diff --git a/CHANGES.md b/CHANGES.md
index 2abf70c86..d0525be02 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -65,6 +65,7 @@
- Fix available size of resize handler [Taiga #10639](https://tree.taiga.io/project/penpot/issue/10639)
- Internal error when install a plugin by penpothub - Try plugin [Taiga #10542](https://tree.taiga.io/project/penpot/issue/10542)
- Add character limitation to asset inputs [Taiga #10669](https://tree.taiga.io/project/penpot/issue/10669)
+- Fix Storybook link 'list of all available icons' wrong path [Taiga #10705](https://tree.taiga.io/project/penpot/issue/10705)
## 2.5.4
diff --git a/backend/src/app/rpc/commands/auth.clj b/backend/src/app/rpc/commands/auth.clj
index 17557bf95..9307ebab8 100644
--- a/backend/src/app/rpc/commands/auth.clj
+++ b/backend/src/app/rpc/commands/auth.clj
@@ -55,7 +55,7 @@
(contains? cf/flags :login-with-password))
(ex/raise :type :restriction
:code :login-disabled
- :hint "login is disabled in this instance"))
+ :hint "login is disabled"))
(letfn [(check-password [cfg profile password]
(if (= (:password profile) "!")
@@ -79,7 +79,8 @@
:code :wrong-credentials))
(when (:is-blocked profile)
(ex/raise :type :restriction
- :code :profile-blocked))
+ :code :profile-blocked
+ :hint "profile is marked as blocked"))
(when-not (check-password cfg profile password)
(ex/raise :type :validation
:code :wrong-credentials))
@@ -183,11 +184,11 @@
(defn- validate-register-attempt!
[cfg params]
- (when (or
- (not (contains? cf/flags :registration))
- (not (contains? cf/flags :login-with-password)))
+ (when (or (not (contains? cf/flags :registration))
+ (not (contains? cf/flags :login-with-password)))
(ex/raise :type :restriction
- :code :registration-disabled))
+ :code :registration-disabled
+ :hint "registration disabled"))
(when (contains? params :invitation-token)
(let [invitation (tokens/verify (::setup/props cfg)
@@ -201,12 +202,14 @@
(when (and (email.blacklist/enabled? cfg)
(email.blacklist/contains? cfg (:email params)))
(ex/raise :type :restriction
- :code :email-domain-is-not-allowed))
+ :code :email-domain-is-not-allowed
+ :hint "email domain in blacklist"))
(when (and (email.whitelist/enabled? cfg)
(not (email.whitelist/contains? cfg (:email params))))
(ex/raise :type :restriction
- :code :email-domain-is-not-allowed))
+ :code :email-domain-is-not-allowed
+ :hint "email domain not in whitelist"))
;; Perform a basic validation of email & password
(when (= (str/lower (:email params))
@@ -219,13 +222,13 @@
(ex/raise :type :restriction
:code :email-has-permanent-bounces
:email (:email params)
- :hint "looks like the email has bounce reports"))
+ :hint "email has bounce reports"))
(when (eml/has-complaint-reports? cfg (:email params))
(ex/raise :type :restriction
:code :email-has-complaints
:email (:email params)
- :hint "looks like the email has complaint reports")))
+ :hint "email has complaint reports")))
(defn prepare-register
[{:keys [::db/pool] :as cfg} {:keys [email] :as params}]
diff --git a/backend/src/app/rpc/commands/files.clj b/backend/src/app/rpc/commands/files.clj
index f409b0bc7..39b3d6c02 100644
--- a/backend/src/app/rpc/commands/files.clj
+++ b/backend/src/app/rpc/commands/files.clj
@@ -328,7 +328,7 @@
(-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params))
- (cfeat/check-file-features! (:features file) (:features params)))
+ (cfeat/check-file-features! (:features file)))
;; This operation is needed for backward comapatibility with frontends that
;; does not support pointer-map resolution mechanism; this just resolves the
@@ -490,7 +490,7 @@
_ (-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params))
- (cfeat/check-file-features! (:features file) (:features params)))
+ (cfeat/check-file-features! (:features file)))
page (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)]
(let [page-id (or page-id (-> file :data :pages first))
@@ -737,7 +737,7 @@
(-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params))
- (cfeat/check-file-features! (:features file) (:features params)))
+ (cfeat/check-file-features! (:features file)))
(binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
{:name (:name file)
diff --git a/backend/src/app/rpc/commands/files_create.clj b/backend/src/app/rpc/commands/files_create.clj
index 9b5e1598d..0502ff487 100644
--- a/backend/src/app/rpc/commands/files_create.clj
+++ b/backend/src/app/rpc/commands/files_create.clj
@@ -91,9 +91,6 @@
:project-id project-id)
team-id (:id team)
- ;; When we create files, we only need to respect the team
- ;; features, because some features can be enabled
- ;; globally, but the team is still not migrated properly.
features (-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params)))
@@ -107,7 +104,7 @@
params (-> params
(assoc :profile-id profile-id)
- (assoc :features (set/difference features cfeat/frontend-only-features)))]
+ (assoc :features features))]
(quotes/check! cfg {::quotes/id ::quotes/files-per-project
::quotes/team-id team-id
@@ -120,7 +117,7 @@
;; to lost team features updating
;; When newly computed features does not match exactly with
- ;; the features defined on team row, we update it.
+ ;; the features defined on team row, we update it
(when (not= features (:features team))
(let [features (db/create-array conn "text" features)]
(db/update! conn :team
diff --git a/backend/src/app/rpc/commands/files_thumbnails.clj b/backend/src/app/rpc/commands/files_thumbnails.clj
index c3a5cb5d4..f4c5e3d14 100644
--- a/backend/src/app/rpc/commands/files_thumbnails.clj
+++ b/backend/src/app/rpc/commands/files_thumbnails.clj
@@ -212,7 +212,7 @@
(-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params))
- (cfeat/check-file-features! (:features file) (:features params)))
+ (cfeat/check-file-features! (:features file)))
{:file-id file-id
:revn (:revn file)
diff --git a/backend/src/app/rpc/commands/files_update.clj b/backend/src/app/rpc/commands/files_update.clj
index 396f3ffbd..73b4afd3d 100644
--- a/backend/src/app/rpc/commands/files_update.clj
+++ b/backend/src/app/rpc/commands/files_update.clj
@@ -142,7 +142,7 @@
features (-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params))
- (cfeat/check-file-features! (:features file) (:features params)))
+ (cfeat/check-file-features! (:features file)))
changes (if changes-with-metadata
(->> changes-with-metadata (mapcat :changes) vec)
diff --git a/backend/src/app/srepl/main.clj b/backend/src/app/srepl/main.clj
index be5c9e57d..6914ff54d 100644
--- a/backend/src/app/srepl/main.clj
+++ b/backend/src/app/srepl/main.clj
@@ -209,100 +209,116 @@
This method allows send flash notifications to specified target destinations.
The message can be a free text or a preconfigured one.
- The destination can be: all, profile-id, team-id, or a coll of them."
- [{:keys [::mbus/msgbus ::db/pool]} & {:keys [dest code message level]
- :or {code :generic level :info}
- :as params}]
+ The destination can be: all, profile-id, team-id, or a coll of them.
+ It also can be:
+
+ {:email \"some@example.com\"}
+ [[:email \"some@example.com\"], ...]
+
+ Command examples:
+
+ (notify! :dest :all :code :maintenance)
+ (notify! :dest :all :code :upgrade-version)
+ "
+ [& {:keys [dest code message level]
+ :or {code :generic level :info}
+ :as params}]
(when-not (contains? #{:success :error :info :warning} level)
(ex/raise :type :assertion
:code :incorrect-level
:hint (str "level '" level "' not supported")))
- (letfn [(send [dest]
- (l/inf :hint "sending notification" :dest (str dest))
- (let [message {:type :notification
- :code code
- :level level
- :version (:full cf/version)
- :subs-id dest
- :message message}
- message (->> (dissoc params :dest :code :message :level)
- (merge message))]
- (mbus/pub! msgbus
- :topic (str dest)
- :message message)))
+ (let [{:keys [::mbus/msgbus ::db/pool]} main/system
- (resolve-profile [email]
- (some-> (db/get* pool :profile {:email (str/lower email)} {:columns [:id]}) :id vector))
+ send
+ (fn [dest]
+ (l/inf :hint "sending notification" :dest (str dest))
+ (let [message {:type :notification
+ :code code
+ :level level
+ :version (:full cf/version)
+ :subs-id dest
+ :message message}
+ message (->> (dissoc params :dest :code :message :level)
+ (merge message))]
+ (mbus/pub! msgbus
+ :topic dest
+ :message message)))
- (resolve-team [team-id]
- (->> (db/query pool :team-profile-rel
- {:team-id team-id}
- {:columns [:profile-id]})
- (map :profile-id)))
+ resolve-profile
+ (fn [email]
+ (some-> (db/get* pool :profile {:email (str/lower email)} {:columns [:id]}) :id vector))
- (resolve-dest [dest]
- (cond
- (= :all dest)
- [uuid/zero]
+ resolve-team
+ (fn [team-id]
+ (->> (db/query pool :team-profile-rel
+ {:team-id team-id}
+ {:columns [:profile-id]})
+ (map :profile-id)))
- (uuid? dest)
- [dest]
+ resolve-dest
+ (fn resolve-dest [dest]
+ (cond
+ (= :all dest)
+ [uuid/zero]
- (string? dest)
- (some-> dest h/parse-uuid resolve-dest)
+ (uuid? dest)
+ [dest]
- (nil? dest)
- (resolve-dest uuid/zero)
+ (string? dest)
+ (some-> dest h/parse-uuid resolve-dest)
- (map? dest)
- (sequence (comp
- (map vec)
- (mapcat resolve-dest))
- dest)
+ (nil? dest)
+ [uuid/zero]
- (and (vector? dest)
- (every? vector? dest))
- (sequence (comp
- (map vec)
- (mapcat resolve-dest))
- dest)
+ (map? dest)
+ (sequence (comp
+ (map vec)
+ (mapcat resolve-dest))
+ dest)
- (and (vector? dest)
- (keyword? (first dest)))
- (let [[op param] dest]
+ (and (vector? dest)
+ (every? vector? dest))
+ (sequence (comp
+ (map vec)
+ (mapcat resolve-dest))
+ dest)
+
+ (and (vector? dest)
+ (keyword? (first dest)))
+ (let [[op param] dest]
+ (cond
+ (= op :email)
(cond
- (= op :email)
- (cond
- (and (coll? param)
- (every? string? param))
- (sequence (comp
- (keep resolve-profile)
- (mapcat identity))
- param)
+ (and (coll? param)
+ (every? string? param))
+ (sequence (comp
+ (keep resolve-profile)
+ (mapcat identity))
+ param)
- (string? param)
- (resolve-profile param))
+ (string? param)
+ (resolve-profile param))
- (= op :team-id)
- (cond
- (coll? param)
- (sequence (comp
- (mapcat resolve-team)
- (keep h/parse-uuid))
- param)
+ (= op :team-id)
+ (cond
+ (coll? param)
+ (sequence (comp
+ (mapcat resolve-team)
+ (keep h/parse-uuid))
+ param)
- (uuid? param)
- (resolve-team param)
+ (uuid? param)
+ (resolve-team param)
- (string? param)
- (some-> param h/parse-uuid resolve-team))
+ (string? param)
+ (some-> param h/parse-uuid resolve-team))
- (= op :profile-id)
- (if (coll? param)
- (sequence (keep h/parse-uuid) param)
- (resolve-dest param))))))]
+ (= op :profile-id)
+ (if (coll? param)
+ (sequence (keep h/parse-uuid) param)
+ (resolve-dest param))))))]
(->> (resolve-dest dest)
(filter some?)
diff --git a/common/src/app/common/features.cljc b/common/src/app/common/features.cljc
index e0fb201e4..8f53a9257 100644
--- a/common/src/app/common/features.cljc
+++ b/common/src/app/common/features.cljc
@@ -85,12 +85,11 @@
;; be applied (per example backend can operate in both modes with or
;; without migration applied)
(def no-migration-features
- (-> #{"fdata/objects-map"
- "fdata/pointer-map"
- "layout/grid"
+ (-> #{"layout/grid"
"fdata/shape-data-type"
"design-tokens/v1"}
- (into frontend-only-features)))
+ (into frontend-only-features)
+ (into backend-only-features)))
(sm/register!
^{::sm/type ::features}
@@ -156,7 +155,6 @@
team-features (into #{} xf-remove-ephimeral (:features team))]
(-> enabled-features
(set/intersection no-migration-features)
- (set/difference frontend-only-features)
(set/union team-features))))
(defn check-client-features!
@@ -165,6 +163,8 @@
frontend client"
[enabled-features client-features]
(when (set? client-features)
+ ;; Check if client declares support for features enabled on
+ ;; backend side
(let [not-supported (-> enabled-features
(set/difference client-features)
(set/difference frontend-only-features)
@@ -174,14 +174,6 @@
:code :feature-not-supported
:feature (first not-supported)
:hint (str/ffmt "client declares no support for '%' features"
- (str/join "," not-supported)))))
-
- (let [not-supported (set/difference client-features supported-features)]
- (when (seq not-supported)
- (ex/raise :type :restriction
- :code :feature-not-supported
- :feature (first not-supported)
- :hint (str/ffmt "backend does not support '%' features requested by client"
(str/join "," not-supported))))))
enabled-features)
@@ -192,63 +184,55 @@
supported by the current backend"
[enabled-features]
(let [not-supported (set/difference enabled-features supported-features)]
- (when (seq not-supported)
+ (when-let [not-supported (first not-supported)]
(ex/raise :type :restriction
:code :feature-not-supported
- :feature (first not-supported)
- :hint (str/ffmt "features '%' not supported"
- (str/join "," not-supported)))))
- enabled-features)
+ :feature not-supported
+ :hint (str/ffmt "feature '%' not supported on this backend" not-supported)))
+ enabled-features))
(defn check-file-features!
"Function used for check feature compability between currently
enabled features set on backend with the provided featured set by
the penpot file"
- ([enabled-features file-features]
- (check-file-features! enabled-features file-features #{}))
- ([enabled-features file-features client-features]
- (let [file-features (into #{} xf-remove-ephimeral file-features)
- ;; We should ignore all features that does not match with the
- ;; `no-migration-features` set because we can't enable them
- ;; as-is, because they probably need migrations
- client-features (set/intersection client-features no-migration-features)]
- (let [not-supported (-> enabled-features
- (set/union client-features)
- (set/difference file-features)
- ;; NOTE: we don't want to raise a feature-mismatch
- ;; exception for features which don't require an
- ;; explicit file migration process or has no real
- ;; effect on file data structure
- (set/difference no-migration-features))]
- (when (seq not-supported)
- (ex/raise :type :restriction
- :code :file-feature-mismatch
- :feature (first not-supported)
- :hint (str/ffmt "enabled features '%' not present in file (missing migration)"
- (str/join "," not-supported)))))
+ [enabled-features file-features]
+ (let [file-features (into #{} xf-remove-ephimeral file-features)
+ not-supported (-> enabled-features
+ (set/difference file-features)
+ ;; NOTE: we don't want to raise a feature-mismatch
+ ;; exception for features which don't require an
+ ;; explicit file migration process or has no real
+ ;; effect on file data structure
+ (set/difference no-migration-features))]
- (check-supported-features! file-features)
+ (when-let [not-supported (first not-supported)]
+ (ex/raise :type :restriction
+ :code :file-feature-mismatch
+ :feature not-supported
+ :hint (str/ffmt "enabled feature '%' not present in file (missing migration)"
+ not-supported)))
- (let [not-supported (-> file-features
- (set/difference enabled-features)
- (set/difference client-features)
- (set/difference backend-only-features)
- (set/difference frontend-only-features))]
+ (check-supported-features! file-features)
- (when (seq not-supported)
- (ex/raise :type :restriction
- :code :file-feature-mismatch
- :feature (first not-supported)
- :hint (str/ffmt "file features '%' not enabled"
- (str/join "," not-supported)))))
+ ;; Components v1 is deprecated
+ (when-not (contains? file-features "components/v2")
+ (ex/raise :type :restriction
+ :code :file-in-components-v1
+ :hint "components v1 is deprecated"))
- ;; Components v1 is deprecated
- (when-not (contains? file-features "components/v2")
- (ex/raise :type :restriction
- :code :file-in-components-v1
- :hint "components v1 is deprecated")))
+ (let [not-supported (-> file-features
+ (set/difference enabled-features)
+ (set/difference backend-only-features)
+ (set/difference frontend-only-features))]
- enabled-features))
+ ;; Check if file has a feature but that feature is not enabled
+ (when-let [not-supported (first not-supported)]
+ (ex/raise :type :restriction
+ :code :file-feature-mismatch
+ :feature not-supported
+ :hint (str/ffmt "file feature '%' not enabled" not-supported))))
+
+ enabled-features))
(defn check-teams-compatibility!
[{source-features :features} {destination-features :features}]
diff --git a/docs/user-guide/introduction/shortcuts.njk b/docs/user-guide/introduction/shortcuts.njk
index 71db0cbb6..0949dc252 100644
--- a/docs/user-guide/introduction/shortcuts.njk
+++ b/docs/user-guide/introduction/shortcuts.njk
@@ -848,7 +848,7 @@ title: Shortcuts
View mode
-The View mode is the area to present and share designs and play the proptotype interactions. More about the View mode.
+The View mode is the area to present and share designs and play the prototype interactions. More about the View mode.
Generic
diff --git a/frontend/src/app/main/data/common.cljs b/frontend/src/app/main/data/common.cljs
index b7ebf2716..b202dfcdb 100644
--- a/frontend/src/app/main/data/common.cljs
+++ b/frontend/src/app/main/data/common.cljs
@@ -72,7 +72,7 @@
(st/emit! (ntf/hide)))
(defn handle-notification
- [{:keys [message code level] :as params}]
+ [{:keys [message code] :as params}]
(ptk/reify ::show-notification
ptk/WatchEvent
(watch [_ _ _]
@@ -80,9 +80,6 @@
:upgrade-version
(rx/of (ntf/dialog
:content (tr "notifications.by-code.upgrade-version")
- :controls :inline-actions
- :type :inline
- :level level
:accept {:label (tr "labels.refresh")
:callback force-reload!}
:tag :notification))
@@ -90,16 +87,14 @@
:maintenance
(rx/of (ntf/dialog
:content (tr "notifications.by-code.maintenance")
- :controls :inline-actions
- :type level
:accept {:label (tr "labels.accept")
:callback hide-notifications!}
:tag :notification))
(rx/of (ntf/dialog
:content message
- :controls :close
- :type level
+ :accept {:label (tr "labels.close")
+ :callback hide-notifications!}
:tag :notification))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
diff --git a/frontend/src/app/main/data/notifications.cljs b/frontend/src/app/main/data/notifications.cljs
index 37caee0ba..5c0082bec 100644
--- a/frontend/src/app/main/data/notifications.cljs
+++ b/frontend/src/app/main/data/notifications.cljs
@@ -19,7 +19,7 @@
(def ^:private schema:notification
[:map {:title "Notification"}
- [:level [::sm/one-of #{:success :error :info :warning}]]
+ [:level {:optional true} [::sm/one-of #{:success :error :info :warning}]]
[:status {:optional true}
[::sm/one-of #{:visible :hide}]]
[:position {:optional true}
@@ -129,15 +129,11 @@
:timeout timeout})))
(defn dialog
- [& {:keys [content controls actions accept cancel position tag level links]
- :or {controls :none position :floating level :info}}]
+ [& {:keys [content accept cancel tag links]}]
(show (d/without-nils
{:content content
- :level level
- :links links
- :position position
- :controls controls
- :actions actions
+ :type :inline
:accept accept
:cancel cancel
+ :links links
:tag tag})))
diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs
index 5d1a96665..727da3790 100644
--- a/frontend/src/app/main/data/workspace.cljs
+++ b/frontend/src/app/main/data/workspace.cljs
@@ -305,7 +305,8 @@
(rx/take-until stopper-s))))))
(defn initialize-workspace
- [file-id]
+ [team-id file-id]
+ (assert (uuid? team-id) "expected valud uuid for `team-id`")
(assert (uuid? file-id) "expected valud uuid for `file-id`")
(ptk/reify ::initialize-workspace
@@ -321,8 +322,7 @@
(watch [_ state stream]
(let [stoper-s (rx/filter (ptk/type? ::finalize-workspace) stream)
rparams (rt/get-params state)
- team-id (get state :current-team-id)
- features (get state :features)
+ features (features/get-enabled-features state team-id)
render-wasm? (contains? features "render-wasm/v1")]
(log/debug :hint "initialize-workspace"
@@ -417,7 +417,7 @@
(unchecked-set ug/global "name" name)))))
(defn finalize-workspace
- [file-id]
+ [_team-id file-id]
(ptk/reify ::finalize-workspace
ptk/UpdateEvent
(update [_ state]
@@ -450,8 +450,9 @@
(ptk/reify ::reload-current-file
ptk/WatchEvent
(watch [_ state _]
- (let [file-id (:current-file-id state)]
- (rx/of (initialize-workspace file-id))))))
+ (let [file-id (:current-file-id state)
+ team-id (:current-team-id state)]
+ (rx/of (initialize-workspace team-id file-id))))))
;; Make this event callable through dynamic resolution
(defmethod ptk/resolve ::reload-current-file [_ _] (reload-current-file))
@@ -492,18 +493,25 @@
(defn initialize-page
[file-id page-id]
(assert (uuid? file-id) "expected valid uuid for `file-id`")
+ (assert (uuid? page-id) "expected valid uuid for `page-id`")
(ptk/reify ::initialize-page
ptk/WatchEvent
(watch [_ state _]
(if-let [page (dsh/lookup-page state file-id page-id)]
- (rx/concat (rx/of (initialize-page* file-id page-id page)
- (dwth/watch-state-changes file-id page-id)
- (dwl/watch-component-changes))
- (let [profile (:profile state)
- props (get profile :props)]
- (when (not (:workspace-visited props))
- (rx/of (select-frame-tool file-id page-id)))))
+ (rx/concat
+ (rx/of (initialize-page* file-id page-id page)
+ (dwth/watch-state-changes file-id page-id)
+ (dwl/watch-component-changes))
+ (let [profile (:profile state)
+ props (get profile :props)]
+ (when (not (:workspace-visited props))
+ (rx/of (select-frame-tool file-id page-id)))))
+
+ ;; NOTE: this redirect is necessary for cases where user
+ ;; explicitly passes an non-existing page-id on the url
+ ;; params, so on check it we can detect that there are no data
+ ;; for the page and redirect user to an existing page
(rx/of (dcm/go-to-workspace :file-id file-id ::rt/replace true))))))
(defn finalize-page
diff --git a/frontend/src/app/main/data/workspace/notifications.cljs b/frontend/src/app/main/data/workspace/notifications.cljs
index e62985f70..dc46e6e35 100644
--- a/frontend/src/app/main/data/workspace/notifications.cljs
+++ b/frontend/src/app/main/data/workspace/notifications.cljs
@@ -65,7 +65,6 @@
(->> (rx/from initmsg)
(rx/map dws/send))
-
;; Subscribe to notifications of the subscription
(->> stream
(rx/filter (ptk/type? ::dws/message))
diff --git a/frontend/src/app/main/data/workspace/versions.cljs b/frontend/src/app/main/data/workspace/versions.cljs
index ee710883a..f2ae3bb3a 100644
--- a/frontend/src/app/main/data/workspace/versions.cljs
+++ b/frontend/src/app/main/data/workspace/versions.cljs
@@ -10,6 +10,7 @@
[app.common.data.macros :as dm]
[app.common.schema :as sm]
[app.main.data.event :as ev]
+ [app.main.data.helpers :as dsh]
[app.main.data.persistence :as dwp]
[app.main.data.workspace :as dw]
[app.main.data.workspace.thumbnails :as th]
@@ -97,7 +98,8 @@
(ptk/reify ::restore-version
ptk/WatchEvent
(watch [_ state _]
- (let [file-id (:current-file-id state)]
+ (let [file-id (:current-file-id state)
+ team-id (:current-team-id state)]
(rx/concat
(rx/of ::dwp/force-persist
(dw/remove-layout-flag :document-history))
@@ -106,7 +108,7 @@
(rx/take 1)
(rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id}))
(rx/tap #(th/clear-queue!))
- (rx/map #(dw/initialize-workspace file-id)))
+ (rx/map #(dw/initialize-workspace team-id file-id)))
(case origin
:version
(rx/of (ptk/event ::ev/event {::ev/name "restore-pin-version"}))
@@ -200,21 +202,23 @@
(ptk/reify ::restore-version-from-plugins
ptk/WatchEvent
- (watch [_ _ _]
- (rx/concat
- (rx/of (ptk/event ::ev/event {::ev/name "restore-version-plugin"})
- ::dwp/force-persist)
+ (watch [_ state _]
+ (let [file (dsh/lookup-file state file-id)
+ team-id (or (:team-id file) (:current-file-id state))]
+ (rx/concat
+ (rx/of (ptk/event ::ev/event {::ev/name "restore-version-plugin"})
+ ::dwp/force-persist)
- ;; FIXME: we should abstract this
- (->> (rx/from-atom refs/persistence-state {:emit-current-value? true})
- (rx/filter #(or (nil? %) (= :saved %)))
- (rx/take 1)
- (rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id}))
- (rx/map #(dw/initialize-workspace file-id)))
+ ;; FIXME: we should abstract this
+ (->> (rx/from-atom refs/persistence-state {:emit-current-value? true})
+ (rx/filter #(or (nil? %) (= :saved %)))
+ (rx/take 1)
+ (rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id}))
+ (rx/map #(dw/initialize-workspace team-id file-id)))
- (->> (rx/of 1)
- (rx/tap resolve)
- (rx/ignore))))))
+ (->> (rx/of 1)
+ (rx/tap resolve)
+ (rx/ignore)))))))
diff --git a/frontend/src/app/main/features.cljs b/frontend/src/app/main/features.cljs
index 49612ff48..146806c26 100644
--- a/frontend/src/app/main/features.cljs
+++ b/frontend/src/app/main/features.cljs
@@ -8,6 +8,7 @@
"A thin, frontend centric abstraction layer and collection of
helpers for `app.common.features` namespace."
(:require
+ [app.common.data.macros :as dm]
[app.common.features :as cfeat]
[app.common.logging :as log]
[app.config :as cf]
@@ -24,6 +25,15 @@
(def global-enabled-features
(cfeat/get-enabled-features cf/flags))
+(defn get-enabled-features
+ "An explicit lookup of enabled features for the current team"
+ [state team-id]
+ (let [team (dm/get-in state [:teams team-id])]
+ (-> global-enabled-features
+ (set/union (get state :features-runtime #{}))
+ (set/difference cfeat/no-migration-features)
+ (set/union (get team :features)))))
+
(defn active-feature?
"Given a state and feature, check if feature is enabled."
[state feature]
diff --git a/frontend/src/app/main/ui.cljs b/frontend/src/app/main/ui.cljs
index f75123239..f67fb755a 100644
--- a/frontend/src/app/main/ui.cljs
+++ b/frontend/src/app/main/ui.cljs
@@ -370,6 +370,6 @@
(if edata
[:> static/exception-page* {:data edata :route route}]
[:> error-boundary* {:fallback static/internal-error*}
- [:& notifications/current-notification]
+ [:> notifications/current-notification*]
(when route
[:> page* {:route route :profile profile}])])]]))
diff --git a/frontend/src/app/main/ui/ds/foundations/assets/icon.mdx b/frontend/src/app/main/ui/ds/foundations/assets/icon.mdx
index c1184acd5..2f2806c49 100644
--- a/frontend/src/app/main/ui/ds/foundations/assets/icon.mdx
+++ b/frontend/src/app/main/ui/ds/foundations/assets/icon.mdx
@@ -5,7 +5,7 @@ import * as IconStories from "./icon.stories"
# Iconography
-See the [list of all available icons](?path=/story/foundations-icons--all-icons).
+See the [list of all available icons](?path=/story/foundations-assets-icon--all).
## Variants
diff --git a/frontend/src/app/main/ui/ds/notifications/actionable.cljs b/frontend/src/app/main/ui/ds/notifications/actionable.cljs
index 9284b5387..550d2a297 100644
--- a/frontend/src/app/main/ui/ds/notifications/actionable.cljs
+++ b/frontend/src/app/main/ui/ds/notifications/actionable.cljs
@@ -17,10 +17,10 @@
[:class {:optional true} :string]
[:variant {:optional true}
[:maybe [:enum "default" "error"]]]
- [:accept-label {:optional true} :string]
- [:cancel-label {:optional true} :string]
- [:on-accept {:optional true} [:fn fn?]]
- [:on-cancel {:optional true} [:fn fn?]]])
+ [:accept-label {:optional true} [:maybe :string]]
+ [:cancel-label {:optional true} [:maybe :string]]
+ [:on-accept {:optional true} [:maybe [:fn fn?]]]
+ [:on-cancel {:optional true} [:maybe [:fn fn?]]]])
(mf/defc actionable*
{::mf/schema schema:actionable}
@@ -45,9 +45,13 @@
[:> :aside props
[:div {:class (stl/css :notification-message)} children]
- [:> button* {:variant "secondary"
- :on-click on-cancel}
- cancel-label]
- [:> button* {:variant (if (= variant "default") "primary" "destructive")
- :on-click on-accept}
- accept-label]]))
+
+ (when cancel-label
+ [:> button* {:variant "secondary"
+ :on-click on-cancel}
+ cancel-label])
+
+ (when accept-label
+ [:> button* {:variant (if (= variant "default") "primary" "destructive")
+ :on-click on-accept}
+ accept-label])]))
diff --git a/frontend/src/app/main/ui/notifications.cljs b/frontend/src/app/main/ui/notifications.cljs
index 5f19792d6..e78ac7494 100644
--- a/frontend/src/app/main/ui/notifications.cljs
+++ b/frontend/src/app/main/ui/notifications.cljs
@@ -14,10 +14,10 @@
[okulary.core :as l]
[rumext.v2 :as mf]))
-(def ref:notification
+(def ^:private ref:notification
(l/derived :notification st/state))
-(mf/defc current-notification
+(mf/defc current-notification*
[]
(let [notification (mf/deref ref:notification)
on-close (mf/use-fn #(st/emit! (ntf/hide)))
diff --git a/frontend/src/app/main/ui/workspace.cljs b/frontend/src/app/main/ui/workspace.cljs
index 71adaf819..0dde3c0a4 100644
--- a/frontend/src/app/main/ui/workspace.cljs
+++ b/frontend/src/app/main/ui/workspace.cljs
@@ -9,6 +9,7 @@
(:require
[app.common.data.macros :as dm]
[app.main.data.common :as dcm]
+ [app.main.data.helpers :as dsh]
[app.main.data.persistence :as dps]
[app.main.data.plugins :as dpl]
[app.main.data.workspace :as dw]
@@ -45,9 +46,10 @@
(mf/defc workspace-content*
{::mf/private true}
[{:keys [file layout page wglobal]}]
+
(let [palete-size (mf/use-state nil)
selected (mf/deref refs/selected-shapes)
- page-id (:id page)
+ page-id (get page :id)
{:keys [vport] :as wlocal} (mf/deref refs/workspace-local)
{:keys [options-mode]} wglobal
@@ -120,10 +122,46 @@
:overlay true
:file-loading true}])
+(defn- make-team-ref
+ [team-id]
+ (l/derived (fn [state]
+ (let [teams (get state :teams)]
+ (get teams team-id)))
+ st/state))
+
+(defn- make-file-ref
+ [file-id]
+ (l/derived (fn [state]
+ ;; NOTE: for ensure ordering of execution, we need to
+ ;; wait the file initialization completly success until
+ ;; mark this file availablea and unlock the rendering
+ ;; of the following components
+ (when (= (get state :current-file-id) file-id)
+ (let [files (get state :files)
+ file (get files file-id)]
+ (-> file
+ (dissoc :data)
+ (assoc ::has-data (contains? file :data))))))
+ st/state))
+
+(defn- make-page-ref
+ [file-id page-id]
+ (l/derived (fn [state]
+ (let [current-page-id (get state :current-page-id)]
+ ;; NOTE: for ensure ordering of execution, we need to
+ ;; wait the page initialization completly success until
+ ;; mark this file availablea and unlock the rendering
+ ;; of the following components
+ (when (= current-page-id page-id)
+ (dsh/lookup-page state file-id page-id))))
+ st/state))
+
(mf/defc workspace-page*
{::mf/private true}
[{:keys [page-id file-id file layout wglobal]}]
- (let [page (mf/deref refs/workspace-page)]
+ (let [page-ref (mf/with-memo [file-id page-id]
+ (make-page-ref file-id page-id))
+ page (mf/deref page-ref)]
(mf/with-effect []
(let [focus-out #(st/emit! (dw/workspace-focus-lost))
@@ -133,8 +171,7 @@
(mf/with-effect [file-id page-id]
(st/emit! (dw/initialize-page file-id page-id))
(fn []
- (when page-id
- (st/emit! (dw/finalize-page file-id page-id)))))
+ (st/emit! (dw/finalize-page file-id page-id))))
(if (some? page)
[:> workspace-content* {:file file
@@ -143,18 +180,9 @@
:layout layout}]
[:> workspace-loader*])))
-(def ^:private ref:file-without-data
- (l/derived (fn [file]
- (-> file
- (dissoc :data)
- (assoc ::has-data (contains? file :data))))
- refs/file
- =))
-
(mf/defc workspace*
- {::mf/props :obj
- ::mf/wrap [mf/memo]}
- [{:keys [project-id file-id page-id layout-name]}]
+ {::mf/wrap [mf/memo]}
+ [{:keys [team-id project-id file-id page-id layout-name]}]
(let [file-id (hooks/use-equal-memo file-id)
page-id (hooks/use-equal-memo page-id)
@@ -162,8 +190,15 @@
layout (mf/deref refs/workspace-layout)
wglobal (mf/deref refs/workspace-global)
- team (mf/deref refs/team)
- file (mf/deref ref:file-without-data)
+ team-ref (mf/with-memo [team-id]
+ (make-team-ref team-id))
+ file-ref (mf/with-memo [file-id]
+ (make-file-ref file-id))
+
+ team (mf/deref team-ref)
+ file (mf/deref file-ref)
+
+ file-loaded? (get file ::has-data)
file-name (:name file)
permissions (:permissions team)
@@ -187,14 +222,14 @@
(when file-name
(dom/set-html-title (tr "title.workspace" file-name))))
- (mf/with-effect [file-id]
- (st/emit! (dw/initialize-workspace file-id))
+ (mf/with-effect [team-id file-id]
+ (st/emit! (dw/initialize-workspace team-id file-id))
(fn []
(st/emit! ::dps/force-persist
- (dw/finalize-workspace file-id))))
+ (dw/finalize-workspace team-id file-id))))
- (mf/with-effect [file page-id]
- (when-not page-id
+ (mf/with-effect [file-id page-id file-loaded?]
+ (when (and file-loaded? (not page-id))
(st/emit! (dcm/go-to-workspace :file-id file-id ::rt/replace true))))
[:> (mf/provider ctx/current-project-id) {:value project-id}
@@ -207,8 +242,7 @@
:style {:background-color background-color
:touch-action "none"}}
[:> context-menu*]
-
- (if (::has-data file)
+ (if (and file-loaded? page-id)
[:> workspace-page*
{:page-id page-id
:file-id file-id
diff --git a/frontend/src/app/main/worker.cljs b/frontend/src/app/main/worker.cljs
index 055e423a3..d29a7ce8e 100644
--- a/frontend/src/app/main/worker.cljs
+++ b/frontend/src/app/main/worker.cljs
@@ -17,8 +17,10 @@
[]
(let [worker (uw/init cf/worker-uri err/on-error)]
(uw/ask! worker {:cmd :configure
- :key :public-uri
- :val cf/public-uri})
+ :config {:public-uri cf/public-uri
+ :build-data cf/build-date
+ :version cf/version}})
+
(set! instance worker)))
(defn ask!
diff --git a/frontend/src/app/util/websocket.cljs b/frontend/src/app/util/websocket.cljs
index 016eb9c0a..5c82ce194 100644
--- a/frontend/src/app/util/websocket.cljs
+++ b/frontend/src/app/util/websocket.cljs
@@ -104,7 +104,9 @@
(defn send!
[ws msg]
- (-send ws (t/encode-str msg)))
+ (if *assert*
+ (-send ws (t/encode-str msg {:type :json-verbose}))
+ (-send ws (t/encode-str msg))))
(defn close!
[ws]
diff --git a/frontend/src/app/worker.cljs b/frontend/src/app/worker.cljs
index 213d20ae2..b07cd1fdc 100644
--- a/frontend/src/app/worker.cljs
+++ b/frontend/src/app/worker.cljs
@@ -135,15 +135,15 @@
(rx/debounce 1)
(rx/subs! (fn [[messages dropped last]]
- ;; Send back the dropped messages replies
+ ;; Send back the dropped messages replies
(doseq [msg dropped]
(drop-message msg))
- ;; Process the message
+ ;; Process the message
(doseq [msg (vals messages)]
(handle-message msg))
- ;; After process the buffer we send a clear
+ ;; After process the buffer we send a clear
(when-not (= last ::clear)
(rx/push! buffer ::clear)))))))
diff --git a/frontend/src/app/worker/impl.cljs b/frontend/src/app/worker/impl.cljs
index 38beeb28d..207073f7b 100644
--- a/frontend/src/app/worker/impl.cljs
+++ b/frontend/src/app/worker/impl.cljs
@@ -50,8 +50,16 @@
(handler (assoc message :cmd :snaps/update-page-index))))
(defmethod handler :configure
- [{:keys [key val]}]
- (log/info :hint "configure worker" :key key :val (dm/str val))
- (case key
- :public-uri
- (set! cf/public-uri val)))
+ [{:keys [config]}]
+ (log/info :hint "configure worker" :keys (keys config))
+
+ (when-let [public-uri (get config :public-uri)]
+ (set! cf/public-uri public-uri))
+
+ (when-let [version (get config :version)]
+ (set! cf/version version))
+
+ (when-let [build-date (get config :build-data)]
+ (set! cf/build-date build-date))
+
+ nil)