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