mirror of https://github.com/logseq/logseq
refactor: block insert && update
parent
7c03c8a89c
commit
019122c18c
|
@ -403,257 +403,232 @@
|
|||
"ls-block"
|
||||
"edit-block"))))
|
||||
|
||||
(defn- with-time-properties
|
||||
[block properties]
|
||||
(if (and (state/enable-block-time?)
|
||||
(not (:block/pre-block? block))
|
||||
(not= "Src" (ffirst (:block/body block))))
|
||||
(let [time (util/time-ms)
|
||||
props (into {} (:block/properties block))]
|
||||
(merge properties
|
||||
(if-let [created-at (get props "created_at")]
|
||||
{"created_at" created-at
|
||||
"last_modified_at" time}
|
||||
{"created_at" time
|
||||
"last_modified_at" time})))
|
||||
properties))
|
||||
|
||||
(defn- block-text-with-time
|
||||
(defn- re-build-block-value
|
||||
([block format value]
|
||||
(block-text-with-time block format value {}))
|
||||
(re-build-block-value block format value {}))
|
||||
([block format value properties]
|
||||
(let [properties (with-time-properties block properties)
|
||||
block-with-title? (boolean (block-with-title value format))]
|
||||
(let [block-with-title? (boolean (block-with-title value format))]
|
||||
(text/re-construct-block-properties format value properties
|
||||
block-with-title?))))
|
||||
|
||||
(defn- compute-new-properties
|
||||
[block new-properties value {:keys [init-properties custom-properties remove-properties]}]
|
||||
(let [text-properties (text/extract-properties value)
|
||||
old-hidden-properties (select-keys (:block/properties block) text/hidden-properties)
|
||||
properties (merge old-hidden-properties
|
||||
init-properties
|
||||
text-properties
|
||||
custom-properties)
|
||||
remove-properties (->
|
||||
(set/difference (set (keys (:block/properties block)))
|
||||
(set (keys text-properties))
|
||||
text/hidden-properties)
|
||||
(set/union (set remove-properties)))]
|
||||
(medley/remove-keys (fn [k] (contains? remove-properties k)) properties)))
|
||||
|
||||
(defn- another-block-with-same-id-exists?
|
||||
[current-id block-id]
|
||||
(and (string? block-id)
|
||||
(util/uuid-string? block-id)
|
||||
(not= current-id (cljs.core/uuid block-id))
|
||||
(db/entity [:block/uuid (cljs.core/uuid block-id)])))
|
||||
|
||||
(defn- create-file-if-not-exists!
|
||||
[repo format page value]
|
||||
(let [format (name format)
|
||||
title (string/capitalize (:page/name page))
|
||||
journal-page? (date/valid-journal-title? title)
|
||||
path (str
|
||||
(if journal-page?
|
||||
config/default-journals-directory
|
||||
(config/get-pages-directory))
|
||||
"/"
|
||||
(if journal-page?
|
||||
(date/journal-title->default title)
|
||||
(-> (:page/name page)
|
||||
(util/page-name-sanity))) "."
|
||||
(if (= format "markdown") "md" format))
|
||||
file-path (str "/" path)
|
||||
dir (config/get-repo-dir repo)]
|
||||
(p/let [exists? (fs/file-exists? dir file-path)]
|
||||
(if exists?
|
||||
(do
|
||||
(notification/show!
|
||||
[:p.content
|
||||
(util/format "File %s already exists!" file-path)]
|
||||
:error)
|
||||
(state/set-editor-op! nil))
|
||||
;; create the file
|
||||
(let [value (re-build-block-value nil format value)
|
||||
content (str (util/default-content-with-title format
|
||||
(or (:page/original-name page)
|
||||
(:page/name page)))
|
||||
value)]
|
||||
(p/let [_ (fs/create-if-not-exists repo dir file-path content)
|
||||
_ (git-handler/git-add repo path)]
|
||||
(file-handler/reset-file! repo path content)
|
||||
(ui-handler/re-render-root!)
|
||||
|
||||
;; Continue to edit the last block
|
||||
(let [blocks (db/get-page-blocks repo (:page/name page))
|
||||
last-block (last blocks)]
|
||||
(edit-last-block-for-new-page! last-block :max)
|
||||
(state/set-editor-op! nil))))))))
|
||||
|
||||
(defn- save-block-when-file-exists!
|
||||
[repo block e new-properties value {:keys [indent-left? rebuild-content? chan chan-callback]}]
|
||||
(let [{:block/keys [uuid file page pre-block? ref-pages ref-blocks]} block
|
||||
file (db/entity repo (:db/id file))
|
||||
file-path (:file/path file)
|
||||
format (format/get-format file-path)
|
||||
file-content (db/get-file repo file-path)
|
||||
value (get-block-new-value block file-content value)
|
||||
value (if rebuild-content?
|
||||
(rebuild-block-content value format)
|
||||
value)
|
||||
block (assoc block :block/content value)
|
||||
{:keys [blocks pages start-pos end-pos]} (if pre-block?
|
||||
(let [new-end-pos (utf8/length (utf8/encode value))]
|
||||
{:blocks [(assoc-in block [:block/meta :end-pos] new-end-pos)]
|
||||
:pages []
|
||||
:start-pos 0
|
||||
:end-pos new-end-pos})
|
||||
(block/parse-block block format))
|
||||
block-retracted-attrs (when-not pre-block?
|
||||
;; TODO: should we retract the whole block instead?
|
||||
(when-let [id (:db/id block)]
|
||||
[[:db/retract id :block/properties]
|
||||
[:db/retract id :block/priority]
|
||||
[:db/retract id :block/deadline]
|
||||
[:db/retract id :block/deadline-ast]
|
||||
[:db/retract id :block/scheduled]
|
||||
[:db/retract id :block/scheduled-ast]
|
||||
[:db/retract id :block/marker]
|
||||
[:db/retract id :block/repeated?]]))
|
||||
[after-blocks block-children-content new-end-pos] (rebuild-after-blocks-indent-outdent repo file block (:end-pos (:block/meta block)) end-pos indent-left?)
|
||||
retract-refs (compute-retract-refs (:db/id e) (first blocks) ref-pages ref-blocks)
|
||||
page-id (:db/id page)
|
||||
page-properties (when pre-block?
|
||||
(if (seq new-properties)
|
||||
[[:db/retract page-id :page/properties]
|
||||
{:db/id page-id
|
||||
:page/properties new-properties}]
|
||||
[[:db/retract page-id :page/properties]]))
|
||||
page-tags (when-let [tags (:tags new-properties)]
|
||||
(mapv (fn [tag] {:page/name (string/lower-case tag)
|
||||
:page/original-name tag}) tags))
|
||||
page-alias (when-let [alias (:alias new-properties)]
|
||||
(map
|
||||
(fn [alias]
|
||||
{:page/original-name alias
|
||||
:page/name (string/lower-case alias)})
|
||||
(remove #{(:page/name page)} alias)))
|
||||
pages (if (seq page-tags)
|
||||
(concat pages page-tags)
|
||||
pages)
|
||||
pages (remove
|
||||
(fn [page]
|
||||
(string/blank? (:page/name page)))
|
||||
pages)
|
||||
page-tags (when (and pre-block? (seq page-tags))
|
||||
(if (seq page-tags)
|
||||
[[:db/retract page-id :page/tags]
|
||||
{:db/id page-id
|
||||
:page/tags page-tags}]
|
||||
[[:db/retract page-id :page/tags]]))
|
||||
page-alias (when (and pre-block? (seq page-alias))
|
||||
(if (seq page-alias)
|
||||
[[:db/retract page-id :page/alias]
|
||||
{:db/id page-id
|
||||
:page/alias page-alias}]
|
||||
[[:db/retract page-id :page/alias]]))]
|
||||
(profile
|
||||
"Save block: "
|
||||
(repo-handler/transact-react-and-alter-file!
|
||||
repo
|
||||
(concat
|
||||
pages
|
||||
block-retracted-attrs
|
||||
(mapv (fn [b] {:block/uuid (:block/uuid b)}) blocks)
|
||||
blocks
|
||||
retract-refs
|
||||
page-properties
|
||||
page-tags
|
||||
page-alias
|
||||
after-blocks)
|
||||
{:key :block/change
|
||||
:data (map (fn [block] (assoc block :block/page page)) blocks)}
|
||||
(let [new-content (new-file-content-indent-outdent block file-content value block-children-content new-end-pos indent-left?)]
|
||||
[[file-path new-content]])
|
||||
(when chan {:chan chan
|
||||
:chan-callback chan-callback})))
|
||||
|
||||
;; fix editing template with multiple headings
|
||||
(when (> (count blocks) 1)
|
||||
(let [new-value (-> (text/remove-level-spaces (:block/content (first blocks)) (:block/format (first blocks)))
|
||||
(string/trim-newline))
|
||||
edit-input-id (state/get-edit-input-id)]
|
||||
(when edit-input-id
|
||||
(state/set-edit-content! edit-input-id new-value))))
|
||||
|
||||
(when (or (seq retract-refs) pre-block?)
|
||||
(ui-handler/re-render-root!))
|
||||
|
||||
(repo-handler/push-if-auto-enabled! repo)))
|
||||
|
||||
(defn save-block-if-changed!
|
||||
([block value]
|
||||
(save-block-if-changed! block value nil))
|
||||
([{:block/keys [uuid content meta file page dummy? format repo pre-block? content ref-pages ref-blocks] :as block}
|
||||
value
|
||||
([block value
|
||||
{:keys [indent-left? init-properties custom-properties remove-properties rebuild-content? chan chan-callback]
|
||||
:or {rebuild-content? true
|
||||
custom-properties nil
|
||||
init-properties nil
|
||||
remove-properties nil}
|
||||
:as opts}]
|
||||
(let [repo (or repo (state/get-current-repo))
|
||||
(let [{:block/keys [uuid content meta file page dummy? format repo pre-block? content ref-pages ref-blocks]} block
|
||||
repo (or repo (state/get-current-repo))
|
||||
e (db/entity repo [:block/uuid uuid])
|
||||
block (assoc (with-block-meta repo block)
|
||||
:block/properties (into {} (:block/properties e)))
|
||||
format (or format (state/get-preferred-format))
|
||||
page (db/entity repo (:db/id page))
|
||||
;; page properties
|
||||
[old-properties new-properties] (when pre-block?
|
||||
[(:page/properties (db/entity (:db/id page)))
|
||||
(mldoc/parse-properties value format)])
|
||||
page-tags (when-let [tags (:tags new-properties)]
|
||||
(mapv (fn [tag] {:page/name (string/lower-case tag)
|
||||
:page/original-name tag}) tags))
|
||||
page-alias (when-let [alias (:alias new-properties)]
|
||||
(map
|
||||
(fn [alias]
|
||||
{:page/original-name alias
|
||||
:page/name (string/lower-case alias)})
|
||||
(remove #{(:page/name page)} alias)))
|
||||
properties (compute-new-properties block new-properties value
|
||||
{:init-properties init-properties
|
||||
:custom-properties custom-properties
|
||||
:remove-properties remove-properties})
|
||||
block-id (get properties "id")]
|
||||
(cond
|
||||
(another-block-with-same-id-exists? uuid block-id)
|
||||
(notification/show!
|
||||
[:p.content
|
||||
(util/format "Block with the id % already exists!" block-id)]
|
||||
:error)
|
||||
|
||||
permalink-changed? (when (and pre-block? (:permalink old-properties))
|
||||
(not= (:permalink old-properties)
|
||||
(:permalink new-properties)))
|
||||
value (if permalink-changed?
|
||||
(db/add-properties! format value {:old_permalink (:permalink old-properties)})
|
||||
value)
|
||||
new-properties (if permalink-changed?
|
||||
(assoc new-properties :old_permalink (:permalink old-properties))
|
||||
new-properties)
|
||||
text-properties (text/extract-properties value)
|
||||
old-hidden-properties (select-keys (:block/properties block) text/hidden-properties)
|
||||
properties (merge old-hidden-properties
|
||||
init-properties
|
||||
text-properties
|
||||
custom-properties)
|
||||
remove-properties (->
|
||||
(set/difference (set (keys (:block/properties block)))
|
||||
(set (keys text-properties))
|
||||
text/hidden-properties)
|
||||
(set/union (set remove-properties)))
|
||||
properties (medley/remove-keys (fn [k] (contains? remove-properties k)) properties)]
|
||||
(let [id (get properties "id")]
|
||||
(cond
|
||||
(and (string? id)
|
||||
(util/uuid-string? id)
|
||||
(not= uuid (cljs.core/uuid id))
|
||||
(db/entity [:block/uuid (cljs.core/uuid id)]))
|
||||
(notification/show!
|
||||
[:p.content
|
||||
(util/format "Block with the id % already exists!" id)]
|
||||
:error)
|
||||
:else
|
||||
(let [value (re-build-block-value block format value properties)
|
||||
content-changed? (not= (string/trim content) (string/trim value))]
|
||||
(when content-changed?
|
||||
(let [file (db/entity repo (:db/id file))]
|
||||
(cond
|
||||
;; Page was referenced but no related file
|
||||
(and page (not file))
|
||||
(create-file-if-not-exists! repo format page value)
|
||||
|
||||
:else
|
||||
(let [value (block-text-with-time block format value properties)
|
||||
content-changed? (not= (text/remove-timestamp-property! (string/trim content))
|
||||
(text/remove-timestamp-property! (string/trim value)))]
|
||||
(cond
|
||||
content-changed?
|
||||
(let [file (db/entity repo (:db/id file))]
|
||||
(cond
|
||||
;; Page was referenced but no related file
|
||||
;; TODO: replace with handler.page/create!
|
||||
(and page (not file))
|
||||
(let [format (name format)
|
||||
title (string/capitalize (:page/name page))
|
||||
journal-page? (date/valid-journal-title? title)
|
||||
path (str
|
||||
(if journal-page?
|
||||
config/default-journals-directory
|
||||
(config/get-pages-directory))
|
||||
"/"
|
||||
(if journal-page?
|
||||
(date/journal-title->default title)
|
||||
(-> (:page/name page)
|
||||
(util/page-name-sanity))) "."
|
||||
(if (= format "markdown") "md" format))
|
||||
file-path (str "/" path)
|
||||
dir (config/get-repo-dir repo)]
|
||||
(p/let [exists? (fs/file-exists? dir file-path)]
|
||||
(if exists?
|
||||
(notification/show!
|
||||
[:p.content
|
||||
(util/format "File %s already exists!" file-path)]
|
||||
:error)
|
||||
;; create the file
|
||||
(let [value (block-text-with-time nil format value)
|
||||
content (str (util/default-content-with-title format
|
||||
(or (:page/original-name page)
|
||||
(:page/name page)))
|
||||
value)]
|
||||
(p/let [_ (fs/create-if-not-exists repo dir file-path content)
|
||||
_ (git-handler/git-add repo path)]
|
||||
(file-handler/reset-file! repo path content)
|
||||
(ui-handler/re-render-root!)
|
||||
(and file page)
|
||||
(save-block-when-file-exists! repo block e new-properties value opts)
|
||||
|
||||
;; Continue to edit the last block
|
||||
(let [blocks (db/get-page-blocks repo (:page/name page))
|
||||
last-block (last blocks)]
|
||||
(edit-last-block-for-new-page! last-block :max)))))))
|
||||
:else
|
||||
nil))))))))
|
||||
|
||||
(and file page)
|
||||
(let [file (db/entity repo (:db/id file))
|
||||
file-path (:file/path file)
|
||||
format (format/get-format file-path)
|
||||
file-content (db/get-file repo file-path)
|
||||
value (get-block-new-value block file-content value)
|
||||
value (if rebuild-content?
|
||||
(rebuild-block-content value format)
|
||||
value)
|
||||
block (assoc block :block/content value)
|
||||
{:keys [blocks pages start-pos end-pos]} (if pre-block?
|
||||
(let [new-end-pos (utf8/length (utf8/encode value))]
|
||||
{:blocks [(assoc-in block [:block/meta :end-pos] new-end-pos)]
|
||||
:pages []
|
||||
:start-pos 0
|
||||
:end-pos new-end-pos})
|
||||
(block/parse-block block format))
|
||||
block-retracted-attrs (when-not pre-block?
|
||||
;; TODO: should we retract the whole block instead?
|
||||
(when-let [id (:db/id block)]
|
||||
[[:db/retract id :block/properties]
|
||||
[:db/retract id :block/priority]
|
||||
[:db/retract id :block/deadline]
|
||||
[:db/retract id :block/deadline-ast]
|
||||
[:db/retract id :block/scheduled]
|
||||
[:db/retract id :block/scheduled-ast]
|
||||
[:db/retract id :block/marker]
|
||||
[:db/retract id :block/repeated?]]))
|
||||
[after-blocks block-children-content new-end-pos] (rebuild-after-blocks-indent-outdent repo file block (:end-pos (:block/meta block)) end-pos indent-left?)
|
||||
retract-refs (compute-retract-refs (:db/id e) (first blocks) ref-pages ref-blocks)
|
||||
page-id (:db/id page)
|
||||
page-properties (when pre-block?
|
||||
(if (seq new-properties)
|
||||
[[:db/retract page-id :page/properties]
|
||||
{:db/id page-id
|
||||
:page/properties new-properties}]
|
||||
[[:db/retract page-id :page/properties]]))
|
||||
pages (if (seq page-tags)
|
||||
(concat pages page-tags)
|
||||
pages)
|
||||
pages (remove
|
||||
(fn [page]
|
||||
(string/blank? (:page/name page)))
|
||||
pages)
|
||||
page-tags (when (and pre-block? (seq page-tags))
|
||||
(if (seq page-tags)
|
||||
[[:db/retract page-id :page/tags]
|
||||
{:db/id page-id
|
||||
:page/tags page-tags}]
|
||||
[[:db/retract page-id :page/tags]]))
|
||||
page-alias (when (and pre-block? (seq page-alias))
|
||||
(if (seq page-alias)
|
||||
[[:db/retract page-id :page/alias]
|
||||
{:db/id page-id
|
||||
:page/alias page-alias}]
|
||||
[[:db/retract page-id :page/alias]]))]
|
||||
(profile
|
||||
"Save block: "
|
||||
(repo-handler/transact-react-and-alter-file!
|
||||
repo
|
||||
(concat
|
||||
pages
|
||||
block-retracted-attrs
|
||||
(mapv (fn [b] {:block/uuid (:block/uuid b)}) blocks)
|
||||
blocks
|
||||
retract-refs
|
||||
page-properties
|
||||
page-tags
|
||||
page-alias
|
||||
after-blocks)
|
||||
{:key :block/change
|
||||
:data (map (fn [block] (assoc block :block/page page)) blocks)}
|
||||
(let [new-content (new-file-content-indent-outdent block file-content value block-children-content new-end-pos indent-left?)]
|
||||
[[file-path new-content]])
|
||||
(when chan {:chan chan
|
||||
:chan-callback chan-callback})))
|
||||
|
||||
;; fix editing template with multiple headings
|
||||
(when (> (count blocks) 1)
|
||||
(let [new-value (-> (text/remove-level-spaces (:block/content (first blocks)) (:block/format (first blocks)))
|
||||
(string/trim-newline))
|
||||
edit-input-id (state/get-edit-input-id)]
|
||||
(when edit-input-id
|
||||
(state/set-edit-content! edit-input-id new-value))))
|
||||
|
||||
(when (or (seq retract-refs) pre-block?)
|
||||
(ui-handler/re-render-root!))
|
||||
|
||||
(repo-handler/push-if-auto-enabled! repo))
|
||||
|
||||
:else
|
||||
nil))
|
||||
|
||||
(seq (state/get-changed-files))
|
||||
(repo-handler/push-if-auto-enabled! repo)
|
||||
|
||||
:else
|
||||
nil)))))))
|
||||
|
||||
(defn insert-new-block-aux!
|
||||
[{:block/keys [uuid content meta file dummy? level repo page format properties collapsed? pre-block?] :as block}
|
||||
value
|
||||
{:keys [create-new-block? ok-handler with-level? new-level current-page blocks-container-id]}]
|
||||
(let [value (or value "")
|
||||
block-page? (and current-page (util/uuid-string? current-page))
|
||||
block-self? (= uuid (and block-page? (medley/uuid current-page)))
|
||||
input (gdom/getElement (state/get-edit-input-id))
|
||||
pos (if new-level
|
||||
(dec (count value))
|
||||
(util/get-input-pos input))
|
||||
repo (or repo (state/get-current-repo))
|
||||
block-has-children? (seq (:block/children block))
|
||||
fst-block-text (subs value 0 pos)
|
||||
(defn- compute-fst-snd-block-text
|
||||
[block format value pos level new-level block-self? block-has-children? with-level?]
|
||||
(let [fst-block-text (subs value 0 pos)
|
||||
snd-block-text (string/triml (subs value pos))
|
||||
fst-block-text (string/trim (if with-level? fst-block-text (block/with-levels fst-block-text format block)))
|
||||
edit-self? (and block-has-children? (zero? pos))
|
||||
snd-block-text-level (cond
|
||||
new-level
|
||||
new-level
|
||||
|
@ -668,159 +643,142 @@
|
|||
snd-block-text
|
||||
(rebuild-block-content
|
||||
(str (config/default-empty-block format snd-block-text-level) " " snd-block-text)
|
||||
format))
|
||||
block (with-block-meta repo block)
|
||||
format))]
|
||||
[fst-block-text snd-block-text]))
|
||||
|
||||
(defn insert-block-to-existing-file!
|
||||
[repo block file page file-path file-content value fst-block-text snd-block-text pos format input {:keys [create-new-block? ok-handler with-level? new-level current-page blocks-container-id]}]
|
||||
(let [{:block/keys [meta pre-block?]} block
|
||||
original-id (:block/uuid block)
|
||||
block-has-children? (seq (:block/children block))
|
||||
edit-self? (and block-has-children? (zero? pos))
|
||||
;; Compute the new value, remove id property from the second block if exists
|
||||
value (if create-new-block?
|
||||
(str fst-block-text "\n" snd-block-text)
|
||||
value)
|
||||
snd-block-text (text/remove-id-property snd-block-text)
|
||||
text-properties (if (zero? pos)
|
||||
{}
|
||||
(text/extract-properties fst-block-text))
|
||||
old-hidden-properties (select-keys (:block/properties block) text/hidden-properties)
|
||||
properties (merge old-hidden-properties
|
||||
text-properties)
|
||||
value (if create-new-block?
|
||||
(str
|
||||
(->
|
||||
(re-build-block-value block format fst-block-text properties)
|
||||
(string/trimr))
|
||||
"\n"
|
||||
(string/triml snd-block-text))
|
||||
(re-build-block-value block format value properties))
|
||||
value (rebuild-block-content value format)
|
||||
[new-content value] (new-file-content block file-content value)
|
||||
parse-result (block/parse-block (assoc block :block/content value) format)
|
||||
id-conflict? (some #(= original-id (:block/uuid %)) (next (:blocks parse-result)))
|
||||
{:keys [blocks pages start-pos end-pos]}
|
||||
(if id-conflict?
|
||||
(let [new-value (string/replace
|
||||
value
|
||||
(re-pattern (str "(?i):(custom_)?id: " original-id))
|
||||
"")]
|
||||
(block/parse-block (assoc block :block/content new-value) format))
|
||||
parse-result)
|
||||
after-blocks (rebuild-after-blocks repo file (:end-pos meta) end-pos)
|
||||
files [[file-path new-content]]
|
||||
block-retracted-attrs (when-not pre-block?
|
||||
;; TODO: should we retract the whole block instead?
|
||||
(when-let [id (:db/id block)]
|
||||
[[:db/retract id :block/properties]
|
||||
[:db/retract id :block/priority]
|
||||
[:db/retract id :block/deadline]
|
||||
[:db/retract id :block/deadline-ast]
|
||||
[:db/retract id :block/scheduled]
|
||||
[:db/retract id :block/scheduled-ast]
|
||||
[:db/retract id :block/marker]
|
||||
[:db/retract id :block/repeated?]]))
|
||||
transact-fn (fn []
|
||||
(repo-handler/transact-react-and-alter-file!
|
||||
repo
|
||||
(concat
|
||||
block-retracted-attrs
|
||||
pages
|
||||
(mapv (fn [b] {:block/uuid (:block/uuid b)}) blocks)
|
||||
blocks
|
||||
after-blocks)
|
||||
{:key :block/insert
|
||||
:data (map (fn [block] (assoc block :block/page page)) blocks)}
|
||||
files)
|
||||
(state/set-editor-op! nil))]
|
||||
|
||||
;; Replace with batch transactions
|
||||
(state/add-tx! transact-fn)
|
||||
|
||||
(let [blocks (remove (fn [block]
|
||||
(nil? (:block/content block))) blocks)
|
||||
page-blocks-atom (db/get-page-blocks-cache-atom repo (:db/id page))
|
||||
first-block-id (:block/uuid (first blocks))
|
||||
[before-part after-part] (and page-blocks-atom
|
||||
(split-with
|
||||
#(not= first-block-id (:block/uuid %))
|
||||
@page-blocks-atom))
|
||||
after-part (rest after-part)
|
||||
blocks-container-id (and blocks-container-id
|
||||
(util/uuid-string? blocks-container-id)
|
||||
(medley/uuid blocks-container-id))]
|
||||
|
||||
;; WORKAROUND: The block won't refresh itself even if the content is empty.
|
||||
(when edit-self?
|
||||
(gobj/set input "value" ""))
|
||||
|
||||
(when ok-handler
|
||||
(ok-handler
|
||||
(if edit-self? (first blocks) (last blocks))))
|
||||
|
||||
;; update page blocks cache if exists
|
||||
(when page-blocks-atom
|
||||
(reset! page-blocks-atom (->> (concat before-part blocks after-part)
|
||||
(remove nil?))))
|
||||
|
||||
;; update block children cache if exists
|
||||
(when blocks-container-id
|
||||
(let [blocks-atom (db/get-block-blocks-cache-atom repo blocks-container-id)
|
||||
[before-part after-part] (and blocks-atom
|
||||
(split-with
|
||||
#(not= first-block-id (:block/uuid %))
|
||||
@blocks-atom))
|
||||
after-part (rest after-part)]
|
||||
(and blocks-atom
|
||||
(reset! blocks-atom (->> (concat before-part blocks after-part)
|
||||
(remove nil?)))))))))
|
||||
|
||||
(defn insert-new-block-aux!
|
||||
[{:block/keys [uuid content meta file dummy? level repo page format properties collapsed? pre-block?] :as block}
|
||||
value
|
||||
{:keys [create-new-block? ok-handler with-level? new-level current-page blocks-container-id]
|
||||
:as opts}]
|
||||
(let [value (or value "")
|
||||
block-page? (and current-page (util/uuid-string? current-page))
|
||||
block-self? (= uuid (and block-page? (medley/uuid current-page)))
|
||||
input (gdom/getElement (state/get-edit-input-id))
|
||||
pos (if new-level
|
||||
(dec (count value))
|
||||
(util/get-input-pos input))
|
||||
repo (or repo (state/get-current-repo))
|
||||
block (with-block-meta repo block)
|
||||
format (:block/format block)
|
||||
page (db/entity repo (:db/id page))
|
||||
file (db/entity repo (:db/id file))
|
||||
insert-block (fn [block file-path file-content]
|
||||
(let [value (if create-new-block?
|
||||
(str fst-block-text "\n" snd-block-text)
|
||||
value)
|
||||
snd-block-text (text/remove-id-property snd-block-text)
|
||||
text-properties (if (zero? pos)
|
||||
{}
|
||||
(text/extract-properties fst-block-text))
|
||||
old-hidden-properties (select-keys (:block/properties block) text/hidden-properties)
|
||||
properties (merge old-hidden-properties
|
||||
text-properties)
|
||||
value (if create-new-block?
|
||||
(str
|
||||
(->
|
||||
(block-text-with-time block format fst-block-text properties)
|
||||
(string/trimr))
|
||||
"\n"
|
||||
(string/triml snd-block-text))
|
||||
(block-text-with-time block format value properties))
|
||||
value (rebuild-block-content value format)
|
||||
[new-content value] (new-file-content block file-content value)
|
||||
parse-result (block/parse-block (assoc block :block/content value) format)
|
||||
id-conflict? (some #(= original-id (:block/uuid %)) (next (:blocks parse-result)))
|
||||
{:keys [blocks pages start-pos end-pos]}
|
||||
(if id-conflict?
|
||||
(let [new-value (string/replace
|
||||
value
|
||||
(re-pattern (str "(?i):(custom_)?id: " original-id))
|
||||
"")]
|
||||
(block/parse-block (assoc block :block/content new-value) format))
|
||||
parse-result)
|
||||
after-blocks (rebuild-after-blocks repo file (:end-pos meta) end-pos)
|
||||
files [[file-path new-content]]
|
||||
block-retracted-attrs (when-not pre-block?
|
||||
;; TODO: should we retract the whole block instead?
|
||||
(when-let [id (:db/id block)]
|
||||
[[:db/retract id :block/properties]
|
||||
[:db/retract id :block/priority]
|
||||
[:db/retract id :block/deadline]
|
||||
[:db/retract id :block/deadline-ast]
|
||||
[:db/retract id :block/scheduled]
|
||||
[:db/retract id :block/scheduled-ast]
|
||||
[:db/retract id :block/marker]
|
||||
[:db/retract id :block/repeated?]]))
|
||||
transact-fn (fn []
|
||||
(repo-handler/transact-react-and-alter-file!
|
||||
repo
|
||||
(concat
|
||||
block-retracted-attrs
|
||||
pages
|
||||
(mapv (fn [b] {:block/uuid (:block/uuid b)}) blocks)
|
||||
blocks
|
||||
after-blocks)
|
||||
{:key :block/insert
|
||||
:data (map (fn [block] (assoc block :block/page page)) blocks)}
|
||||
files)
|
||||
(state/set-editor-op! nil))]
|
||||
|
||||
;; Replace with batch transactions
|
||||
(state/add-tx! transact-fn)
|
||||
|
||||
(let [blocks (remove (fn [block]
|
||||
(nil? (:block/content block))) blocks)
|
||||
page-blocks-atom (db/get-page-blocks-cache-atom repo (:db/id page))
|
||||
first-block-id (:block/uuid (first blocks))
|
||||
[before-part after-part] (and page-blocks-atom
|
||||
(split-with
|
||||
#(not= first-block-id (:block/uuid %))
|
||||
@page-blocks-atom))
|
||||
after-part (rest after-part)
|
||||
blocks-container-id (and blocks-container-id
|
||||
(util/uuid-string? blocks-container-id)
|
||||
(medley/uuid blocks-container-id))]
|
||||
|
||||
; WORKAROUND: The block won't refresh itself even if the content is empty.
|
||||
(when edit-self?
|
||||
(gobj/set input "value" ""))
|
||||
|
||||
(when ok-handler
|
||||
(ok-handler
|
||||
(if edit-self? (first blocks) (last blocks))))
|
||||
|
||||
;; update page blocks cache if exists
|
||||
(when page-blocks-atom
|
||||
(reset! page-blocks-atom (->> (concat before-part blocks after-part)
|
||||
(remove nil?))))
|
||||
|
||||
;; update block children cache if exists
|
||||
(when blocks-container-id
|
||||
(let [blocks-atom (db/get-block-blocks-cache-atom repo blocks-container-id)
|
||||
[before-part after-part] (and blocks-atom
|
||||
(split-with
|
||||
#(not= first-block-id (:block/uuid %))
|
||||
@blocks-atom))
|
||||
after-part (rest after-part)]
|
||||
(and blocks-atom
|
||||
(reset! blocks-atom (->> (concat before-part blocks after-part)
|
||||
(remove nil?)))))))))]
|
||||
block-has-children? (seq (:block/children block))
|
||||
[fst-block-text snd-block-text] (compute-fst-snd-block-text block format value pos level new-level block-self? block-has-children? with-level?)]
|
||||
(cond
|
||||
(and (not file) page)
|
||||
;; TODO: replace with handler.page/create!
|
||||
(let [format (name format)
|
||||
title (string/capitalize (:page/name page))
|
||||
journal-page? (date/valid-journal-title? title)
|
||||
path (str
|
||||
(if journal-page?
|
||||
config/default-journals-directory
|
||||
(config/get-pages-directory))
|
||||
"/"
|
||||
(if journal-page?
|
||||
(date/journal-title->default title)
|
||||
(-> (:page/name page)
|
||||
(util/page-name-sanity)))
|
||||
"."
|
||||
(if (= format "markdown") "md" format))
|
||||
file-path (str "/" path)
|
||||
dir (config/get-repo-dir repo)]
|
||||
(p/let [exists? (fs/file-exists? dir file-path)]
|
||||
(if exists?
|
||||
(do (notification/show!
|
||||
[:p.content
|
||||
(util/format "File %s already exists!"
|
||||
file-path)]
|
||||
:error)
|
||||
(state/set-editor-op! nil))
|
||||
;; create the file
|
||||
(let [content (util/default-content-with-title format (or
|
||||
(:page/original-name page)
|
||||
(:page/name page)))
|
||||
value (block-text-with-time nil format value)
|
||||
new-content (str content value "\n" snd-block-text)]
|
||||
(p/let [_ (fs/create-if-not-exists repo dir file-path new-content)
|
||||
_ (git-handler/git-add repo path)]
|
||||
(file-handler/reset-file! repo path new-content)
|
||||
(ui-handler/re-render-root!)
|
||||
|
||||
;; Continue to edit the last block
|
||||
(let [blocks (db/get-page-blocks repo (:page/name page))
|
||||
last-block (last blocks)]
|
||||
(edit-last-block-for-new-page! last-block 0))
|
||||
|
||||
(state/set-editor-op! nil))))))
|
||||
(let [value (str value "\n" snd-block-text)]
|
||||
(create-file-if-not-exists! repo format page value))
|
||||
|
||||
file
|
||||
(let [file-path (:file/path file)
|
||||
file-content (db/get-file repo file-path)]
|
||||
(insert-block block file-path file-content))
|
||||
(insert-block-to-existing-file! repo block file page file-path file-content value fst-block-text snd-block-text pos format input opts))
|
||||
|
||||
:else
|
||||
nil)))
|
||||
|
@ -867,39 +825,42 @@
|
|||
time-properties)))
|
||||
|
||||
(defn insert-new-block!
|
||||
[state]
|
||||
(when (and (not config/publishing?)
|
||||
;; skip this operation if it's inserting
|
||||
(not= :insert (state/get-editor-op)))
|
||||
(state/set-editor-op! :insert)
|
||||
(let [{:keys [block value format id config]} (get-state state)
|
||||
block-id (:block/uuid block)
|
||||
block (or (db/pull [:block/uuid block-id])
|
||||
block)
|
||||
collapsed? (:block/collapsed? block)
|
||||
repo (or (:block/repo block) (state/get-current-repo))
|
||||
last-child (and collapsed?
|
||||
(last (db/get-block-and-children-no-cache repo (:block/uuid block))))
|
||||
last-child (when (not= (:block/uuid last-child)
|
||||
(:block/uuid block))
|
||||
last-child)
|
||||
new-block (or last-child block)
|
||||
new-value (if last-child (:block/content last-child) value)
|
||||
properties (with-timetracking-properties new-block new-value)]
|
||||
;; save the current block and insert a new block
|
||||
(insert-new-block-aux!
|
||||
(assoc new-block :block/properties properties)
|
||||
new-value
|
||||
{:create-new-block? true
|
||||
:ok-handler
|
||||
(fn [last-block]
|
||||
(let [last-id (:block/uuid last-block)]
|
||||
(edit-block! last-block 0 format id)
|
||||
(clear-when-saved!)))
|
||||
:with-level? (if last-child true false)
|
||||
:new-level (and last-child (:block/level block))
|
||||
:blocks-container-id (:id config)
|
||||
:current-page (state/get-current-page)}))))
|
||||
([state]
|
||||
(insert-new-block! state nil))
|
||||
([state block-value]
|
||||
(when (and (not config/publishing?)
|
||||
;; skip this operation if it's inserting
|
||||
(not= :insert (state/get-editor-op)))
|
||||
(state/set-editor-op! :insert)
|
||||
(let [{:keys [block value format id config]} (get-state state)
|
||||
value (if (string? block-value) block-value value)
|
||||
block-id (:block/uuid block)
|
||||
block (or (db/pull [:block/uuid block-id])
|
||||
block)
|
||||
collapsed? (:block/collapsed? block)
|
||||
repo (or (:block/repo block) (state/get-current-repo))
|
||||
last-child (and collapsed?
|
||||
(last (db/get-block-and-children-no-cache repo (:block/uuid block))))
|
||||
last-child (when (not= (:block/uuid last-child)
|
||||
(:block/uuid block))
|
||||
last-child)
|
||||
new-block (or last-child block)
|
||||
new-value (if last-child (:block/content last-child) value)
|
||||
properties (with-timetracking-properties new-block new-value)]
|
||||
;; save the current block and insert a new block
|
||||
(insert-new-block-aux!
|
||||
(assoc new-block :block/properties properties)
|
||||
new-value
|
||||
{:create-new-block? true
|
||||
:ok-handler
|
||||
(fn [last-block]
|
||||
(let [last-id (:block/uuid last-block)]
|
||||
(edit-block! last-block 0 format id)
|
||||
(clear-when-saved!)))
|
||||
:with-level? (if last-child true false)
|
||||
:new-level (and last-child (:block/level block))
|
||||
:blocks-container-id (:id config)
|
||||
:current-page (state/get-current-page)})))))
|
||||
|
||||
(defn insert-new-block-without-save-previous!
|
||||
[config last-block]
|
||||
|
|
|
@ -138,14 +138,6 @@
|
|||
(and id (util/uuid-string? id)))))))]
|
||||
(string/join "\n" lines)))
|
||||
|
||||
(defn remove-timestamp-property!
|
||||
[content]
|
||||
(let [lines (->> (string/split-lines content)
|
||||
(remove #(let [s (string/lower-case (string/trim %))]
|
||||
(or (string/starts-with? s ":created_at:")
|
||||
(string/starts-with? s ":last_modified_at:")))))]
|
||||
(string/join "\n" lines)))
|
||||
|
||||
(defn build-properties-str
|
||||
[properties]
|
||||
(when (seq properties)
|
||||
|
|
|
@ -106,18 +106,6 @@
|
|||
"hello\n:PROPERTIES:\n:id: f9873a81-07b9-4246-b910-53a6f5ec7e04\na: b\n:END:\n"
|
||||
"hello\n:PROPERTIES:\na: b\n:END:"))
|
||||
|
||||
(defn remove-timestamp-property!
|
||||
[]
|
||||
(are [x y] (= (text/remove-timestamp-property! x) y)
|
||||
"hello\n:PROPERTIES:\n:created_at: 1\n:END:\n"
|
||||
"hello\n:PROPERTIES:\n:END:"
|
||||
|
||||
"hello\n:PROPERTIES:\n:created_at: 1\n:last_modified_at: 2\n:END:\n"
|
||||
"hello\n:PROPERTIES:\n:END:"
|
||||
|
||||
"hello\n:PROPERTIES:\n:a: b\n:created_at: 1\n:last_modified_at: 2\n:END:\n"
|
||||
"hello\n:PROPERTIES:\n:a: b\n:END:"))
|
||||
|
||||
(deftest re-construct-block-properties
|
||||
[]
|
||||
(testing "block content without a title"
|
||||
|
|
Loading…
Reference in New Issue