diff --git a/src/main/frontend/modules/outliner/pipeline.cljs b/src/main/frontend/modules/outliner/pipeline.cljs index d4929feb4..4978af125 100644 --- a/src/main/frontend/modules/outliner/pipeline.cljs +++ b/src/main/frontend/modules/outliner/pipeline.cljs @@ -41,14 +41,36 @@ [(:db/id (:block/page block))] (map :db/id (:block/refs block)) parents-refs)) + ;; Usually has changed as new-refs has page id while old-refs doesn't refs-changed? (not= old-refs new-refs) children (db-model/get-block-children-ids repo (:block/uuid block)) - children-refs (map (fn [id] - (let [entity (db/entity [:block/uuid id])] - {:db/id (:db/id entity) - :block/path-refs (concat - (map :db/id (:block/path-refs entity)) - new-refs)})) children)] + ;; Builds map of children ids to their parent id and :block/refs ids + children-maps (into {} + (map (fn [id] + (let [entity (db/entity [:block/uuid id])] + [(:db/id entity) + {:parent-id (get-in entity [:block/parent :db/id]) + :block-ref-ids (map :db/id (:block/refs entity))}])) + children)) + children-refs (map (fn [[id {:keys [block-ref-ids] :as child-map}]] + {:db/id id + ;; Recalculate :block/path-refs as db contains stale data for this attribute + :block/path-refs + (set/union + ;; Refs from top-level parent + new-refs + ;; Refs from current block + block-ref-ids + ;; Refs from parents in between top-level + ;; parent and current block + (loop [parent-refs #{} + parent-id (:parent-id child-map)] + (if-let [parent (children-maps parent-id)] + (recur (into parent-refs (:block-ref-ids parent)) + (:parent-id parent)) + ;; exits when top-level parent is reached + parent-refs)))}) + children-maps)] (swap! *computed-ids set/union (set (cons (:block/uuid block) children))) (util/concat-without-nil [(when (and (seq new-refs) diff --git a/src/test/frontend/modules/outliner/pipeline_test.cljs b/src/test/frontend/modules/outliner/pipeline_test.cljs new file mode 100644 index 000000000..698328f4a --- /dev/null +++ b/src/test/frontend/modules/outliner/pipeline_test.cljs @@ -0,0 +1,54 @@ +(ns frontend.modules.outliner.pipeline-test + (:require [cljs.test :refer [deftest is use-fixtures testing]] + [datascript.core :as d] + [frontend.state :as state] + [frontend.db :as db] + [frontend.modules.outliner.pipeline :as pipeline] + [frontend.test.helper :as test-helper :refer [load-test-files]])) + +(use-fixtures :each {:before (fn [] + ;; Set current-repo explicitly since it's not the default + (state/set-current-repo! test-helper/test-db) + (test-helper/start-test-db!)) + :after (fn [] + (state/set-current-repo! nil) + (test-helper/destroy-test-db!))}) + +(defn- get-blocks [db] + (->> (d/q '[:find (pull ?b [* {:block/path-refs [:block/name :db/id]}]) + :in $ + :where [?b :block/content] [(missing? $ ?b :block/pre-block?)]] + db) + (map first))) + +(deftest compute-block-path-refs + (load-test-files [{:file/path "pages/page1.md" + :file/content "prop:: #bar +- parent #foo + - child #baz + - grandchild #bing"}]) + (testing "when a block's :refs change, descendants of block have correct :block/path-refs" + (let [conn (db/get-db test-helper/test-db false) + blocks (get-blocks @conn) + ;; Update parent block to replace #foo with #bar + new-tag-id (:db/id (d/entity @conn [:block/name "bar"])) + modified-blocks (map #(if (= "parent #foo" (:block/content %)) + (assoc % + :block/refs [{:db/id new-tag-id}] + :block/path-refs [{:db/id new-tag-id}]) + %) + blocks) + refs-tx (pipeline/compute-block-path-refs {:tx-meta {:outliner-op :save-block}} modified-blocks) + _ (d/transact! conn (concat (map (fn [m] [:db/retract (:db/id m) :block/path-refs]) refs-tx) + refs-tx)) + updated-blocks (->> (get-blocks @conn) + (map #(hash-map :block/content (:block/content %) + :path-ref-names (mapv :block/name (:block/path-refs %)))))] + (is (= [;; still have old parent content b/c we're only testing :block/path-refs updates + {:block/content "parent #foo" + :path-ref-names ["page1" "bar"]} + {:block/content "child #baz" + :path-ref-names ["page1" "bar" "baz"]} + {:block/content "grandchild #bing" + :path-ref-names ["page1" "bar" "baz" "bing"]}] + updated-blocks))))) \ No newline at end of file