Merge branch 'staging' into develop

This commit is contained in:
Andrey Antukh 2025-04-07 11:32:41 +02:00
commit 17f7f920c4
24 changed files with 309 additions and 246 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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}])])]]))

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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