Merge pull request #6234 from penpot/niwinz-staging-bugfixes-3

🐛 Several bugfixes and backports
This commit is contained in:
Alejandro 2025-04-07 11:15:32 +02:00 committed by GitHub
commit 3197dfddd9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 359 additions and 359 deletions

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

@ -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]
@ -107,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)]
@ -164,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]
@ -497,7 +496,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

@ -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

@ -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/difference 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

@ -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

@ -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

@ -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"