Merge tag '2.6.0'

This commit is contained in:
Andrey Antukh 2025-04-09 10:58:39 +02:00
commit faead09174
43 changed files with 489 additions and 479 deletions

View File

@ -1,6 +1,6 @@
# CHANGELOG # CHANGELOG
## 2.6.0 (Unreleased) ## 2.6.0
### :rocket: Epics and highlights ### :rocket: Epics and highlights
@ -25,6 +25,7 @@
- [DESIGN TOKENS] Import and export tokens from a JSON file. - [DESIGN TOKENS] Import and export tokens from a JSON file.
- [DESIGN TOKENS] Apply Themes and Sets at document level. - [DESIGN TOKENS] Apply Themes and Sets at document level.
- Add more descriptive tooltip to boards for first time users [Taiga #9426](https://tree.taiga.io/project/penpot/us/9426) - Add more descriptive tooltip to boards for first time users [Taiga #9426](https://tree.taiga.io/project/penpot/us/9426)
- First State of a Project Changes Consolidation [Taia #10605](https://tree.taiga.io/project/penpot/us/10605)
### :bug: Bugs fixed ### :bug: Bugs fixed
@ -47,6 +48,7 @@
- Fix available size of resize handler [Taiga #10639](https://tree.taiga.io/project/penpot/issue/10639) - 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) - 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) - 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 ## 2.5.4

View File

@ -4,7 +4,7 @@
"license": "MPL-2.0", "license": "MPL-2.0",
"author": "Kaleidos INC", "author": "Kaleidos INC",
"private": true, "private": true,
"packageManager": "yarn@4.6.0+sha512.5383cc12567a95f1d668fbe762dfe0075c595b4bfff433be478dbbe24e05251a8e8c3eb992a986667c1d53b6c3a9c85b8398c35a960587fbd9fa3a0915406728", "packageManager": "yarn@4.8.1+sha512.bc946f2a022d7a1a38adfc15b36a66a3807a67629789496c3714dd1703d2e6c6b1c69ff9ec3b43141ac7a1dd853b7685638eb0074300386a59c18df351ef8ff6",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/penpot/penpot" "url": "https://github.com/penpot/penpot"

View File

@ -25,7 +25,6 @@
(let [claims (-> {} (let [claims (-> {}
(into (::session/token-claims request)) (into (::session/token-claims request))
(into (::actoken/token-claims request)))] (into (::actoken/token-claims request)))]
{:request/path (:path request) {:request/path (:path request)
:request/method (:method request) :request/method (:method request)
:request/params (:params request) :request/params (:params request)

View File

@ -55,7 +55,7 @@
(contains? cf/flags :login-with-password)) (contains? cf/flags :login-with-password))
(ex/raise :type :restriction (ex/raise :type :restriction
:code :login-disabled :code :login-disabled
:hint "login is disabled in this instance")) :hint "login is disabled"))
(letfn [(check-password [cfg profile password] (letfn [(check-password [cfg profile password]
(if (= (:password profile) "!") (if (= (:password profile) "!")
@ -79,7 +79,8 @@
:code :wrong-credentials)) :code :wrong-credentials))
(when (:is-blocked profile) (when (:is-blocked profile)
(ex/raise :type :restriction (ex/raise :type :restriction
:code :profile-blocked)) :code :profile-blocked
:hint "profile is marked as blocked"))
(when-not (check-password cfg profile password) (when-not (check-password cfg profile password)
(ex/raise :type :validation (ex/raise :type :validation
:code :wrong-credentials)) :code :wrong-credentials))
@ -183,11 +184,11 @@
(defn- validate-register-attempt! (defn- validate-register-attempt!
[cfg params] [cfg params]
(when (or (when (or (not (contains? cf/flags :registration))
(not (contains? cf/flags :registration)) (not (contains? cf/flags :login-with-password)))
(not (contains? cf/flags :login-with-password)))
(ex/raise :type :restriction (ex/raise :type :restriction
:code :registration-disabled)) :code :registration-disabled
:hint "registration disabled"))
(when (contains? params :invitation-token) (when (contains? params :invitation-token)
(let [invitation (tokens/verify (::setup/props cfg) (let [invitation (tokens/verify (::setup/props cfg)
@ -201,12 +202,14 @@
(when (and (email.blacklist/enabled? cfg) (when (and (email.blacklist/enabled? cfg)
(email.blacklist/contains? cfg (:email params))) (email.blacklist/contains? cfg (:email params)))
(ex/raise :type :restriction (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) (when (and (email.whitelist/enabled? cfg)
(not (email.whitelist/contains? cfg (:email params)))) (not (email.whitelist/contains? cfg (:email params))))
(ex/raise :type :restriction (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 ;; Perform a basic validation of email & password
(when (= (str/lower (:email params)) (when (= (str/lower (:email params))
@ -219,13 +222,13 @@
(ex/raise :type :restriction (ex/raise :type :restriction
:code :email-has-permanent-bounces :code :email-has-permanent-bounces
:email (:email params) :email (:email params)
:hint "looks like the email has bounce reports")) :hint "email has bounce reports"))
(when (eml/has-complaint-reports? cfg (:email params)) (when (eml/has-complaint-reports? cfg (:email params))
(ex/raise :type :restriction (ex/raise :type :restriction
:code :email-has-complaints :code :email-has-complaints
:email (:email params) :email (:email params)
:hint "looks like the email has complaint reports"))) :hint "email has complaint reports")))
(defn prepare-register (defn prepare-register
[{:keys [::db/pool] :as cfg} {:keys [email] :as params}] [{:keys [::db/pool] :as cfg} {:keys [email] :as params}]

View File

@ -328,7 +328,7 @@
(-> (cfeat/get-team-enabled-features cf/flags team) (-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params)) (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 ;; This operation is needed for backward comapatibility with frontends that
;; does not support pointer-map resolution mechanism; this just resolves the ;; does not support pointer-map resolution mechanism; this just resolves the
@ -490,7 +490,7 @@
_ (-> (cfeat/get-team-enabled-features cf/flags team) _ (-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params)) (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)] page (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg file-id)]
(let [page-id (or page-id (-> file :data :pages first)) (let [page-id (or page-id (-> file :data :pages first))
@ -737,7 +737,7 @@
(-> (cfeat/get-team-enabled-features cf/flags team) (-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params)) (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)] (binding [pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
{:name (:name file) {:name (:name file)

View File

@ -91,9 +91,6 @@
:project-id project-id) :project-id project-id)
team-id (:id team) 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) features (-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params))) (cfeat/check-client-features! (:features params)))
@ -107,7 +104,7 @@
params (-> params params (-> params
(assoc :profile-id profile-id) (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/check! cfg {::quotes/id ::quotes/files-per-project
::quotes/team-id team-id ::quotes/team-id team-id
@ -120,7 +117,7 @@
;; to lost team features updating ;; to lost team features updating
;; When newly computed features does not match exactly with ;; 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)) (when (not= features (:features team))
(let [features (db/create-array conn "text" features)] (let [features (db/create-array conn "text" features)]
(db/update! conn :team (db/update! conn :team

View File

@ -212,7 +212,7 @@
(-> (cfeat/get-team-enabled-features cf/flags team) (-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params)) (cfeat/check-client-features! (:features params))
(cfeat/check-file-features! (:features file) (:features params))) (cfeat/check-file-features! (:features file)))
{:file-id file-id {:file-id file-id
:revn (:revn file) :revn (:revn file)

View File

@ -142,7 +142,7 @@
features (-> (cfeat/get-team-enabled-features cf/flags team) features (-> (cfeat/get-team-enabled-features cf/flags team)
(cfeat/check-client-features! (:features params)) (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 (if changes-with-metadata
(->> changes-with-metadata (mapcat :changes) vec) (->> changes-with-metadata (mapcat :changes) vec)

View File

@ -209,100 +209,116 @@
This method allows send flash notifications to specified target destinations. This method allows send flash notifications to specified target destinations.
The message can be a free text or a preconfigured one. 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." The destination can be: all, profile-id, team-id, or a coll of them.
[{:keys [::mbus/msgbus ::db/pool]} & {:keys [dest code message level] It also can be:
:or {code :generic level :info}
:as params}] {: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) (when-not (contains? #{:success :error :info :warning} level)
(ex/raise :type :assertion (ex/raise :type :assertion
:code :incorrect-level :code :incorrect-level
:hint (str "level '" level "' not supported"))) :hint (str "level '" level "' not supported")))
(letfn [(send [dest] (let [{:keys [::mbus/msgbus ::db/pool]} main/system
(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)))
(resolve-profile [email] send
(some-> (db/get* pool :profile {:email (str/lower email)} {:columns [:id]}) :id vector)) (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] resolve-profile
(->> (db/query pool :team-profile-rel (fn [email]
{:team-id team-id} (some-> (db/get* pool :profile {:email (str/lower email)} {:columns [:id]}) :id vector))
{:columns [:profile-id]})
(map :profile-id)))
(resolve-dest [dest] resolve-team
(cond (fn [team-id]
(= :all dest) (->> (db/query pool :team-profile-rel
[uuid/zero] {:team-id team-id}
{:columns [:profile-id]})
(map :profile-id)))
(uuid? dest) resolve-dest
[dest] (fn resolve-dest [dest]
(cond
(= :all dest)
[uuid/zero]
(string? dest) (uuid? dest)
(some-> dest h/parse-uuid resolve-dest) [dest]
(nil? dest) (string? dest)
(resolve-dest uuid/zero) (some-> dest h/parse-uuid resolve-dest)
(map? dest) (nil? dest)
(sequence (comp [uuid/zero]
(map vec)
(mapcat resolve-dest))
dest)
(and (vector? dest) (map? dest)
(every? vector? dest)) (sequence (comp
(sequence (comp (map vec)
(map vec) (mapcat resolve-dest))
(mapcat resolve-dest)) dest)
dest)
(and (vector? dest) (and (vector? dest)
(keyword? (first dest))) (every? vector? dest))
(let [[op param] dest] (sequence (comp
(map vec)
(mapcat resolve-dest))
dest)
(and (vector? dest)
(keyword? (first dest)))
(let [[op param] dest]
(cond
(= op :email)
(cond (cond
(= op :email) (and (coll? param)
(cond (every? string? param))
(and (coll? param) (sequence (comp
(every? string? param)) (keep resolve-profile)
(sequence (comp (mapcat identity))
(keep resolve-profile) param)
(mapcat identity))
param)
(string? param) (string? param)
(resolve-profile param)) (resolve-profile param))
(= op :team-id) (= op :team-id)
(cond (cond
(coll? param) (coll? param)
(sequence (comp (sequence (comp
(mapcat resolve-team) (mapcat resolve-team)
(keep h/parse-uuid)) (keep h/parse-uuid))
param) param)
(uuid? param) (uuid? param)
(resolve-team param) (resolve-team param)
(string? param) (string? param)
(some-> param h/parse-uuid resolve-team)) (some-> param h/parse-uuid resolve-team))
(= op :profile-id) (= op :profile-id)
(if (coll? param) (if (coll? param)
(sequence (keep h/parse-uuid) param) (sequence (keep h/parse-uuid) param)
(resolve-dest param))))))] (resolve-dest param))))))]
(->> (resolve-dest dest) (->> (resolve-dest dest)
(filter some?) (filter some?)

View File

@ -4,7 +4,7 @@
"license": "MPL-2.0", "license": "MPL-2.0",
"author": "Kaleidos INC", "author": "Kaleidos INC",
"private": true, "private": true,
"packageManager": "yarn@4.6.0+sha512.5383cc12567a95f1d668fbe762dfe0075c595b4bfff433be478dbbe24e05251a8e8c3eb992a986667c1d53b6c3a9c85b8398c35a960587fbd9fa3a0915406728", "packageManager": "yarn@4.8.1+sha512.bc946f2a022d7a1a38adfc15b36a66a3807a67629789496c3714dd1703d2e6c6b1c69ff9ec3b43141ac7a1dd853b7685638eb0074300386a59c18df351ef8ff6",
"type": "module", "type": "module",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -85,12 +85,11 @@
;; be applied (per example backend can operate in both modes with or ;; be applied (per example backend can operate in both modes with or
;; without migration applied) ;; without migration applied)
(def no-migration-features (def no-migration-features
(-> #{"fdata/objects-map" (-> #{"layout/grid"
"fdata/pointer-map"
"layout/grid"
"fdata/shape-data-type" "fdata/shape-data-type"
"design-tokens/v1"} "design-tokens/v1"}
(into frontend-only-features))) (into frontend-only-features)
(into backend-only-features)))
(sm/register! (sm/register!
^{::sm/type ::features} ^{::sm/type ::features}
@ -158,7 +157,6 @@
team-features (into #{} xf-remove-ephimeral (:features team))] team-features (into #{} xf-remove-ephimeral (:features team))]
(-> enabled-features (-> enabled-features
(set/intersection no-migration-features) (set/intersection no-migration-features)
(set/difference frontend-only-features)
(set/union team-features)))) (set/union team-features))))
(defn check-client-features! (defn check-client-features!
@ -167,6 +165,8 @@
frontend client" frontend client"
[enabled-features client-features] [enabled-features client-features]
(when (set? client-features) (when (set? client-features)
;; Check if client declares support for features enabled on
;; backend side
(let [not-supported (-> enabled-features (let [not-supported (-> enabled-features
(set/difference client-features) (set/difference client-features)
(set/difference frontend-only-features) (set/difference frontend-only-features)
@ -176,14 +176,6 @@
:code :feature-not-supported :code :feature-not-supported
:feature (first not-supported) :feature (first not-supported)
:hint (str/ffmt "client declares no support for '%' features" :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)))))) (str/join "," not-supported))))))
enabled-features) enabled-features)
@ -194,57 +186,49 @@
supported by the current backend" supported by the current backend"
[enabled-features] [enabled-features]
(let [not-supported (set/difference enabled-features supported-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 (ex/raise :type :restriction
:code :feature-not-supported :code :feature-not-supported
:feature (first not-supported) :feature not-supported
:hint (str/ffmt "features '%' not supported" :hint (str/ffmt "feature '%' not supported on this backend" not-supported)))
(str/join "," not-supported))))) enabled-features))
enabled-features)
(defn check-file-features! (defn check-file-features!
"Function used for check feature compability between currently "Function used for check feature compability between currently
enabled features set on backend with the provided featured set by enabled features set on backend with the provided featured set by
the penpot file" the penpot file"
([enabled-features file-features] [enabled-features file-features]
(check-file-features! enabled-features file-features #{})) (let [file-features (into #{} xf-remove-ephimeral file-features)
([enabled-features file-features client-features] not-supported (-> enabled-features
(let [file-features (into #{} xf-remove-ephimeral file-features) (set/difference file-features)
;; We should ignore all features that does not match with the ;; NOTE: we don't want to raise a feature-mismatch
;; `no-migration-features` set because we can't enable them ;; exception for features which don't require an
;; as-is, because they probably need migrations ;; explicit file migration process or has no real
client-features (set/intersection client-features no-migration-features)] ;; effect on file data structure
(let [not-supported (-> enabled-features (set/difference no-migration-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)))))
(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 (check-supported-features! file-features)
(set/difference enabled-features)
(set/difference client-features)
(set/difference backend-only-features)
(set/difference frontend-only-features))]
(when (seq not-supported) (let [not-supported (-> file-features
(ex/raise :type :restriction (set/difference enabled-features)
:code :file-feature-mismatch (set/difference backend-only-features)
:feature (first not-supported) (set/difference frontend-only-features))]
:hint (str/ffmt "file features '%' not enabled"
(str/join "," not-supported))))))
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! (defn check-teams-compatibility!
[{source-features :features} {destination-features :features}] [{source-features :features} {destination-features :features}]

View File

@ -4,7 +4,7 @@
"license": "MPL-2.0", "license": "MPL-2.0",
"author": "Kaleidos INC", "author": "Kaleidos INC",
"private": true, "private": true,
"packageManager": "yarn@4.7.0+sha512.5a0afa1d4c1d844b3447ee3319633797bcd6385d9a44be07993ae52ff4facabccafb4af5dcd1c2f9a94ac113e5e9ff56f6130431905884414229e284e37bb7c9", "packageManager": "yarn@4.8.1+sha512.bc946f2a022d7a1a38adfc15b36a66a3807a67629789496c3714dd1703d2e6c6b1c69ff9ec3b43141ac7a1dd853b7685638eb0074300386a59c18df351ef8ff6",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/penpot/penpot" "url": "https://github.com/penpot/penpot"

View File

@ -4,7 +4,7 @@
"license": "MPL-2.0", "license": "MPL-2.0",
"author": "Kaleidos INC", "author": "Kaleidos INC",
"private": true, "private": true,
"packageManager": "yarn@4.7.0+sha512.5a0afa1d4c1d844b3447ee3319633797bcd6385d9a44be07993ae52ff4facabccafb4af5dcd1c2f9a94ac113e5e9ff56f6130431905884414229e284e37bb7c9", "packageManager": "yarn@4.8.1+sha512.bc946f2a022d7a1a38adfc15b36a66a3807a67629789496c3714dd1703d2e6c6b1c69ff9ec3b43141ac7a1dd853b7685638eb0074300386a59c18df351ef8ff6",
"browserslist": [ "browserslist": [
"defaults" "defaults"
], ],

View File

@ -15,7 +15,6 @@
[app.main.data.profile :as dp] [app.main.data.profile :as dp]
[app.main.data.websocket :as ws] [app.main.data.websocket :as ws]
[app.main.errors] [app.main.errors]
[app.main.features :as feat]
[app.main.rasterizer :as thr] [app.main.rasterizer :as thr]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui :as ui] [app.main.ui :as ui]
@ -67,7 +66,6 @@
(watch [_ _ stream] (watch [_ _ stream]
(rx/merge (rx/merge
(rx/of (ev/initialize) (rx/of (ev/initialize)
(feat/initialize)
(dp/refresh-profile)) (dp/refresh-profile))
;; Watch for profile deletion events ;; Watch for profile deletion events

View File

@ -13,7 +13,6 @@
[app.common.types.shape-tree :as ctst] [app.common.types.shape-tree :as ctst]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.helpers :as dsh] [app.main.data.helpers :as dsh]
[app.main.features :as features]
[app.main.worker :as uw] [app.main.worker :as uw]
[app.util.time :as dt] [app.util.time :as dt]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
@ -182,8 +181,8 @@
(let [file-id (or file-id (:current-file-id state)) (let [file-id (or file-id (:current-file-id state))
uchg (vec undo-changes) uchg (vec undo-changes)
rchg (vec redo-changes) rchg (vec redo-changes)
features (features/get-team-enabled-features state) features (get state :features)
permissions (:permissions state)] permissions (get state :permissions)]
;; Prevent commit changes by a viewer team member (it really should never happen) ;; Prevent commit changes by a viewer team member (it really should never happen)
(when (:can-edit permissions) (when (:can-edit permissions)

View File

@ -16,7 +16,6 @@
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
[app.main.data.persistence :as-alias dps] [app.main.data.persistence :as-alias dps]
[app.main.features :as features]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.router :as rt] [app.main.router :as rt]
[app.main.store :as st] [app.main.store :as st]
@ -73,7 +72,7 @@
(st/emit! (ntf/hide))) (st/emit! (ntf/hide)))
(defn handle-notification (defn handle-notification
[{:keys [message code level] :as params}] [{:keys [message code] :as params}]
(ptk/reify ::show-notification (ptk/reify ::show-notification
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ _ _]
@ -81,9 +80,6 @@
:upgrade-version :upgrade-version
(rx/of (ntf/dialog (rx/of (ntf/dialog
:content (tr "notifications.by-code.upgrade-version") :content (tr "notifications.by-code.upgrade-version")
:controls :inline-actions
:type :inline
:level level
:accept {:label (tr "labels.refresh") :accept {:label (tr "labels.refresh")
:callback force-reload!} :callback force-reload!}
:tag :notification)) :tag :notification))
@ -91,16 +87,14 @@
:maintenance :maintenance
(rx/of (ntf/dialog (rx/of (ntf/dialog
:content (tr "notifications.by-code.maintenance") :content (tr "notifications.by-code.maintenance")
:controls :inline-actions
:type level
:accept {:label (tr "labels.accept") :accept {:label (tr "labels.accept")
:callback hide-notifications!} :callback hide-notifications!}
:tag :notification)) :tag :notification))
(rx/of (ntf/dialog (rx/of (ntf/dialog
:content message :content message
:controls :close :accept {:label (tr "labels.close")
:type level :callback hide-notifications!}
:tag :notification)))))) :tag :notification))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -112,7 +106,7 @@
(ptk/reify ::show-shared-dialog (ptk/reify ::show-shared-dialog
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [features (features/get-team-enabled-features state) (let [features (get state :features)
file (dsh/lookup-file state) file (dsh/lookup-file state)
data (get file :data)] data (get file :data)]
@ -169,8 +163,8 @@
(ptk/reify ::export-files (ptk/reify ::export-files
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [features (features/get-team-enabled-features state) (let [features (get state :features)
team-id (:current-team-id state)] team-id (get state :current-team-id)]
(->> (rx/from files) (->> (rx/from files)
(rx/mapcat (rx/mapcat
(fn [file] (fn [file]

View File

@ -19,7 +19,6 @@
[app.main.data.helpers :as dsh] [app.main.data.helpers :as dsh]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.websocket :as dws] [app.main.data.websocket :as dws]
[app.main.features :as features]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.sse :as sse] [app.util.sse :as sse]
@ -57,8 +56,7 @@
(rx/filter (fn [{:keys [topic] :as msg}] (rx/filter (fn [{:keys [topic] :as msg}]
(or (= topic uuid/zero) (or (= topic uuid/zero)
(= topic profile-id)))) (= topic profile-id))))
(rx/map process-message) (rx/map process-message)))
(rx/ignore)))
(rx/take-until stopper)))))) (rx/take-until stopper))))))
@ -497,7 +495,7 @@
base-name (tr "dashboard.new-file-prefix") base-name (tr "dashboard.new-file-prefix")
name (or name name (or name
(cfh/generate-unique-name base-name unames :immediate-suffix? true)) (cfh/generate-unique-name base-name unames :immediate-suffix? true))
features (-> (features/get-team-enabled-features state) features (-> (get state :features)
(set/difference cfeat/frontend-only-features)) (set/difference cfeat/frontend-only-features))
params (-> params params (-> params
(assoc :name name) (assoc :name name)

View File

@ -12,7 +12,6 @@
[app.common.schema :as sm] [app.common.schema :as sm]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.features :as features]
[app.main.repo :as rp] [app.main.repo :as rp]
[beicon.v2.core :as rx] [beicon.v2.core :as rx]
[potok.v2.core :as ptk])) [potok.v2.core :as ptk]))
@ -47,7 +46,7 @@
(ptk/reify ::export-files (ptk/reify ::export-files
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [features (features/get-team-enabled-features state) (let [features (get state :features)
team-id (:current-team-id state) team-id (:current-team-id state)
evname (if (= format :legacy-zip) evname (if (= format :legacy-zip)
"export-standard-files" "export-standard-files"

View File

@ -19,7 +19,7 @@
(def ^:private schema:notification (def ^:private schema:notification
[:map {:title "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} [:status {:optional true}
[::sm/one-of #{:visible :hide}]] [::sm/one-of #{:visible :hide}]]
[:position {:optional true} [:position {:optional true}
@ -129,15 +129,11 @@
:timeout timeout}))) :timeout timeout})))
(defn dialog (defn dialog
[& {:keys [content controls actions accept cancel position tag level links] [& {:keys [content accept cancel tag links]}]
:or {controls :none position :floating level :info}}]
(show (d/without-nils (show (d/without-nils
{:content content {:content content
:level level :type :inline
:links links
:position position
:controls controls
:actions actions
:accept accept :accept accept
:cancel cancel :cancel cancel
:links links
:tag tag}))) :tag tag})))

View File

@ -101,7 +101,7 @@
(let [permissions (get team :permissions) (let [permissions (get team :permissions)
features (get team :features)] features (get team :features)]
(rx/of #(assoc % :permissions permissions) (rx/of #(assoc % :permissions permissions)
(features/initialize (or features #{})) (features/initialize features)
(fetch-members team-id)))))) (fetch-members team-id))))))
ptk/EffectEvent ptk/EffectEvent
@ -255,12 +255,12 @@
(dm/assert! (string? name)) (dm/assert! (string? name))
(ptk/reify ::create-team (ptk/reify ::create-team
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it _ _]
(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)
features (features/get-enabled-features state) features features/global-enabled-features
params {:name name :features features}] params {:name name :features features}]
(->> (rp/cmd! :create-team (with-meta params (meta it))) (->> (rp/cmd! :create-team (with-meta params (meta it)))
(rx/tap on-success) (rx/tap on-success)
(rx/map team-created) (rx/map team-created)
@ -272,11 +272,11 @@
[{:keys [name emails role] :as params}] [{:keys [name emails role] :as params}]
(ptk/reify ::create-team-with-invitations (ptk/reify ::create-team-with-invitations
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it _ _]
(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)
features (features/get-enabled-features state) features features/global-enabled-features
params {:name name params {:name name
:emails emails :emails emails
:role role :role role

View File

@ -184,7 +184,7 @@
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [team-id (:id team) (let [team-id (:id team)
team {:members users}] team (assoc team :members users)]
(-> state (-> state
(assoc :share-links share-links) (assoc :share-links share-links)
(assoc :current-team-id team-id) (assoc :current-team-id team-id)

View File

@ -205,30 +205,29 @@
(d/index-by :id)))))) (d/index-by :id))))))
(defn- fetch-libraries (defn- fetch-libraries
[file-id] [file-id features]
(ptk/reify ::fetch-libries (ptk/reify ::fetch-libries
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ _ _]
(let [features (features/get-team-enabled-features state)] (->> (rp/cmd! :get-file-libraries {:file-id file-id})
(->> (rp/cmd! :get-file-libraries {:file-id file-id}) (rx/mapcat
(rx/mapcat (fn [libraries]
(fn [libraries] (rx/concat
(rx/concat (rx/of (libraries-fetched file-id libraries))
(rx/of (libraries-fetched file-id libraries)) (rx/merge
(rx/merge (->> (rx/from libraries)
(->> (rx/from libraries) (rx/merge-map
(rx/merge-map (fn [{:keys [id synced-at]}]
(fn [{:keys [id synced-at]}] (->> (rp/cmd! :get-file {:id id :features features})
(->> (rp/cmd! :get-file {:id id :features features}) (rx/map #(assoc % :synced-at synced-at :library-of file-id)))))
(rx/map #(assoc % :synced-at synced-at :library-of file-id))))) (rx/mapcat resolve-file)
(rx/mapcat resolve-file) (rx/map library-resolved))
(rx/map library-resolved)) (->> (rx/from libraries)
(->> (rx/from libraries) (rx/map :id)
(rx/map :id) (rx/mapcat (fn [file-id]
(rx/mapcat (fn [file-id] (rp/cmd! :get-file-object-thumbnails {:file-id file-id :tag "component"})))
(rp/cmd! :get-file-object-thumbnails {:file-id file-id :tag "component"}))) (rx/map dwl/library-thumbnails-fetched)))
(rx/map dwl/library-thumbnails-fetched))) (rx/of (check-libraries-synchronozation file-id libraries)))))))))
(rx/of (check-libraries-synchronozation file-id libraries))))))))))
(defn- workspace-initialized (defn- workspace-initialized
[file-id] [file-id]
@ -246,28 +245,16 @@
(fbs/fix-broken-shapes))))) (fbs/fix-broken-shapes)))))
(defn- bundle-fetched (defn- bundle-fetched
[{:keys [features file thumbnails]}] [{:keys [file file-id thumbnails] :as bundle}]
(ptk/reify ::bundle-fetched (ptk/reify ::bundle-fetched
IDeref IDeref
(-deref [_] (-deref [_] bundle)
{:features features
:file file
:thumbnails thumbnails})
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [file-id (:id file)] (-> state
(-> state (assoc :thumbnails thumbnails)
(assoc :thumbnails thumbnails) (update :files assoc file-id file)))))
(update :files assoc file-id file))))
ptk/WatchEvent
(watch [_ state _]
(let [team-id (:current-team-id state)
file-id (:id file)]
(rx/of (dwn/initialize team-id file-id)
(dwsl/initialize-shape-layout)
(fetch-libraries file-id))))))
(defn zoom-to-frame (defn zoom-to-frame
[] []
@ -296,46 +283,30 @@
(defn- fetch-bundle (defn- fetch-bundle
"Multi-stage file bundle fetch coordinator" "Multi-stage file bundle fetch coordinator"
[file-id] [file-id features]
(ptk/reify ::fetch-bundle (ptk/reify ::fetch-bundle
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ _ stream]
(let [features (features/get-team-enabled-features state) (let [stopper-s (rx/filter (ptk/type? ::finalize-workspace) stream)]
render-wasm? (contains? features "render-wasm/v1") (->> (rx/zip (rp/cmd! :get-file {:id file-id :features features})
stopper-s (rx/filter (ptk/type? ::finalize-workspace) stream) (get-file-object-thumbnails file-id))
team-id (:current-team-id state)] (rx/take 1)
(rx/mapcat
(->> (rx/concat (fn [[file thumbnails]]
;; Firstly load wasm module if it is enabled and fonts (->> (resolve-file file)
(rx/merge (rx/map (fn [file]
(if ^boolean render-wasm? {:file file
(->> (rx/from @wasm/module) :file-id file-id
(rx/ignore)) :features features
(rx/empty)) :thumbnails thumbnails})))))
(rx/map bundle-fetched)
(->> stream
(rx/filter (ptk/type? ::df/fonts-loaded))
(rx/take 1)
(rx/ignore))
(rx/of (df/fetch-fonts team-id)))
;; Then fetch file and thumbnails
(->> (rx/zip (rp/cmd! :get-file {:id file-id :features features})
(get-file-object-thumbnails file-id))
(rx/take 1)
(rx/mapcat
(fn [[file thumbnails]]
(->> (resolve-file file)
(rx/map (fn [file]
{:file file
:features features
:thumbnails thumbnails})))))
(rx/map bundle-fetched)))
(rx/take-until stopper-s)))))) (rx/take-until stopper-s))))))
(defn initialize-workspace (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`") (assert (uuid? file-id) "expected valud uuid for `file-id`")
(ptk/reify ::initialize-workspace (ptk/reify ::initialize-workspace
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
@ -347,24 +318,51 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(log/debug :hint "initialize-workspace" :file-id (dm/str file-id)) (let [stoper-s (rx/filter (ptk/type? ::finalize-workspace) stream)
(let [stoper-s (rx/filter (ptk/type? ::finalize-workspace) stream) rparams (rt/get-params state)
rparams (rt/get-params state)] features (features/get-enabled-features state team-id)
render-wasm? (contains? features "render-wasm/v1")]
(log/debug :hint "initialize-workspace"
:team-id (dm/str team-id)
:file-id (dm/str file-id))
(->> (rx/merge (->> (rx/merge
(rx/of (ntf/hide) (rx/concat
(dcmt/retrieve-comment-threads file-id) ;; Fetch all essential data that should be loaded before the file
(dcmt/fetch-profiles) (rx/merge
(fetch-bundle file-id)) (if ^boolean render-wasm?
(->> (rx/from @wasm/module)
(rx/ignore))
(rx/empty))
(->> stream
(rx/filter (ptk/type? ::df/fonts-loaded))
(rx/take 1)
(rx/ignore))
(rx/of (ntf/hide)
(dcmt/retrieve-comment-threads file-id)
(dcmt/fetch-profiles)
(df/fetch-fonts team-id)))
;; Once the essential data is fetched, lets proceed to
;; fetch teh file bunldle
(rx/of (fetch-bundle file-id features)))
(->> stream (->> stream
(rx/filter (ptk/type? ::bundle-fetched)) (rx/filter (ptk/type? ::bundle-fetched))
(rx/take 1) (rx/take 1)
(rx/map deref) (rx/map deref)
(rx/mapcat (fn [{:keys [file]}] (rx/mapcat
(rx/of (dpj/initialize-project (:project-id file)) (fn [{:keys [file]}]
(-> (workspace-initialized file-id) (rx/of (dpj/initialize-project (:project-id file))
(with-meta {:file-id file-id})))))) (dwn/initialize team-id file-id)
(dwsl/initialize-shape-layout)
(fetch-libraries file-id features)
(-> (workspace-initialized file-id)
(with-meta {:team-id team-id
:file-id file-id}))))))
(->> stream (->> stream
(rx/filter (ptk/type? ::dps/persistence-notification)) (rx/filter (ptk/type? ::dps/persistence-notification))
@ -410,7 +408,7 @@
(unchecked-set ug/global "name" name))))) (unchecked-set ug/global "name" name)))))
(defn finalize-workspace (defn finalize-workspace
[file-id] [_team-id file-id]
(ptk/reify ::finalize-workspace (ptk/reify ::finalize-workspace
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
@ -430,7 +428,6 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [project-id (:current-project-id state)] (let [project-id (:current-project-id state)]
(rx/of (dwn/finalize file-id) (rx/of (dwn/finalize file-id)
(dpj/finalize-project project-id) (dpj/finalize-project project-id)
(dwsl/finalize-shape-layout) (dwsl/finalize-shape-layout)
@ -444,14 +441,13 @@
(ptk/reify ::reload-current-file (ptk/reify ::reload-current-file
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [file-id (:current-file-id state)] (let [file-id (:current-file-id state)
(rx/of (initialize-workspace file-id)))))) team-id (:current-team-id state)]
(rx/of (initialize-workspace team-id file-id))))))
;; Make this event callable through dynamic resolution ;; Make this event callable through dynamic resolution
(defmethod ptk/resolve ::reload-current-file [_ _] (reload-current-file)) (defmethod ptk/resolve ::reload-current-file [_ _] (reload-current-file))
(def ^:private xf:collect-file-media (def ^:private xf:collect-file-media
"Resolve and collect all file media on page objects" "Resolve and collect all file media on page objects"
(comp (map second) (comp (map second)
@ -488,18 +484,25 @@
(defn initialize-page (defn initialize-page
[file-id page-id] [file-id page-id]
(assert (uuid? file-id) "expected valid uuid for `file-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/reify ::initialize-page
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(if-let [page (dsh/lookup-page state file-id page-id)] (if-let [page (dsh/lookup-page state file-id page-id)]
(rx/concat (rx/of (initialize-page* file-id page-id page) (rx/concat
(dwth/watch-state-changes file-id page-id) (rx/of (initialize-page* file-id page-id page)
(dwl/watch-component-changes)) (dwth/watch-state-changes file-id page-id)
(let [profile (:profile state) (dwl/watch-component-changes))
props (get profile :props)] (let [profile (:profile state)
(when (not (:workspace-visited props)) props (get profile :props)]
(rx/of (select-frame-tool file-id page-id))))) (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)))))) (rx/of (dcm/go-to-workspace :file-id file-id ::rt/replace true))))))
(defn finalize-page (defn finalize-page
@ -1410,7 +1413,7 @@
(let [objects (dsh/lookup-page-objects state) (let [objects (dsh/lookup-page-objects state)
selected (->> (dsh/lookup-selected state) selected (->> (dsh/lookup-selected state)
(cfh/clean-loops objects)) (cfh/clean-loops objects))
features (-> (features/get-team-enabled-features state) features (-> (get state :features)
(set/difference cfeat/frontend-only-features)) (set/difference cfeat/frontend-only-features))
file-id (:current-file-id state) file-id (:current-file-id state)
@ -1648,9 +1651,10 @@
objects (dsh/lookup-page-objects state)] objects (dsh/lookup-page-objects state)]
(when-let [shape (get objects selected)] (when-let [shape (get objects selected)]
(let [props (cts/extract-props shape) (let [props (cts/extract-props shape)
features (-> (features/get-team-enabled-features state) features (-> (get state :features)
(set/difference cfeat/frontend-only-features)) (set/difference cfeat/frontend-only-features))
version (-> (dsh/lookup-file state) :version) version (-> (dsh/lookup-file state)
(get :version))
copy-data {:type :copied-props copy-data {:type :copied-props
:features features :features features
@ -1784,8 +1788,8 @@
(ptk/reify ::paste-transit-shapes (ptk/reify ::paste-transit-shapes
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [file-id (:current-file-id state) (let [file-id (:current-file-id state)
features (features/get-team-enabled-features state)] features (get state :features)]
(when-not (paste-data-valid? pdata) (when-not (paste-data-valid? pdata)
(ex/raise :type :validation (ex/raise :type :validation
@ -1856,7 +1860,7 @@
(ptk/reify ::paste-transit-props (ptk/reify ::paste-transit-props
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [features (features/get-team-enabled-features state) (let [features (get state :features)
selected (dsh/lookup-selected state)] selected (dsh/lookup-selected state)]
(when (paste-data-valid? pdata) (when (paste-data-valid? pdata)

View File

@ -1398,7 +1398,7 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [features (features/get-team-enabled-features state)] (let [features (get state :features)]
(rx/concat (rx/concat
(rx/merge (rx/merge
(->> (rp/cmd! :link-file-to-library {:file-id file-id :library-id library-id}) (->> (rp/cmd! :link-file-to-library {:file-id file-id :library-id library-id})

View File

@ -65,7 +65,6 @@
(->> (rx/from initmsg) (->> (rx/from initmsg)
(rx/map dws/send)) (rx/map dws/send))
;; Subscribe to notifications of the subscription ;; Subscribe to notifications of the subscription
(->> stream (->> stream
(rx/filter (ptk/type? ::dws/message)) (rx/filter (ptk/type? ::dws/message))

View File

@ -10,6 +10,7 @@
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.schema :as sm] [app.common.schema :as sm]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.data.helpers :as dsh]
[app.main.data.persistence :as dwp] [app.main.data.persistence :as dwp]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.data.workspace.thumbnails :as th] [app.main.data.workspace.thumbnails :as th]
@ -97,7 +98,8 @@
(ptk/reify ::restore-version (ptk/reify ::restore-version
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (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/concat
(rx/of ::dwp/force-persist (rx/of ::dwp/force-persist
(dw/remove-layout-flag :document-history)) (dw/remove-layout-flag :document-history))
@ -106,7 +108,7 @@
(rx/take 1) (rx/take 1)
(rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id})) (rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id}))
(rx/tap #(th/clear-queue!)) (rx/tap #(th/clear-queue!))
(rx/map #(dw/initialize-workspace file-id))) (rx/map #(dw/initialize-workspace team-id file-id)))
(case origin (case origin
:version :version
(rx/of (ptk/event ::ev/event {::ev/name "restore-pin-version"})) (rx/of (ptk/event ::ev/event {::ev/name "restore-pin-version"}))
@ -200,21 +202,23 @@
(ptk/reify ::restore-version-from-plugins (ptk/reify ::restore-version-from-plugins
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ state _]
(rx/concat (let [file (dsh/lookup-file state file-id)
(rx/of (ptk/event ::ev/event {::ev/name "restore-version-plugin"}) team-id (or (:team-id file) (:current-file-id state))]
::dwp/force-persist) (rx/concat
(rx/of (ptk/event ::ev/event {::ev/name "restore-version-plugin"})
::dwp/force-persist)
;; FIXME: we should abstract this ;; FIXME: we should abstract this
(->> (rx/from-atom refs/persistence-state {:emit-current-value? true}) (->> (rx/from-atom refs/persistence-state {:emit-current-value? true})
(rx/filter #(or (nil? %) (= :saved %))) (rx/filter #(or (nil? %) (= :saved %)))
(rx/take 1) (rx/take 1)
(rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id})) (rx/mapcat #(rp/cmd! :restore-file-snapshot {:file-id file-id :id id}))
(rx/map #(dw/initialize-workspace file-id))) (rx/map #(dw/initialize-workspace team-id file-id)))
(->> (rx/of 1) (->> (rx/of 1)
(rx/tap resolve) (rx/tap resolve)
(rx/ignore)))))) (rx/ignore)))))))

View File

@ -8,12 +8,12 @@
"A thin, frontend centric abstraction layer and collection of "A thin, frontend centric abstraction layer and collection of
helpers for `app.common.features` namespace." helpers for `app.common.features` namespace."
(:require (:require
[app.common.data.macros :as dm]
[app.common.features :as cfeat] [app.common.features :as cfeat]
[app.common.logging :as log] [app.common.logging :as log]
[app.config :as cf] [app.config :as cf]
[app.main.store :as st] [app.main.store :as st]
[app.render-wasm :as wasm] [app.render-wasm :as wasm]
[beicon.v2.core :as rx]
[clojure.set :as set] [clojure.set :as set]
[cuerdas.core :as str] [cuerdas.core :as str]
[okulary.core :as l] [okulary.core :as l]
@ -26,38 +26,32 @@
(cfeat/get-enabled-features cf/flags)) (cfeat/get-enabled-features cf/flags))
(defn get-enabled-features (defn get-enabled-features
[state] "An explicit lookup of enabled features for the current team"
(-> (get state :features-runtime #{}) [state team-id]
(set/intersection cfeat/no-migration-features) (let [team (dm/get-in state [:teams team-id])]
(set/union global-enabled-features)))
(defn get-team-enabled-features
[state]
(let [runtime-features (:features-runtime state #{})
team-features (->> (:features-team state #{})
(into #{} cfeat/xf-remove-ephimeral))]
(-> global-enabled-features (-> global-enabled-features
(set/union runtime-features) (set/union (get state :features-runtime #{}))
(set/intersection cfeat/no-migration-features) (set/intersection cfeat/no-migration-features)
(set/union team-features)))) (set/union (get team :features)))))
(def features-ref
(l/derived get-team-enabled-features st/state =))
(defn active-feature? (defn active-feature?
"Given a state and feature, check if feature is enabled" "Given a state and feature, check if feature is enabled."
[state feature] [state feature]
(assert (contains? cfeat/supported-features feature) "not supported feature") (assert (contains? cfeat/supported-features feature) "feature not supported")
(or (contains? (get state :features-runtime) feature) (let [runtime-features (get state :features-runtime)
(if (contains? cfeat/no-migration-features feature) enabled-features (get state :features)]
(or (contains? global-enabled-features feature) (or (contains? runtime-features feature)
(contains? (get state :features-team) feature)) (if (contains? cfeat/no-migration-features feature)
(contains? (get state :features-team state) feature)))) (or (contains? global-enabled-features feature)
(contains? enabled-features feature))
(contains? enabled-features feature)))))
(def ^:private features-ref
(l/derived (l/key :features) st/state))
(defn use-feature (defn use-feature
"A react hook that checks if feature is currently enabled" "A react hook that checks if feature is currently enabled"
[feature] [feature]
(assert (contains? cfeat/supported-features feature) "Not supported feature")
(let [enabled-features (mf/deref features-ref)] (let [enabled-features (mf/deref features-ref)]
(contains? enabled-features feature))) (contains? enabled-features feature)))
@ -71,14 +65,16 @@
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(assert (contains? cfeat/supported-features feature) "not supported feature") (assert (contains? cfeat/supported-features feature) "not supported feature")
(update state :features-runtime (fn [features] (-> state
(if (contains? features feature) (update :features-runtime (fn [features]
(do (if (contains? features feature)
(log/trc :hint "feature disabled" :feature feature) (do
(disj features feature)) (log/trc :hint "feature disabled" :feature feature)
(do (disj features feature))
(log/trc :hint "feature enabled" :feature feature) (do
(conj features feature)))))))) (log/trc :hint "feature enabled" :feature feature)
(conj features feature)))))
(update :features-runtime set/intersection cfeat/no-migration-features)))))
(defn enable-feature (defn enable-feature
[feature] [feature]
@ -90,46 +86,28 @@
state state
(do (do
(log/trc :hint "feature enabled" :feature feature) (log/trc :hint "feature enabled" :feature feature)
(update state :features-runtime (fnil conj #{}) feature)))))) (-> state
(update :features-runtime (fnil conj #{}) feature)
(update :features-runtime set/intersection cfeat/no-migration-features)))))))
(defn initialize (defn initialize
([] (initialize #{})) [features]
([team-features] (ptk/reify ::initialize
(assert (set? team-features) "expected a set of features") ptk/UpdateEvent
(assert (every? string? team-features) "expected a set of strings") (update [_ state]
(let [features (-> global-enabled-features
(set/union (get state :features-runtime #{}))
(set/union features))]
(assoc state :features features)))
(ptk/reify ::initialize ptk/EffectEvent
ptk/UpdateEvent (effect [_ state _]
(update [_ state] (let [features (get state :features)]
(let [runtime-features (get state :features/runtime #{}) (if (contains? features "render-wasm/v1")
team-features (into #{} (wasm/initialize true)
cfeat/xf-supported-features (wasm/initialize false))
team-features)]
(-> state
(assoc :features-runtime runtime-features)
(assoc :features-team team-features))))
ptk/WatchEvent (log/inf :hint "initialized"
(watch [_ _ _] :enabled (str/join "," features)
(when *assert* :runtime (str/join "," (:features-runtime state)))))))
(->> (rx/from cfeat/no-migration-features)
;; text editor v2 isn't enabled by default even in devenv
;; wasm render v1 isn't enabled by default even in devenv
(rx/filter #(not (or (contains? cfeat/backend-only-features %)
(= "text-editor/v2" %)
(= "render-wasm/v1" %)
(= "design-tokens/v1" %))))
(rx/observe-on :async)
(rx/map enable-feature))))
ptk/EffectEvent
(effect [_ state _]
(let [features (get-team-enabled-features state)]
(if (contains? features "render-wasm/v1")
(wasm/initialize true)
(wasm/initialize false))
(log/inf :hint "initialized"
:enabled (str/join "," features)
:runtime (str/join "," (:features-runtime state))))))))

View File

@ -370,6 +370,6 @@
(if edata (if edata
[:> static/exception-page* {:data edata :route route}] [:> static/exception-page* {:data edata :route route}]
[:> error-boundary* {:fallback static/internal-error*} [:> error-boundary* {:fallback static/internal-error*}
[:& notifications/current-notification] [:> notifications/current-notification*]
(when route (when route
[:> page* {:route route :profile profile}])])]])) [:> page* {:route route :profile profile}])])]]))

View File

@ -17,7 +17,6 @@
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
[app.main.data.project :as dpj] [app.main.data.project :as dpj]
[app.main.data.team :as dtm] [app.main.data.team :as dtm]
[app.main.features :as features]
[app.main.fonts :as fonts] [app.main.fonts :as fonts]
[app.main.rasterizer :as thr] [app.main.rasterizer :as thr]
[app.main.refs :as refs] [app.main.refs :as refs]
@ -60,7 +59,7 @@
(->> (wrk/ask! {:cmd :thumbnails/generate-for-file (->> (wrk/ask! {:cmd :thumbnails/generate-for-file
:revn revn :revn revn
:file-id file-id :file-id file-id
:features (features/get-team-enabled-features @st/state)}) :features (get @st/state :features)})
(rx/mapcat (fn [{:keys [fonts] :as result}] (rx/mapcat (fn [{:keys [fonts] :as result}]
(->> (fonts/render-font-styles fonts) (->> (fonts/render-font-styles fonts)
(rx/map (fn [styles] (rx/map (fn [styles]

View File

@ -15,7 +15,6 @@
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.data.notifications :as ntf] [app.main.data.notifications :as ntf]
[app.main.errors :as errors] [app.main.errors :as errors]
[app.main.features :as features]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.file-uploader :refer [file-uploader]] [app.main.ui.components.file-uploader :refer [file-uploader]]
[app.main.ui.ds.product.loader :refer [loader*]] [app.main.ui.ds.product.loader :refer [loader*]]
@ -162,29 +161,32 @@
(defn- analyze-entries (defn- analyze-entries
[state entries] [state entries]
(->> (uw/ask-many! (let [features (get @st/state :features)]
{:cmd :analyze-import (->> (uw/ask-many!
:files entries {:cmd :analyze-import
:features @features/features-ref}) :files entries
(rx/mapcat #(rx/delay emit-delay (rx/of %))) :features features})
(rx/filter some?) (rx/mapcat #(rx/delay emit-delay (rx/of %)))
(rx/subs! (rx/filter some?)
(fn [message] (rx/subs!
(swap! state update-with-analyze-result message))))) (fn [message]
(swap! state update-with-analyze-result message))))))
(defn- import-files (defn- import-files
[state project-id entries] [state project-id entries]
(st/emit! (ptk/data-event ::ev/event {::ev/name "import-files" (st/emit! (ptk/data-event ::ev/event {::ev/name "import-files"
:num-files (count entries)})) :num-files (count entries)}))
(->> (uw/ask-many!
{:cmd :import-files (let [features (get @st/state :features)]
:project-id project-id (->> (uw/ask-many!
:files entries {:cmd :import-files
:features @features/features-ref}) :project-id project-id
(rx/filter (comp uuid? :file-id)) :files entries
(rx/subs! :features features})
(fn [message] (rx/filter (comp uuid? :file-id))
(swap! state update-entry-status message))))) (rx/subs!
(fn [message]
(swap! state update-entry-status message))))))
(mf/defc import-entry* (mf/defc import-entry*
{::mf/props :obj {::mf/props :obj

View File

@ -7,8 +7,8 @@
(ns app.main.ui.dashboard.placeholder (ns app.main.ui.dashboard.placeholder
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.config :as cf]
[app.main.data.event :as ev] [app.main.data.event :as ev]
[app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.dashboard.import :as udi] [app.main.ui.dashboard.import :as udi]
[app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]] [app.main.ui.ds.product.empty-placeholder :refer [empty-placeholder*]]
@ -55,9 +55,10 @@
(mf/deps create-fn) (mf/deps create-fn)
(fn [_] (fn [_]
(create-fn "dashboard:empty-folder-placeholder"))) (create-fn "dashboard:empty-folder-placeholder")))
show-text (mf/use-state nil) show-text (mf/use-state nil)
on-mouse-enter (mf/use-fn #(reset! show-text true)) on-mouse-enter (mf/use-fn #(reset! show-text true))
on-mouse-leave (mf/use-fn #(reset! show-text nil))] on-mouse-leave (mf/use-fn #(reset! show-text nil))
files (mf/deref refs/files)]
(cond (cond
(true? dragging?) (true? dragging?)
[:ul [:ul
@ -79,18 +80,14 @@
:tag-name "span"}])] :tag-name "span"}])]
:else :else
(if (cf/external-feature-flag "add-file-02" "test") (if (= (count files) 0)
[:> empty-placeholder-projects* {:on-create on-click :on-finish-import on-finish-import :project-id project-id}] [:> empty-placeholder-projects* {:on-create on-click :on-finish-import on-finish-import :project-id project-id}]
[:div {:class (stl/css :grid-empty-placeholder)} [:div {:class (stl/css :grid-empty-placeholder)}
(if (cf/external-feature-flag "add-file-01" "test") [:button {:class (stl/css :create-new)
[:button {:class (stl/css :create-new) :on-click on-click
:on-click on-click :on-mouse-enter on-mouse-enter
:on-mouse-enter on-mouse-enter :on-mouse-leave on-mouse-leave}
:on-mouse-leave on-mouse-leave} (if @show-text (tr "dashboard.empty-project.create") i/add)]]))))
(if @show-text (tr "dashboard.empty-project.create") i/add)]
[:button {:class (stl/css :create-new)
:on-click on-click}
i/add])]))))
(mf/defc loading-placeholder (mf/defc loading-placeholder
[] []

View File

@ -5,7 +5,7 @@ import * as IconStories from "./icon.stories"
# Iconography # 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 ## Variants

View File

@ -17,10 +17,10 @@
[:class {:optional true} :string] [:class {:optional true} :string]
[:variant {:optional true} [:variant {:optional true}
[:maybe [:enum "default" "error"]]] [:maybe [:enum "default" "error"]]]
[:accept-label {:optional true} :string] [:accept-label {:optional true} [:maybe :string]]
[:cancel-label {:optional true} :string] [:cancel-label {:optional true} [:maybe :string]]
[:on-accept {:optional true} [:fn fn?]] [:on-accept {:optional true} [:maybe [:fn fn?]]]
[:on-cancel {:optional true} [:fn fn?]]]) [:on-cancel {:optional true} [:maybe [:fn fn?]]]])
(mf/defc actionable* (mf/defc actionable*
{::mf/schema schema:actionable} {::mf/schema schema:actionable}
@ -45,9 +45,13 @@
[:> :aside props [:> :aside props
[:div {:class (stl/css :notification-message)} children] [:div {:class (stl/css :notification-message)} children]
[:> button* {:variant "secondary"
:on-click on-cancel} (when cancel-label
cancel-label] [:> button* {:variant "secondary"
[:> button* {:variant (if (= variant "default") "primary" "destructive") :on-click on-cancel}
:on-click on-accept} cancel-label])
accept-label]]))
(when accept-label
[:> button* {:variant (if (= variant "default") "primary" "destructive")
:on-click on-accept}
accept-label])]))

View File

@ -14,10 +14,10 @@
[okulary.core :as l] [okulary.core :as l]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(def ref:notification (def ^:private ref:notification
(l/derived :notification st/state)) (l/derived :notification st/state))
(mf/defc current-notification (mf/defc current-notification*
[] []
(let [notification (mf/deref ref:notification) (let [notification (mf/deref ref:notification)
on-close (mf/use-fn #(st/emit! (ntf/hide))) on-close (mf/use-fn #(st/emit! (ntf/hide)))

View File

@ -9,6 +9,7 @@
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.main.data.common :as dcm] [app.main.data.common :as dcm]
[app.main.data.helpers :as dsh]
[app.main.data.persistence :as dps] [app.main.data.persistence :as dps]
[app.main.data.plugins :as dpl] [app.main.data.plugins :as dpl]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
@ -45,9 +46,10 @@
(mf/defc workspace-content* (mf/defc workspace-content*
{::mf/private true} {::mf/private true}
[{:keys [file layout page wglobal]}] [{:keys [file layout page wglobal]}]
(let [palete-size (mf/use-state nil) (let [palete-size (mf/use-state nil)
selected (mf/deref refs/selected-shapes) 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 [vport] :as wlocal} (mf/deref refs/workspace-local)
{:keys [options-mode]} wglobal {:keys [options-mode]} wglobal
@ -120,10 +122,46 @@
:overlay true :overlay true
:file-loading 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/defc workspace-page*
{::mf/private true} {::mf/private true}
[{:keys [page-id file-id file layout wglobal]}] [{: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 [] (mf/with-effect []
(let [focus-out #(st/emit! (dw/workspace-focus-lost)) (let [focus-out #(st/emit! (dw/workspace-focus-lost))
@ -133,8 +171,7 @@
(mf/with-effect [file-id page-id] (mf/with-effect [file-id page-id]
(st/emit! (dw/initialize-page file-id page-id)) (st/emit! (dw/initialize-page file-id page-id))
(fn [] (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) (if (some? page)
[:> workspace-content* {:file file [:> workspace-content* {:file file
@ -143,18 +180,9 @@
:layout layout}] :layout layout}]
[:> workspace-loader*]))) [:> 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/defc workspace*
{::mf/props :obj {::mf/wrap [mf/memo]}
::mf/wrap [mf/memo]} [{:keys [team-id project-id file-id page-id layout-name]}]
[{:keys [project-id file-id page-id layout-name]}]
(let [file-id (hooks/use-equal-memo file-id) (let [file-id (hooks/use-equal-memo file-id)
page-id (hooks/use-equal-memo page-id) page-id (hooks/use-equal-memo page-id)
@ -162,8 +190,15 @@
layout (mf/deref refs/workspace-layout) layout (mf/deref refs/workspace-layout)
wglobal (mf/deref refs/workspace-global) wglobal (mf/deref refs/workspace-global)
team (mf/deref refs/team) team-ref (mf/with-memo [team-id]
file (mf/deref ref:file-without-data) (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) file-name (:name file)
permissions (:permissions team) permissions (:permissions team)
@ -187,14 +222,14 @@
(when file-name (when file-name
(dom/set-html-title (tr "title.workspace" file-name)))) (dom/set-html-title (tr "title.workspace" file-name))))
(mf/with-effect [file-id] (mf/with-effect [team-id file-id]
(st/emit! (dw/initialize-workspace file-id)) (st/emit! (dw/initialize-workspace team-id file-id))
(fn [] (fn []
(st/emit! ::dps/force-persist (st/emit! ::dps/force-persist
(dw/finalize-workspace file-id)))) (dw/finalize-workspace team-id file-id))))
(mf/with-effect [file page-id] (mf/with-effect [file-id page-id file-loaded?]
(when-not page-id (when (and file-loaded? (not page-id))
(st/emit! (dcm/go-to-workspace :file-id file-id ::rt/replace true)))) (st/emit! (dcm/go-to-workspace :file-id file-id ::rt/replace true))))
[:> (mf/provider ctx/current-project-id) {:value project-id} [:> (mf/provider ctx/current-project-id) {:value project-id}
@ -208,8 +243,7 @@
:style {:background-color background-color :style {:background-color background-color
:touch-action "none"}} :touch-action "none"}}
[:> context-menu*] [:> context-menu*]
(if (and file-loaded? page-id)
(if (::has-data file)
[:> workspace-page* [:> workspace-page*
{:page-id page-id {:page-id page-id
:file-id file-id :file-id file-id

View File

@ -17,8 +17,10 @@
[] []
(let [worker (uw/init cf/worker-uri err/on-error)] (let [worker (uw/init cf/worker-uri err/on-error)]
(uw/ask! worker {:cmd :configure (uw/ask! worker {:cmd :configure
:key :public-uri :config {:public-uri cf/public-uri
:val cf/public-uri}) :build-data cf/build-date
:version cf/version}})
(set! instance worker))) (set! instance worker)))
(defn ask! (defn ask!

View File

@ -13,7 +13,6 @@
[app.main.data.exports.files :as exports.files] [app.main.data.exports.files :as exports.files]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.data.workspace.versions :as dwv] [app.main.data.workspace.versions :as dwv]
[app.main.features :as features]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.store :as st] [app.main.store :as st]
[app.main.worker :as uw] [app.main.worker :as uw]
@ -237,7 +236,7 @@
:else :else
(let [file (u/locate-file id) (let [file (u/locate-file id)
features (features/get-team-enabled-features @st/state) features (:features @st/state)
team-id (:current-team-id @st/state) team-id (:current-team-id @st/state)
format (case format format (case format
"zip" :legacy-zip "zip" :legacy-zip

View File

@ -41,7 +41,7 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ _ _]
(rx/of (features/initialize (or features #{})))))) (rx/of (features/initialize features)))))
(defn- fetch-team (defn- fetch-team
[& {:keys [file-id]}] [& {:keys [file-id]}]
@ -98,7 +98,7 @@
(ptk/reify ::fetch-objects-bundle (ptk/reify ::fetch-objects-bundle
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [features (features/get-team-enabled-features state)] (let [features (get state :features)]
(->> (rx/zip (->> (rx/zip
(repo/cmd! :get-font-variants {:file-id file-id :share-id share-id}) (repo/cmd! :get-font-variants {:file-id file-id :share-id share-id})
(repo/cmd! :get-page {:file-id file-id (repo/cmd! :get-page {:file-id file-id
@ -237,7 +237,7 @@
(ptk/reify ::fetch-components-bundle (ptk/reify ::fetch-components-bundle
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(let [features (features/get-team-enabled-features state)] (let [features (get state :features)]
(->> (repo/cmd! :get-file {:id file-id :features features}) (->> (repo/cmd! :get-file {:id file-id :features features})
(rx/map (fn [file] #(assoc % :file file)))))))) (rx/map (fn [file] #(assoc % :file file))))))))
@ -309,7 +309,6 @@
(defn ^:export init (defn ^:export init
[] []
(st/emit! (features/initialize))
(init-ui)) (init-ui))
(defn reinit (defn reinit

View File

@ -104,7 +104,9 @@
(defn send! (defn send!
[ws msg] [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! (defn close!
[ws] [ws]

View File

@ -135,15 +135,15 @@
(rx/debounce 1) (rx/debounce 1)
(rx/subs! (fn [[messages dropped last]] (rx/subs! (fn [[messages dropped last]]
;; Send back the dropped messages replies ;; Send back the dropped messages replies
(doseq [msg dropped] (doseq [msg dropped]
(drop-message msg)) (drop-message msg))
;; Process the message ;; Process the message
(doseq [msg (vals messages)] (doseq [msg (vals messages)]
(handle-message msg)) (handle-message msg))
;; After process the buffer we send a clear ;; After process the buffer we send a clear
(when-not (= last ::clear) (when-not (= last ::clear)
(rx/push! buffer ::clear))))))) (rx/push! buffer ::clear)))))))

View File

@ -50,8 +50,16 @@
(handler (assoc message :cmd :snaps/update-page-index)))) (handler (assoc message :cmd :snaps/update-page-index))))
(defmethod handler :configure (defmethod handler :configure
[{:keys [key val]}] [{:keys [config]}]
(log/info :hint "configure worker" :key key :val (dm/str val)) (log/info :hint "configure worker" :keys (keys config))
(case key
:public-uri (when-let [public-uri (get config :public-uri)]
(set! cf/public-uri val))) (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)

View File

@ -27,7 +27,6 @@
[app.main.data.workspace.selection :as dws] [app.main.data.workspace.selection :as dws]
[app.main.data.workspace.shortcuts] [app.main.data.workspace.shortcuts]
[app.main.errors :as errors] [app.main.errors :as errors]
[app.main.features :as features]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.store :as st] [app.main.store :as st]
[app.util.debug :as dbg] [app.util.debug :as dbg]
@ -393,7 +392,7 @@
(ptk/reify ::repair-current-file (ptk/reify ::repair-current-file
ptk/EffectEvent ptk/EffectEvent
(effect [_ state _] (effect [_ state _]
(let [features (features/get-team-enabled-features state) (let [features (:features state)
sid (:session-id state) sid (:session-id state)
file (dsh/lookup-file state) file (dsh/lookup-file state)
@ -430,7 +429,3 @@
(defn ^:export set-shape-ref (defn ^:export set-shape-ref
[id shape-ref] [id shape-ref]
(st/emit! (dw/set-shape-ref id shape-ref))) (st/emit! (dw/set-shape-ref id shape-ref)))
(defn ^:export enable-text-v2
[]
(st/emit! (features/enable-feature "text-editor/v2")))

View File

@ -20,13 +20,12 @@
nil) nil)
(defn ^:export get-enabled [] (defn ^:export get-enabled []
(clj->js (features/get-enabled-features @st/state))) (clj->js features/global-enabled-features))
(defn ^:export get-team-enabled [] (defn ^:export get-team-enabled []
(clj->js (features/get-team-enabled-features @st/state))) (clj->js (get @st/state :features)))
(defn ^:export plugins [] (defn ^:export plugins []
(st/emit! (features/enable-feature "plugins/runtime")) (st/emit! (features/enable-feature "plugins/runtime"))
(plugins/init-plugins-runtime!) (plugins/init-plugins-runtime!)
nil) nil)

View File

@ -4,7 +4,7 @@
"license": "MPL-2.0", "license": "MPL-2.0",
"author": "Kaleidos INC", "author": "Kaleidos INC",
"private": true, "private": true,
"packageManager": "yarn@4.2.2", "packageManager": "yarn@4.8.1+sha512.bc946f2a022d7a1a38adfc15b36a66a3807a67629789496c3714dd1703d2e6c6b1c69ff9ec3b43141ac7a1dd853b7685638eb0074300386a59c18df351ef8ff6",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/penpot/penpot" "url": "https://github.com/penpot/penpot"