♻️ Refactor workspace thumbails (again)

We probably need a counter of number of types this
code was refactored hehe
This commit is contained in:
Andrey Antukh 2023-09-21 11:35:28 +02:00
parent 4ac2a64a2a
commit d0c1a9683a
11 changed files with 290 additions and 275 deletions

View file

@ -15,7 +15,6 @@
[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.features :as feat]
[app.main.imposters :as imp]
[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]
@ -114,7 +113,6 @@
(theme/init! cf/themes) (theme/init! cf/themes)
(cur/init-styles) (cur/init-styles)
(thr/init!) (thr/init!)
(imp/init!)
(init-ui) (init-ui)
(st/emit! (initialize))) (st/emit! (initialize)))

View file

@ -385,9 +385,10 @@
;; we only need to proceed when page-index is properly loaded ;; we only need to proceed when page-index is properly loaded
(when-let [pindex (-> state :workspace-data :pages-index)] (when-let [pindex (-> state :workspace-data :pages-index)]
(if (contains? pindex page-id) (if (contains? pindex page-id)
(let [file-id (:current-file-id state)]
(rx/of (preload-data-uris page-id) (rx/of (preload-data-uris page-id)
(dwth/watch-state-changes) (dwth/watch-state-changes file-id page-id)
(dwl/watch-component-changes)) (dwl/watch-component-changes)))
(let [page-id (dm/get-in state [:workspace-data :pages 0])] (let [page-id (dm/get-in state [:workspace-data :pages 0])]
(rx/of (go-to-page page-id)))))))) (rx/of (go-to-page page-id))))))))

View file

@ -13,7 +13,7 @@
[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.workspace.changes :as dch] [app.main.data.workspace.changes :as dch]
;; [app.main.data.workspace.thumbnails :as dwt] [app.main.data.workspace.thumbnails :as dwt]
[app.main.features :as features] [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]
@ -156,10 +156,9 @@
(->> (rp/cmd! :update-file params) (->> (rp/cmd! :update-file params)
(rx/mapcat (fn [lagged] (rx/mapcat (fn [lagged]
(log/debug :hint "changes persisted" :lagged (count lagged)) (log/debug :hint "changes persisted" :lagged (count lagged))
(let [ (let [frame-updates
;; frame-updates (-> (group-by :page-id changes)
;; (-> (group-by :page-id changes) (update-vals #(into #{} (mapcat :frames) %)))
;; (update-vals #(into #{} (mapcat :frames) %)))
commits commits
(->> @pending-commits (->> @pending-commits
@ -167,10 +166,11 @@
(rx/concat (rx/concat
(rx/merge (rx/merge
#_(->> (rx/from frame-updates) (->> (rx/from frame-updates)
(rx/mapcat (fn [[page-id frames]] (rx/mapcat (fn [[page-id frames]]
(->> frames (map #(vector page-id %))))) (->> frames (map (fn [frame-id] [file-id page-id frame-id])))))
(rx/map (fn [[_ frame-id]] (dwt/update-thumbnail frame-id)))) (rx/map (fn [data]
(ptk/data-event ::dwt/update data))))
(->> (rx/from (concat lagged commits)) (->> (rx/from (concat lagged commits))
(rx/merge-map (rx/merge-map

View file

@ -7,67 +7,92 @@
(ns app.main.data.workspace.thumbnails (ns app.main.data.workspace.thumbnails
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.logging :as log] [app.common.logging :as l]
;; [app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
;; [app.common.uuid :as uuid] [app.common.uuid :as uuid]
;; [app.main.data.workspace.changes :as dch] [app.main.data.workspace.changes :as dch]
;; [app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.notifications :as-alias wnt]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.rasterizer :as thr] [app.main.rasterizer :as thr]
;; [app.main.refs :as refs] [app.main.refs :as refs]
[app.main.render :as render]
[app.main.repo :as rp] [app.main.repo :as rp]
;; [app.main.store :as st]
[app.util.http :as http] [app.util.http :as http]
[app.util.imposters :as imps]
[app.util.time :as tp] [app.util.time :as tp]
[app.util.timers :as tm] [app.util.timers :as tm]
[app.util.webapi :as wapi] [app.util.webapi :as wapi]
[beicon.core :as rx] [beicon.core :as rx]
[cuerdas.core :as str]
[potok.core :as ptk])) [potok.core :as ptk]))
(log/set-level! :debug) (l/set-level! :info)
(declare set-workspace-thumbnail) (defn fmt-object-id
[file-id page-id frame-id]
(str/ffmt "%/%/%" file-id page-id frame-id))
(defn get-thumbnail (defn get-thumbnail
[id] [state file-id page-id frame-id & {:keys [object-id]}]
(let [object-id (dm/str id)
tp (tp/tpoint-ms)] (let [object-id (or object-id (fmt-object-id file-id page-id frame-id))
(->> (rx/of id) tp (tp/tpoint-ms)
(rx/mapcat @imps/render-fn) objects (wsh/lookup-page-objects state page-id)
(rx/filter #(= object-id (unchecked-get % "id"))) shape (get objects frame-id)]
(->> (render/render-frame objects shape object-id)
(rx/take 1) (rx/take 1)
(rx/map (fn [imposter] (rx/filter some?)
{:data (unchecked-get imposter "data")
:styles (unchecked-get imposter "styles")
:width (unchecked-get imposter "width")}))
(rx/mapcat thr/render) (rx/mapcat thr/render)
(rx/map (fn [blob] (wapi/create-uri blob))) (rx/map (fn [blob] (wapi/create-uri blob)))
(rx/tap #(log/debug :hint "generated thumbnail" :elapsed (dm/str (tp) "ms")))))) (rx/tap #(l/dbg :hint "thumbnail rendered"
:elapsed (dm/str (tp) "ms"))))))
(defn clear-thumbnail (defn- clear-thumbnail
[frame-id] ([file-id page-id frame-id]
(clear-thumbnail file-id (fmt-object-id file-id page-id frame-id)))
([file-id object-id]
(let [emit-rpc? (volatile! false)]
(ptk/reify ::clear-thumbnail (ptk/reify ::clear-thumbnail
ptk/UpdateEvent cljs.core/IDeref
(update [_ state] (-deref [_] object-id)
(let [object-id (dm/str frame-id)]
(when-let [uri (dm/get-in state [:workspace-thumbnails object-id])]
(tm/schedule-on-idle (partial wapi/revoke-uri uri)))
(update state :workspace-thumbnails dissoc object-id)))))
(defn set-workspace-thumbnail
[id uri]
(let [prev-uri* (volatile! nil)]
(ptk/reify ::set-workspace-thumbnail
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [object-id (dm/str id) (let [uri (dm/get-in state [:workspace-thumbnails object-id])]
prev-uri (dm/get-in state [:workspace-thumbnails object-id])] (if (some? uri)
(do
(l/dbg :hint "clear thumbnail" :object-id object-id)
(vreset! emit-rpc? true)
(tm/schedule-on-idle (partial wapi/revoke-uri uri))
(update state :workspace-thumbnails dissoc object-id))
state)))
ptk/WatchEvent
(watch [_ _ _]
(when ^boolean @emit-rpc?
(->> (rp/cmd! :delete-file-object-thumbnail {:file-id file-id :object-id object-id})
(rx/catch rx/empty)
(rx/ignore))))))))
(defn- assoc-thumbnail
[object-id uri]
(let [prev-uri* (volatile! nil)]
(ptk/reify ::assoc-thumbnail
ptk/UpdateEvent
(update [_ state]
(let [prev-uri (dm/get-in state [:workspace-thumbnails object-id])]
(some->> prev-uri (vreset! prev-uri*)) (some->> prev-uri (vreset! prev-uri*))
(l/trc :hint "assoc thumbnail" :object-id object-id :uri uri)
(update state :workspace-thumbnails assoc object-id uri))) (update state :workspace-thumbnails assoc object-id uri)))
ptk/EffectEvent ptk/EffectEvent
(effect [_ _ _] (effect [_ _ _]
(tm/schedule-on-idle #(some-> ^boolean @prev-uri* wapi/revoke-uri)))))) (tm/schedule-on-idle
(fn []
(when-let [uri (deref prev-uri*)]
(wapi/revoke-uri uri))))))))
(defn duplicate-thumbnail (defn duplicate-thumbnail
[old-id new-id] [old-id new-id]
@ -81,25 +106,21 @@
(defn update-thumbnail (defn update-thumbnail
"Updates the thumbnail information for the given frame `id`" "Updates the thumbnail information for the given frame `id`"
([id]
[file-id page-id frame-id]
(let [object-id (fmt-object-id file-id page-id frame-id)]
(ptk/reify ::update-thumbnail (ptk/reify ::update-thumbnail
cljs.core/IDeref
(-deref [_] object-id)
ptk/WatchEvent ptk/WatchEvent
(watch [_ state _] (watch [_ state stream]
(let [object-id (dm/str id) (l/dbg :hint "update thumbnail" :object-id object-id)
file-id (:current-file-id state)]
(rx/concat
;; Delete the thumbnail first so if we interrupt we can regenerate after
(->> (rp/cmd! :delete-file-object-thumbnail {:file-id file-id :object-id object-id})
(rx/catch rx/empty))
;; Send the update to the back-end ;; Send the update to the back-end
(->> (get-thumbnail id) (->> (get-thumbnail state file-id page-id frame-id {:object-id object-id})
(rx/filter (fn [data] (and (some? data) (some? file-id)))) (rx/mapcat (fn [uri]
(rx/merge-map
(fn [uri]
(rx/merge (rx/merge
(rx/of (set-workspace-thumbnail object-id uri)) (rx/of (assoc-thumbnail object-id uri))
(->> (http/send! {:uri uri :response-type :blob :method :get}) (->> (http/send! {:uri uri :response-type :blob :method :get})
(rx/map :body) (rx/map :body)
(rx/mapcat (fn [blob] (rx/mapcat (fn [blob]
@ -110,13 +131,20 @@
(rp/cmd! :create-file-object-thumbnail params)))) (rp/cmd! :create-file-object-thumbnail params))))
(rx/catch rx/empty) (rx/catch rx/empty)
(rx/ignore))))) (rx/ignore)))))
(rx/catch (fn [cause]
(.error js/console cause)
(rx/empty)))
(rx/catch #(do (.error js/console %) ;; We cancel all the stream if user starts editing while
(rx/empty)))))))))) ;; thumbnail is generating
(rx/take-until
(->> stream
(rx/filter (ptk/type? ::clear-thumbnail))
(rx/filter #(= (deref %) object-id)))))))))
#_(defn- extract-frame-changes (defn- extract-frame-changes
"Process a changes set in a commit to extract the frames that are changing" "Process a changes set in a commit to extract the frames that are changing"
[[event [old-data new-data]]] [page-id [event [old-data new-data]]]
(let [changes (-> event deref :changes) (let [changes (-> event deref :changes)
extract-ids extract-ids
@ -129,7 +157,7 @@
[])) []))
get-frame-id get-frame-id
(fn [[page-id id]] (fn [[_ id]]
(let [old-objects (wsh/lookup-data-objects old-data page-id) (let [old-objects (wsh/lookup-data-objects old-data page-id)
new-objects (wsh/lookup-data-objects new-data page-id) new-objects (wsh/lookup-data-objects new-data page-id)
@ -140,29 +168,31 @@
new-frame-id (if (cph/frame-shape? new-shape) id (:frame-id new-shape))] new-frame-id (if (cph/frame-shape? new-shape) id (:frame-id new-shape))]
(cond-> #{} (cond-> #{}
(and old-frame-id (not= uuid/zero old-frame-id)) (and (some? old-frame-id) (not= uuid/zero old-frame-id))
(conj [page-id old-frame-id]) (conj old-frame-id)
(and new-frame-id (not= uuid/zero new-frame-id)) (and (some? new-frame-id) (not= uuid/zero new-frame-id))
(conj [page-id new-frame-id]))))] (conj new-frame-id))))]
(into #{} (into #{}
(comp (mapcat extract-ids) (comp (mapcat extract-ids)
(filter (fn [[page-id']] (= page-id page-id')))
(mapcat get-frame-id)) (mapcat get-frame-id))
changes))) changes)))
(defn watch-state-changes (defn watch-state-changes
"Watch the state for changes inside frames. If a change is detected will force a rendering "Watch the state for changes inside frames. If a change is detected will force a rendering
of the frame data so the thumbnail can be updated." of the frame data so the thumbnail can be updated."
[] [file-id page-id]
(ptk/reify ::watch-state-changes (ptk/reify ::watch-state-changes
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _stream] (watch [_ _ stream]
#_(let [ (let [stopper-s (rx/filter
stopper (fn [event]
(->> stream (as-> (ptk/type event) type
(rx/filter #(or (= :app.main.data.workspace/finalize-page (ptk/type %)) (or (= :app.main.data.workspace/finalize-page type)
(= ::watch-state-changes (ptk/type %))))) (= ::watch-state-changes type))))
stream)
workspace-data-s workspace-data-s
(->> (rx/concat (->> (rx/concat
@ -170,27 +200,60 @@
(rx/from-atom refs/workspace-data {:emit-current-value? true})) (rx/from-atom refs/workspace-data {:emit-current-value? true}))
;; We need to keep the old-objects so we can check the frame for the ;; We need to keep the old-objects so we can check the frame for the
;; deleted objects ;; deleted objects
(rx/buffer 2 1)) (rx/buffer 2 1)
change-s
(->> stream
(rx/filter #(or (dch/commit-changes? %)
(= (ptk/type %) :app.main.data.workspace.notifications/handle-file-change)))
(rx/observe-on :async))
frame-changes-s
(->> change-s
(rx/with-latest-from workspace-data-s)
(rx/flat-map extract-frame-changes)
(rx/share)) (rx/share))
]
changes-s
(->> (rx/merge
;; LOCAL CHANGES
(->> stream
(rx/filter dch/commit-changes?)
(rx/observe-on :async)
(rx/with-latest-from workspace-data-s)
(rx/flat-map (partial extract-frame-changes page-id))
(rx/tap #(l/trc :hint "inconming change" :origin "local" :frame-id (dm/str %))))
;; NOTIFICATIONS CHANGES
(->> stream
(rx/filter (ptk/type? ::wnt/handle-file-change))
(rx/observe-on :async)
(rx/with-latest-from workspace-data-s)
(rx/flat-map (partial extract-frame-changes page-id))
(rx/tap #(l/trc :hint "inconming change" :origin "notifications" :frame-id (dm/str %))))
;; PERSISTENCE CHANGES
(->> stream
(rx/filter (ptk/type? ::update))
(rx/map deref)
(rx/filter (fn [[file-id page-id]]
(and (= file-id file-id)
(= page-id page-id))))
(rx/map (fn [[_ _ frame-id]] frame-id))
(rx/tap #(l/trc :hint "inconming change" :origin "persistence" :frame-id (dm/str %)))))
(rx/share))
;; BUFFER NOTIFIER (window of 5s of inactivity)
notifier-s
(->> changes-s
(rx/debounce 5000)
(rx/tap #(l/trc :hint "buffer initialized")))]
(->> (rx/merge (->> (rx/merge
(->> frame-changes-s ;; Perform instant thumbnail cleaning of affected frames
(rx/filter (fn [[page-id _]] (not= page-id (:current-page-id @st/state)))) ;; and interrupt any ongoing update-thumbnail process
(rx/map (fn [[_ frame-id]] (clear-thumbnail frame-id)))) ;; related to current frame-id
(->> changes-s
(rx/map (fn [frame-id]
(clear-thumbnail file-id page-id frame-id))))
;; Generate thumbnails in batchs, once user becomes
;; inactive for some instant
(->> changes-s
(rx/buffer-until notifier-s)
(rx/mapcat #(into #{} %))
(rx/map (fn [frame-id]
(update-thumbnail file-id page-id frame-id)))))
(rx/take-until stopper-s))))))
(->> frame-changes-s
(rx/filter (fn [[page-id _]] (= page-id (:current-page-id @st/state))))
(rx/map (fn [[_ frame-id]] (update-thumbnail frame-id)))))
(rx/take-until stopper))))))

View file

@ -1,77 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.imposters
(:require ["react-dom/server" :as rds]
[app.common.data.macros :as dm]
[app.common.geom.rect :as grc]
[app.common.geom.shapes :as gsh]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.fonts :as fonts]
[app.main.refs :as refs]
[app.main.render :as render]
[app.main.store :as st]
[app.main.ui.shapes.text.fontfaces :as ff]
[app.util.imposters :as imps]
[app.util.thumbnails :as th]
[beicon.core :as rx]
[rumext.v2 :as mf]))
(defn render
"Render the frame and store it in the imposter map"
([id shape objects]
(render id shape objects nil))
([id shape objects fonts]
(let [object-id (dm/str id)
shape (if (nil? shape) (get objects id) shape)
fonts (if (nil? fonts) (ff/shape->fonts shape objects) fonts)
all-children (deref (refs/all-children-objects id))
bounds
(if (:show-content shape)
(gsh/shapes->rect (cons shape all-children))
(-> shape :points grc/points->rect))
x (dm/get-prop bounds :x)
y (dm/get-prop bounds :y)
width (dm/get-prop bounds :width)
height (dm/get-prop bounds :height)
viewbox (dm/fmt "% % % %" x y width height)
[fixed-width fixed-height] (th/get-proportional-size width height)
data (rds/renderToStaticMarkup
(mf/element render/frame-imposter-svg
{:objects objects
:frame shape
:vbox viewbox
:width width
:height height
:show-thumbnails? false}))]
(->> (fonts/render-font-styles-cached fonts)
(rx/catch rx/empty)
(rx/map (fn [styles] #js {:id object-id
:data data
:viewbox viewbox
:width fixed-width
:height fixed-height
:styles styles}))))))
(defn render-by-id
"Render the shape by its id (IMPORTANT! id as uuid, not string)"
[id]
(dm/assert! "expected uuid" (uuid? id))
(let [objects (wsh/lookup-page-objects @st/state)
shape (get objects id)
fonts (ff/shape->fonts shape objects)]
(render id shape objects fonts)))
(defn init!
"Initializes the render function"
[]
(imps/init! render-by-id))

View file

@ -475,15 +475,12 @@
(dm/get-in state [:viewer-local :zoom-type])) (dm/get-in state [:viewer-local :zoom-type]))
st/state)) st/state))
(def thumbnail-data (defn workspace-thumbnail-by-id
(l/derived #(get % :workspace-thumbnails {}) st/state)) [object-id]
(defn thumbnail-frame-data
[frame-id]
(l/derived (l/derived
(fn [thumbnails] (fn [state]
(get thumbnails (dm/str frame-id))) (dm/get-in state [:workspace-thumbnails object-id]))
thumbnail-data)) st/state))
(def workspace-text-modifier (def workspace-text-modifier
(l/derived :workspace-text-modifier st/state)) (l/derived :workspace-text-modifier st/state))

View file

@ -14,11 +14,13 @@
(:require (:require
["react-dom/server" :as rds] ["react-dom/server" :as rds]
[app.common.colors :as clr] [app.common.colors :as clr]
[app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.rect :as grc] [app.common.geom.rect :as grc]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.geom.shapes.bounds :as gsb] [app.common.geom.shapes.bounds :as gsb]
[app.common.logging :as l]
[app.common.math :as mth] [app.common.math :as mth]
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
@ -43,6 +45,7 @@
[app.util.http :as http] [app.util.http :as http]
[app.util.object :as obj] [app.util.object :as obj]
[app.util.strings :as ust] [app.util.strings :as ust]
[app.util.thumbnails :as th]
[app.util.timers :as ts] [app.util.timers :as ts]
[beicon.core :as rx] [beicon.core :as rx]
[clojure.set :as set] [clojure.set :as set]
@ -229,17 +232,11 @@
[:& shape-wrapper {:shape item [:& shape-wrapper {:shape item
:key (:id item)}])]]]])) :key (:id item)}])]]]]))
;; Component that serves for render frame thumbnails, mainly used in (mf/defc frame-imposter
;; the viewer and inspector {::mf/wrap-props false}
(mf/defc frame-imposter-svg [{:keys [objects frame vbox width height]}]
{::mf/wrap [mf/memo]} (let [shape-wrapper (shape-wrapper-factory objects)]
[{:keys [objects frame vbox width height show-thumbnails?] :as props}] [:& (mf/provider muc/render-thumbnails) {:value false}
(let [shape-wrapper
(mf/use-memo
(mf/deps objects)
#(shape-wrapper-factory objects))]
[:& (mf/provider muc/render-thumbnails) {:value show-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)
@ -538,3 +535,45 @@
#js {:data data :render-embed? true :include-metadata? true #js {:data data :render-embed? true :include-metadata? true
:source (name source)})] :source (name source)})]
(rds/renderToStaticMarkup elem)))))))) (rds/renderToStaticMarkup elem))))))))
(defn render-frame
[objects shape object-id]
(let [shape-id (dm/get-prop shape :id)
fonts (ff/shape->fonts shape objects)
bounds (if (:show-content shape)
(let [ids (cph/get-children-ids objects shape-id)
children (sequence (keep (d/getf objects)) ids)]
(gsh/shapes->rect (cons shape children)))
(-> shape :points grc/points->rect))
x (dm/get-prop bounds :x)
y (dm/get-prop bounds :y)
width (dm/get-prop bounds :width)
height (dm/get-prop bounds :height)
viewbox (str/ffmt "% % % %" x y width height)
[fixed-width
fixed-height] (th/get-proportional-size width height)
data (rds/renderToStaticMarkup
(mf/element frame-imposter
#js {:objects objects
:frame shape
:vbox viewbox
:width width
:height height}))]
(->> (fonts/render-font-styles-cached fonts)
(rx/catch (fn [cause]
(l/err :hint "unexpected error on rendering imposter"
:cause cause)
(rx/empty)))
(rx/map (fn [styles]
{:id object-id
:data data
:viewbox viewbox
:width fixed-width
:height fixed-height
:styles styles})))))

View file

@ -74,6 +74,7 @@
(if ^boolean (cph/frame-shape? shape) (if ^boolean (cph/frame-shape? shape)
[:& root-frame-wrapper [:& root-frame-wrapper
{:shape shape {:shape shape
:objects objects
:thumbnail? (not (contains? active-frames (dm/get-prop shape :id)))}] :thumbnail? (not (contains? active-frames (dm/get-prop shape :id)))}]
[:& shape-wrapper {:shape shape}])])]]])) [:& shape-wrapper {:shape shape}])])]]]))

View file

@ -6,20 +6,23 @@
(ns app.main.ui.workspace.shapes.frame (ns app.main.ui.workspace.shapes.frame
(:require (:require
[app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
;; [app.common.geom.rect :as grc] [app.common.geom.rect :as grc]
;; [app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
;; [app.main.data.workspace.thumbnails :as dwt] [app.main.data.workspace.thumbnails :as dwt]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.context :as ctx]
[app.main.ui.shapes.embed :as embed] [app.main.ui.shapes.embed :as embed]
[app.main.ui.shapes.frame :as frame] [app.main.ui.shapes.frame :as frame]
[app.main.ui.shapes.shape :refer [shape-container]] [app.main.ui.shapes.shape :refer [shape-container]]
[app.main.ui.workspace.shapes.common :refer [check-shape-props]] [app.main.ui.workspace.shapes.common :refer [check-shape-props]]
[app.main.ui.workspace.shapes.frame.dynamic-modifiers :as fdm] [app.main.ui.workspace.shapes.frame.dynamic-modifiers :as fdm]
;; [app.util.debug :as dbg] [app.util.debug :as dbg]
[app.util.timers :as tm]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(defn frame-shape-factory (defn frame-shape-factory
@ -46,6 +49,12 @@
[new-props old-props] [new-props old-props]
(and (= (unchecked-get new-props "thumbnail?") (and (= (unchecked-get new-props "thumbnail?")
(unchecked-get old-props "thumbnail?")) (unchecked-get old-props "thumbnail?"))
(identical?
(unchecked-get new-props "objects")
(unchecked-get old-props "objects"))
^boolean
(check-shape-props new-props old-props))) (check-shape-props new-props old-props)))
(defn nested-frame-wrapper-factory (defn nested-frame-wrapper-factory
@ -69,10 +78,8 @@
(fdm/use-dynamic-modifiers objects (mf/ref-val node-ref) modifiers) (fdm/use-dynamic-modifiers objects (mf/ref-val node-ref) modifiers)
[:& frame-shape {:shape shape :ref node-ref}])))) [:& frame-shape {:shape shape :ref node-ref}]))))
(defn root-frame-wrapper-factory (defn root-frame-wrapper-factory
[shape-wrapper] [shape-wrapper]
(let [frame-shape (frame-shape-factory shape-wrapper)] (let [frame-shape (frame-shape-factory shape-wrapper)]
(mf/fnc frame-wrapper (mf/fnc frame-wrapper
{::mf/wrap [#(mf/memo' % check-props)] {::mf/wrap [#(mf/memo' % check-props)]
@ -80,43 +87,45 @@
[props] [props]
(let [shape (unchecked-get props "shape") (let [shape (unchecked-get props "shape")
;; thumbnail? (unchecked-get props "thumbnail?") thumbnail? (unchecked-get props "thumbnail?")
objects (unchecked-get props "objects")
;; page-id (mf/use-ctx ctx/current-page-id) file-id (mf/use-ctx ctx/current-file-id)
frame-id (:id shape) page-id (mf/use-ctx ctx/current-page-id)
frame-id (dm/get-prop shape :id)
objects (wsh/lookup-page-objects @st/state)
container-ref (mf/use-ref nil) container-ref (mf/use-ref nil)
content-ref (mf/use-ref nil) content-ref (mf/use-ref nil)
;; all-children-ref (mf/with-memo [frame-id] ;; FIXME: apply specific rendering optimizations separating to a component
;; (refs/all-children-objects frame-id)) bounds (if (:show-content shape)
;; all-children (mf/deref all-children-ref) (let [ids (cph/get-children-ids objects frame-id)
children (sequence (keep (d/getf objects)) ids)]
(gsh/shapes->rect (cons shape children)))
(-> shape :points grc/points->rect))
;; bounds x (dm/get-prop bounds :x)
;; (if (:show-content shape) y (dm/get-prop bounds :y)
;; (gsh/shapes->rect (cons shape all-children)) width (dm/get-prop bounds :width)
;; (-> shape :points grc/points->rect)) height (dm/get-prop bounds :height)
;; x (dm/get-prop bounds :x) thumbnail-uri* (mf/with-memo [file-id page-id frame-id]
;; y (dm/get-prop bounds :y) (let [object-id (dwt/fmt-object-id file-id page-id frame-id)]
;; width (dm/get-prop bounds :width) (refs/workspace-thumbnail-by-id object-id)))
;; height (dm/get-prop bounds :height) thumbnail-uri (mf/deref thumbnail-uri*)
;; thumbnail-uri* (mf/with-memo [frame-id]
;; (refs/thumbnail-frame-data frame-id))
;; thumbnail-uri (mf/deref thumbnail-uri*)
modifiers-ref (mf/with-memo [frame-id] modifiers-ref (mf/with-memo [frame-id]
(refs/workspace-modifiers-by-frame-id frame-id)) (refs/workspace-modifiers-by-frame-id frame-id))
modifiers (mf/deref modifiers-ref) modifiers (mf/deref modifiers-ref)
;; debug? (dbg/enabled? :thumbnails) hidden? (true? (:hidden shape))]
]
#_(when-not (some? thumbnail-uri) ;; NOTE: we don't add deps because we want this to be executed
(st/emit! (dwt/update-thumbnail frame-id))) ;; once on mount with only referenced the initial data
(mf/with-effect []
(when-not (some? thumbnail-uri)
(tm/schedule-on-idle
#(st/emit! (dwt/update-thumbnail file-id page-id frame-id)))))
(fdm/use-dynamic-modifiers objects (mf/ref-val content-ref) modifiers) (fdm/use-dynamic-modifiers objects (mf/ref-val content-ref) modifiers)
@ -124,24 +133,20 @@
[:g.frame-container [:g.frame-container
{:id (dm/str "frame-container-" frame-id) {:id (dm/str "frame-container-" frame-id)
:key "frame-container" :key "frame-container"
;; :ref on-container-ref :opacity (when ^boolean hidden? 0)}
:opacity (when (:hidden shape) 0)}
;; When thumbnail is enabled. [:g.frame-imposter
#_[:g.frame-imposter
;; Render thumbnail image.
[:image.thumbnail-bitmap [:image.thumbnail-bitmap
{;; :ref on-imposter-ref {:x x
:x x
:y y :y y
:width width :width width
:height height :height height
:href thumbnail-uri :href thumbnail-uri
:style {:display (when-not thumbnail? "none")}}] :style {:display (when-not ^boolean thumbnail? "none")}}]
;; Render border around image when we are debugging ;; Render border around image when we are debugging
;; thumbnails. ;; thumbnails.
(when ^boolean debug? (when (dbg/enabled? :thumbnails)
[:rect {:x (+ x 2) [:rect {:x (+ x 2)
:y (+ y 2) :y (+ y 2)
:width (- width 4) :width (- width 4)
@ -150,8 +155,10 @@
:stroke-width 2}])] :stroke-width 2}])]
;; When thumbnail is disabled. ;; When thumbnail is disabled.
(when-not false #_thumbnail? (when (or (not ^boolean thumbnail?)
(not ^boolean thumbnail-uri))
[:g.frame-content [:g.frame-content
{:id (dm/str "frame-content-" frame-id) {:id (dm/str "frame-content-" frame-id)
:ref container-ref} :ref container-ref}
[:& frame-shape {:shape shape :ref content-ref}]])]])))) [:& frame-shape {:shape shape :ref content-ref}]])]]))))

View file

@ -240,9 +240,10 @@
dest-shape-id (:id dest-shape) dest-shape-id (:id dest-shape)
;; FIXME: broken
thumbnail-data-ref (mf/use-memo thumbnail-data-ref (mf/use-memo
(mf/deps page-id dest-shape-id) (mf/deps page-id dest-shape-id)
#(refs/thumbnail-frame-data dest-shape-id)) #(refs/workspace-thumbnail-by-id dest-shape-id))
thumbnail-data (mf/deref thumbnail-data-ref) thumbnail-data (mf/deref thumbnail-data-ref)
dest-shape (cond-> dest-shape dest-shape (cond-> dest-shape

View file

@ -1,15 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.util.imposters)
;; This is needed to avoid a circular dependency between
;; app.main.ui.workspace.shapes.frame and app.util.imposters
(defonce render-fn (atom nil))
(defn init!
[fn]
(reset! render-fn fn))