🐛 Fix features related issues with render entrypoint (exporter)

This commit is contained in:
Andrey Antukh 2023-11-13 18:09:25 +01:00
parent 0a656e9e62
commit 26d3d7f1a8
8 changed files with 264 additions and 227 deletions

View file

@ -443,10 +443,17 @@
"Given the page data and the object-id returns the page data with all "Given the page data and the object-id returns the page data with all
other not needed objects removed from the `:objects` data other not needed objects removed from the `:objects` data
structure." structure."
[{:keys [objects] :as page} object-id] [page id-or-ids]
(let [objects (->> (cph/get-children-with-self objects object-id) (update page :objects (fn [objects]
(filter some?))] (reduce (fn [result object-id]
(assoc page :objects (d/index-by :id objects)))) (->> (cph/get-children-with-self objects object-id)
(filter some?)
(d/index-by :id)
(merge result)))
{}
(if (uuid? id-or-ids)
[id-or-ids]
id-or-ids)))))
(defn- prune-thumbnails (defn- prune-thumbnails
"Given the page data, removes the `:thumbnail` prop from all "Given the page data, removes the `:thumbnail` prop from all
@ -480,7 +487,7 @@
page)))] page)))]
(cond-> (prune-thumbnails page) (cond-> (prune-thumbnails page)
(uuid? object-id) (some? object-id)
(prune-objects object-id)))) (prune-objects object-id))))
(def schema:get-page (def schema:get-page
@ -488,7 +495,7 @@
[:file-id ::sm/uuid] [:file-id ::sm/uuid]
[:page-id {:optional true} ::sm/uuid] [:page-id {:optional true} ::sm/uuid]
[:share-id {:optional true} ::sm/uuid] [:share-id {:optional true} ::sm/uuid]
[:object-id {:optional true} ::sm/uuid] [:object-id {:optional true} [:or ::sm/uuid ::sm/coll-of-uuid]]
[:features {:optional true} ::cfeat/features]]) [:features {:optional true} ::cfeat/features]])
(sv/defmethod ::get-page (sv/defmethod ::get-page
@ -500,7 +507,8 @@
If you specify the object-id, the page-id parameter becomes If you specify the object-id, the page-id parameter becomes
mandatory. mandatory.
Mainly used for rendering purposes." Mainly used for rendering purposes on the exporter. It does not
accepts client features."
{::doc/added "1.17" {::doc/added "1.17"
::sm/params schema:get-page} ::sm/params schema:get-page}
[cfg {:keys [::rpc/profile-id file-id share-id] :as params}] [cfg {:keys [::rpc/profile-id file-id share-id] :as params}]

View file

@ -138,14 +138,20 @@
(declare get-team) (declare get-team)
(def ^:private schema:get-team (def ^:private schema:get-team
[:and
[:map {:title "get-team"} [:map {:title "get-team"}
[:id ::sm/uuid]]) [:id {:optional true} ::sm/uuid]
[:file-id {:optional true} ::sm/uuid]]
[:fn (fn [params]
(or (contains? params :id)
(contains? params :file-id)))]])
(sv/defmethod ::get-team (sv/defmethod ::get-team
{::doc/added "1.17" {::doc/added "1.17"
::sm/params schema:get-team} ::sm/params schema:get-team}
[cfg {:keys [::rpc/profile-id id]}] [cfg {:keys [::rpc/profile-id id file-id]}]
(db/tx-run! cfg #(get-team % :profile-id profile-id :team-id id))) (db/tx-run! cfg #(get-team % :profile-id profile-id :team-id id :file-id file-id)))
(defn get-team (defn get-team
[conn & {:keys [profile-id team-id project-id file-id] :as params}] [conn & {:keys [profile-id team-id project-id file-id] :as params}]

View file

@ -143,6 +143,11 @@
([s options transformer] ([s options transformer]
(m/decoder s options transformer))) (m/decoder s options transformer)))
(defn lazy-decoder
[s transformer]
(let [vfn (delay (decoder s transformer))]
(fn [v] (@vfn v))))
(defn humanize-data (defn humanize-data
[{:keys [schema errors value]} & {:keys [length level]}] [{:keys [schema errors value]} & {:keys [length level]}]
(let [errors (mapv #(update % :schema form) errors)] (let [errors (mapv #(update % :schema form) errors)]

View file

@ -43,7 +43,6 @@
[app.main.ui.shapes.text :as text] [app.main.ui.shapes.text :as text]
[app.main.ui.shapes.text.fontfaces :as ff] [app.main.ui.shapes.text.fontfaces :as ff]
[app.util.http :as http] [app.util.http :as http]
[app.util.object :as obj]
[app.util.strings :as ust] [app.util.strings :as ust]
[app.util.thumbnails :as th] [app.util.thumbnails :as th]
[app.util.timers :as ts] [app.util.timers :as ts]
@ -83,11 +82,11 @@
(let [shape-wrapper (shape-wrapper-factory objects) (let [shape-wrapper (shape-wrapper-factory objects)
frame-shape (frame/frame-shape shape-wrapper)] frame-shape (frame/frame-shape shape-wrapper)]
(mf/fnc frame-wrapper (mf/fnc frame-wrapper
[{:keys [shape] :as props}] {::mf/wrap-props false}
[{:keys [shape]}]
(let [render-thumbnails? (mf/use-ctx muc/render-thumbnails) (let [thumbnails? (mf/use-ctx muc/render-thumbnails)
childs (mapv #(get objects %) (:shapes shape))] childs (mapv (d/getf objects) (:shapes shape))]
(if (and render-thumbnails? (some? (:thumbnail shape))) (if (and thumbnails? (some? (:thumbnail shape)))
[:& frame/frame-thumbnail {:shape shape :bounds (:children-bounds shape)}] [:& frame/frame-thumbnail {:shape shape :bounds (:children-bounds shape)}]
[:& frame-shape {:shape shape :childs childs}]))))) [:& frame-shape {:shape shape :childs childs}])))))
@ -193,8 +192,8 @@
(mf/defc page-svg (mf/defc page-svg
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}
[{:keys [data thumbnails? render-embed? include-metadata?] :as props [{:keys [data use-thumbnails embed include-metadata] :as props
:or {render-embed? false include-metadata? false}}] :or {embed false include-metadata false}}]
(let [objects (:objects data) (let [objects (:objects data)
shapes (cph/get-immediate-children objects) shapes (cph/get-immediate-children objects)
dim (calculate-dimensions objects) dim (calculate-dimensions objects)
@ -206,20 +205,20 @@
(mf/deps objects) (mf/deps objects)
#(shape-wrapper-factory objects))] #(shape-wrapper-factory objects))]
[:& (mf/provider muc/render-thumbnails) {:value thumbnails?} [:& (mf/provider muc/render-thumbnails) {:value use-thumbnails}
[:& (mf/provider embed/context) {:value render-embed?} [:& (mf/provider embed/context) {:value embed}
[:& (mf/provider export/include-metadata-ctx) {:value include-metadata?} [:& (mf/provider export/include-metadata-ctx) {:value include-metadata}
[:svg {:view-box vbox [:svg {:view-box vbox
:version "1.1" :version "1.1"
:xmlns "http://www.w3.org/2000/svg" :xmlns "http://www.w3.org/2000/svg"
:xmlnsXlink "http://www.w3.org/1999/xlink" :xmlnsXlink "http://www.w3.org/1999/xlink"
:xmlns:penpot (when include-metadata? "https://penpot.app/xmlns") :xmlns:penpot (when include-metadata "https://penpot.app/xmlns")
:style {:width "100%" :style {:width "100%"
:height "100%" :height "100%"
:background bgcolor} :background bgcolor}
:fill "none"} :fill "none"}
(when include-metadata? (when include-metadata
[:& export/export-page {:id (:id data) :options (:options data)}]) [:& export/export-page {:id (:id data) :options (:options data)}])
(let [shapes (->> shapes (let [shapes (->> shapes
@ -250,9 +249,9 @@
;; the viewer and inspector ;; the viewer and inspector
(mf/defc frame-svg (mf/defc frame-svg
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}
[{:keys [objects frame zoom show-thumbnails?] :or {zoom 1} :as props}] [{:keys [objects frame zoom use-thumbnails] :or {zoom 1} :as props}]
(let [frame-id (:id frame) (let [frame-id (:id frame)
include-metadata? (mf/use-ctx export/include-metadata-ctx) include-metadata (mf/use-ctx export/include-metadata-ctx)
bounds (gsb/get-object-bounds objects frame) bounds (gsb/get-object-bounds objects frame)
@ -294,14 +293,14 @@
height (* (:height bounds) zoom) height (* (:height bounds) zoom)
vbox (format-viewbox {:width (:width bounds 0) :height (:height bounds 0)})] vbox (format-viewbox {:width (:width bounds 0) :height (:height bounds 0)})]
[:& (mf/provider muc/render-thumbnails) {:value show-thumbnails?} [:& (mf/provider muc/render-thumbnails) {:value use-thumbnails}
[:svg {:view-box vbox [:svg {:view-box vbox
:width (ust/format-precision width viewbox-decimal-precision) :width (ust/format-precision width viewbox-decimal-precision)
:height (ust/format-precision height viewbox-decimal-precision) :height (ust/format-precision height viewbox-decimal-precision)
:version "1.1" :version "1.1"
:xmlns "http://www.w3.org/2000/svg" :xmlns "http://www.w3.org/2000/svg"
:xmlnsXlink "http://www.w3.org/1999/xlink" :xmlnsXlink "http://www.w3.org/1999/xlink"
:xmlns:penpot (when include-metadata? "https://penpot.app/xmlns") :xmlns:penpot (when include-metadata "https://penpot.app/xmlns")
:fill "none"} :fill "none"}
[:& shape-wrapper {:shape frame}]]])) [:& shape-wrapper {:shape frame}]]]))
@ -312,7 +311,7 @@
[{:keys [objects root-shape zoom] :or {zoom 1} :as props}] [{:keys [objects root-shape zoom] :or {zoom 1} :as props}]
(when root-shape (when root-shape
(let [root-shape-id (:id root-shape) (let [root-shape-id (:id root-shape)
include-metadata? (mf/use-ctx export/include-metadata-ctx) include-metadata (mf/use-ctx export/include-metadata-ctx)
vector vector
(mf/use-memo (mf/use-memo
@ -348,7 +347,7 @@
:version "1.1" :version "1.1"
:xmlns "http://www.w3.org/2000/svg" :xmlns "http://www.w3.org/2000/svg"
:xmlnsXlink "http://www.w3.org/1999/xlink" :xmlnsXlink "http://www.w3.org/1999/xlink"
:xmlns:penpot (when include-metadata? "https://penpot.app/xmlns") :xmlns:penpot (when include-metadata "https://penpot.app/xmlns")
:fill "none"} :fill "none"}
[:> shape-container {:shape root-shape'} [:> shape-container {:shape root-shape'}
@ -357,8 +356,8 @@
(mf/defc object-svg (mf/defc object-svg
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}
[{:keys [objects object-id render-embed?] [{:keys [objects object-id embed]
:or {render-embed? false} :or {embed false}
:as props}] :as props}]
(let [object (get objects object-id) (let [object (get objects object-id)
object (cond-> object object (cond-> object
@ -375,7 +374,7 @@
(shape-wrapper-factory objects))] (shape-wrapper-factory objects))]
[:& (mf/provider export/include-metadata-ctx) {:value false} [:& (mf/provider export/include-metadata-ctx) {:value false}
[:& (mf/provider embed/context) {:value render-embed?} [:& (mf/provider embed/context) {:value embed}
[:svg {:id (dm/str "screenshot-" object-id) [:svg {:id (dm/str "screenshot-" object-id)
:view-box vbox :view-box vbox
:width (ust/format-precision width viewbox-decimal-precision) :width (ust/format-precision width viewbox-decimal-precision)
@ -439,20 +438,16 @@
:group [:& group-wrapper {:shape root-shape :view-box vbox}] :group [:& group-wrapper {:shape root-shape :view-box vbox}]
:frame [:& frame-wrapper {:shape root-shape :view-box vbox}])]])) :frame [:& frame-wrapper {:shape root-shape :view-box vbox}])]]))
(mf/defc components-sprite-svg (mf/defc components-svg
{::mf/wrap-props false} {::mf/wrap-props false}
[props] [{:keys [data children embed include-metadata source]}]
(let [data (obj/get props "data") (let [source (keyword (d/nilv source "components"))]
children (obj/get props "children") [:& (mf/provider embed/context) {:value embed}
render-embed? (obj/get props "render-embed?") [:& (mf/provider export/include-metadata-ctx) {:value include-metadata}
include-metadata? (obj/get props "include-metadata?")
source (keyword (obj/get props "source" "components"))]
[:& (mf/provider embed/context) {:value render-embed?}
[:& (mf/provider export/include-metadata-ctx) {:value include-metadata?}
[:svg {:version "1.1" [:svg {:version "1.1"
:xmlns "http://www.w3.org/2000/svg" :xmlns "http://www.w3.org/2000/svg"
:xmlnsXlink "http://www.w3.org/1999/xlink" :xmlnsXlink "http://www.w3.org/1999/xlink"
:xmlns:penpot (when include-metadata? "https://penpot.app/xmlns") :xmlns:penpot (when include-metadata "https://penpot.app/xmlns")
:style {:display (when-not (some? children) "none")} :style {:display (when-not (some? children) "none")}
:fill "none"} :fill "none"}
[:defs [:defs
@ -511,7 +506,7 @@
(->> (rx/of data) (->> (rx/of data)
(rx/map (rx/map
(fn [data] (fn [data]
(let [elem (mf/element page-svg #js {:data data :render-embed? true :include-metadata? true})] (let [elem (mf/element page-svg #js {:data data :embed true :include-metadata true})]
(rds/renderToStaticMarkup elem))))))) (rds/renderToStaticMarkup elem)))))))
(defn render-components (defn render-components
@ -531,8 +526,8 @@
(->> (rx/of data) (->> (rx/of data)
(rx/map (rx/map
(fn [data] (fn [data]
(let [elem (mf/element components-sprite-svg (let [elem (mf/element components-svg
#js {:data data :render-embed? true :include-metadata? true #js {:data data :embed true :include-metadata true
:source (name source)})] :source (name source)})]
(rds/renderToStaticMarkup elem)))))))) (rds/renderToStaticMarkup elem))))))))

View file

@ -88,7 +88,7 @@
(assoc :thumbnail (get thumbnail-data (dm/str page-id (:id frame)))) (assoc :thumbnail (get thumbnail-data (dm/str page-id (:id frame))))
(assoc :children-bounds children-bounds)) (assoc :children-bounds children-bounds))
:objects objects :objects objects
:show-thumbnails? true}]] :use-thumbnails true}]]
[:div.thumbnail-info [:div.thumbnail-info
[:span.name {:title (:name frame)} (:name frame)]]])) [:span.name {:title (:name frame)} (:name frame)]]]))

View file

@ -8,125 +8,54 @@
"The main entry point for UI part needed by the exporter." "The main entry point for UI part needed by the exporter."
(:require (:require
[app.common.geom.shapes.bounds :as gsb] [app.common.geom.shapes.bounds :as gsb]
[app.common.logging :as l] [app.common.logging :as log]
[app.common.math :as mth] [app.common.math :as mth]
[app.common.spec :as us] [app.common.schema :as sm]
[app.common.types.components-list :as ctkl] [app.common.types.components-list :as ctkl]
[app.common.uri :as u] [app.common.uri :as u]
[app.main.data.fonts :as df] [app.main.data.fonts :as df]
[app.main.features :as feat] [app.main.data.users :as du]
[app.main.features :as features]
[app.main.render :as render] [app.main.render :as render]
[app.main.repo :as repo] [app.main.repo :as repo]
[app.main.store :as st] [app.main.store :as st]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.globals :as glob] [app.util.globals :as glob]
[beicon.core :as rx] [beicon.core :as rx]
[clojure.spec.alpha :as s]
[cuerdas.core :as str] [cuerdas.core :as str]
[garden.core :refer [css]] [garden.core :refer [css]]
[okulary.core :as l]
[potok.core :as ptk]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (log/setup! {:app :info})
;; SETUP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(l/setup! {:app :info}) (defn- fetch-team
[& {:keys [file-id]}]
(defonce app-root (ptk/reify ::fetch-team
(let [el (dom/get-element "app")] ptk/WatchEvent
(mf/create-root el))) (watch [_ _ _]
(->> (repo/cmd! :get-team {:file-id file-id})
(declare ^:private render-single-object) (rx/mapcat (fn [team]
(declare ^:private render-components) (rx/of (du/set-current-team team)
(declare ^:private render-objects) (ptk/data-event ::team-fetched team))))))))
(defn- parse-params
[loc]
(let [href (unchecked-get loc "href")]
(some-> href u/uri :query u/query-string->map)))
(defn init-ui
[]
(when-let [params (parse-params glob/location)]
(when-let [component (case (:route params)
"objects" (render-objects params)
"components" (render-components params)
nil)]
(mf/render! app-root component))))
(defn ^:export init
[]
(st/emit! (feat/initialize))
(init-ui))
(defn reinit
[]
(mf/unmount! app-root)
(init-ui))
(defn ^:dev/after-load after-load
[]
(reinit))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; COMPONENTS ;; COMPONENTS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ---- SINGLE OBJECT (def ^:private ref:objects
(l/derived :objects st/state))
(defn use-resource
"A general purpose hook for retrieve or subscribe to remote changes
using the reactive-streams mechanism mechanism.
It receives a function to execute for retrieve the stream that will
be used for creating the subscription. The function should be
stable, so is the responsibility of the user of this hook to
properly memoize it.
TODO: this should be placed in some generic hooks namespace but his
right now is pending of refactor and it will be done later."
[f]
(let [[state ^js update-state!] (mf/useState {:loaded? false})]
(mf/with-effect [f]
(update-state! (fn [prev] (assoc prev :refreshing? true)))
(let [on-value (fn [data]
(update-state! #(-> %
(assoc :refreshing? false)
(assoc :loaded? true)
(merge data))))
subs (rx/subscribe (f) on-value)]
#(rx/dispose! subs)))
state))
(mf/defc object-svg (mf/defc object-svg
[{:keys [page-id file-id share-id object-id render-embed?]}] {::mf/wrap-props false}
(let [components-v2 (feat/use-feature "components/v2") [{:keys [object-id embed]}]
fetch-state (mf/use-fn (let [objects (mf/deref ref:objects)]
(mf/deps file-id page-id share-id object-id components-v2)
(fn []
(let [features (cond-> #{} components-v2 (conj "components/v2"))]
(->> (rx/zip
(repo/cmd! :get-font-variants {:file-id file-id :share-id share-id})
(repo/cmd! :get-page {:file-id file-id
:page-id page-id
:share-id share-id
:object-id object-id
:features features}))
(rx/tap (fn [[fonts]]
(when (seq fonts)
(st/emit! (df/fonts-fetched fonts)))))
(rx/map (comp :objects second))
(rx/map (fn [objects]
(let [objects (render/adapt-objects-for-shape objects object-id)]
{:objects objects
:object (get objects object-id)})))))))
{:keys [objects object]} (use-resource fetch-state)]
;; Set the globa CSS to assign the page size, needed for PDF ;; Set the globa CSS to assign the page size, needed for PDF
;; exportation process. ;; exportation process.
(mf/with-effect [object] (mf/with-effect [objects]
(when object (when-let [object (get objects object-id)]
(let [{:keys [width height]} (gsb/get-object-bounds [objects] object)] (let [{:keys [width height]} (gsb/get-object-bounds [objects] object)]
(dom/set-page-style! (dom/set-page-style!
{:size (str/concat {:size (str/concat
@ -137,90 +66,107 @@
[:& render/object-svg [:& render/object-svg
{:objects objects {:objects objects
:object-id object-id :object-id object-id
:render-embed? render-embed?}]))) :embed embed}])))
(mf/defc objects-svg (mf/defc objects-svg
[{:keys [page-id file-id share-id object-ids render-embed?]}] {::mf/wrap-props false}
(let [components-v2 (feat/use-feature "components/v2") [{:keys [object-ids embed]}]
fetch-state (mf/use-fn (when-let [objects (mf/deref ref:objects)]
(mf/deps file-id page-id share-id components-v2)
(fn []
(let [features (cond-> #{} components-v2 (conj "components/v2"))]
(->> (rx/zip
(repo/cmd! :get-font-variants {:file-id file-id :share-id share-id})
(repo/cmd! :get-page {:file-id file-id
:page-id page-id
:share-id share-id
:features features}))
(rx/tap (fn [[fonts]]
(when (seq fonts)
(st/emit! (df/fonts-fetched fonts)))))
(rx/map (fn [[_ page]] {:objects (:objects page)}))))))
{:keys [objects]} (use-resource fetch-state)]
(when objects
(for [object-id object-ids] (for [object-id object-ids]
(let [objects (render/adapt-objects-for-shape objects object-id)] (let [objects (render/adapt-objects-for-shape objects object-id)]
[:& render/object-svg [:& render/object-svg
{:objects objects {:objects objects
:key (str object-id) :key (str object-id)
:object-id object-id :object-id object-id
:render-embed? render-embed?}]))))) :embed embed}]))))
(s/def ::page-id ::us/uuid) (defn- fetch-objects-bundle
(s/def ::file-id ::us/uuid) [& {:keys [file-id page-id share-id object-id] :as options}]
(s/def ::share-id ::us/uuid) (ptk/reify ::fetch-objects-bundle
(s/def ::object-id ptk/WatchEvent
(s/or :single ::us/uuid (watch [_ state _]
:multiple (s/coll-of ::us/uuid))) (let [features (features/get-team-enabled-features state)]
(s/def ::embed ::us/boolean) (->> (rx/zip
(repo/cmd! :get-font-variants {:file-id file-id :share-id share-id})
(repo/cmd! :get-page {:file-id file-id
:page-id page-id
:share-id share-id
:object-id object-id
:features features}))
(rx/tap (fn [[fonts]]
(when (seq fonts)
(st/emit! (df/fonts-fetched fonts)))))
(rx/observe-on :async)
(rx/map (comp :objects second))
(rx/map (fn [objects]
(let [objects (render/adapt-objects-for-shape objects object-id)]
#(assoc % :objects objects)))))))))
(s/def ::render-objects (def ^:private schema:render-objects
(s/keys :req-un [::file-id ::page-id ::object-id] [:map {:title "render-objets"}
:opt-un [::render-embed ::share-id])) [:page-id ::sm/uuid]
[:file-id ::sm/uuid]
[:share-id {:optional true} ::sm/uuid]
[:embed {:optional true} :boolean]
[:object-id
[:or
::sm/uuid
::sm/coll-of-uuid]]])
(def ^:private render-objects-decoder
(sm/lazy-decoder schema:render-objects
sm/default-transformer))
(def ^:private render-objects-validator
(sm/lazy-validator schema:render-objects))
(defn- render-objects (defn- render-objects
[params] [params]
(let [{:keys [file-id (let [{:keys [file-id page-id embed share-id object-id] :as params} (render-objects-decoder params)]
page-id (if-not (render-objects-validator params)
render-embed (do
share-id] (js/console.error "invalid arguments")
:as params} (sm/pretty-explain schema:render-objects params)
(us/conform ::render-objects params) nil)
[type object-id] (:object-id params)] (do
(case type (st/emit! (ptk/reify ::initialize-render-objects
:single ptk/WatchEvent
(watch [_ _ stream]
(rx/merge
(rx/of (fetch-team :file-id file-id))
(->> stream
(rx/filter (ptk/type? ::team-fetched))
(rx/observe-on :async)
(rx/map (constantly params))
(rx/map fetch-objects-bundle))))))
(if (uuid? object-id)
(mf/html (mf/html
[:& object-svg [:& object-svg
{:file-id file-id {:file-id file-id
:page-id page-id :page-id page-id
:share-id share-id :share-id share-id
:object-id object-id :object-id object-id
:render-embed? render-embed}]) :embed embed}])
:multiple
(mf/html (mf/html
[:& objects-svg [:& objects-svg
{:file-id file-id {:file-id file-id
:page-id page-id :page-id page-id
:share-id share-id :share-id share-id
:object-ids (into #{} object-id) :object-ids (into #{} object-id)
:render-embed? render-embed}])))) :embed embed}]))))))
;; ---- COMPONENTS SPRITE ;; ---- COMPONENTS SPRITE
(mf/defc components-sprite-svg (mf/defc components-svg
[{:keys [file-id embed] :as props}] {::mf/wrap-props false}
(let [fetch (mf/use-fn [{:keys [embed component-id]}]
(mf/deps file-id) (let [file-ref (mf/with-memo [] (l/derived :file st/state))
(fn [] (repo/cmd! :get-file {:id file-id}))) state (mf/use-state {:component-id component-id})]
(when-let [file (mf/deref file-ref)]
file (use-resource fetch)
state (mf/use-state nil)]
(when file
[:* [:*
[:style [:style
(css [[:body (css [[:body
@ -266,7 +212,7 @@
[:a {:on-click on-click} (:name data)]]))] [:a {:on-click on-click} (:name data)]]))]
[:main [:main
[:& render/components-sprite-svg [:& render/components-svg
{:data (:data file) {:data (:data file)
:embed embed} :embed embed}
@ -275,16 +221,93 @@
]))) ])))
(s/def ::component-id ::us/uuid) (defn- fetch-components-bundle
(s/def ::render-components [& {:keys [file-id]}]
(s/keys :req-un [::file-id] (ptk/reify ::fetch-components-bundle
:opt-un [::embed ::component-id])) ptk/WatchEvent
(watch [_ state _]
(let [features (features/get-team-enabled-features state)]
(->> (repo/cmd! :get-file {:id file-id :features features})
(rx/map (fn [file] #(assoc % :file file))))))))
(def ^:private schema:render-components
[:map {:title "render-components"}
[:file-id ::sm/uuid]
[:embed {:optional true} :boolean]
[:component-id {:optional true} ::sm/uuid]])
(def ^:private render-components-decoder
(sm/lazy-decoder schema:render-components
sm/default-transformer))
(def ^:private render-components-validator
(sm/lazy-validator schema:render-components))
(defn render-components (defn render-components
[params] [params]
(let [{:keys [file-id component-id embed]} (us/conform ::render-components params)] (let [{:keys [file-id component-id embed] :as params} (render-components-decoder params)]
(if-not (render-components-validator params)
(do
(js/console.error "invalid arguments")
(sm/pretty-explain schema:render-components params)
nil)
(do
(st/emit! (ptk/reify ::initialize-render-components
ptk/WatchEvent
(watch [_ _ stream]
(rx/merge
(rx/of (fetch-team :file-id file-id))
(->> stream
(rx/filter (ptk/type? ::team-fetched))
(rx/observe-on :async)
(rx/map (constantly params))
(rx/map fetch-components-bundle))))))
(mf/html (mf/html
[:& components-sprite-svg [:& components-svg
{:file-id file-id {:component-id component-id
:component-id component-id :embed embed}])))))
:embed embed}])))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; SETUP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defonce app-root
(let [el (dom/get-element "app")]
(mf/create-root el)))
(declare ^:private render-single-object)
(declare ^:private render-components)
(declare ^:private render-objects)
(defn- parse-params
[loc]
(let [href (unchecked-get loc "href")]
(some-> href u/uri :query u/query-string->map)))
(defn init-ui
[]
(when-let [params (parse-params glob/location)]
(when-let [component (case (:route params)
"objects" (render-objects params)
"components" (render-components params)
nil)]
(mf/render! app-root component))))
(defn ^:export init
[]
(st/emit! (features/initialize))
(init-ui))
(defn reinit
[]
(init-ui))
(defn ^:dev/after-load after-load
[]
(reinit))

View file

@ -63,8 +63,8 @@
(let [objects (:objects page) (let [objects (:objects page)
frame (some->> page :thumbnail-frame-id (get objects)) frame (some->> page :thumbnail-frame-id (get objects))
element (if frame element (if frame
(mf/element render/frame-svg #js {:objects objects :frame frame :show-thumbnails? true}) (mf/element render/frame-svg #js {:objects objects :frame frame :use-thumbnails true})
(mf/element render/page-svg #js {:data page :thumbnails? true :render-embed? true})) (mf/element render/page-svg #js {:data page :use-thumbnails true :embed true}))
data (rds/renderToStaticMarkup element)] data (rds/renderToStaticMarkup element)]
{:data data {:data data
:fonts @fonts/loaded-hints :fonts @fonts/loaded-hints