Merge branch 'feat/db' into refactor/page-parent

This commit is contained in:
Gabriel Horner
2025-05-20 13:32:26 -04:00
52 changed files with 718 additions and 493 deletions

View File

@@ -13,12 +13,12 @@
[frontend.handler.notification :as notification]
[frontend.handler.plugin :as plugin-handler]
[frontend.handler.property.file :as property-file]
[frontend.util.ref :as ref]
[frontend.search :as search]
[frontend.state :as state]
[frontend.util :as util]
[frontend.util.cursor :as cursor]
[frontend.util.file-based.priority :as priority]
[frontend.util.ref :as ref]
[goog.dom :as gdom]
[goog.object :as gobj]
[logseq.common.config :as common-config]
@@ -439,11 +439,10 @@
(println "draw file created, " path))
text)) "Draw a graph with Excalidraw"])
(when (util/electron?)
["Upload an asset"
[[:editor/click-hidden-file-input :id]]
"Upload file types like image, pdf, docx, etc.)"
:icon/upload])
["Upload an asset"
[[:editor/click-hidden-file-input :id]]
"Upload file types like image, pdf, docx, etc.)"
:icon/upload]
["Template" [[:editor/input command-trigger nil]
[:editor/search-template]] "Insert a created template here"
@@ -473,7 +472,7 @@
commands)
;; Allow user to modify or extend, should specify how to extend.
(state/get-commands)
(when-let [plugin-commands (seq (some->> (state/get-plugins-slash-commands)
(mapv #(vec (concat % [nil :icon/puzzle])))))]

View File

@@ -29,27 +29,6 @@
:else
content))
(defn- recur-replace-uuid-in-block-title
"Return block-title"
[ent max-depth]
(let [ref-set (loop [result-refs (:block/refs ent)
current-refs (:block/refs ent)
depth 0]
(if (or (>= depth max-depth) (empty? current-refs))
result-refs
(let [next-refs (set (mapcat :block/refs current-refs))
result-refs' (apply conj result-refs next-refs)]
(if (= (count result-refs') (count result-refs))
result-refs
(recur (apply conj result-refs next-refs) next-refs (inc depth))))))]
(loop [result (db-content/id-ref->title-ref (:block/title ent) ref-set true)
last-result nil
depth 0]
(if (or (>= depth max-depth)
(= last-result result))
result
(recur (db-content/id-ref->title-ref result ref-set true) result (inc depth))))))
(defn- transform-content
[repo db {:block/keys [collapsed? format pre-block? title page properties] :as b} level {:keys [heading-to-list?]} context]
(let [db-based? (sqlite-util/db-based-graph? repo)
@@ -60,7 +39,7 @@
markdown? (= :markdown format)
title (if db-based?
;; replace [[uuid]] with block's content
(recur-replace-uuid-in-block-title (d/entity db (:db/id b)) 10)
(db-content/recur-replace-uuid-in-block-title (d/entity db (:db/id b)))
title)
content (or title "")
page-first-child? (= (:db/id b) (ldb/get-first-child db (:db/id page)))

View File

@@ -656,7 +656,7 @@
All page-names are sanitized except page-name-in-block"
[state
{:keys [contents-page? whiteboard-page? html-export? other-position? show-unique-title? stop-click-event?
{:keys [contents-page? whiteboard-page? other-position? show-unique-title? stop-click-event?
on-context-menu]
:or {stop-click-event? true}
:as config}
@@ -701,12 +701,13 @@
(reset! *mouse-down? true))))
:on-pointer-up (fn [e]
(when @*mouse-down?
(util/stop e)
(state/clear-edit!)
(when-not (or (:disable-click? config)
(:disable-redirect? config))
(when-not (:disable-click? config)
(open-page-ref config page-entity e page-name contents-page?))
(reset! *mouse-down? false)))
:on-key-up (fn [e] (when (and e (= (.-key e) "Enter") (not other-position?))
(util/stop e)
(state/clear-edit!)
(open-page-ref config page-entity e page-name contents-page?)))}
on-context-menu
@@ -726,7 +727,7 @@
(last child)
(let [{:keys [content children]} (last child)
page-name (subs content 2 (- (count content) 2))]
(rum/with-key (page-reference html-export? page-name (assoc config :children children) nil) page-name))))
(rum/with-key (page-reference (assoc config :children children) page-name nil) page-name))))
(let [page-component (cond
(and label
(string? label)
@@ -943,26 +944,22 @@
config (assoc config :block entity)]
(cond
entity
(if (or (ldb/page? entity) (not (:block/page entity)))
(let [page-name (some-> (:block/title entity) util/page-name-sanity-lc)
whiteboard-page? (model/whiteboard-page? entity)
inner (page-inner (assoc config :whiteboard-page? whiteboard-page?) entity children label)
modal? (shui-dialog/has-modal?)]
(if (and (not (util/mobile?))
(not= page-name (:id config))
(not (false? preview?))
(not disable-preview?)
(not modal?))
(page-preview-trigger (assoc config :children inner) entity)
inner))
(block-reference config (:block/uuid entity)
(if (string? label)
(gp-mldoc/inline->edn label (mldoc/get-default-config :markdown))
label)))
(let [page-name (some-> (:block/title entity) util/page-name-sanity-lc)
whiteboard-page? (model/whiteboard-page? entity)
inner (page-inner (assoc config :whiteboard-page? whiteboard-page?) entity children label)
modal? (shui-dialog/has-modal?)]
(if (and (not (util/mobile?))
(not= page-name (:id config))
(not (false? preview?))
(not disable-preview?)
(not modal?))
(page-preview-trigger (assoc config :children inner) entity)
inner))
(and (:block/name page) show-non-exists-page?)
(page-inner config (merge
{:block/title (:block/name page)
{:block/title (or (:block/title page)
(:block/name page))
:block/name (:block/name page)}
page) children label)
@@ -977,8 +974,9 @@
(rum/defc page-cp
[config page]
(rum/with-key (page-cp-inner config page)
(or (str (:db/id page)) (str (:block/uuid page)) (:block/name page))))
(let [id (or (:db/id page) (:block/uuid page) (:block/name page))]
(rum/with-key (page-cp-inner config page)
(str id))))
(rum/defc asset-reference
[config title path]
@@ -1068,58 +1066,62 @@
(declare block-positioned-properties)
(rum/defc page-reference < rum/reactive db-mixins/query
"Component for page reference"
[html-export? uuid-or-title* {:keys [nested-link? show-brackets? id] :as config} label]
[{:keys [html-export? nested-link? show-brackets? id] :as config*} uuid-or-title* label]
(when uuid-or-title*
(let [uuid-or-title (if (string? uuid-or-title*)
(string/trim uuid-or-title*)
(let [str-id (string/trim uuid-or-title*)]
(if (util/uuid-string? str-id)
(parse-uuid str-id)
str-id))
uuid-or-title*)
show-brackets? (if (some? show-brackets?) show-brackets? (state/show-brackets?))
contents-page? (= "contents" (string/lower-case (str id)))
block* (db/get-page uuid-or-title)
block (or (some-> (:db/id block*) db/sub-block) block*)
config' (assoc config
:label (mldoc/plain->text label)
:contents-page? contents-page?
:show-icon? true?)
asset? (some? (:logseq.property.asset/type block))
page? (ldb/page? block)
brackets? (and (or show-brackets? nested-link?)
(not html-export?)
(not contents-page?)
page?)]
(when-not (= (:db/id block) (:db/id (:block config)))
(cond
(and asset? (img-audio-video? block))
(asset-cp config block)
self-reference? (when (set? (:ref-set config*))
(contains? (:ref-set config*) uuid-or-title))]
(when-not self-reference?
(let [config (update config* :ref-set (fn [s]
(let [bid (:block/uuid (:block config*))]
(if (nil? s)
#{bid}
(conj s bid uuid-or-title)))))
show-brackets? (if (some? show-brackets?) show-brackets? (state/show-brackets?))
contents-page? (= "contents" (string/lower-case (str id)))
block* (db/get-page uuid-or-title)
block (or (some-> (:db/id block*) db/sub-block) block*)
config' (assoc config
:label (mldoc/plain->text label)
:contents-page? contents-page?
:show-icon? true?)
asset? (some? (:logseq.property.asset/type block))
brackets? (and (or show-brackets? nested-link?)
(not html-export?)
(not contents-page?))]
(when-not (= (:db/id block) (:db/id (:block config)))
(cond
(and asset? (img-audio-video? block))
(asset-cp config block)
(or page? (:block/tags block))
[:span.page-reference
{:data-ref (str uuid-or-title)}
(when brackets?
[:span.text-gray-500.bracket page-ref/left-brackets])
(when (and (config/db-based-graph?) (ldb/class-instance? (db/entity :logseq.class/Task) block))
[:div.inline-block
{:style {:margin-right 1
:margin-top -2
:vertical-align "middle"}
:on-pointer-down (fn [e]
(util/stop e))}
(block-positioned-properties config block :block-left)])
(page-cp config' (if (uuid? uuid-or-title)
{:block/uuid uuid-or-title}
{:block/name uuid-or-title}))
(when brackets?
[:span.text-gray-500.bracket page-ref/right-brackets])]
(and (string? uuid-or-title) (string/ends-with? uuid-or-title ".excalidraw"))
[:div.draw {:on-click (fn [e]
(.stopPropagation e))}
(excalidraw uuid-or-title (:block/uuid config))]
(and (string? uuid-or-title) (string/ends-with? uuid-or-title ".excalidraw"))
[:div.draw {:on-click (fn [e]
(.stopPropagation e))}
(excalidraw uuid-or-title (:block/uuid config))]
:else
(page-cp config' (if (uuid? uuid-or-title)
{:block/uuid uuid-or-title}
{:block/name uuid-or-title})))))))
:else
[:span.page-reference
{:data-ref (str uuid-or-title)}
(when brackets?
[:span.text-gray-500.bracket page-ref/left-brackets])
(when (and (config/db-based-graph?) (ldb/class-instance? (db/entity :logseq.class/Task) block))
[:div.inline-block
{:style {:margin-right 1
:margin-top -2
:vertical-align "middle"}
:on-pointer-down (fn [e]
(util/stop e))}
(block-positioned-properties config block :block-left)])
(page-cp config' (if (uuid? uuid-or-title)
{:block/uuid uuid-or-title}
{:block/name uuid-or-title}))
(when brackets?
[:span.text-gray-500.bracket page-ref/right-brackets])])))))))
(defn- latex-environment-content
[name option content]
@@ -1340,13 +1342,18 @@
(set-block! block)))
[])
(when-not self-reference?
(if block
(cond
(config/db-based-graph?)
(page-reference config block-id label)
block
(let [config' (update config :ref-set (fn [s]
(let [bid (:block/uuid (:block config))]
(if (nil? s)
#{bid}
(conj s bid block-id)))))]
(block-reference-aux config' block label))
:else
(invalid-node-ref block-id)))))
(defn- render-macro
@@ -1475,7 +1482,7 @@
(block-reference config id label))
(not (string/includes? s "."))
(page-reference (:html-export? config) s config label)
(page-reference config s label)
(path/protocol-url? s)
(->elem :a {:href s
@@ -1507,9 +1514,9 @@
(map-inline config label)))
:else
(page-reference (:html-export? config) s config label)))
(page-reference config s label)))
(defn- link-cp [config html-export? link]
(defn- link-cp [config link]
(let [{:keys [url label title metadata full_text]} link]
(match url
["Block_ref" id]
@@ -1533,7 +1540,7 @@
(let [label* (if (seq (mldoc/plain->text label)) label nil)]
(if (and (string? page) (string/blank? page))
[:span (ref/->page-ref page)]
(page-reference (:html-export? config) page config label*)))))
(page-reference config page label*)))))
["Embed_data" src]
(image-link config url src nil metadata full_text)
@@ -1555,7 +1562,7 @@
block (db/entity [:block/uuid id])]
(if (:block/pre-block? block)
(let [page (:block/page block)]
(page-reference html-export? (:block/name page) config label))
(page-reference config (:block/name page) label))
(block-reference config (:link path) label)))
(= protocol "file")
@@ -1977,7 +1984,7 @@
(nested-link config html-export? link)
["Link" link]
(link-cp config html-export? link)
(link-cp config link)
[(:or "Verbatim" "Code") s]
[:code s]
@@ -2052,7 +2059,6 @@
selected? (contains? selected block-id)]
(when-not selected?
(util/clear-selection!)
(state/conj-selection-block! (gdom/getElement block-id) :down)
(editor-handler/highlight-block! uuid)))
(editor-handler/block->data-transfer! uuid event false)
@@ -2171,7 +2177,7 @@
(reset! *bullet-dragging? true)
(util/stop-propagation event)
(bullet-drag-start event block uuid block-id))
:on-drag-end (fn [_]
:on-drag-end (fn [_e]
(reset! *bullet-dragging? false))
:blockid (str uuid)
:class (str (when collapsed? "bullet-closed")
@@ -3039,7 +3045,6 @@
(block-content config block edit-input-id block-id *show-query?))))
(rum/defcs ^:large-vars/cleanup-todo block-content-or-editor < rum/reactive
(rum/local false ::hover?)
[state config {:block/keys [uuid] :as block} {:keys [edit-input-id block-id edit? hide-block-refs-count? refs-count *hide-block-refs? *show-query?]}]
(let [format (if (config/db-based-graph? (state/get-current-repo))
:markdown
@@ -3076,7 +3081,10 @@
:format format}
edit-input-id
config))]
[:div.flex.flex-1.w-full.block-content-wrapper {:style {:display "flex"}}
[:div.flex.flex-1.w-full.block-content-wrapper
{:style {:display "flex"}}
(when-let [actions-cp (:page-title-actions-cp config)]
(actions-cp block))
(block-content-with-error config block edit-input-id block-id *show-query? editor-box)
(when (and (not hide-block-refs-count?)
@@ -3182,7 +3190,6 @@
rest)
config (assoc config
:breadcrumb? true
:disable-redirect? true
:disable-preview? true
:stop-click-event? false)]
(when (seq parents)
@@ -3615,8 +3622,6 @@
[:div.flex.flex-col.w-full
[:div.block-main-content.flex.flex-row.gap-2
(when-let [actions-cp (:page-title-actions-cp config)]
(actions-cp block))
(when page-icon
page-icon)

View File

@@ -524,10 +524,16 @@
}
}
.block-main-content {
.ls-page-title-container .block-content-wrapper {
.ls-page-title-actions {
@apply absolute -top-4 opacity-0;
left: -2px;
}
&:hover {
& > .db-page-title-actions {
& > .ls-page-title-actions {
@apply delay-300 transition-opacity opacity-100;
}
}
}

View File

@@ -13,7 +13,7 @@
(when (seq children)
[:ul
(for [child (sort-by :block/title children)]
(let [title [:li.ml-2 (block/page-reference false (:block/uuid child) {:show-brackets? false} nil)]]
(let [title [:li.ml-2 (block/page-reference {:show-brackets? false} (:block/uuid child) nil)]]
(if (seq (:logseq.property.class/_extends child))
(ui/foldable
title

View File

@@ -933,8 +933,13 @@
nil)
(defn- on-mouse-up
[_e]
(editor-handler/show-action-bar!))
[e]
(when-not (or (.closest (.-target e) ".block-control-wrap")
(.closest (.-target e) "button")
(.closest (.-target e) "input")
(.closest (.-target e) "textarea")
(.closest (.-target e) "a"))
(editor-handler/show-action-bar!)))
(rum/defcs ^:large-vars/cleanup-todo root-container < rum/reactive
(mixins/event-mixin

View File

@@ -134,6 +134,20 @@
:other-attrs {:block/link (:db/id page')}}))))
(page-handler/on-chosen-handler input id pos format)))
(defn- matched-pages-with-new-page [partial-matched-pages db-tag? q]
(if (or (db/page-exists? q (if db-tag?
#{:logseq.class/Tag}
;; Page existence here should be the same as entity-util/page?.
;; Don't show 'New page' if a page has any of these tags
db-class/page-classes))
(and db-tag? (some ldb/class? (:block/_alias (db/get-page q)))))
partial-matched-pages
(if db-tag?
(concat [{:block/title (str (t :new-tag) " " q)}]
partial-matched-pages)
(cons {:block/title (str (t :new-page) " " q)}
partial-matched-pages))))
(rum/defc page-search-aux
[id format embed? db-tag? q current-pos input pos]
(let [db-based? (config/db-based-graph? (state/get-current-repo))
@@ -157,25 +171,11 @@
date/nlp-pages)
(take 10))))
;; reorder, shortest and starts-with first.
(let [matched-pages-with-new-page
(fn [partial-matched-pages]
(if (or (db/page-exists? q (if db-tag?
#{:logseq.class/Tag}
;; Page existence here should be the same as entity-util/page?.
;; Don't show 'New page' if a page has any of these tags
db-class/page-classes))
(and db-tag? (some ldb/class? (:block/_alias (db/get-page q)))))
partial-matched-pages
(if db-tag?
(concat [{:block/title (str (t :new-tag) " " q)}]
partial-matched-pages)
(cons {:block/title (str (t :new-page) " " q)}
partial-matched-pages))))]
(if (and (seq matched-pages)
(gstring/caseInsensitiveStartsWith (:block/title (first matched-pages)) q))
(cons (first matched-pages)
(matched-pages-with-new-page (rest matched-pages)))
(matched-pages-with-new-page matched-pages))))]
(if (and (seq matched-pages)
(gstring/caseInsensitiveStartsWith (:block/title (first matched-pages)) q))
(cons (first matched-pages)
(matched-pages-with-new-page (rest matched-pages) db-tag? q))
(matched-pages-with-new-page matched-pages db-tag? q)))]
[:<>
(ui/auto-complete
matched-pages'
@@ -184,7 +184,9 @@
(page-handler/page-not-exists-handler input id q current-pos))
:item-render (fn [block _chosen?]
(let [block' (if-let [id (:block/uuid block)]
(or (db/entity [:block/uuid id]) block)
(if-let [e (db/entity [:block/uuid id])]
(assoc e :block/title (:block/title block))
block)
block)]
[:div.flex.flex-col
(when (and (:block/uuid block') (:block/parent block'))
@@ -218,10 +220,11 @@
(ui/icon "letter-n" {:size 14}))])
(let [title (if db-tag?
(let [target (first (:block/_alias block'))]
(let [target (first (:block/_alias block'))
title (:block/title block)]
(if (ldb/class? target)
(str (:block/title block') " -> alias: " (:block/title target))
(:block/title block')))
(str title " -> alias: " (:block/title target))
title))
(block-handler/block-unique-title block'))]
(search-handler/highlight-exact-query title q))]]))
:empty-placeholder [:div.text-gray-500.text-sm.px-4.py-2 (if db-tag?

View File

@@ -12,7 +12,6 @@
[frontend.handler.export.opml :as export-opml]
[frontend.handler.export.text :as export-text]
[frontend.handler.notification :as notification]
[frontend.idb :as idb]
[frontend.image :as image]
[frontend.mobile.util :as mobile-util]
[frontend.state :as state]
@@ -33,51 +32,55 @@
[:div.flex.flex-col.gap-4
[:div.font-medium.opacity-50
"Schedule backup"]
(if backup-folder
[:div.flex.flex-row.items-center.gap-1.text-sm
[:div.opacity-50 (str "Backup folder:")]
backup-folder
(shui/button
{:variant :ghost
:class "!px-1 !py-1"
:title "Change backup folder"
:on-click (fn []
(p/do!
(db/transact! [[:db/retractEntity :logseq.kv/graph-backup-folder]])
(reset! *backup-folder nil)))
:size :sm}
(ui/icon "edit"))]
(shui/button
{:variant :default
:on-click (fn []
(p/let [result (utils/openDirectory #js {:mode "readwrite"})
handle (first result)
folder-name (.-name handle)]
(idb/set-item!
(str "handle/" (js/btoa repo) "/" folder-name) handle)
(db/transact! [(ldb/kv :logseq.kv/graph-backup-folder folder-name)])
(reset! *backup-folder folder-name)))}
"Set backup folder first"))
[:div.opacity-50.text-sm
"Backup will be created every hour."]
(if (utils/nfsSupported)
[:<>
(if backup-folder
[:div.flex.flex-row.items-center.gap-1.text-sm
[:div.opacity-50 (str "Backup folder:")]
backup-folder
(shui/button
{:variant :ghost
:class "!px-1 !py-1"
:title "Change backup folder"
:on-click (fn []
(p/do!
(db/transact! [[:db/retractEntity :logseq.kv/graph-backup-folder]])
(reset! *backup-folder nil)))
:size :sm}
(ui/icon "edit"))]
(shui/button
{:variant :default
:on-click (fn []
(p/let [[folder-name _handle] (export/choose-backup-folder repo)]
(reset! *backup-folder folder-name)))}
"Set backup folder first"))
[:div.opacity-50.text-sm
"Backup will be created every hour."]
(when backup-folder
(shui/button
{:variant :default
:on-click (fn []
(->
(p/let [result (export/backup-db-graph repo)]
(case result
true
(notification/show! "Backup successful!" :success)
:graph-not-changed
(notification/show! "Graph has not been updated since last export." :success)
nil)
(export/auto-db-backup! repo {:backup-now? false}))
(p/catch (fn [error]
(println "Failed to backup.")
(js/console.error error)))))}
"Backup now"))]))
(when backup-folder
(shui/button
{:variant :default
:on-click (fn []
(->
(p/let [result (export/backup-db-graph repo :set-folder)]
(case result
true
(notification/show! "Backup successful!" :success)
:graph-not-changed
(notification/show! "Graph has not been updated since last export." :success)
nil)
(export/auto-db-backup! repo {:backup-now? false}))
(p/catch (fn [error]
(println "Failed to backup.")
(js/console.error error)))))}
"Backup now"))]
[:div
[:span "Your browser doesn't support "]
[:a
{:href "https://developer.chrome.com/docs/capabilities/web-apis/file-system-access"
:target "_blank"}
"The File System Access API"]
[:span ", please switch to a Chromium-based browser."]])]))
(rum/defc export
[]
@@ -133,7 +136,7 @@
"Export debug transit file"]
[:p.text-sm.opacity-70.mb-0 "Any sensitive data will be removed in the exported transit file, you can send it to us for debugging."]])
(when (and db-based? util/web-platform? (utils/nfsSupported))
(when (and db-based? util/web-platform?)
[:div
[:hr]
(auto-backup)])]])))

View File

@@ -60,10 +60,7 @@
(when (and (string? page) page)
(let [full-page (->> (take (inc idx) namespace)
util/string-join-path)]
(block/page-reference false
full-page
{}
page))))
(block/page-reference {} full-page page))))
(interpose [:span.mx-2.opacity-30 "/"]))])]
{:default-collapsed? false
:title-trigger? true})])))

View File

@@ -402,8 +402,10 @@
(str "collab-" current-repo))
(rtc-indicator/indicator)])
(when (user-handler/logged-in?)
(rtc-indicator/downloading-detail))
(when (user-handler/logged-in?)
(rtc-indicator/downloading-detail))
(when (user-handler/logged-in?)
(rtc-indicator/uploading-detail))
(when (and current-repo
(not (config/demo-graph? current-repo))

View File

@@ -29,9 +29,10 @@
opts (dissoc opts :color?)
item (cond
(and (= :emoji (:type icon')) (:id icon'))
[:em-emoji (merge {:id (:id icon')
:style {:line-height 1}}
opts)]
[:span.ui__icon
[:em-emoji (merge {:id (:id icon')
:style {:line-height 1}}
opts)]]
(and (= :tabler-icon (:type icon')) (:id icon'))
(ui/icon (:id icon') opts))]

View File

@@ -420,11 +420,11 @@
(rum/defc db-page-title-actions
[page]
[:div.absolute.-top-4.left-0.opacity-0.db-page-title-actions
[:div.ls-page-title-actions
[:div.flex.flex-row.items-center.gap-2
(when-not (:logseq.property/icon (db/entity (:db/id page)))
(shui/button
{:variant :outline
{:variant :ghost
:size :sm
:class "px-2 py-0 h-6 text-xs text-muted-foreground"
:on-click (fn [e]
@@ -434,7 +434,7 @@
"Add icon"))
(shui/button
{:variant :outline
{:variant :ghost
:size :sm
:class "px-2 py-0 h-6 text-xs text-muted-foreground"
:on-click (fn [e]

View File

@@ -92,7 +92,7 @@
}
}
.db-page-title-actions {
.ls-page-title-actions {
&:has(button[data-popup-active]) {
@apply opacity-100;
}

View File

@@ -33,6 +33,7 @@
[lambdaisland.glogi :as log]
[logseq.common.util.macro :as macro-util]
[logseq.db :as ldb]
[logseq.db.frontend.content :as db-content]
[logseq.db.frontend.entity-util :as entity-util]
[logseq.db.frontend.property :as db-property]
[logseq.db.frontend.property.type :as db-property-type]
@@ -748,7 +749,7 @@
id (:db/id node)
[header label] (if (integer? id)
(let [node-title (if (seq (:logseq.property/classes property))
(:block/title node)
(db-content/recur-replace-uuid-in-block-title node)
(block-handler/block-unique-title node))
title (subs node-title 0 256)
node (or (db/entity id) node)
@@ -756,7 +757,7 @@
header (when-not (db/page? node)
(when-let [breadcrumb (state/get-component :block/breadcrumb)]
[:div.text-xs.opacity-70
(breadcrumb {:search? true} (state/get-current-repo) (:block/uuid block) {})]))
(breadcrumb {:search? true} (state/get-current-repo) (:block/uuid node) {})]))
label [:div.flex.flex-row.items-center.gap-1
(when-not (or (:logseq.property/classes property)
(= (:db/ident property) :block/tags))
@@ -1092,10 +1093,7 @@
(closed-value-item value opts)
(or (entity-util/page? value)
(and (seq (:block/tags value))
;; FIXME: page-cp should be renamed to node-cp and
;; support this case and maybe other complex cases.
(not (string/includes? (:block/title value) "[["))))
(seq (:block/tags value)))
(when value
(let [opts {:disable-preview? true
:tag? tag?
@@ -1158,7 +1156,7 @@
(if editing?
(popup-content nil)
(let [show! (fn [e]
(util/stop e)
(state/clear-selection!)
(let [target (when e (.-target e))]
(when-not (or config/publishing?
(util/shift-key? e)
@@ -1173,7 +1171,7 @@
{:ref *el
:id trigger-id
:tabIndex 0
:on-click show!
:on-pointer-down show!
:on-key-down (fn [e]
(case (util/ekey e)
("Backspace" "Delete")

View File

@@ -38,10 +38,11 @@
(rum/defc block-cp < rum/reactive
[repo idx block]
(let [id (:block/uuid block)]
(page/page-cp {:parameters {:path {:name (str id)}}
:sidebar? true
:sidebar/idx idx
:repo repo})))
[:div.mt-2
(page/page-cp {:parameters {:path {:name (str id)}}
:sidebar? true
:sidebar/idx idx
:repo repo})]))
(defn get-scrollable-container
[]
@@ -68,77 +69,89 @@
:sidebar-key sidebar-key} repo block-id {:indent? false})]
(block-cp repo idx block)]))
(rum/defc search-title < rum/reactive
[*input]
(let [input (rum/react *input)
input' (if (string/blank? input) "Blank input" input)]
[:span.overflow-hidden.text-ellipsis input']))
(rum/defc sidebar-search
[repo block-type init-key input *input]
(rum/with-key
(cmdk/cmdk-block {:initial-input input
:sidebar? true
:on-input-change (fn [new-value]
(reset! *input new-value))
:on-input-blur (fn [new-value]
(state/sidebar-replace-block! [repo input block-type]
[repo new-value block-type]))})
(str init-key)))
(defn- <build-sidebar-item
[repo idx db-id block-type *db-id init-key]
(p/do!
(db-async/<get-block repo db-id)
(let [lookup (cond
(integer? db-id) db-id
(uuid? db-id) [:block/uuid db-id]
:else nil)
entity (when lookup (db/entity repo lookup))
page? (ldb/page? entity)
block-render (fn []
(when entity
(if page?
[[:.flex.items-center.page-title.gap-1
(icon/get-node-icon-cp entity {:class "text-md"})
[:span.overflow-hidden.text-ellipsis (:block/title entity)]]
(page-cp repo (str (:block/uuid entity)))]
(block-with-breadcrumb repo entity idx [repo db-id block-type] false))))]
(case (keyword block-type)
:contents
(when-let [page (db/get-page "Contents")]
[[:.flex.items-center (ui/icon "list-details" {:class "text-md mr-2"}) (t :right-side-bar/contents)]
(page-cp repo (str (:block/uuid page)))])
(->
(p/do!
(when-not (contains? #{:contents :search} block-type)
(db-async/<get-block repo db-id))
(let [lookup (cond
(integer? db-id) db-id
(uuid? db-id) [:block/uuid db-id]
:else nil)
entity (when lookup (db/entity repo lookup))
page? (ldb/page? entity)
block-render (fn []
(when entity
(if page?
[[:.flex.items-center.page-title.gap-1
(icon/get-node-icon-cp entity {:class "text-md"})
[:span.overflow-hidden.text-ellipsis (:block/title entity)]]
(page-cp repo (str (:block/uuid entity)))]
(block-with-breadcrumb repo entity idx [repo db-id block-type] false))))]
(case (keyword block-type)
:contents
(when-let [page (db/get-page "Contents")]
[[:.flex.items-center (ui/icon "list-details" {:class "text-md mr-2"}) (t :right-side-bar/contents)]
(page-cp repo (str (:block/uuid page)))])
:help
[[:.flex.items-center (ui/icon "help" {:class "text-md mr-2"}) (t :right-side-bar/help)] (onboarding/help)]
:help
[[:.flex.items-center (ui/icon "help" {:class "text-md mr-2"}) (t :right-side-bar/help)] (onboarding/help)]
:page-graph
[[:.flex.items-center (ui/icon "hierarchy" {:class "text-md mr-2"}) (t :right-side-bar/page-graph)]
(page/page-graph)]
:page-graph
[[:.flex.items-center (ui/icon "hierarchy" {:class "text-md mr-2"}) (t :right-side-bar/page-graph)]
(page/page-graph)]
:block-ref
(let [lookup (if (integer? db-id) db-id [:block/uuid db-id])]
(when-let [block (db/entity repo lookup)]
[(t :right-side-bar/block-ref)
(block-with-breadcrumb repo block idx [repo db-id block-type] true)]))
:block-ref
(let [lookup (if (integer? db-id) db-id [:block/uuid db-id])]
(when-let [block (db/entity repo lookup)]
[(t :right-side-bar/block-ref)
(block-with-breadcrumb repo block idx [repo db-id block-type] true)]))
:block
(block-render)
:block
(block-render)
:page
(block-render)
:page
(block-render)
:search
[[:.flex.items-center.page-title
(ui/icon "search" {:class "text-md mr-2"})
(let [input (rum/react *db-id)
input' (if (string/blank? input) "Blank input" input)]
[:span.overflow-hidden.text-ellipsis input'])]
(rum/with-key
(cmdk/cmdk-block {:initial-input db-id
:sidebar? true
:on-input-change (fn [new-value]
(reset! *db-id new-value))
:on-input-blur (fn [new-value]
(state/sidebar-replace-block! [repo db-id block-type]
[repo new-value block-type]))})
(str init-key))]
:search
[[:.flex.items-center.page-title
(ui/icon "search" {:class "text-md mr-2"})
(search-title *db-id)]
(sidebar-search repo block-type init-key db-id *db-id)]
:shortcut-settings
[[:.flex.items-center (ui/icon "command" {:class "text-md mr-2"}) (t :help/shortcuts)]
(shortcut-settings)]
:rtc
[[:.flex.items-center (ui/icon "cloud" {:class "text-md mr-2"}) "(Dev) RTC"]
(rtc-debug-ui/rtc-debug-ui)]
:shortcut-settings
[[:.flex.items-center (ui/icon "command" {:class "text-md mr-2"}) (t :help/shortcuts)]
(shortcut-settings)]
:rtc
[[:.flex.items-center (ui/icon "cloud" {:class "text-md mr-2"}) "(Dev) RTC"]
(rtc-debug-ui/rtc-debug-ui)]
:profiler
[[:.flex.items-center (ui/icon "cloud" {:class "text-md mr-2"}) "(Dev) Profiler"]
(profiler/profiler)]
:profiler
[[:.flex.items-center (ui/icon "cloud" {:class "text-md mr-2"}) "(Dev) Profiler"]
(profiler/profiler)]
["" [:span]]))))
["" [:span]])))
(p/catch (fn [error]
(js/console.error error)))))
(defonce *drag-to
(atom nil))

View File

@@ -1,7 +1,6 @@
(ns frontend.components.rtc.indicator
"RTC state indicator"
(:require [cljs-time.core :as t]
[clojure.pprint :as pprint]
(:require [clojure.pprint :as pprint]
[frontend.common.missionary :as c.m]
[frontend.db :as db]
[frontend.flows :as flows]
@@ -138,19 +137,25 @@
pprint/pprint
with-out-str)]])]))
(defn- downloading?
[detail-info]
(when-let [{:keys [created-at sub-type]} (first (:download-logs detail-info))]
(and (not= :download-completed sub-type)
(> 600 ;; 10min
(/ (- (t/now) created-at) 1000)))))
(defn- uploading?
[detail-info]
(when-let [{:keys [created-at sub-type]} (first (:upload-logs detail-info))]
(and (not= :upload-completed sub-type)
(> 600
(/ (- (t/now) created-at) 1000)))))
(rum/defc indicator
[]
(let [detail-info (hooks/use-flow-state (m/watch *detail-info))
_ (hooks/use-flow-state flows/current-login-user-flow)
online? (hooks/use-flow-state flows/network-online-event-flow)
rtc-state (:rtc-state detail-info)
unpushed-block-update-count (:pending-local-ops detail-info)
{:keys [local-tx remote-tx]} detail-info]
[:div.cp__rtc-sync
[:div.hidden {"data-testid" "rtc-tx"} (pr-str {:local-tx local-tx :remote-tx remote-tx})]
[:div.cp__rtc-sync-indicator.flex.flex-row.items-center.gap-1
(shui/button-ghost-icon :cloud
{:on-click #(shui/popup-show! (.-target %)
(details online?)
{:align "end"})
:class (util/classnames [{:cloud true
:on (and online? (= :open rtc-state))
:idle (and online? (= :open rtc-state) (zero? unpushed-block-update-count))
:queuing (pos? unpushed-block-update-count)}])})]]))
(def ^:private *accumulated-download-logs (atom []))
(c.m/run-background-task
@@ -163,6 +168,17 @@
(swap! *accumulated-download-logs (fn [logs] (take 20 (conj logs log)))))))
rtc-flows/rtc-download-log-flow))
(def ^:private *accumulated-upload-logs (atom []))
(c.m/run-background-task
::update-accumulated-upload-logs
(m/reduce
(fn [_ log]
(when log
(if (= :upload-completed (:sub-type log))
(reset! *accumulated-upload-logs [])
(swap! *accumulated-upload-logs (fn [logs] (take 20 (conj logs log)))))))
rtc-flows/rtc-upload-log-flow))
(defn- accumulated-logs-flow
[*acc-logs]
(->> (m/watch *acc-logs)
@@ -181,39 +197,14 @@
(for [log download-logs]
[:div (:message log)])])))
(rum/defc indicator
(rum/defc uploading-logs
[]
(let [detail-info (hooks/use-flow-state (m/watch *detail-info))
_ (hooks/use-flow-state flows/current-login-user-flow)
online? (hooks/use-flow-state flows/network-online-event-flow)
uploading?' (uploading? detail-info)
downloading?' (downloading? detail-info)
rtc-state (:rtc-state detail-info)
unpushed-block-update-count (:pending-local-ops detail-info)
{:keys [local-tx remote-tx]} detail-info]
[:div.cp__rtc-sync
[:div.hidden {"data-testid" "rtc-tx"} (pr-str {:local-tx local-tx :remote-tx remote-tx})]
[:div.cp__rtc-sync-indicator.flex.flex-row.items-center.gap-1
(when downloading?'
(shui/button
{:class "opacity-50"
:variant :ghost
:size :sm}
"Downloading..."))
(when uploading?'
(shui/button
{:class "opacity-50"
:variant :ghost
:size :sm}
"Uploading..."))
(shui/button-ghost-icon :cloud
{:on-click #(shui/popup-show! (.-target %)
(details online?)
{:align "end"})
:class (util/classnames [{:cloud true
:on (and online? (= :open rtc-state))
:idle (and online? (= :open rtc-state) (zero? unpushed-block-update-count))
:queuing (pos? unpushed-block-update-count)}])})]]))
(let [upload-logs-flow (accumulated-logs-flow *accumulated-upload-logs)
upload-logs (hooks/use-flow-state upload-logs-flow)]
(when (seq upload-logs)
[:div
(for [log upload-logs]
[:div (:message log)])])))
(def ^:private downloading?-flow
(->> rtc-flows/rtc-download-log-flow
@@ -231,3 +222,20 @@
(downloading-logs)
{:align "end"})}
"Downloading...")))
(def ^:private upload?-flow
(->> rtc-flows/rtc-upload-log-flow
(m/eduction (map (fn [log] (not= :upload-completed (:sub-type log)))))
(c.m/continue-flow false)))
(rum/defc uploading-detail
[]
(when (true? (hooks/use-flow-state upload?-flow))
(shui/button
{:class "opacity-50"
:variant :ghost
:size :sm
:on-click #(shui/popup-show! (.-target %)
(uploading-logs)
{:align "end"})}
"Uploading...")))

View File

@@ -99,7 +99,7 @@ independent of format as format specific heading characters are stripped"
(let [block (db-utils/entity repo block-id)
ref-tags (distinct (concat (:block/tags block) (:block/refs block)))]
(= (-> block-content
(db-content/id-ref->title-ref ref-tags true)
(db-content/id-ref->title-ref ref-tags)
(db-content/content-id-ref->page ref-tags)
heading-content->route-name)
(string/lower-case external-content))))

View File

@@ -1252,15 +1252,22 @@
(defonce *action-bar-timeout (atom nil))
(defn popup-exists?
[id]
(some->> (shui-popup/get-popups)
(some #(some-> % (:id) (str) (string/includes? (str id))))))
(defn show-action-bar!
[& {:keys [delay]
:or {delay 200}}]
(when (config/db-based-graph?)
(when (and (config/db-based-graph?) (not (popup-exists? :selection-action-bar)))
(when-let [timeout @*action-bar-timeout]
(js/clearTimeout timeout))
(state/pub-event! [:editor/hide-action-bar])
(let [timeout (js/setTimeout #(state/pub-event! [:editor/show-action-bar]) delay)]
(reset! *action-bar-timeout timeout))))
(when (seq (remove (fn [b] (dom/has-class? b "ls-table-cell"))
(state/get-selection-blocks)))
(let [timeout (js/setTimeout #(state/pub-event! [:editor/show-action-bar]) delay)]
(reset! *action-bar-timeout timeout)))))
(defn- select-block-up-down
[direction]
@@ -3333,11 +3340,6 @@
(cursor/select-up-down input direction anchor cursor-rect)))
(select-block-up-down direction))))
(defn popup-exists?
[id]
(some->> (shui-popup/get-popups)
(some #(some-> % (:id) (str) (string/includes? (str id))))))
(defn editor-commands-popup-exists?
[]
(popup-exists? "editor.commands"))

View File

@@ -196,14 +196,15 @@
(defmethod handle :capture-error [[_ {:keys [error payload]}]]
(let [[user-uuid graph-uuid tx-id] @sync/graphs-txid
payload (assoc payload
:user-id user-uuid
:graph-id graph-uuid
:tx-id tx-id
:db-based (config/db-based-graph? (state/get-current-repo))
:schema-version (str db-schema/version)
:db-schema-version (when-let [db (frontend.db/get-db)]
(str (:kv/value (frontend.db/entity db :logseq.kv/schema-version)))))]
payload (merge
{:schema-version (str db-schema/version)
:db-schema-version (when-let [db (frontend.db/get-db)]
(str (:kv/value (frontend.db/entity db :logseq.kv/schema-version))))
:user-id user-uuid
:graph-id graph-uuid
:tx-id tx-id
:db-based (config/db-based-graph? (state/get-current-repo))}
payload)]
(Sentry/captureException error
(bean/->js {:tags payload}))))

View File

@@ -250,11 +250,33 @@
(.remove (.-handle file))))
old-versioned-files)))
(defn backup-db-graph
(defn choose-backup-folder
[repo]
(p/let [result (utils/openDirectory #js {:mode "readwrite"})
handle (first result)
folder-name (.-name handle)]
(js/console.dir handle)
(idb/set-item!
(str "handle/" (js/btoa repo) "/" folder-name) handle)
(db/transact! [(ldb/kv :logseq.kv/graph-backup-folder folder-name)])
[folder-name handle]))
(defn backup-db-graph
[repo _backup-type]
(when (and repo (= repo (state/get-current-repo)))
(when-let [backup-folder (ldb/get-key-value (db/get-db repo) :logseq.kv/graph-backup-folder)]
(p/let [handle (idb/get-item (str "handle/" (js/btoa repo) "/" backup-folder))
;; ensure file handle exists
;; ask user to choose a folder again when access expires
(p/let [handle (try
(idb/get-item (str "handle/" (js/btoa repo) "/" backup-folder))
(catch :default _e
(throw (ex-info "Backup file handle no longer exists" {:repo repo}))))
[_folder handle] (try
(utils/verifyPermission handle true)
[backup-folder handle]
(catch :default e
(js/console.error e)
(choose-backup-folder repo)))
repo-name (common-sqlite/sanitize-db-name repo)]
(if handle
(->
@@ -297,9 +319,9 @@
(when (and (config/db-based-graph? repo) util/web-platform? (utils/nfsSupported))
(cancel-db-backup!)
(when backup-now? (backup-db-graph repo))
(when backup-now? (backup-db-graph repo :backup-now))
;; run backup every hour
(let [interval (js/setInterval #(backup-db-graph repo)
(let [interval (js/setInterval #(backup-db-graph repo :auto)
(* 1 60 60 1000))]
(reset! *backup-interval interval)))))

View File

@@ -210,3 +210,7 @@
{:content (str "The graph '" graph "' already exists. Please try again with another name.")
:status :error}])
(create-db full-graph-name opts)))))
(defn fix-broken-graph!
[graph]
(state/<invoke-db-worker :thread-api/fix-broken-graph graph))

View File

@@ -84,7 +84,7 @@
(let [page (db/get-page page-name)
whiteboard? (db/whiteboard-page? page)]
(if (and (not config/dev?)
(or (ldb/hidden? page)
(or (and (ldb/hidden? page) (not (ldb/property? page)))
(and (ldb/built-in? page) (ldb/private-built-in-page? page))))
(notification/show! "Cannot go to an internal page." :warning)
(if-let [source (and (not ignore-alias?) (db/get-alias-source-page (state/get-current-repo) (:db/id page)))]

View File

@@ -19,6 +19,7 @@
[frontend.handler.paste :as paste-handler]
[frontend.handler.plugin :as plugin-handler]
[frontend.handler.plugin-config :as plugin-config-handler]
[frontend.handler.repo :as repo-handler]
[frontend.handler.route :as route-handler]
[frontend.handler.search :as search-handler]
[frontend.handler.ui :as ui-handler]
@@ -592,6 +593,11 @@
:inactive (not (util/electron?))
:fn commit/show-commit-modal!}
:dev/fix-broken-graph {:binding []
:inactive (not (state/developer-mode?))
:db-graph? true
:fn #(repo-handler/fix-broken-graph! (state/get-current-repo))}
:dev/replace-graph-with-db-file {:binding []
:inactive (or (not (util/electron?)) (not (state/developer-mode?)))
:fn :frontend.handler.common.developer/replace-graph-with-db-file}
@@ -859,6 +865,7 @@
:dev/show-page-ast
:dev/replace-graph-with-db-file
:dev/validate-db
:dev/fix-broken-graph
:ui/customize-appearance])
(with-meta {:before m/enable-when-not-editing-mode!}))
@@ -1051,6 +1058,7 @@
:dev/show-page-ast
:dev/replace-graph-with-db-file
:dev/validate-db
:dev/fix-broken-graph
:ui/clear-all-notifications]
:shortcut.category/plugins

View File

@@ -19,7 +19,7 @@
result (state/<invoke-db-worker :thread-api/search-build-blocks-indice repo)
blocks (if file-based?
(->> result
;; remove built-in properties from content
;; remove built-in properties from content
(map
#(update % :content
(fn [content]

View File

@@ -1877,7 +1877,7 @@ Similar to re-frame subscriptions"
(if (and page
;; TODO: Use config/dev? when it's not a circular dep
(not goog.DEBUG)
(or (ldb/hidden? page)
(or (and (ldb/hidden? page) (not (ldb/property? page)))
(and (ldb/built-in? page) (ldb/private-built-in-page? page))))
(pub-event! [:notification/show {:content "Cannot open an internal page." :status :warning}])
(when db-id

View File

@@ -275,6 +275,17 @@ html.is-mobile {
display: inline-block;
}
.ui__icon svg {
filter: brightness(0.8);
transition: filter .15s;
will-change: filter;
}
.ui__icon:hover svg {
filter: brightness(1);
transition-duration: .15s;
}
.type-icon {
@apply text-base text-center flex items-center justify-center rounded border mr-2 relative;

View File

@@ -790,6 +790,11 @@
(->> blocks
(remove (fn [b] (= "true" (d/attr b "data-embed")))))))
#?(:cljs
(defn remove-property-value-blocks [blocks]
(->> blocks
(remove (fn [b] (d/has-class? b "property-value-container"))))))
#?(:cljs
(defn get-selected-text
[]
@@ -898,7 +903,8 @@
(defn get-prev-block-non-collapsed-non-embed
[block]
(when-let [blocks (->> (get-blocks-noncollapse)
remove-embedded-blocks)]
remove-embedded-blocks
remove-property-value-blocks)]
(when-let [index (.indexOf blocks block)]
(let [idx (dec index)]
(when (>= idx 0)

View File

@@ -906,6 +906,12 @@
move-top-parents-to-library
update-children-parent-and-order)))))
(defn- empty-placeholder-add-block-uuid
[_conn _search-db]
[{:db/ident :logseq.property/empty-placeholder
:block/uuid (common-uuid/gen-uuid :builtin-block-uuid :logseq.property/empty-placeholder)}])
(def ^:large-vars/cleanup-todo schema-version->updates
"A vec of tuples defining datascript migrations. Each tuple consists of the
schema version integer and a migration map. A migration map can have keys of :properties, :classes
@@ -1005,8 +1011,8 @@
[62 {:fix remove-block-schema}]
[63 {:properties [:logseq.property.table/pinned-columns]}]
[64 {:fix update-view-filter}]
;;;; schema-version format: "<major>.<minor>"
;;;; int number equals to "<major>" (without <minor>)
;;;; schema-version format: "<major>.<minor>"
;;;; int number equals to "<major>" (without <minor>)
["64.1" {:properties [:logseq.property.view/group-by-property]
:fix add-view-icons}]
["64.2" {:properties [:logseq.property.view/feature-type]
@@ -1018,7 +1024,8 @@
["64.6" {:fix cardinality-one-multiple-values}]
["64.7" {:fix rename-repeated-properties}]
["64.8" {:fix rename-task-properties}]
["64.9" {:fix fix-rename-parent-to-extends}]])
["64.9" {:fix empty-placeholder-add-block-uuid}]
["64.10" {:fix fix-rename-parent-to-extends}]])
(let [[major minor] (last (sort (map (comp (juxt :major :minor) db-schema/parse-schema-version first)
schema-version->updates)))

View File

@@ -100,39 +100,43 @@
[^js pool data]
(.importDb ^js pool repo-path data))
(comment
(defn- get-all-datoms-from-sqlite-db
[db]
(some->> (.exec db #js {:sql "select * from kvs"
:rowMode "array"})
bean/->clj
(mapcat
(fn [[_addr content _addresses]]
(let [content' (sqlite-util/transit-read content)
datoms (when (map? content')
(:keys content'))]
datoms)))
distinct
(map (fn [[e a v t]]
(d/datom e a v t)))))
(defn- get-all-datoms-from-sqlite-db
[db]
(some->> (.exec db #js {:sql "select * from kvs"
:rowMode "array"})
bean/->clj
(mapcat
(fn [[_addr content _addresses]]
(let [content' (sqlite-util/transit-read content)
datoms (when (map? content')
(:keys content'))]
datoms)))
distinct
(map (fn [[e a v t]]
(d/datom e a v t)))))
(defn- rebuild-db-from-datoms!
"Persistent-sorted-set has been broken, used addresses can't be found"
[datascript-conn sqlite-db import-type]
(let [datoms (get-all-datoms-from-sqlite-db sqlite-db)
db (d/init-db [] db-schema/schema
{:storage (storage/storage @datascript-conn)})
db (d/db-with db
(map (fn [d]
[:db/add (:e d) (:a d) (:v d) (:t d)]) datoms))]
(prn :debug :rebuild-db-from-datoms :datoms-count (count datoms))
;; export db first
(when-not import-type
(worker-util/post-message :notification ["The SQLite db will be exported to avoid any data-loss." :warning false])
(worker-util/post-message :export-current-db []))
(.exec sqlite-db #js {:sql "delete from kvs"})
(d/reset-conn! datascript-conn db)
(db-migrate/fix-db! datascript-conn))))
(defn- rebuild-db-from-datoms!
"Persistent-sorted-set has been broken, used addresses can't be found"
[datascript-conn sqlite-db]
(let [datoms (get-all-datoms-from-sqlite-db sqlite-db)
db (d/init-db [] db-schema/schema
{:storage (storage/storage @datascript-conn)})
db (d/db-with db
(map (fn [d]
[:db/add (:e d) (:a d) (:v d) (:t d)]) datoms))]
(prn :debug :rebuild-db-from-datoms :datoms-count (count datoms))
(worker-util/post-message :notification ["The SQLite db will be exported to avoid any data-loss." :warning false])
(worker-util/post-message :export-current-db [])
(.exec sqlite-db #js {:sql "delete from kvs"})
(d/reset-conn! datascript-conn db)
(db-migrate/fix-db! datascript-conn)))
(defn- fix-broken-graph
[graph]
(let [conn (worker-state/get-datascript-conn graph)
sqlite-db (worker-state/get-sqlite-conn graph)]
(when (and conn sqlite-db)
(rebuild-db-from-datoms! conn sqlite-db))))
(comment
(defn- gc-kvs-table!
@@ -180,9 +184,25 @@
(when (and compare-result (not (neg? compare-result))) ; >= 64.8
(worker-util/post-message :capture-error
{:error "db-missing-addresses-v2"
:payload {:missing-addresses missing-addresses}}))))
:payload {:missing-addresses (str missing-addresses)
:db-schema-version (str version-in-db)}}))))
missing-addresses))
(def get-to-delete-unused-addresses-sql
"WITH to_delete(addr) AS (
SELECT value
FROM json_each(?)
),
referenced(addr) AS (
SELECT json_each.value
FROM kvs
JOIN json_each(kvs.addresses)
WHERE kvs.addr NOT IN (SELECT addr FROM to_delete)
AND json_each.value IN (SELECT addr FROM to_delete)
)
SELECT addr FROM to_delete
WHERE addr NOT IN (SELECT addr FROM referenced)")
(defn upsert-addr-content!
"Upsert addr+data-seq. Update sqlite-cli/upsert-addr-content! when making changes"
[db data delete-addrs*]
@@ -193,19 +213,19 @@
(.exec tx #js {:sql "INSERT INTO kvs (addr, content, addresses) values ($addr, $content, $addresses) on conflict(addr) do update set content = $content, addresses = $addresses"
:bind item}))))
(when (seq delete-addrs)
(.transaction db (fn [tx]
(doseq [addr delete-addrs]
(.exec tx #js {:sql "Delete from kvs WHERE addr = ? AND NOT EXISTS (SELECT 1 FROM json_each(addresses) WHERE value = ?);"
:bind #js [addr]}))))
(let [result (.exec db #js {:sql get-to-delete-unused-addresses-sql
:bind (js/JSON.stringify (clj->js delete-addrs))
:rowMode "array"})
non-refed-addrs (map #(aget % 0) result)]
(when (seq non-refed-addrs)
(.transaction db (fn [tx]
(doseq [addr non-refed-addrs]
(.exec tx #js {:sql "Delete from kvs where addr = ?"
:bind #js [addr]}))))))
(let [missing-addrs (when worker-util/dev?
(seq (find-missing-addresses nil db {:delete-addrs delete-addrs})))]
(if (seq missing-addrs)
(worker-util/post-message :notification [(str "Bug!! Missing addresses: " missing-addrs) :error false])
(when (seq delete-addrs)
(.transaction db (fn [tx]
(doseq [addr delete-addrs]
(.exec tx #js {:sql "Delete from kvs WHERE addr = ? AND NOT EXISTS (SELECT 1 FROM json_each(addresses) WHERE value = ?);"
:bind #js [addr]}))))))))))
(when (seq missing-addrs)
(worker-util/post-message :notification [(str "Bug!! Missing addresses: " missing-addrs) :error false]))))))
(defn restore-data-from-addr
"Update sqlite-cli/restore-data-from-addr when making changes"
@@ -771,6 +791,10 @@
[repo]
(get-all-page-titles-with-cache repo))
(def-thread-api :thread-api/fix-broken-graph
[graph]
(fix-broken-graph graph))
(comment
(def-thread-api :general/dangerousRemoveAllDbs
[]

View File

@@ -10,6 +10,7 @@
[logseq.common.util :as common-util]
[logseq.common.util.namespace :as ns-util]
[logseq.db :as ldb]
[logseq.db.frontend.content :as db-content]
[logseq.db.sqlite.util :as sqlite-util]
[logseq.graph-parser.text :as text]))
@@ -193,7 +194,8 @@ DROP TRIGGER IF EXISTS blocks_au;
(defn- page-or-object?
[entity]
(and (or (ldb/page? entity) (ldb/object? entity))
(not (ldb/hidden? entity))))
(not (ldb/hidden? entity))
(not (ldb/hidden? (:block/page entity)))))
(defn get-all-fuzzy-supported-blocks
"Only pages and objects are supported now."
@@ -205,7 +207,9 @@ DROP TRIGGER IF EXISTS blocks_au;
(map :e)))
blocks (->> (distinct (concat page-ids object-ids))
(map #(d/entity db %)))]
(remove ldb/hidden? blocks)))
(->> blocks
(remove ldb/hidden?)
(remove #(ldb/hidden? (:block/page %))))))
(defn- sanitize
[content]
@@ -219,13 +223,9 @@ DROP TRIGGER IF EXISTS blocks_au;
(ldb/closed-value? block)
(and (string? title) (> (count title) 10000))
(string/blank? title)) ; empty page or block
;; Should properties be included in the search indice?
;; It could slow down the search indexing, also it can be confusing
;; if the showing properties are not useful to users.
;; (let [content (if (and db-based? (seq (:block/properties block)))
;; (str content (when (not= content "") "\n") (get-db-properties-str db properties))
;; content)])
(let [title (ldb/get-title-with-parents (assoc block :block.temp/search? true))]
(let [title (-> block
(update :block/title ldb/get-title-with-parents)
db-content/recur-replace-uuid-in-block-title)]
(when uuid
{:id (str uuid)
:page (str (or (:block/uuid page) uuid))
@@ -370,11 +370,11 @@ DROP TRIGGER IF EXISTS blocks_au;
set)
blocks-to-add-set)]
{:blocks-to-remove (->>
(keep #(d/entity db-before %) blocks-to-remove-set)
(remove ldb/hidden?))
(keep #(d/entity db-before %) blocks-to-remove-set))
:blocks-to-add (->>
(keep #(d/entity db-after %) blocks-to-add-set')
(remove ldb/hidden?))})))
(remove ldb/hidden?)
(remove #(ldb/hidden? (:block/page %))))})))
(defn- get-affected-blocks
[repo tx-report]

View File

@@ -786,4 +786,5 @@
:dev/show-page-ast "(Dev) Show page AST"
:dev/replace-graph-with-db-file "(Dev) Replace graph with its db.sqlite file"
:dev/validate-db "(Dev) Validate current graph"
:dev/fix-broken-graph "(Dev) Fix current broken graph"
:window/close "Close window"}}