diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 8a19ef1871..b516e9e75a 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -42,7 +42,6 @@ [app.main.data.workspace.common :as dwc] [app.main.data.workspace.drawing :as dwd] [app.main.data.workspace.edition :as dwe] - [app.main.data.workspace.fix-broken-shapes :as fbs] [app.main.data.workspace.fix-deleted-fonts :as fdf] [app.main.data.workspace.groups :as dwg] [app.main.data.workspace.guides :as dwgu] @@ -232,8 +231,7 @@ ptk/WatchEvent (watch [_ _ _] (rx/of (dp/check-open-plugin) - (fdf/fix-deleted-fonts) - (fbs/fix-broken-shapes))))) + (fdf/fix-deleted-fonts-for-local-library file-id))))) (defn- bundle-fetched [{:keys [file file-id thumbnails] :as bundle}] diff --git a/frontend/src/app/main/data/workspace/fix_broken_shapes.cljs b/frontend/src/app/main/data/workspace/fix_broken_shapes.cljs deleted file mode 100644 index 78f88dc823..0000000000 --- a/frontend/src/app/main/data/workspace/fix_broken_shapes.cljs +++ /dev/null @@ -1,56 +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.data.workspace.fix-broken-shapes - (:require - [app.main.data.changes :as dch] - [app.main.data.helpers :as dsh] - [beicon.v2.core :as rx] - [potok.v2.core :as ptk])) - -(defn- generate-broken-link-changes - [attr {:keys [objects id] :as container}] - (let [base {:type :fix-obj :fix :broken-children attr id} - contains? (partial contains? objects) - xform (comp - ;; FIXME: Ensure all obj have id field (this is needed - ;; because some bug adds an ephimeral shape with id ZERO, - ;; with a single attr `:shapes` having a vector of ids - ;; pointing to not existing shapes). That happens on - ;; components. THIS IS A WORKAOURD - (map (fn [[id obj]] - (if (some? (:id obj)) - obj - (assoc obj :id id)))) - - ;; Remove all valid shapes - (remove (fn [obj] - (every? contains? (:shapes obj)))) - - (map (fn [obj] - (assoc base :id (:id obj)))))] - - (sequence xform objects))) - -(defn fix-broken-shapes - [] - (ptk/reify ::fix-broken-shapes - ptk/WatchEvent - (watch [it state _] - (let [fdata (dsh/lookup-file-data state) - changes (concat - (mapcat (partial generate-broken-link-changes :page-id) - (vals (:pages-index fdata))) - (mapcat (partial generate-broken-link-changes :component-id) - (vals (:components fdata))))] - - (if (seq changes) - (rx/of (dch/commit-changes - {:origin it - :redo-changes (vec changes) - :undo-changes [] - :save-undo? false})) - (rx/empty)))))) diff --git a/frontend/src/app/main/data/workspace/fix_deleted_fonts.cljs b/frontend/src/app/main/data/workspace/fix_deleted_fonts.cljs index 1ffe8a1ba4..fb33a74dc7 100644 --- a/frontend/src/app/main/data/workspace/fix_deleted_fonts.cljs +++ b/frontend/src/app/main/data/workspace/fix_deleted_fonts.cljs @@ -14,8 +14,9 @@ [beicon.v2.core :as rx] [potok.v2.core :as ptk])) -;; This event will update the file so the texts with non existing custom fonts try to be fixed. -;; This can happen when: +;; This event will update the file so the texts with non existing +;; custom fonts try to be fixed. This can happen when: +;; ;; - Exporting/importing files to different teams or penpot instances ;; - Moving files from one team to another in the same instance ;; - Custom fonts are explicitly deleted in the team area @@ -23,112 +24,99 @@ (defn- calculate-alternative-font-id [value] (let [fonts (deref fonts/fontsdb)] - (->> (vals fonts) - (filter #(= (:family %) value)) - (first) - :id))) + (reduce-kv (fn [_ _ font] + (if (= (:family font) value) + (reduced (:id font)) + nil)) + nil + fonts))) (defn- has-invalid-font-family? [node] - (let [fonts (deref fonts/fontsdb) - font-family (:font-family node) - alternative-font-id (calculate-alternative-font-id font-family)] + (let [fonts (deref fonts/fontsdb) + font-family (:font-family node)] (and (some? font-family) - (nil? (get fonts (:font-id node))) - (some? alternative-font-id)))) + (nil? (get fonts (:font-id node)))))) -(defn- should-fix-deleted-font-shape? +(defn- shape-has-invalid-font-family?? [shape] - (let [text-nodes (txt/node-seq txt/is-text-node? (:content shape))] - (and (cfh/text-shape? shape) - (some has-invalid-font-family? text-nodes)))) - -(defn- should-fix-deleted-font-component? - [component] - (let [xf (comp (map val) - (filter should-fix-deleted-font-shape?))] - (first (sequence xf (:objects component))))) + (and (cfh/text-shape? shape) + (some has-invalid-font-family? + (txt/node-seq txt/is-text-node? (:content shape))))) (defn- fix-deleted-font [node] - (let [alternative-font-id (calculate-alternative-font-id (:font-family node))] - (cond-> node - (some? alternative-font-id) (assoc :font-id alternative-font-id)))) + (if-let [alternative-font-id (calculate-alternative-font-id (:font-family node))] + (assoc node :font-id alternative-font-id) + node)) -(defn- fix-deleted-font-shape +(defn- fix-shape-content [shape] - (let [transform (partial txt/transform-nodes has-invalid-font-family? fix-deleted-font)] - (update shape :content transform))) + (txt/transform-nodes has-invalid-font-family? fix-deleted-font + (:content shape))) -(defn- fix-deleted-font-component - [component] - (update component - :objects - (fn [objects] - (update-vals objects fix-deleted-font-shape)))) - -(defn fix-deleted-font-typography +(defn- fix-typography [typography] - (let [alternative-font-id (calculate-alternative-font-id (:font-family typography))] - (cond-> typography - (some? alternative-font-id) (assoc :font-id alternative-font-id)))) + (if-let [alternative-font-id (calculate-alternative-font-id (:font-family typography))] + (assoc typography :font-id alternative-font-id) + typography)) -(defn- generate-deleted-font-shape-changes +(defn- generate-page-changes [{:keys [objects id]}] - (sequence - (comp (map val) - (filter should-fix-deleted-font-shape?) - (map (fn [shape] - {:type :mod-obj - :id (:id shape) - :page-id id - :operations [{:type :set - :attr :content - :val (:content (fix-deleted-font-shape shape))} - {:type :set - :attr :position-data - :val nil}]}))) - objects)) + (reduce-kv (fn [changes shape-id shape] + (if (shape-has-invalid-font-family?? shape) + (conj changes {:type :mod-obj + :id shape-id + :page-id id + :operations [{:type :set + :attr :content + :val (fix-shape-content shape)} + {:type :set + :attr :position-data + :val nil}]}) + changes)) + [] + objects)) -(defn- generate-deleted-font-components-changes +(defn- generate-library-changes [fdata] - (sequence - (comp (map val) - (filter should-fix-deleted-font-component?) - (map (fn [component] - {:type :mod-component - :id (:id component) - :objects (-> (fix-deleted-font-component component) :objects)}))) - (:components fdata))) + (reduce-kv (fn [changes _ typography] + (if (has-invalid-font-family? typography) + (conj changes {:type :mod-typography + :typography (fix-typography typography)}) + changes)) + [] + (:typographies fdata))) -(defn- generate-deleted-font-typography-changes - [fdata] - (sequence - (comp (map val) - (filter has-invalid-font-family?) - (map (fn [typography] - {:type :mod-typography - :typography (fix-deleted-font-typography typography)}))) - (:typographies fdata))) - -(defn fix-deleted-fonts - [] - (ptk/reify ::fix-deleted-fonts +(defn fix-deleted-fonts-for-local-library + "Looks the file local library for deleted fonts and emit changes if + invalid but fixable typographyes found." + [file-id] + (ptk/reify ::fix-deleted-fonts-for-local-library ptk/WatchEvent (watch [it state _] - (let [fdata (dsh/lookup-file-data state) - pages (:pages-index fdata) - - shape-changes (mapcat generate-deleted-font-shape-changes (vals pages)) - components-changes (generate-deleted-font-components-changes fdata) - typography-changes (generate-deleted-font-typography-changes fdata) - changes (concat shape-changes - components-changes - typography-changes)] - (if (seq changes) + (let [fdata (dsh/lookup-file-data state file-id)] + (when-let [changes (-> (generate-library-changes fdata) + (not-empty))] (rx/of (dwc/commit-changes {:origin it - :redo-changes (vec changes) + :redo-changes changes :undo-changes [] - :save-undo? false})) - (rx/empty)))))) + :save-undo? false}))))))) + +;; FIXME: would be nice to not execute this code twice per page in the +;; same working session, maybe some local memoization can improve that + +(defn fix-deleted-fonts-for-page + [file-id page-id] + (ptk/reify ::fix-deleted-fonts-for-page + ptk/WatchEvent + (watch [it state _] + (let [page (dsh/lookup-page state file-id page-id)] + (when-let [changes (-> (generate-page-changes page) + (not-empty))] + (rx/of (dwc/commit-changes + {:origin it + :redo-changes changes + :undo-changes [] + :save-undo? false}))))))) diff --git a/frontend/src/app/main/data/workspace/pages.cljs b/frontend/src/app/main/data/workspace/pages.cljs index a9c9d3354e..5b91d10864 100644 --- a/frontend/src/app/main/data/workspace/pages.cljs +++ b/frontend/src/app/main/data/workspace/pages.cljs @@ -24,6 +24,7 @@ [app.main.data.helpers :as dsh] [app.main.data.persistence :as-alias dps] [app.main.data.workspace.drawing :as dwd] + [app.main.data.workspace.fix-deleted-fonts :as fdf] [app.main.data.workspace.layout :as layout] [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.thumbnails :as dwth] @@ -104,6 +105,7 @@ (if (dsh/lookup-page state file-id page-id) (rx/concat (rx/of (initialize-page* file-id page-id) + (fdf/fix-deleted-fonts-for-page file-id page-id) (dwth/watch-state-changes file-id page-id) (dwl/watch-component-changes)) (let [profile (:profile state)