feat: preserve expand/collapse state

Also, fixed several issues related to data migration
pull/1698/head
Tienson Qin 2021-04-29 19:56:57 +08:00
parent 70d1952eb6
commit 402c5db938
13 changed files with 115 additions and 249 deletions

View File

@ -14,7 +14,6 @@
[dommy.core :as d]
[datascript.core :as dc]
[goog.dom :as gdom]
[frontend.handler.expand :as expand]
[frontend.components.svg :as svg]
[frontend.components.datetime :as datetime-comp]
[frontend.ui :as ui]
@ -1019,12 +1018,12 @@
:path-params {:name (str uuid)}}))))
(rum/defcs block-control < rum/reactive
[state config block uuid block-id body children dummy? *control-show? *collapsed?]
[state config block uuid block-id body children dummy? *control-show?]
(let [has-child? (and
(not (:pre-block? block))
(or (seq children)
(seq body)))
collapsed? (rum/react *collapsed?)
collapsed? (get (:block/properties block) :collapsed)
control-show? (util/react *control-show?)
dark? (= "dark" (state/sub :ui/theme))]
[:div.mr-2.flex.flex-row.items-center
@ -1039,10 +1038,8 @@
:margin-right 2}
:on-click (fn [e]
(util/stop e)
(if collapsed?
(expand/expand! block)
(expand/collapse! block))
(reset! *collapsed? (not collapsed?)))}
(when-not (and (not collapsed?) (not has-child?))
(editor-handler/set-block-property! uuid :collapsed (not collapsed?))))}
(cond
(and control-show? collapsed?)
(svg/caret-right)
@ -1407,8 +1404,9 @@
(editor-handler/unhighlight-blocks!))
(rum/defc block-content < rum/reactive
[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?)
[config {:block/keys [uuid title body meta content marker dummy? page format repo children pre-block? properties idx container block-refs-count scheduled deadline repeated?] :as block} edit-input-id block-id slide?]
(let [collapsed? (get properties :collapsed)
dragging? (rum/react *dragging?)
content (if (string? content) (string/trim content) "")
mouse-down-key (if (util/ios?)
:on-click
@ -1478,12 +1476,12 @@
(when (and (= marker "DONE")
(state/enable-timetracking?))
(let [start-time (or
(get properties "now")
(get properties "doing")
(get properties "in-progress")
(get properties "later")
(get properties "todo"))
finish-time (get properties "done")]
(get properties :now)
(get properties :doing)
(get properties :in-progress)
(get properties :later)
(get properties :todo))
finish-time (get properties :done)]
(when (and start-time finish-time (> finish-time start-time))
[:div.text-sm.absolute.time-spent {:style {:top 0
:right 0
@ -1493,7 +1491,7 @@
(utils/timeConversion (- finish-time start-time))]])))]))
(rum/defc block-content-or-editor < rum/reactive
[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?]
[config {:block/keys [uuid title body meta content dummy? page format repo children pre-block? 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)
@ -1653,23 +1651,13 @@
(rum/defcs block-container < rum/static
{:init (fn [state]
(let [block (last (:rum/args state))
collapsed? (:block/collapsed? block)]
(assoc state
::control-show? (atom false)
::collapsed? (atom collapsed?))))
:did-mount (fn [state]
(let [block (nth (:rum/args state) 1)
collapsed? (:block/collapsed? block)]
(when collapsed?
(expand/collapse! block))
state))
(assoc state ::control-show? (atom false)))
: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 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 pre-block? top? properties refs-with-children] :as block}]
(let [*control-show? (get state ::control-show?)
*collapsed? (get state ::collapsed?)
collapsed? (get properties :collapsed)
ref? (boolean (:ref? config))
breadcrumb-show? (:breadcrumb-show? config)
sidebar? (boolean (:sidebar? config))
@ -1709,7 +1697,7 @@
[:div.flex-1.flex-row
(when (not slide?)
(block-control config block uuid block-id body children dummy? *control-show? *collapsed?))
(block-control config block uuid block-id body children dummy? *control-show?))
(let [edit-input-id (str "edit-block-" unique-dom-id uuid)]
(block-content-or-editor config block edit-input-id block-id slide?))]

View File

@ -113,9 +113,9 @@
[:p "Template already exists!"]
:error)
(do
(editor-handler/set-block-property! block-id "template" title)
(editor-handler/set-block-property! block-id :template title)
(when (false? including-parent?)
(editor-handler/set-block-property! block-id "including-parent" false))
(editor-handler/set-block-property! block-id :including-parent false))
(state/hide-custom-context-menu!)))))))])
(ui/menu-link
{:key "Make template"
@ -136,14 +136,14 @@
(for [color block-background-colors]
[:a.m-2.shadow-sm
{:on-click (fn [_e]
(editor-handler/set-block-property! block-id :background-color color))}
(editor-handler/set-block-property! block-id "background-color" color))}
[:div.heading-bg {:style {:background-color color}}]])]
[:a.text-sm
{:title (t :remove-background)
:style {:margin-right 14
:margin-top 4}
:on-click (fn [_e]
(editor-handler/remove-block-property! block-id :background-color))}
(editor-handler/remove-block-property! block-id "background-color"))}
"Clear"]]
(let [empty-properties? (not (text/contains-properties? (:block/content block)))
all-hidden? (text/properties-hidden? (:block/properties block))]

View File

@ -1141,7 +1141,7 @@
(defn get-all-templates
[]
(let [pred (fn [db properties]
(some? (get properties "template")))]
(some? (:template properties)))]
(->> (d/q
'[:find ?b ?p
:in $ ?pred
@ -1151,7 +1151,7 @@
(conn/get-conn)
pred)
(map (fn [[e m]]
[(get m "template") e]))
[(get m :template) e]))
(into {}))))
(defonce blocks-count-cache (atom nil))

View File

@ -224,7 +224,7 @@
'?v
(uniq-symbol counter "?v"))]
[['?b :block/properties '?prop]
[(list 'get '?prop (name (nth e 1))) sym]
[(list 'get '?prop (keyword (nth e 1))) sym]
(list
'or
[(list '= sym v)]
@ -233,7 +233,7 @@
(and (= 'property fe)
(= 2 (count e)))
[['?b :block/properties '?prop]
[(list 'get '?prop (name (nth e 1)))]]
[(list 'get '?prop (keyword (nth e 1)))]]
(= 'todo fe)
(let [markers (if (coll? (first (rest e)))

View File

@ -130,6 +130,5 @@
:block/scheduled
:block/deadline
:block/repeated?
:block/properties
}
)

View File

@ -158,7 +158,7 @@
(:name (first (second block)))))
(defonce non-parsing-properties
(atom #{:background-color}))
(atom #{"background-color" "background_color"}))
(defn extract-properties
[[_ properties] _start-pos _end-pos]
@ -179,25 +179,33 @@
(medley/map-kv (fn [k v]
(let [v (string/trim v)
k (string/replace k " " "-")
k (string/replace k "_" "-")]
(cond
(and (= "\"" (first v) (last v))) ; wrapped in ""
[(string/lower-case k) (string/trim (subs v 1 (dec (count v))))]
k (string/replace k "_" "-")
k (string/lower-case k)
v (cond
(= v "true")
true
(= v "false")
false
(contains? @non-parsing-properties (string/lower-case k))
[(string/lower-case k) v]
(re-find #"^\d+$" v)
(util/safe-parse-int v)
:else
(let [k' (and k (string/trim (string/lower-case k)))
v' v
;; built-in collections
comma? (contains? #{"tags" "alias"} k)
v' (if (and k' v'
(contains? config/markers k')
(util/safe-parse-int v'))
(util/safe-parse-int v')
(text/split-page-refs-without-brackets v' comma?))]
[k' v']))))))]
(and (= "\"" (first v) (last v))) ; wrapped in ""
(string/trim (subs v 1 (dec (count v))))
(contains? @non-parsing-properties (string/lower-case k))
v
:else
(let [v' v
;; built-in collections
comma? (contains? #{"tags" "alias"} k)]
(if (and k v'
(contains? config/markers k)
(util/safe-parse-int v'))
(util/safe-parse-int v')
(text/split-page-refs-without-brackets v' comma?))))]
[(keyword k) v]))))]
{:properties properties
:page-refs page-refs}))
@ -427,8 +435,9 @@
(recur headings block-body (rest blocks) timestamps properties last-pos last-level children))
(heading-block? block)
(let [id (or (when-let [custom-id (or (get-in properties [:properties "custom_id"])
(get-in properties [:properties "id"]))]
(let [id (or (when-let [custom-id (or (get-in properties [:properties :custom-id])
(get-in properties [:properties :custom_id])
(get-in properties [:properties :id]))]
(let [custom-id (string/trim custom-id)]
(when (util/uuid-string? custom-id)
(uuid custom-id))))

View File

@ -46,10 +46,6 @@
[]
(storage/set :db-schema db-schema/schema))
(defn refactored-version?
[]
(:block/name (storage/get :db-schema)))
(defn- get-me-and-repos
[]
(let [me (and js/window.user (bean/->clj js/window.user))
@ -152,17 +148,10 @@
(when-not config/dev? (init-sentry)))]
(set! js/window.onload f)))
(defn clear-stores-and-init!
[me logged?]
(let [example-repo {:url config/local-repo
:example? true}]
(p/let [repos (get-repos)]
(doseq [repo (distinct (conj repos example-repo))]
(repo-handler/remove-repo! repo))
(p/let [_ (idb/clear-local-storage-and-idb!)
repos [example-repo]]
(state/set-repos! repos)
(restore-and-setup! me repos logged?)))))
(defn clear-stores-and-refresh!
[]
(p/let [_ (idb/clear-local-storage-and-idb!)]
(js/window.location.reload)))
(defn start!
[render]
@ -180,11 +169,9 @@
(events/run!)
(if-not (refactored-version?)
(clear-stores-and-init! me logged?)
(p/let [repos (get-repos)]
(state/set-repos! repos)
(restore-and-setup! me repos logged?)))
(p/let [repos (get-repos)]
(state/set-repos! repos)
(restore-and-setup! me repos logged?))
(reset! db/*sync-search-indice-f search/sync-search-indice!)
(db/run-batch-txs!)

View File

@ -25,40 +25,6 @@
block)]
@ids))
(defn collapse-block!
[block]
(let [repo (:block/repo block)]
(db/transact! repo
[{:block/uuid (:block/uuid block)
:block/collapsed? true}])))
(defn collapse-blocks!
[block-ids]
(let [repo (state/get-current-repo)]
(db/transact! repo
(map
(fn [id]
{:block/uuid id
:block/collapsed? true})
block-ids))))
(defn expand-block!
[block]
(let [repo (:block/repo block)]
(db/transact! repo
[{:block/uuid (:block/uuid block)
:block/collapsed? false}])))
(defn expand-blocks!
[block-ids]
(let [repo (state/get-current-repo)]
(db/transact! repo
(map
(fn [id]
{:block/uuid id
:block/collapsed? false})
block-ids))))
;; TODO: should we remove this dummy block and use the page's root block instead?
(defn with-dummy-block
([blocks format]

View File

@ -9,7 +9,6 @@
[frontend.handler.ui :as ui-handler]
[frontend.handler.repo :as repo-handler]
[frontend.handler.notification :as notification]
[frontend.handler.expand :as expand]
[frontend.handler.block :as block-handler]
[frontend.format.mldoc :as mldoc]
[frontend.format :as format]
@ -325,7 +324,7 @@
e (db/entity repo [:block/uuid uuid])
format (or format (state/get-preferred-format))
page (db/entity repo (:db/id page))
block-id (when (map? properties) (get properties "id"))]
block-id (when (map? properties) (get properties :id))]
(cond
(another-block-with-same-id-exists? uuid block-id)
(notification/show!
@ -727,14 +726,16 @@
(defn remove-block-property!
[block-id key]
(block-property-aux! block-id key nil)
(let [key (keyword key)]
(block-property-aux! block-id key nil))
(db/refresh! (state/get-current-repo)
{:key :block/change
:data [(db/pull [:block/uuid block-id])]}))
(defn set-block-property!
[block-id key value]
(block-property-aux! block-id key value)
(let [key (keyword key)]
(block-property-aux! block-id key value))
(db/refresh! (state/get-current-repo)
{:key :block/change
:data [(db/pull [:block/uuid block-id])]}))
@ -1521,22 +1522,30 @@
(defn expand!
[]
(when-let [current-block (state/get-edit-block)]
(expand/expand! current-block)))
(remove-block-property! (:block/uuid current-block) :collapsed)))
(defn collapse!
[]
(when-let [current-block (state/get-edit-block)]
(expand/collapse! current-block)))
(set-block-property! (:block/uuid current-block) :collapsed true)))
;; TODO:
(defn cycle-collapse!
[e]
(when (and
;; not input, t
(nil? (state/get-edit-input-id))
(not (state/get-editor-show-input))
(string/blank? (:search/q @state/state)))
(util/stop e)
(expand/cycle!)))
;; (let [current-page (state/get-current-page)]
;; (when (and
;; ;; not input, t
;; (nil? (state/get-edit-input-id))
;; (not (state/get-editor-show-input))
;; (string/blank? (:search/q @state/state))
;; current-page)
;; (util/stop e)
;; (let [page (db/pull [:block/name (string/lower-case current-page)])
;; collapsed? (boolean (get (:block/properties page) :collapsed))]
;; (prn {:current-page current-page
;; :collapsed? collapsed?})
;; (set-block-property! (:block/uuid page) :collapsed (not collapsed?)))))
)
(defn on-tab
"direction = :left|:right, only indent or outdent when blocks are siblings"
@ -1740,7 +1749,7 @@
;; Save it so it'll be parsed correctly in the future
(set-block-property! (:block/uuid chosen)
"ID"
:id
uuid-string)
(when-let [input (gdom/getElement id)]
@ -1800,8 +1809,8 @@
:block/file (select-keys file [:db/id])
:block/format format
:block/properties (apply dissoc (:block/properties %)
(concat ["id" "custom_id"]
exclude-properties))
(concat [:id :custom_id :custom-id]
exclude-properties))
:block/meta (dissoc (:block/meta %) :start-pos :end-pos)
:block/content new-content
:block/title new-title}
@ -1827,11 +1836,11 @@
(let [repo (state/get-current-repo)
block (db/entity db-id)
block-uuid (:block/uuid block)
including-parent? (not= "false" (get (:block/properties block) "including-parent"))
including-parent? (not (false? (:including-parent (:block/properties block))))
blocks (if including-parent? (db/get-block-and-children repo block-uuid) (db/get-block-children repo block-uuid))
level-blocks-map (blocks-with-level blocks)
tree (blocks-vec->tree (vals level-blocks-map))]
(paste-block-tree-at-point tree ["template" "including-parent"]
(paste-block-tree-at-point tree [:template :including-parent]
(fn [content]
(-> content
(text/remove-property "template")

View File

@ -1,102 +0,0 @@
(ns frontend.handler.expand
(:require [dommy.core :as d]
[goog.dom :as gdom]
[goog.object :as gobj]
[frontend.util :as util]
[frontend.state :as state]
[frontend.handler.block :as block-handler]))
(defn- hide!
[element]
(d/set-style! element :display "none"))
(defn- show!
[element]
(d/set-style! element :display ""))
(defn collapse!
[block]
(let [has-title? (seq (:block/title block))
uuid (:block/uuid block)
nodes (array-seq (js/document.getElementsByClassName (str uuid)))]
(doseq [node nodes]
(d/add-class! node "collapsed")
(when has-title?
(when-let [e (.querySelector node ".block-body")]
(hide! e)))
(when-let [e (.querySelector node ".block-children")]
(hide! e)
(let [elements (d/by-class node "ls-block")]
(doseq [element elements]
(hide! element))))
(block-handler/collapse-block! block))))
(defn expand!
[block]
(let [uuid (:block/uuid block)
nodes (array-seq (js/document.getElementsByClassName (str uuid)))]
(doseq [node nodes]
(when-let [e (.querySelector node ".block-body")]
(show! e))
(when-let [e (.querySelector node ".block-children")]
(let [elements (d/by-class node "ls-block")]
(doseq [element elements]
(show! element)))
(show! e))
(block-handler/expand-block! block))))
(defn set-bullet-closed!
[element]
(when element
(when-let [node (.querySelector element ".bullet-container")]
(d/add-class! node "bullet-closed"))))
;; Collapse acts like TOC
;; There are three modes to cycle:
;; 1. Collapse all blocks which levels are greater than 2
;; 2. Hide all block's body (user can still see the block title)
;; 3. Show everything
(defn cycle!
[]
(let [mode (state/next-collapse-mode)
get-blocks (fn []
(let [elements (d/by-class "ls-block")
result (group-by (fn [e]
(let [level (d/attr e "level")]
(and level
(> (util/parse-int level) 2)))) elements)]
[(get result true) (get result false)]))]
(case mode
:show-all
(do
(doseq [element (d/by-class "ls-block")]
(show! element))
(let [elements (d/by-class "block-body")]
(doseq [element elements]
(show! element)))
(doseq [element (d/by-class "bullet-closed")]
(d/remove-class! element "bullet-closed"))
(doseq [element (d/by-class "block-children")]
(show! element)))
:hide-block-body
(let [elements (d/by-class "block-body")]
(doseq [element elements]
(d/set-style! element :display "none")
(when-let [parent (util/rec-get-block-node element)]
(set-bullet-closed! parent))))
:hide-block-children
(let [[elements top-level-elements] (get-blocks)
level-2-elements (filter (fn [e]
(let [level (d/attr e "level")]
(and level
(= (util/parse-int level) 2)
(not (d/has-class? e "pre-block")))))
top-level-elements)]
(doseq [element elements]
(hide! element))
(doseq [element level-2-elements]
(when (= "true" (d/attr element "haschild"))
(set-bullet-closed! element)))))
(state/cycle-collapse!)))

View File

@ -19,7 +19,9 @@
[frontend.db.model :as db-model]
[frontend.config :as config]
[lambdaisland.glogi :as log]
[frontend.encrypt :as encrypt]))
[frontend.encrypt :as encrypt]
[frontend.search :as search]
[frontend.storage :as storage]))
(defn remove-ignore-files
[files]
@ -278,25 +280,32 @@
(p/finally (fn [_]
(state/set-graph-syncing? false))))))))
(defn refresh!
[repo ok-handler]
(when repo
(state/set-nfs-refreshing! true)
(p/let [_ (reload-dir! repo)
_ (ok-handler)]
(state/set-nfs-refreshing! false))))
(defn rebuild-index!
[repo ok-handler]
(when repo
(state/set-nfs-refreshing! true)
;; TODO: What about other relationships?
(db-model/remove-all-aliases! repo)
(search/reset-indice! repo)
(db/remove-conn! repo)
(db/clear-query-state!)
(db/start-db-conn! (state/get-me) repo)
(p/let [_ (reload-dir! repo true)
_ (ok-handler)]
(state/set-nfs-refreshing! false))))
(defn refactored-version?
[]
(:block/name (storage/get :db-schema)))
(defn refresh!
[repo ok-handler]
(if (refactored-version?)
(rebuild-index! repo ok-handler)
(when repo
(state/set-nfs-refreshing! true)
(p/let [_ (reload-dir! repo)
_ (ok-handler)]
(state/set-nfs-refreshing! false)))))
(defn supported?
[]
(or (utils/nfsSupported) (util/electron?)))

View File

@ -120,7 +120,8 @@
(fn [{:keys [item]}]
(:name item))
result)
(remove nil?))))))))
(remove nil?)
(util/distinct-by :name))))))))
(defn file-search
([q]

View File

@ -137,7 +137,7 @@
(def hidden-properties
(set/union
#{:id :custom-id :background-color :heading}
#{:id :custom-id :background-color :heading :collapsed}
config/markers))
(defn properties-hidden?
@ -195,8 +195,8 @@
:or {remove-blank? true
block-with-title? true}}]
(let [content (string/triml content)
properties (if (= (get properties "heading") "false")
(dissoc properties "heading")
properties (if (false? (get properties :heading))
(dissoc properties :heading)
properties)
properties (if remove-blank?
(remove (fn [[k _v]] (string/blank? k)) properties)