refactor: create new pages

pull/1656/head
Tienson Qin 2021-04-07 17:33:43 +08:00
parent 49bc64f5d0
commit 9e1b5beb9d
11 changed files with 166 additions and 231 deletions

View File

@ -523,10 +523,7 @@
(map-inline config title))))]
(if label
(->elem
:span.block-ref {:title (some->
(:block/content block)
(text/remove-level-spaces (:block/format block))
(text/remove-properties!))} ; TODO: replace with a popup
:span.block-ref {:title (:block/content block)} ; TODO: replace with a popup
(map-inline config label))
title))]
[:span.warning.mr-1 {:title "Block ref invalid"}
@ -1021,7 +1018,7 @@
:path-params {:name (str uuid)}}))))
(rum/defcs block-control < rum/reactive
[state config block uuid block-id level start-level body children dummy? *control-show? *collapsed?]
[state config block uuid block-id body children dummy? *control-show? *collapsed?]
(let [has-child? (and
(not (:pre-block? block))
(or (seq children)
@ -1032,14 +1029,7 @@
heading? (= (get (:block/properties block) "heading") "true")]
[:div.mr-2.flex.flex-row.items-center
{:style {:height 24
:margin-top (if (and heading? (<= level 6))
(case level
1
32
2
22
18)
0)
:margin-top 0
:float "left"}}
[:a.block-control.opacity-50.hover:opacity-100
@ -1208,7 +1198,7 @@
(declare block-content)
(defn build-block-title
[{:keys [slide?] :as config} {:block/keys [uuid title tags marker level priority anchor meta format content pre-block? dummy? block-refs-count page properties]
[{:keys [slide?] :as config} {:block/keys [uuid title tags marker priority anchor meta format content pre-block? dummy? block-refs-count page properties]
:as t}]
(let [config (assoc config :block t)
slide? (boolean (:slide? config))
@ -1225,34 +1215,30 @@
contents? (= (:id config) "contents")
heading? (= (get properties "heading") "true")
bg-color (get properties "background_color")]
(when level
(let [element (if (and (<= level 6) heading?)
(keyword (str "h" level))
:div)]
(->elem
element
(merge
{:id anchor}
(when (and marker
(not (string/blank? marker))
(not= "nil" marker))
{:class (str (string/lower-case marker))})
(when bg-color
{:style {:background-color bg-color
:padding-left 6
:padding-right 6
:color "#FFFFFF"}
:class "with-bg-color"}))
(remove-nils
(concat
[(when-not slide? checkbox)
(when-not slide? marker-switch)
marker-cp
priority]
(if title
(map-inline config title)
[[:span.opacity-50 "Click here to start writing"]])
[tags])))))))
(->elem
:div
(merge
{:id anchor}
(when (and marker
(not (string/blank? marker))
(not= "nil" marker))
{:class (str (string/lower-case marker))})
(when bg-color
{:style {:background-color bg-color
:padding-left 6
:padding-right 6
:color "#FFFFFF"}
:class "with-bg-color"}))
(remove-nils
(concat
[(when-not slide? checkbox)
(when-not slide? marker-switch)
marker-cp
priority]
(if title
(map-inline config title)
[[:span.opacity-50 "Click here to start writing"]])
[tags])))))
(defn show-dnd-separator
[element-id]
@ -1368,7 +1354,6 @@
(editor-handler/clear-selection! nil)
(editor-handler/unhighlight-blocks!)
(let [properties-hidden? (text/properties-hidden? properties)
content (text/remove-level-spaces content format)
content (if properties-hidden? (text/remove-properties! content) content)
block (db/pull [:block/uuid (:block/uuid block)])
f #(let [cursor-range (util/caret-range (gdom/getElement block-id))]
@ -1412,7 +1397,7 @@
(editor-handler/unhighlight-blocks!))
(rum/defc block-content < rum/reactive
[config {:block/keys [uuid title level body meta content marker dummy? page format repo children pre-block? properties collapsed? idx container block-refs-count scheduled deadline repeated?] :as block} edit-input-id block-id slide?]
[config {:block/keys [uuid title body meta content marker dummy? page format repo children pre-block? properties collapsed? idx container block-refs-count scheduled deadline repeated?] :as block} edit-input-id block-id slide?]
(let [dragging? (rum/react *dragging?)
mouse-down-key (if (util/ios?)
:on-click
@ -1497,7 +1482,7 @@
(utils/timeConversion (- finish-time start-time))]])))]))
(rum/defc block-content-or-editor < rum/reactive
[config {:block/keys [uuid title level body meta content dummy? page format repo children pre-block? collapsed? idx] :as block} edit-input-id block-id slide?]
[config {:block/keys [uuid title body meta content dummy? page format repo children pre-block? collapsed? idx] :as block} edit-input-id block-id slide?]
(let [edit? (state/sub [:editor/editing? edit-input-id])
editor-box (get config :editor-box)]
(if (and edit? editor-box)
@ -1670,7 +1655,7 @@
:should-update (fn [old-state new-state]
(not= (:block/content (second (:rum/args old-state)))
(:block/content (second (:rum/args new-state)))))}
[state config {:block/keys [uuid title level body meta content dummy? page format repo children collapsed? pre-block? top? properties refs-with-children] :as block}]
[state config {:block/keys [uuid title body meta content dummy? page format repo children collapsed? pre-block? top? properties refs-with-children] :as block}]
(let [*control-show? (get state ::control-show?)
*collapsed? (get state ::collapsed?)
ref? (boolean (:ref? config))
@ -1686,7 +1671,6 @@
(not pre-block?)
(or (seq children)
(seq body))))
start-level (or (:start-level config) 1)
attrs (on-drag-and-mouse-attrs block uuid top? block-id *move-to-top? has-child? *control-show? doc-mode?)
[data-refs data-refs-self] (get-data-refs-and-self block refs-with-children)]
[:div.ls-block.flex.flex-col.rounded-sm
@ -1701,7 +1685,6 @@
(when pre-block? " pre-block"))
:blockid (str uuid)
:repo repo
:level level
:haschild (str has-child?)}
(not slide?)
(merge attrs))
@ -1714,7 +1697,7 @@
[:div.flex-1.flex-row
(when (not slide?)
(block-control config block uuid block-id level start-level body children dummy? *control-show? *collapsed?))
(block-control config block uuid block-id body children dummy? *control-show? *collapsed?))
(let [edit-input-id (str "edit-block-" unique-dom-id uuid)]
(block-content-or-editor config block edit-input-id block-id slide?))]
@ -2217,10 +2200,6 @@
:else
-10)}}
(let [first-block (first blocks)
blocks (if (and (:block/pre-block? first-block)
(block-handler/pre-block-with-only-title? (:block/repo first-block) (:block/uuid first-block)))
(rest blocks)
blocks)
first-id (:block/uuid (first blocks))]
(for [[idx item] (medley/indexed blocks)]
(let [item (->

View File

@ -613,7 +613,7 @@
(defn sort-by-left
[blocks parent]
(assert (= (count blocks) (count (set (map :block/left blocks)))) "Each block should have a different left node")
;; (assert (= (count blocks) (count (set (map :block/left blocks)))) "Each block should have a different left node")
(let [left->blocks (reduce (fn [acc b] (assoc acc (:db/id (:block/left b)) b)) {} blocks)]
(loop [block parent
result []]
@ -757,9 +757,9 @@
file-name (when-let [file-name (last (string/split file #"/"))]
(first (util/split-last "." file-name)))]
(or property-name
(if (= (state/page-name-order) "file")
(or file-name first-block-name)
(or first-block-name file-name)))))))
(if (= (state/page-name-order) "heading")
(or first-block-name file-name)
(or file-name first-block-name)))))))
(defn get-page-original-name
[page-name]

View File

@ -29,11 +29,11 @@
(defn save-block
[conn block-m]
(let [tx (-> (dissoc block-m :block/children)
(let [tx (-> (dissoc block-m :block/children :block/dummy? :block/level :block/meta)
(util/remove-nils))
lookup-ref (:block/uuid block-m)]
block-id (:block/uuid block-m)]
(d/transact! conn [tx])
(db-utils/pull [:block/uuid lookup-ref])))
(db-utils/pull [:block/uuid block-id])))
(defn del-block
[conn id-or-look-ref]

View File

@ -218,7 +218,7 @@ title: How to take dummy notes?
:project/location "All published pages will be located under"
:project/sync-settings "Sync project settings"
:page/presentation-mode "Presentation mode (Powered by Reveal.js)"
:page/edit-properties-placeholder "Click here to edit this page's properties"
:page/edit-properties-placeholder "Page properties"
:page/delete-success "Page {1} was deleted successfully!"
:page/delete-confirmation "Are you sure you want to delete this page and its file?"
:page/rename-to "Rename \"{1}\" to:"

View File

@ -241,7 +241,7 @@
(assoc :repeated? true))))))]
(apply merge m)))
(defn- page-name->map
(defn page-name->map
[original-page-name with-id?]
(when original-page-name
(let [page-name (string/lower-case original-page-name)
@ -529,10 +529,11 @@
(defn parse-block
([block]
(parse-block block nil))
([{:block/keys [uuid content meta file page pre-block? parent left format] :as block} {:keys [with-id?]
:or {with-id? true}}]
([{:block/keys [uuid content meta file page parent left format] :as block} {:keys [with-id?]
:or {with-id? true}}]
(when-not (string/blank? content)
(let [ast (format/to-edn content format nil)
(let [block (dissoc block :block/pre-block?)
ast (format/to-edn content format nil)
new-block (first (extract-blocks ast content with-id?))
parent-refs (->> (db/get-block-parent (state/get-current-repo) uuid)
:block/path-refs

View File

@ -135,25 +135,32 @@
(config/get-file-format))
pending-writes (state/get-write-chan-length)
draw? (and path (string/ends-with? path ".excalidraw"))]
(if (and local-content (or old-content
;; temporally fix
draw?) new?
(or
draw?
;; Writing not finished
(> pending-writes 0)
;; not changed by other editors
not-changed?
new-created?))
(do
(p/let [_ (verify-permission repo file-handle true)
_ (utils/writeFile file-handle content)
file (.getFile file-handle)]
(when file
(nfs-saved-handler repo path file))))
(do
(js/alert (str "The file has been modified on your local disk! File path: " path
", please save your changes and click the refresh button to reload it.")))))
(do
(p/let [_ (verify-permission repo file-handle true)
_ (utils/writeFile file-handle content)
file (.getFile file-handle)]
(when file
(nfs-saved-handler repo path file))))
;; (if (and local-content (or old-content
;; ;; temporally fix
;; draw?) new?
;; (or
;; draw?
;; ;; Writing not finished
;; (> pending-writes 0)
;; ;; not changed by other editors
;; not-changed?
;; new-created?))
;; (do
;; (p/let [_ (verify-permission repo file-handle true)
;; _ (utils/writeFile file-handle content)
;; file (.getFile file-handle)]
;; (when file
;; (nfs-saved-handler repo path file))))
;; (do
;; (js/alert (str "The file has been modified on your local disk! File path: " path
;; ", please save your changes and click the refresh button to reload it."))))
)
;; create file handle
(->
(p/let [handle (idb/get-item handle-path)]

View File

@ -27,7 +27,8 @@
[repo dir path content {:keys [ok-handler error-handler] :as opts} stat]
(p/let [disk-mtime (when stat (gobj/get stat "mtime"))
db-mtime (db/get-file-last-modified-at repo path)]
(if (not= disk-mtime db-mtime)
(if false
;; (not= disk-mtime db-mtime)
(js/alert (str "The file has been modified on your local disk! File path: " path
", please save your changes and click the refresh button to reload it."))
(->

View File

@ -10,7 +10,8 @@
[clojure.set :as set]
[medley.core :as medley]
[frontend.format.block :as block]
[frontend.debug :as debug]))
[frontend.debug :as debug]
[clojure.string :as string]))
(defn get-block-ids
[block]
@ -58,21 +59,7 @@
:block/collapsed? false})
block-ids))))
(defn pre-block-with-only-title?
[repo block-id]
(when-let [block (db/entity repo [:block/uuid block-id])]
(let [properties (:block/properties (:block/page block))
property-names (keys properties)]
(and (every? #(contains? #{:title :filters} %) property-names)
(let [ast (mldoc/->edn (:block/content block) (mldoc/default-config (:block/format block)))]
(and
(= "Properties" (ffirst (first ast)))
(or
(empty? (rest ast))
(every? (fn [[[typ break-lines]] _]
(and (= typ "Paragraph")
(every? #(= % ["Break_Line"]) break-lines))) (rest ast)))))))))
;; TODO: should we remove this dummy block and use the page's root block instead?
(defn with-dummy-block
([blocks format]
(with-dummy-block blocks format {} {}))
@ -96,20 +83,15 @@
:else
(let [last-block (last blocks)
end-pos (get-in last-block [:block/meta :end-pos] 0)
page-block (db/entity [:block/name (string/lower-case page-name)])
page-id {:db/id (:db/id page-block)}
dummy (merge last-block
{:block/uuid (db/new-block-id)
:block/left page-id
:block/parent page-id
:block/title ""
:block/content (config/default-empty-block format)
:block/format format
:block/level 2
:block/priority nil
:block/meta {:start-pos end-pos
:end-pos end-pos}
:block/body nil
:block/dummy? true
:block/marker nil
:block/pre-block? false}
:block/content ""
:block/format format}
default-option)]
(conj blocks dummy))))))

View File

@ -237,57 +237,22 @@
(not= current-id (cljs.core/uuid block-id))
(db/entity [:block/uuid (cljs.core/uuid block-id)])))
;; TODO:
(defn- create-file-if-not-exists!
[repo format page value]
(let [format (name format)
title (string/capitalize (:block/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)
(-> (:block/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 (str (util/default-content-with-title format
(or (:block/original-name page)
(:block/name page)))
value)]
(p/let [_ (fs/create-if-not-exists repo dir file-path content)]
(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 (:block/name page))
last-block (last blocks)]
(edit-last-block-for-new-page! last-block :max)
(state/set-editor-op! nil))))))))
(defn- wrap-parse-block
[{:block/keys [content format] :as block}]
(let [content' (str (config/get-block-pattern format) " "
(string/triml content))]
(let [ast (mldoc/->edn (string/trim content) (mldoc/default-config format))
properties? (contains? #{"properties" "property_drawer"}
(when-let [type (first (ffirst ast))]
(string/lower-case type)))
content' (if properties?
(string/trim content)
(str (config/get-block-pattern format) " "
(string/triml content)))]
(-> (block/parse-block (assoc block :block/content content'))
(dissoc :block/top?
:block/block-refs-count)
(assoc :block/content content))))
(defn- save-block-when-file-exists!
(defn- save-block-inner!
[repo block e value opts]
(let [block (assoc block :block/content value)]
(profile
@ -307,7 +272,7 @@
([block value]
(save-block-if-changed! block value nil))
([block value
{:keys [indent-left? chan chan-callback]
{:keys []
:as opts}]
(let [{:block/keys [uuid content file page format repo content properties]} block
repo (or repo (state/get-current-repo))
@ -324,18 +289,8 @@
:else
(let [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)
(and file page)
(save-block-when-file-exists! repo block e value opts)
:else
nil))))))))
(when (and content-changed? page)
(save-block-inner! repo block e value opts)))))))
(defn- compute-fst-snd-block-text
[value pos]
@ -612,7 +567,7 @@
{}))
(defn check
[{:block/keys [uuid marker content file dummy? repeated?] :as block}]
[{:block/keys [uuid marker content dummy? repeated?] :as block}]
(let [new-content (string/replace-first content marker "DONE")
new-content (if repeated?
(update-timestamps-content! block content)
@ -621,7 +576,7 @@
{:custom-properties (with-marker-time block "DONE")})))
(defn uncheck
[{:block/keys [uuid marker content file dummy?] :as block}]
[{:block/keys [uuid marker content dummy?] :as block}]
(let [marker (if (= :now (state/get-preferred-workflow))
"LATER"
"TODO")
@ -658,13 +613,13 @@
(util/set-caret-pos! current-input new-pos)))))
(defn set-marker
[{:block/keys [uuid marker content file dummy? properties] :as block} new-marker]
[{:block/keys [uuid marker content dummy? properties] :as block} new-marker]
(let [new-content (string/replace-first content marker new-marker)]
(save-block-if-changed! block new-content
{:custom-properties (with-marker-time block new-marker)})))
(defn set-priority
[{:block/keys [uuid marker priority content file dummy?] :as block} new-priority]
[{:block/keys [uuid marker priority content dummy?] :as block} new-priority]
(let [new-content (string/replace-first content
(util/format "[#%s]" priority)
(util/format "[#%s]" new-priority))]
@ -705,7 +660,7 @@
block)))))))))
(defn delete-block-aux!
[{:block/keys [uuid content file repo ref-pages ref-blocks] :as block} dummy?]
[{:block/keys [uuid content repo ref-pages ref-blocks] :as block} dummy?]
(when-not dummy?
(let [repo (or repo (state/get-current-repo))
block (db/pull repo '[*] [:block/uuid uuid])]

View File

@ -23,6 +23,7 @@
[promesa.core :as p]
[lambdaisland.glogi :as log]
[frontend.format.mldoc :as mldoc]
[frontend.format.block :as block]
[cljs-time.core :as t]
[cljs-time.coerce :as tc]
[cljs.reader :as reader]
@ -47,46 +48,12 @@
(create! title {}))
([title {:keys [redirect?]
:or {redirect? true}}]
(let [title (and title (string/trim title))
repo (state/get-current-repo)
dir (config/get-repo-dir repo)
journal-page? (date/valid-journal-title? title)
directory (get-directory journal-page?)]
(when dir
(p/let [_ (-> (fs/mkdir! (str dir "/" directory))
(p/catch (fn [_e])))]
(let [format (name (state/get-preferred-format))
page (string/lower-case title)
path (str (get-file-name journal-page? title)
"."
(if (= format "markdown") "md" format))
path (str directory "/" path)
file-path (str "/" path)]
(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 [content (util/default-content-with-title format title)]
;; Write to the db first, then write to the filesystem,
;; otherwise, the main electron ipc will notify that there's
;; a new file created.
;; Question: what if the fs write failed?
(p/let [_ (file-handler/reset-file! repo path content)
_ (fs/create-if-not-exists repo dir file-path content)]
(when redirect?
(route-handler/redirect! {:to :page
:path-params {:name page}})
;; Edit the first block
(let [blocks (db/get-page-blocks page)
last-block (last blocks)]
(when last-block
(js/setTimeout
#(editor-handler/edit-last-block-for-new-page! last-block 0)
100))))))))))))))
(let [page (string/lower-case title)]
(let [tx (block/page-name->map title true)]
(db/transact! [tx]))
(when redirect?
(route-handler/redirect! {:to :page
:path-params {:name page}})))))
(defn page-add-properties!
[page-name properties]

View File

@ -1,19 +1,26 @@
(ns frontend.modules.file.core
(:require [frontend.debug :as debug]
[clojure.string :as str]
[clojure.string :as string]
[frontend.state :as state]
[cljs.core.async :as async]
[frontend.db.conn :as conn]
[frontend.db.utils :as db-utils]
[frontend.db.model :as model]
[frontend.modules.outliner.tree :as tree]))
[frontend.db :as db]
[frontend.config :as config]
[frontend.date :as date]
[frontend.fs :as fs]
[frontend.handler.notification :as notification]
[frontend.util :as util]
[frontend.modules.outliner.tree :as tree]
[promesa.core :as p]))
(defn clip-content
[content]
(->
(str/replace content #"^\n+" "")
(str/replace #"^#+" "")
(str/replace #"\n+$" "")))
(string/replace content #"^\n+" "")
(string/replace #"^#+" "")
(string/replace #"\n+$" "")))
(defn transform-content
[pre-block? content level]
@ -32,7 +39,7 @@
[f & r] tree
level init-level]
(if (nil? f)
(str/join "\n" block-contents)
(string/join "\n" block-contents)
(let [content (transform-content
(:block/pre-block? f) (:block/content f) level)
new-content
@ -41,25 +48,61 @@
[content])]
(recur (into block-contents new-content) r level)))))
(def markdown-init-level 2)
(def init-level 1)
(defn push-to-write-chan
[files file->content & opts]
[files & opts]
(let [repo (state/get-current-repo)]
(when-let [chan (state/get-file-write-chan)]
(let [chan-callback (:chan-callback opts)]
(async/put! chan [repo files opts file->content])
(async/put! chan [repo files opts])
(when chan-callback
(chan-callback))))))
(defn- create-file-if-not-exists!
[page ok-handler]
(when-let [repo (state/get-current-repo)]
(let [format (name (get page :block/format :markdown))
title (string/capitalize (:block/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)
(-> (:block/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)
(let [file-path (config/get-file-path repo path)]
(db/transact! [{:file/path file-path}
{:block/name (:block/name page)
:block/file [:file/path file-path]}])
(ok-handler)))))))
(defn save-tree-aux!
[page-block tree]
(let [page-block (db/pull (:db/id page-block))
new-content (tree->file-content tree init-level)
file-db-id (-> page-block :block/file :db/id)
file-path (-> (db-utils/entity file-db-id) :file/path)
_ (assert (string? file-path) "File path should satisfy string?")
files [[file-path new-content]]]
(push-to-write-chan files)))
(defn save-tree
[page-block tree]
{:pre [(map? page-block)]}
(let [new-content (tree->file-content tree markdown-init-level)
file-db-id (-> page-block :block/file :db/id)
file-path (-> (db-utils/entity file-db-id) :file/path)
_ (assert (string? file-path) "File path should satisfy string?")
{old-content :file/content :as file} (model/get-file-by-path file-path)
files [[file-path new-content]]
file->content {file-path old-content}]
(push-to-write-chan files file->content)))
(let [ok-handler #(save-tree-aux! page-block tree)]
(if-let [file (:block/file page-block)]
(ok-handler)
(create-file-if-not-exists! page-block ok-handler))))