mirror of
https://github.com/logseq/logseq.git
synced 2026-06-01 19:01:22 +00:00
Merge branch 'feat/db' into enhance/plugin-web
This commit is contained in:
@@ -21,13 +21,14 @@
|
||||
:cell (fn [_table row _column]
|
||||
(component-block/page-cp {} row))
|
||||
:type :string}
|
||||
{:id :block/type
|
||||
:name "Type"
|
||||
:cell (fn [_table row _column]
|
||||
(let [type (get row :block/type)]
|
||||
[:div.capitalize (if (= type "class") "tag" type)]))
|
||||
:get-value (fn [row] (get row :block/type))
|
||||
:type :string}
|
||||
(when (not (config/db-based-graph? (state/get-current-repo)))
|
||||
{:id :block/type
|
||||
:name "Page type"
|
||||
:cell (fn [_table row _column]
|
||||
(let [type (get row :block/type)]
|
||||
[:div.capitalize type]))
|
||||
:get-value (fn [row] (get row :block/type))
|
||||
:type :string})
|
||||
{:id :block.temp/refs-count
|
||||
:name (t :page/backlinks)
|
||||
:cell (fn [_table row _column] (:block.temp/refs-count row))
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
[frontend.components.query :as query]
|
||||
[frontend.components.query.builder :as query-builder-component]
|
||||
[frontend.components.svg :as svg]
|
||||
[frontend.components.title :as title]
|
||||
[frontend.components.select :as select]
|
||||
[frontend.config :as config]
|
||||
[frontend.context.i18n :refer [t]]
|
||||
@@ -701,7 +700,9 @@
|
||||
(when (and show-icon? (not tag?))
|
||||
(let [own-icon (get page-entity (pu/get-pid :logseq.property/icon))
|
||||
emoji? (and (map? own-icon) (= (:type own-icon) :emoji))]
|
||||
(when-let [icon (icon-component/get-node-icon-cp page-entity {:color? true :not-text-or-page? true})]
|
||||
(when-let [icon (icon-component/get-node-icon-cp page-entity {:color? true
|
||||
:not-text-or-page? true
|
||||
:own-icon? true})]
|
||||
[:span {:class (str "icon-emoji-wrap " (when emoji? "as-emoji"))}
|
||||
icon])))
|
||||
[:span
|
||||
@@ -722,7 +723,7 @@
|
||||
(->elem :span (map-inline config label))
|
||||
|
||||
show-unique-title?
|
||||
(title/block-unique-title page-entity)
|
||||
(block-handler/block-unique-title page-entity)
|
||||
|
||||
:else
|
||||
(let [title (:block/title page-entity)
|
||||
@@ -755,7 +756,7 @@
|
||||
(db-content/content-id-ref->page s (:block/refs page-entity))
|
||||
:else
|
||||
s)
|
||||
s (if tag? (str "#" s) s)]
|
||||
s (if (and tag? (not (:hide-tag-symbol? config))) (str "#" s) s)]
|
||||
(if (ldb/page? page-entity)
|
||||
s
|
||||
(block-title config page-entity))))]
|
||||
@@ -2588,8 +2589,9 @@
|
||||
(rum/defcs block-tag <
|
||||
(rum/local false ::hover?)
|
||||
[state block tag config popup-opts]
|
||||
(let [*hover? (::hover? state)]
|
||||
[:div.block-tag
|
||||
(let [*hover? (::hover? state)
|
||||
hover? @*hover?]
|
||||
[:div.block-tag.items-center
|
||||
{:key (str "tag-" (:db/id tag))
|
||||
:on-mouse-over #(reset! *hover? true)
|
||||
:on-mouse-out #(reset! *hover? false)
|
||||
@@ -2614,18 +2616,23 @@
|
||||
:on-click #(db-property-handler/delete-property-value! (:db/id block) :block/tags (:db/id tag))}
|
||||
"Remove tag")])
|
||||
popup-opts))}
|
||||
(if (and hover? (not (ldb/private-tags (:db/ident tag))))
|
||||
[:a.inline.close.flex.transition-opacity.duration-300.ease-in
|
||||
{:class (if @*hover? "!opacity-100" "!opacity-0")
|
||||
:title "Remove this tag"
|
||||
:on-pointer-down
|
||||
(fn [e]
|
||||
(util/stop e)
|
||||
(db-property-handler/delete-property-value! (:db/id block) :block/tags (:db/id tag)))}
|
||||
(ui/icon "x" {:size 14
|
||||
:style {:margin-top 1}})]
|
||||
[:a.hash-symbol {:style {:margin-left 5}}
|
||||
"#"])
|
||||
(page-cp (assoc config
|
||||
:disable-preview? true
|
||||
:tag? true
|
||||
:disable-preview? true)
|
||||
tag)
|
||||
[:a.close.flex.transition-opacity.duration-300.ease-in
|
||||
{:class (if @*hover? "!opacity-100" "!opacity-0")
|
||||
:title "Remove this tag"
|
||||
:on-pointer-down
|
||||
(fn [e]
|
||||
(util/stop e)
|
||||
(db-property-handler/delete-property-value! (:db/id block) :block/tags (:db/id tag)))}
|
||||
(ui/icon "x" {:size 15})]]))
|
||||
:hide-tag-symbol? true)
|
||||
tag)]))
|
||||
|
||||
(rum/defc tags-cp
|
||||
"Tags without inline or hidden tags"
|
||||
@@ -2635,14 +2642,15 @@
|
||||
(:block/tags block)
|
||||
(remove (fn [t]
|
||||
(or (ldb/inline-tag? (:block/raw-title block) t)
|
||||
(:logseq.property.class/hide-from-node t)))))
|
||||
(:logseq.property.class/hide-from-node t)
|
||||
(contains? ldb/internal-tags (:db/ident t))))))
|
||||
popup-opts {:align :end
|
||||
:content-props {:on-click (fn [] (shui/popup-hide!))
|
||||
:class "w-60"}}
|
||||
tags-count (count block-tags)]
|
||||
(when (seq block-tags)
|
||||
(if (< tags-count 3)
|
||||
[:div.block-tags
|
||||
[:div.block-tags.gap-1
|
||||
(for [tag block-tags]
|
||||
(rum/with-key
|
||||
(block-tag block tag config popup-opts)
|
||||
@@ -2653,13 +2661,14 @@
|
||||
(fn []
|
||||
(for [tag block-tags]
|
||||
[:div.flex.flex-row.items-center.gap-1
|
||||
(shui/button
|
||||
{:title "Remove tag"
|
||||
:variant :ghost
|
||||
:class "!p-1 text-muted-foreground"
|
||||
:size :sm
|
||||
:on-click #(db-property-handler/delete-property-value! (:db/id block) :block/tags (:db/id tag))}
|
||||
(ui/icon "X" {:size 14}))
|
||||
(when-not (ldb/private-tags (:db/ident tag))
|
||||
(shui/button
|
||||
{:title "Remove tag"
|
||||
:variant :ghost
|
||||
:class "!p-1 text-muted-foreground"
|
||||
:size :sm
|
||||
:on-click #(db-property-handler/delete-property-value! (:db/id block) :block/tags (:db/id tag))}
|
||||
(ui/icon "X" {:size 14})))
|
||||
(page-cp (assoc config
|
||||
:tag? true
|
||||
:disable-preview? true
|
||||
@@ -2672,7 +2681,7 @@
|
||||
:tag? true
|
||||
:disable-preview? true
|
||||
:disable-click? true) tag)])
|
||||
[:div.text-sm.opacity-50
|
||||
[:div.text-sm.opacity-50.ml-1
|
||||
(str "+" (- tags-count 2))]])))))
|
||||
|
||||
(rum/defc block-positioned-properties
|
||||
|
||||
@@ -949,7 +949,7 @@ html.is-mac {
|
||||
|
||||
.positioned-properties.block-right {
|
||||
button {
|
||||
@apply whitespace-nowrap mr-0.5;
|
||||
@apply whitespace-nowrap;
|
||||
}
|
||||
|
||||
.block-title-wrap {
|
||||
@@ -970,14 +970,18 @@ html.is-mac {
|
||||
}
|
||||
|
||||
.block-tag {
|
||||
@apply pr-1 flex flex-row items-center gap-1;
|
||||
@apply flex flex-row items-center;
|
||||
}
|
||||
|
||||
.block-tag a.tag {
|
||||
@apply flex text-sm font-normal items-center opacity-70;
|
||||
}
|
||||
|
||||
.block-tag a.tag:hover {
|
||||
.block-tag a.hash-symbol {
|
||||
@apply text-sm text-sm font-normal opacity-70;
|
||||
}
|
||||
|
||||
.block-tag a.tag:hover, .block-tag a.hash-symbol:hover {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
@@ -1013,7 +1017,7 @@ html.is-mac {
|
||||
}
|
||||
|
||||
.ls-page-title .block-tags {
|
||||
@apply relative -right-1 min-h-full;
|
||||
@apply relative min-h-full;
|
||||
}
|
||||
|
||||
.ls-code-editor-wrap {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
[electron.ipc :as ipc]
|
||||
[frontend.components.block :as block]
|
||||
[frontend.components.cmdk.list-item :as list-item]
|
||||
[frontend.components.title :as title]
|
||||
[frontend.config :as config]
|
||||
[frontend.context.i18n :refer [t]]
|
||||
[frontend.db :as db]
|
||||
@@ -18,6 +17,7 @@
|
||||
[frontend.handler.page :as page-handler]
|
||||
[frontend.handler.route :as route-handler]
|
||||
[frontend.handler.whiteboard :as whiteboard-handler]
|
||||
[frontend.handler.block :as block-handler]
|
||||
[frontend.mixins :as mixins]
|
||||
[frontend.modules.shortcut.core :as shortcut]
|
||||
[frontend.modules.shortcut.utils :as shortcut-utils]
|
||||
@@ -235,7 +235,7 @@
|
||||
"whiteboard"
|
||||
:else
|
||||
"page")
|
||||
title (title/block-unique-title page)
|
||||
title (block-handler/block-unique-title page)
|
||||
title' (if source-page (str title " -> alias: " (:block/title source-page)) title)]
|
||||
(hash-map :icon icon
|
||||
:icon-theme :gray
|
||||
@@ -245,7 +245,7 @@
|
||||
(defn- block-item
|
||||
[repo block current-page !input]
|
||||
(let [id (:block/uuid block)
|
||||
text (title/block-unique-title block)
|
||||
text (block-handler/block-unique-title block)
|
||||
icon "letter-n"]
|
||||
{:icon icon
|
||||
:icon-theme :gray
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
[frontend.components.block :as block]
|
||||
[dommy.core :as d]
|
||||
[frontend.components.content :as cp-content]
|
||||
[frontend.components.title :as title]
|
||||
[frontend.config :as config]
|
||||
[frontend.context.i18n :refer [t tt]]
|
||||
[frontend.db :as db]
|
||||
@@ -32,6 +31,7 @@
|
||||
[frontend.handler.user :as user-handler]
|
||||
[frontend.handler.whiteboard :as whiteboard-handler]
|
||||
[frontend.handler.recent :as recent-handler]
|
||||
[frontend.handler.block :as block-handler]
|
||||
[frontend.mixins :as mixins]
|
||||
[frontend.mobile.action-bar :as action-bar]
|
||||
[frontend.mobile.footer :as footer]
|
||||
@@ -139,7 +139,7 @@
|
||||
:class "w-60"}})
|
||||
(util/stop e))}
|
||||
(ldb/object? page)
|
||||
(assoc :title (title/block-unique-title page)))
|
||||
(assoc :title (block-handler/block-unique-title page)))
|
||||
[:span.page-icon icon]
|
||||
[:span.page-title {:class (when untitled? "opacity-50")
|
||||
:style {:display "ruby"}}
|
||||
@@ -214,7 +214,7 @@
|
||||
db-based?
|
||||
(concat [:tag/tasks :tag/assets])
|
||||
(not db-based?)
|
||||
(#(cons :whiteboards %)) )
|
||||
(#(cons :whiteboards %)))
|
||||
[checked-navs set-checked-navs!] (rum/use-state (or (storage/get :ls-sidebar-navigations)
|
||||
[:whiteboards :flashcards :graph-view :all-pages]))]
|
||||
|
||||
@@ -362,7 +362,7 @@
|
||||
(for [page pages]
|
||||
[:li.recent-item.select-none.font-medium
|
||||
{:key (str "recent-" (:db/id page))
|
||||
:title (title/block-unique-title page)
|
||||
:title (block-handler/block-unique-title page)
|
||||
:draggable true
|
||||
:on-drag-start (fn [event] (editor-handler/block->data-transfer! (:block/name page) event true))
|
||||
:data-ref name}
|
||||
|
||||
@@ -446,7 +446,7 @@
|
||||
flex: 1;
|
||||
|
||||
.page {
|
||||
@apply px-6;
|
||||
@apply px-4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,21 +4,22 @@
|
||||
[frontend.db :as db]
|
||||
[frontend.db-mixins :as db-mixins]
|
||||
[logseq.shui.ui :as shui]
|
||||
[rum.core :as rum]))
|
||||
[rum.core :as rum]
|
||||
[frontend.util :as util]))
|
||||
|
||||
(rum/defc configure-property < rum/reactive db-mixins/query
|
||||
[page]
|
||||
(let [page (db/sub-block (:db/id page))]
|
||||
[:div.pb-4.-ml-1
|
||||
(shui/button
|
||||
{:variant "ghost"
|
||||
:class "opacity-50 hover:opacity-90"
|
||||
:size :sm
|
||||
:on-click (fn [^js e]
|
||||
(shui/popup-show! (.-target e)
|
||||
(fn []
|
||||
(property-config/dropdown-editor page nil {:debug? (.-altKey e)}))
|
||||
{:content-props {:class "ls-property-dropdown-editor as-root"}
|
||||
:align "start"
|
||||
:as-dropdown? true}))}
|
||||
"Configure property")]))
|
||||
(shui/tabs-trigger
|
||||
{:value "configure"
|
||||
:class "py-1 text-xs"
|
||||
:on-pointer-down (fn [e]
|
||||
(util/stop e))
|
||||
:on-click (fn [^js e]
|
||||
(shui/popup-show! (.-target e)
|
||||
(fn []
|
||||
(property-config/dropdown-editor page nil {:debug? (.-altKey e)}))
|
||||
{:content-props {:class "ls-property-dropdown-editor as-root"}
|
||||
:align "start"
|
||||
:as-dropdown? true}))}
|
||||
"Configure property")))
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
[frontend.components.file-based.datetime :as datetime-comp]
|
||||
[frontend.components.search :as search]
|
||||
[frontend.components.svg :as svg]
|
||||
[frontend.components.title :as title]
|
||||
[frontend.config :as config]
|
||||
[frontend.context.i18n :refer [t]]
|
||||
[frontend.date :as date]
|
||||
@@ -19,6 +18,7 @@
|
||||
[frontend.handler.paste :as paste-handler]
|
||||
[frontend.handler.property.util :as pu]
|
||||
[frontend.handler.search :as search-handler]
|
||||
[frontend.handler.block :as block-handler]
|
||||
[frontend.mixins :as mixins]
|
||||
[frontend.search :refer [fuzzy-search]]
|
||||
[frontend.state :as state]
|
||||
@@ -35,7 +35,8 @@
|
||||
[logseq.shui.ui :as shui]
|
||||
[promesa.core :as p]
|
||||
[react-draggable]
|
||||
[rum.core :as rum]))
|
||||
[rum.core :as rum]
|
||||
[logseq.db.frontend.class :as db-class]))
|
||||
|
||||
(defn filter-commands
|
||||
[page? commands]
|
||||
@@ -156,7 +157,12 @@
|
||||
;; 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? "class" "page"))
|
||||
(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
|
||||
(into #{:logseq.class/Page :logseq.class/Tag :logseq.class/Property}
|
||||
db-class/page-children-classes)))
|
||||
(and db-tag? (some ldb/class? (:block/_alias (db/get-page q)))))
|
||||
partial-matched-pages
|
||||
(if db-tag?
|
||||
@@ -215,7 +221,7 @@
|
||||
(if (ldb/class? target)
|
||||
(str (:block/title block) " -> alias: " (:block/title target))
|
||||
(:block/title block)))
|
||||
(title/block-unique-title block))]
|
||||
(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?
|
||||
"Search for a tag"
|
||||
|
||||
@@ -693,7 +693,7 @@
|
||||
[:div.cp__file-sync-page-histories-right
|
||||
[:h1.title.text-xl
|
||||
"Current version"]
|
||||
(page/page-blocks-cp (state/get-current-repo) page-entity nil)]
|
||||
(page/page-blocks-cp page-entity nil)]
|
||||
|
||||
;; ready loading
|
||||
[:div.flex.items-center.h-full.justify-center.w-full.absolute.ready-loading
|
||||
|
||||
@@ -61,7 +61,9 @@
|
||||
(defn get-node-icon-cp
|
||||
[node-entity opts]
|
||||
(let [opts' (merge {:size 14} opts)
|
||||
node-icon (get-node-icon node-entity)]
|
||||
node-icon (if (:own-icon? opts)
|
||||
(get node-entity (pu/get-pid :logseq.property/icon))
|
||||
(get-node-icon node-entity))]
|
||||
(when-not (or (string/blank? node-icon) (and (contains? #{"letter-n" "page"} node-icon) (:not-text-or-page? opts)))
|
||||
[:div.icon-cp-container.flex.items-center
|
||||
(merge {:style {:color (or (:color node-icon) "inherit")}}
|
||||
|
||||
@@ -154,53 +154,45 @@
|
||||
|
||||
(if loading?
|
||||
(ui/skeleton)
|
||||
[:div.flex.flex-col.gap-2.mt-2
|
||||
|
||||
(ui/foldable
|
||||
[:div.font-medium.opacity-60.as-toggle
|
||||
"Tagged Nodes"]
|
||||
(fn []
|
||||
[:div.mt-2
|
||||
(views/view view-entity {:config config
|
||||
:data data
|
||||
:set-data! set-data!
|
||||
:views-title (class-views class views view-entity {:set-view-entity! set-view-entity!
|
||||
:set-views! set-views!})
|
||||
:columns columns
|
||||
:add-new-object! (if (= :logseq.class/Asset (:db/ident class))
|
||||
(fn [_e]
|
||||
(shui/dialog-open!
|
||||
(fn []
|
||||
[:div.flex.flex-col.gap-2
|
||||
[:div.font-medium "Add assets"]
|
||||
(filepicker/picker
|
||||
{:on-change (fn [_e files]
|
||||
(p/do!
|
||||
(editor-handler/upload-asset! nil files :markdown editor-handler/*asset-uploading? true)
|
||||
(set-data! (get-class-objects class))
|
||||
(shui/dialog-close!)))})])))
|
||||
#(add-new-class-object! class set-data!))
|
||||
:show-add-property? true
|
||||
:add-property! (fn []
|
||||
(state/pub-event! [:editor/new-property {:block class
|
||||
:class-schema? true}]))
|
||||
:on-delete-rows (fn [table selected-rows]
|
||||
(let [pages (filter ldb/page? selected-rows)
|
||||
blocks (remove ldb/page? selected-rows)]
|
||||
(p/do!
|
||||
(ui-outliner-tx/transact!
|
||||
{:outliner-op :delete-blocks}
|
||||
(when (seq blocks)
|
||||
(outliner-op/delete-blocks! blocks nil))
|
||||
(let [page-ids (map :db/id pages)
|
||||
tx-data (map (fn [pid] [:db/retract pid :block/tags (:db/id class)]) page-ids)]
|
||||
(when (seq tx-data)
|
||||
(outliner-op/transact! tx-data {:outliner-op :save-block}))))
|
||||
(set-data! (get-class-objects class))
|
||||
(when-let [f (get-in table [:data-fns :set-row-selection!])]
|
||||
(f {})))))})])
|
||||
{:disable-on-pointer-down? true
|
||||
:default-collapsed? (:sidebar? config)})])))
|
||||
(views/view view-entity
|
||||
{:config config
|
||||
:data data
|
||||
:set-data! set-data!
|
||||
:views-title (class-views class views view-entity {:set-view-entity! set-view-entity!
|
||||
:set-views! set-views!})
|
||||
:columns columns
|
||||
:add-new-object! (if (= :logseq.class/Asset (:db/ident class))
|
||||
(fn [_e]
|
||||
(shui/dialog-open!
|
||||
(fn []
|
||||
[:div.flex.flex-col.gap-2
|
||||
[:div.font-medium "Add assets"]
|
||||
(filepicker/picker
|
||||
{:on-change (fn [_e files]
|
||||
(p/do!
|
||||
(editor-handler/upload-asset! nil files :markdown editor-handler/*asset-uploading? true)
|
||||
(set-data! (get-class-objects class))
|
||||
(shui/dialog-close!)))})])))
|
||||
#(add-new-class-object! class set-data!))
|
||||
:show-add-property? true
|
||||
:add-property! (fn []
|
||||
(state/pub-event! [:editor/new-property {:block class
|
||||
:class-schema? true}]))
|
||||
:on-delete-rows (fn [table selected-rows]
|
||||
(let [pages (filter ldb/page? selected-rows)
|
||||
blocks (remove ldb/page? selected-rows)]
|
||||
(p/do!
|
||||
(ui-outliner-tx/transact!
|
||||
{:outliner-op :delete-blocks}
|
||||
(when (seq blocks)
|
||||
(outliner-op/delete-blocks! blocks nil))
|
||||
(let [page-ids (map :db/id pages)
|
||||
tx-data (map (fn [pid] [:db/retract pid :block/tags (:db/id class)]) page-ids)]
|
||||
(when (seq tx-data)
|
||||
(outliner-op/transact! tx-data {:outliner-op :save-block}))))
|
||||
(set-data! (get-class-objects class))
|
||||
(when-let [f (get-in table [:data-fns :set-row-selection!])]
|
||||
(f {})))))}))))
|
||||
|
||||
(rum/defcs class-objects < rum/reactive db-mixins/query mixins/container-id
|
||||
[state class {:keys [current-page? sidebar?]}]
|
||||
@@ -251,35 +243,32 @@
|
||||
[])
|
||||
|
||||
(when (false? loading?)
|
||||
(ui/foldable
|
||||
[:div.font-medium.opacity-50 "Nodes with Property"]
|
||||
[:div.mt-2
|
||||
(views/view view-entity {:config config
|
||||
:data data
|
||||
:set-data! set-data!
|
||||
:title-key :views.table/property-nodes
|
||||
:columns columns
|
||||
:add-new-object! #(add-new-property-object! property set-data!)
|
||||
(views/view view-entity
|
||||
{:config config
|
||||
:data data
|
||||
:set-data! set-data!
|
||||
:title-key :views.table/property-nodes
|
||||
:columns columns
|
||||
:add-new-object! #(add-new-property-object! property set-data!)
|
||||
;; TODO: Add support for adding column
|
||||
:show-add-property? false
|
||||
:on-delete-rows (when-not (contains? #{:logseq.property/built-in? :logseq.property/parent}
|
||||
(:db/ident property))
|
||||
(fn [table selected-rows]
|
||||
(let [pages (filter ldb/page? selected-rows)
|
||||
blocks (remove ldb/page? selected-rows)]
|
||||
(p/do!
|
||||
(ui-outliner-tx/transact!
|
||||
{:outliner-op :delete-blocks}
|
||||
(when (seq blocks)
|
||||
(outliner-op/delete-blocks! blocks nil))
|
||||
(let [page-ids (map :db/id pages)
|
||||
tx-data (map (fn [pid] [:db/retract pid (:db/ident property)]) page-ids)]
|
||||
(when (seq tx-data)
|
||||
(outliner-op/transact! tx-data {:outliner-op :save-block}))))
|
||||
(set-data! (get-property-related-objects (state/get-current-repo) property))
|
||||
(when-let [f (get-in table [:data-fns :set-row-selection!])]
|
||||
(f {}))))))})]
|
||||
{:disable-on-pointer-down? true}))))
|
||||
:show-add-property? false
|
||||
:on-delete-rows (when-not (contains? #{:logseq.property/built-in? :logseq.property/parent}
|
||||
(:db/ident property))
|
||||
(fn [table selected-rows]
|
||||
(let [pages (filter ldb/page? selected-rows)
|
||||
blocks (remove ldb/page? selected-rows)]
|
||||
(p/do!
|
||||
(ui-outliner-tx/transact!
|
||||
{:outliner-op :delete-blocks}
|
||||
(when (seq blocks)
|
||||
(outliner-op/delete-blocks! blocks nil))
|
||||
(let [page-ids (map :db/id pages)
|
||||
tx-data (map (fn [pid] [:db/retract pid (:db/ident property)]) page-ids)]
|
||||
(when (seq tx-data)
|
||||
(outliner-op/transact! tx-data {:outliner-op :save-block}))))
|
||||
(set-data! (get-property-related-objects (state/get-current-repo) property))
|
||||
(when-let [f (get-in table [:data-fns :set-row-selection!])]
|
||||
(f {}))))))}))))
|
||||
|
||||
;; Show all nodes containing the given property
|
||||
(rum/defcs property-related-objects < rum/reactive db-mixins/query mixins/container-id
|
||||
|
||||
@@ -196,7 +196,7 @@
|
||||
|
||||
(rum/defcs page-blocks-cp < rum/reactive db-mixins/query
|
||||
{:will-mount (fn [state]
|
||||
(let [page-e (second (:rum/args state))
|
||||
(let [page-e (first (:rum/args state))
|
||||
page-name (:block/name page-e)]
|
||||
(when (and page-name
|
||||
(db/journal-page? page-name)
|
||||
@@ -204,7 +204,7 @@
|
||||
(date/journal-title->int (date/today))))
|
||||
(state/pub-event! [:journal/insert-template page-name])))
|
||||
state)}
|
||||
[state repo page-e {:keys [sidebar? whiteboard?] :as config}]
|
||||
[state page-e {:keys [sidebar? whiteboard?] :as config}]
|
||||
(when page-e
|
||||
(let [page-name (or (:block/name page-e)
|
||||
(str (:block/uuid page-e)))
|
||||
@@ -220,44 +220,35 @@
|
||||
(remove (fn [b] (some? (get b (:db/ident block)))) children)
|
||||
|
||||
:else
|
||||
children)
|
||||
db-based? (config/db-based-graph? repo)]
|
||||
[:<>
|
||||
(let [blocks (cond
|
||||
(and
|
||||
(not block?)
|
||||
(empty? children) page-e)
|
||||
(dummy-block page-e)
|
||||
children)]
|
||||
(cond
|
||||
(and
|
||||
(not block?)
|
||||
(empty? children) page-e)
|
||||
(dummy-block page-e)
|
||||
|
||||
:else
|
||||
(let [document-mode? (state/sub :document/mode?)
|
||||
hiccup-config (merge
|
||||
{:id (if block? (str block-id) page-name)
|
||||
:db/id (:db/id block)
|
||||
:block? block?
|
||||
:editor-box editor/box
|
||||
:document/mode? document-mode?}
|
||||
config)
|
||||
config (common-handler/config-with-document-mode hiccup-config)
|
||||
blocks (if block? [block] (db/sort-by-order children block))]
|
||||
(let [add-button? (not (or config/publishing?
|
||||
(let [last-child-id (model/get-block-deep-last-open-child-id (db/get-db) (:db/id (last blocks)))
|
||||
block' (if last-child-id (db/entity last-child-id) (last blocks))]
|
||||
(string/blank? (:block/title block')))))]
|
||||
[:div
|
||||
{:class (when add-button? "show-add-button")}
|
||||
(page-blocks-inner page-e blocks config sidebar? whiteboard? block-id)
|
||||
(let [args (if block-id
|
||||
{:block-uuid block-id}
|
||||
{:page page-name})]
|
||||
(add-button args (:container-id config)))])))]
|
||||
(if (and db-based? (or (ldb/class? block) (ldb/property? block)))
|
||||
[:div.mt-4.ml-2.-mb-1
|
||||
(ui/foldable
|
||||
[:div.font-medium.as-toggle {:class "pl-0.5"} "Notes"]
|
||||
[:div.ml-1.-mb-2 blocks]
|
||||
{:disable-on-pointer-down? true})]
|
||||
blocks))])))
|
||||
:else
|
||||
(let [document-mode? (state/sub :document/mode?)
|
||||
hiccup-config (merge
|
||||
{:id (if block? (str block-id) page-name)
|
||||
:db/id (:db/id block)
|
||||
:block? block?
|
||||
:editor-box editor/box
|
||||
:document/mode? document-mode?}
|
||||
config)
|
||||
config (common-handler/config-with-document-mode hiccup-config)
|
||||
blocks (if block? [block] (db/sort-by-order children block))]
|
||||
(let [add-button? (not (or config/publishing?
|
||||
(let [last-child-id (model/get-block-deep-last-open-child-id (db/get-db) (:db/id (last blocks)))
|
||||
block' (if last-child-id (db/entity last-child-id) (last blocks))]
|
||||
(string/blank? (:block/title block')))))]
|
||||
[:div
|
||||
{:class (when add-button? "show-add-button")}
|
||||
(page-blocks-inner page-e blocks config sidebar? whiteboard? block-id)
|
||||
(let [args (if block-id
|
||||
{:block-uuid block-id}
|
||||
{:page page-name})]
|
||||
(add-button args (:container-id config)))]))))))
|
||||
|
||||
(rum/defc today-queries < rum/reactive
|
||||
[repo today? sidebar?]
|
||||
@@ -301,10 +292,11 @@
|
||||
(rum/defc page-title-editor < rum/reactive
|
||||
[page {:keys [*input-value *title-value *edit? untitled? page-name old-name whiteboard-page?]}]
|
||||
(let [input-ref (rum/create-ref)
|
||||
tag-idents (map :db/ident (:block/tags page))
|
||||
collide? #(and (not= (util/page-name-sanity-lc page-name)
|
||||
(util/page-name-sanity-lc @*title-value))
|
||||
(db/page-exists? page-name (:block/type page))
|
||||
(db/page-exists? @*title-value (:block/type page)))
|
||||
(db/page-exists? page-name tag-idents)
|
||||
(db/page-exists? @*title-value tag-idents))
|
||||
rollback-fn #(let [old-name (if untitled? "" old-name)]
|
||||
(reset! *title-value old-name)
|
||||
(gobj/set (rum/deref input-ref) "value" old-name)
|
||||
@@ -559,6 +551,65 @@
|
||||
(plugins/hook-ui-slot :page-head-actions-slotted nil)
|
||||
(plugins/hook-ui-items :pagebar)])))
|
||||
|
||||
(rum/defc tabs
|
||||
[page opts]
|
||||
(let [class? (ldb/class? page)
|
||||
property? (ldb/property? page)
|
||||
both? (and class? property?)
|
||||
default-tab (cond
|
||||
both?
|
||||
"tag"
|
||||
class?
|
||||
"tag"
|
||||
:else
|
||||
"property")]
|
||||
[:div.page-tabs
|
||||
(shui/tabs
|
||||
{:defaultValue default-tab
|
||||
:class (str "w-full")}
|
||||
(when (or both? property?)
|
||||
[:div.flex.flex-row.gap-1.items-center
|
||||
(shui/tabs-list
|
||||
{:class "h-8"}
|
||||
(when class?
|
||||
(shui/tabs-trigger
|
||||
{:value "tag"
|
||||
:class "py-1 text-xs"}
|
||||
"Tagged nodes"))
|
||||
(when property?
|
||||
(shui/tabs-trigger
|
||||
{:value "property"
|
||||
:class "py-1 text-xs"}
|
||||
"Nodes with property"))
|
||||
(when property?
|
||||
(db-page/configure-property page)))])
|
||||
|
||||
(when class?
|
||||
(shui/tabs-content
|
||||
{:value "tag"}
|
||||
(objects/class-objects page opts)))
|
||||
(when property?
|
||||
(shui/tabs-content
|
||||
{:value "property"}
|
||||
(objects/property-related-objects page (:current-page? opts)))))]))
|
||||
|
||||
(rum/defc sidebar-page-properties
|
||||
[config page]
|
||||
(let [[collapsed? set-collapsed!] (rum/use-state true)]
|
||||
[:div.ls-sidebar-page-properties.flex.flex-col.gap-2.mt-2
|
||||
[:div
|
||||
(shui/button
|
||||
{:variant :ghost
|
||||
:size :sm
|
||||
:class "px-1 text-muted-foreground"
|
||||
:on-click #(set-collapsed! (not collapsed?))}
|
||||
[:span.text-xs (str (if collapsed? "Open" "Hide")) " properties"])]
|
||||
|
||||
(when-not collapsed?
|
||||
[:<>
|
||||
(component-block/db-properties-cp config page {:sidebar-properties? true})
|
||||
[:hr.my-4]])]))
|
||||
|
||||
;; A page is just a logical block
|
||||
(rum/defcs ^:large-vars/cleanup-todo page-inner < rum/reactive db-mixins/query mixins/container-id
|
||||
(rum/local false ::all-collapsed?)
|
||||
@@ -605,9 +656,8 @@
|
||||
|
||||
(if (and whiteboard-page? (not sidebar?))
|
||||
[:div ((state/get-component :whiteboard/tldraw-preview) (:block/uuid page))] ;; FIXME: this is not reactive
|
||||
[:div.relative.page-inner
|
||||
(when (or (and db-based? (not block?))
|
||||
(and (not db-based?) (not sidebar?) (not block?)))
|
||||
[:div.relative.grid.gap-8.page-inner
|
||||
(when-not (or block? sidebar?)
|
||||
[:div.flex.flex-row.space-between
|
||||
(when (and (or (mobile-util/native-platform?) (util/mobile?)) (not db-based?))
|
||||
[:div.flex.flex-row.pr-2
|
||||
@@ -625,14 +675,9 @@
|
||||
:preview? preview?})))
|
||||
(lsp-pagebar-slot)])
|
||||
|
||||
(when (and db-based? (ldb/property? page))
|
||||
(db-page/configure-property page))
|
||||
|
||||
(when (and db-based? class-page?)
|
||||
(objects/class-objects page {:current-page? option :sidebar? sidebar?}))
|
||||
|
||||
(when (and db-based? (ldb/property? page))
|
||||
(objects/property-related-objects page (:current-page? option)))
|
||||
(when (and db-based? sidebar?)
|
||||
[:div.-mb-8
|
||||
(sidebar-page-properties config page)])
|
||||
|
||||
(when (and block? (not sidebar?) (not whiteboard?))
|
||||
(let [config (merge config {:id "block-parent"
|
||||
@@ -640,10 +685,13 @@
|
||||
[:div.mb-4
|
||||
(component-block/breadcrumb config repo block-id {:level-limit 3})]))
|
||||
|
||||
(when (and db-based? (or class-page? (ldb/property? page)))
|
||||
(tabs page {:current-page? option :sidebar? sidebar?}))
|
||||
|
||||
[:div.ls-page-blocks
|
||||
(page-blocks-cp repo page (merge option {:sidebar? sidebar?
|
||||
:container-id (:container-id state)
|
||||
:whiteboard? whiteboard?}))]])
|
||||
(page-blocks-cp page (merge option {:sidebar? sidebar?
|
||||
:container-id (:container-id state)
|
||||
:whiteboard? whiteboard?}))]])
|
||||
|
||||
(when (not preview?)
|
||||
[:div {:style {:padding-left 9}}
|
||||
|
||||
@@ -65,8 +65,6 @@
|
||||
@apply rounded-sm;
|
||||
|
||||
&.title {
|
||||
@apply mb-3;
|
||||
|
||||
.block-main-container {
|
||||
@apply gap-2;
|
||||
}
|
||||
@@ -258,5 +256,5 @@ html.is-native-ios {
|
||||
}
|
||||
|
||||
.ls-page-blocks {
|
||||
@apply min-h-[60px] -mb-2;
|
||||
}
|
||||
@apply min-h-[60px];
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
[frontend.handler.property.util :as pu]
|
||||
[frontend.config :as config]
|
||||
[frontend.db :as db]
|
||||
[frontend.db.model :as db-model]
|
||||
[frontend.db-mixins :as db-mixins]
|
||||
[frontend.db.async :as db-async]
|
||||
[frontend.handler.db-based.property :as db-property-handler]
|
||||
@@ -99,7 +100,8 @@
|
||||
add-class-property? (and (ldb/class? block) class-schema?)]
|
||||
(when *property (reset! *property property))
|
||||
(p/do!
|
||||
(when *show-new-property-config? (reset! *show-new-property-config? false))
|
||||
(when *show-new-property-config?
|
||||
(reset! *show-new-property-config? false))
|
||||
(when (= (:type schema) :node) (reset! *show-class-select? true))
|
||||
(components-pu/update-property! property property-name schema)
|
||||
(cond
|
||||
@@ -142,30 +144,42 @@
|
||||
(rum/defc property-select
|
||||
[exclude-properties select-opts]
|
||||
(let [[properties set-properties!] (rum/use-state nil)
|
||||
[classes set-classes!] (rum/use-state nil)
|
||||
[excluded-properties set-excluded-properties!] (rum/use-state nil)]
|
||||
(rum/use-effect!
|
||||
(fn []
|
||||
(p/let [properties (db-async/<db-based-get-all-properties (state/get-current-repo))]
|
||||
(p/let [repo (state/get-current-repo)
|
||||
properties (db-async/<db-based-get-all-properties repo)
|
||||
classes (->> (db-model/get-all-classes repo)
|
||||
(remove ldb/built-in?))]
|
||||
(set-classes! classes)
|
||||
(set-properties! (remove exclude-properties properties))
|
||||
(set-excluded-properties! (->> properties
|
||||
(filter exclude-properties)
|
||||
(map :block/title)
|
||||
set))))
|
||||
[])
|
||||
[:div.ls-property-add.flex.flex-row.items-center.property-key
|
||||
[:div.ls-property-key
|
||||
(select/select (merge
|
||||
{:items (map (fn [x]
|
||||
{:label (:block/title x)
|
||||
:value (:block/uuid x)}) properties)
|
||||
:extract-fn :label
|
||||
:dropdown? false
|
||||
:close-modal? false
|
||||
:new-case-sensitive? true
|
||||
:show-new-when-not-exact-match? true
|
||||
:exact-match-exclude-items (fn [s] (contains? excluded-properties s))
|
||||
:input-default-placeholder "Add or change property"}
|
||||
select-opts))]]))
|
||||
(let [items (concat
|
||||
(map (fn [x]
|
||||
{:label (:block/title x)
|
||||
:value (:block/uuid x)}) properties)
|
||||
(map (fn [x]
|
||||
{:label (:block/title x)
|
||||
:value (:block/uuid x)
|
||||
:group "Tags"}) classes))]
|
||||
[:div.ls-property-add.flex.flex-row.items-center.property-key
|
||||
[:div.ls-property-key
|
||||
(select/select (merge
|
||||
{:items items
|
||||
:grouped? true
|
||||
:extract-fn :label
|
||||
:dropdown? false
|
||||
:close-modal? false
|
||||
:new-case-sensitive? true
|
||||
:show-new-when-not-exact-match? true
|
||||
:exact-match-exclude-items (fn [s] (contains? excluded-properties s))
|
||||
:input-default-placeholder "Add or change property"}
|
||||
select-opts))]])))
|
||||
|
||||
(rum/defc property-icon
|
||||
[property property-type]
|
||||
@@ -194,7 +208,7 @@
|
||||
(fn [{:keys [value label]}]
|
||||
(reset! *property-key (if (uuid? value) label value))
|
||||
(let [property (when (uuid? value) (db/entity [:block/uuid value]))]
|
||||
(when (and *show-new-property-config? (not property))
|
||||
(when (and *show-new-property-config? (not (ldb/property? property)))
|
||||
(reset! *show-new-property-config? true))
|
||||
(reset! *property property)
|
||||
(when property
|
||||
@@ -221,10 +235,26 @@
|
||||
(not (seq (:property/closed-values property))))
|
||||
(pv/<create-new-block! block property "")
|
||||
|
||||
;; using class as property
|
||||
(and property (ldb/class? property))
|
||||
(let [schema (assoc (:block/schema property)
|
||||
:type :node)]
|
||||
(p/do!
|
||||
(db/transact! (state/get-current-repo)
|
||||
[{:db/id (:db/id property)
|
||||
:db/ident (:db/ident property)
|
||||
:db/cardinality :db.cardinality/one
|
||||
:db/valueType :db.type/ref
|
||||
:db/index true
|
||||
:block/tags :logseq.class/Property
|
||||
:block/schema schema
|
||||
:property/schema.classes (:db/id property)}]
|
||||
{:outliner-op :save-block})
|
||||
(reset! *show-new-property-config? false)))
|
||||
|
||||
(or (not= :default type)
|
||||
(and (= :default type) (seq (:property/closed-values property))))
|
||||
(p/do!
|
||||
(reset! *show-new-property-config? false))))))))
|
||||
(reset! *show-new-property-config? false)))))))
|
||||
|
||||
(rum/defc property-key-title
|
||||
[block property class-schema?]
|
||||
@@ -333,17 +363,20 @@
|
||||
*show-new-property-config? (::show-new-property-config? state)
|
||||
*show-class-select? (::show-class-select? state)
|
||||
*property-schema (::property-schema state)
|
||||
block-type (keyword (get block :block/type :block))
|
||||
page? (ldb/page? block)
|
||||
block-types (let [types (ldb/get-entity-types block)]
|
||||
(cond-> types
|
||||
(and page? (not (contains? types :page)))
|
||||
(conj :page)
|
||||
(empty? types)
|
||||
#{:block}))
|
||||
exclude-properties (fn [m]
|
||||
(let [view-context (get-in m [:block/schema :view-context] :all)
|
||||
block-types (if (and page? (not= block-type :page))
|
||||
#{:page block-type}
|
||||
#{block-type})]
|
||||
(let [view-context (get-in m [:block/schema :view-context] :all)]
|
||||
(or (contains? #{:logseq.property/query} (:db/ident m))
|
||||
(and (not page?) (contains? #{:block/alias} (:db/ident m)))
|
||||
;; Filters out properties from being in wrong :view-context and :never view-contexts
|
||||
(and (not= view-context :all) (not (contains? block-types view-context))))))
|
||||
(and (not= view-context :all) (not (contains? block-types view-context)))
|
||||
(and (ldb/built-in? block) (contains? #{:logseq.property/parent} (:db/ident m))))))
|
||||
property (rum/react *property)
|
||||
property-key (rum/react *property-key)]
|
||||
[:div.ls-property-input.flex.flex-1.flex-row.items-center.flex-wrap.gap-1
|
||||
@@ -392,7 +425,7 @@
|
||||
|
||||
(rum/defcs new-property < rum/reactive
|
||||
[state block opts]
|
||||
(when (and (not config/publishing?) (:class-schema? opts))
|
||||
(when-not config/publishing?
|
||||
[:div.ls-new-property {:style {:margin-left 6 :margin-top 1}}
|
||||
[:a.fade-link.flex.jtrigger
|
||||
{:tab-index 0
|
||||
@@ -553,7 +586,7 @@
|
||||
:will-remount (fn [state]
|
||||
(let [block (db/entity (:db/id (::block state)))]
|
||||
(assoc state ::classes (async-load-classes! block))))}
|
||||
[state _target-block {:keys [class-schema?] :as opts}]
|
||||
[state _target-block {:keys [class-schema? sidebar-properties?] :as opts}]
|
||||
(let [id (::id state)
|
||||
db-id (:db/id (::block state))
|
||||
block (db/sub-block db-id)
|
||||
@@ -563,7 +596,6 @@
|
||||
(and (set? ids) (contains? ids (:block/uuid block))))))
|
||||
_ (doseq [class (::classes state)]
|
||||
(db/sub-block (:db/id class)))
|
||||
page? (db/page? block)
|
||||
class? (ldb/class? block)
|
||||
block-properties (:block/properties block)
|
||||
properties (cond
|
||||
@@ -652,20 +684,38 @@
|
||||
(when (and class? (nil? (:logseq.property.class/properties block)))
|
||||
[[:logseq.property.class/properties nil]]))
|
||||
remove-built-in-or-other-position-properties)]
|
||||
(when-not (and (empty? full-properties) (not (:class-schema? opts)))
|
||||
[:div.ls-properties-area
|
||||
{:id id
|
||||
:class (util/classnames [{:class-properties class-schema?
|
||||
:ls-page-properties (and page? (not class-schema?))}])
|
||||
:tab-index 0
|
||||
:on-key-up #(when-let [block (and (= "Escape" (.-key %))
|
||||
(.closest (.-target %) "[blockid]"))]
|
||||
(let [target (.-target %)]
|
||||
(when-not (d/has-class? target "ls-popup-closed")
|
||||
(state/set-selection-blocks! [block])
|
||||
(some-> js/document.activeElement (.blur)))
|
||||
(d/remove-class! target "ls-popup-closed")))}
|
||||
(let [properties' (remove (fn [[k _v]] (contains? #{:logseq.property/icon :logseq.property/query} k)) full-properties)]
|
||||
(properties-section block (if class-schema? properties properties') opts))
|
||||
(cond
|
||||
(and (empty? full-properties) (not (:class-schema? opts)))
|
||||
(when sidebar-properties?
|
||||
(rum/with-key (new-property block opts) (str id "-add-property")))
|
||||
|
||||
(rum/with-key (new-property block opts) (str id "-add-property"))])))
|
||||
:else
|
||||
(let [remove-properties #{:logseq.property/icon :logseq.property/query}
|
||||
properties' (remove (fn [[k _v]] (contains? remove-properties k)) full-properties)
|
||||
properties'' (cond->> properties'
|
||||
(not class-schema?)
|
||||
(remove (fn [[k _v]] (= k :logseq.property.class/properties))))
|
||||
page? (ldb/page? block)]
|
||||
[:div.ls-properties-area
|
||||
{:id id
|
||||
:class (util/classnames [{:class-properties class-schema?
|
||||
:ls-page-properties (and page? (not class-schema?))}])
|
||||
:tab-index 0
|
||||
:on-key-up #(when-let [block (and (= "Escape" (.-key %))
|
||||
(.closest (.-target %) "[blockid]"))]
|
||||
(let [target (.-target %)]
|
||||
(when-not (d/has-class? target "ls-popup-closed")
|
||||
(state/set-selection-blocks! [block])
|
||||
(some-> js/document.activeElement (.blur)))
|
||||
(d/remove-class! target "ls-popup-closed")))}
|
||||
(properties-section block (if class-schema? properties properties'') opts)
|
||||
|
||||
(when page?
|
||||
(rum/with-key (new-property block opts) (str id "-add-property")))
|
||||
|
||||
(when page?
|
||||
(let [properties'' (filter (fn [[k _v]] (= k :logseq.property.class/properties)) properties')]
|
||||
(when (seq properties'')
|
||||
[:<>
|
||||
(when-not class-schema? [:hr.my-4])
|
||||
(properties-section block (if class-schema? properties properties'') opts)])))]))))
|
||||
|
||||
@@ -29,9 +29,7 @@
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.ls-block .property-pair,
|
||||
.property-block .property-value,
|
||||
.block-property-value {
|
||||
.ls-block .property-pair, .ls-sidebar-page-properties .property-pair, .property-block .property-value, .block-property-value {
|
||||
margin-left: 7px;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
(let [toggle-fn #(do
|
||||
(when (fn? on-hide) (on-hide))
|
||||
(shui/popup-hide! id))
|
||||
classes (model/get-all-classes (state/get-current-repo) {:except-root-class? true})
|
||||
classes (model/get-all-readable-classes (state/get-current-repo) {:except-root-class? true})
|
||||
options (map (fn [class]
|
||||
{:label (:block/title class)
|
||||
:value (:block/uuid class)})
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
[dommy.core :as d]
|
||||
[frontend.components.icon :as icon-component]
|
||||
[frontend.components.select :as select]
|
||||
[frontend.components.title :as title]
|
||||
[frontend.config :as config]
|
||||
[frontend.date :as date]
|
||||
[frontend.db :as db]
|
||||
@@ -34,7 +33,8 @@
|
||||
[logseq.db.frontend.property.type :as db-property-type]
|
||||
[logseq.shui.ui :as shui]
|
||||
[promesa.core :as p]
|
||||
[rum.core :as rum]))
|
||||
[rum.core :as rum]
|
||||
[clojure.set :as set]))
|
||||
|
||||
(rum/defc property-empty-btn-value
|
||||
[property & opts]
|
||||
@@ -506,13 +506,16 @@
|
||||
(when (and property-type (not= property-type :node))
|
||||
(if (= property-type :page)
|
||||
(not (db/page? node))
|
||||
(not= property-type (some-> (:block/type node) keyword))))))
|
||||
(not (contains? (ldb/get-entity-types node) property-type))))))
|
||||
result)))))
|
||||
|
||||
options (map (fn [node]
|
||||
(let [id (or (:value node) (:db/id node))
|
||||
[header label] (if (integer? id)
|
||||
(let [title (subs (title/block-unique-title node) 0 256)
|
||||
(let [node-title (if (seq (:property/schema.classes property))
|
||||
(:block/title node)
|
||||
(block-handler/block-unique-title node))
|
||||
title (subs node-title 0 256)
|
||||
node (or (db/entity id) node)
|
||||
icon (get-node-icon node)
|
||||
header (when-not (db/page? node)
|
||||
@@ -529,7 +532,10 @@
|
||||
:header header
|
||||
:label-value (:block/title node)
|
||||
:label label
|
||||
:value id))) nodes)
|
||||
:value id
|
||||
:disabled? (and tags? (contains?
|
||||
(set/union #{:logseq.class/Journal :logseq.class/Whiteboard} ldb/internal-tags)
|
||||
(:db/ident node)))))) nodes)
|
||||
classes' (remove (fn [class] (= :logseq.class/Root (:db/ident class))) classes)
|
||||
opts' (cond->
|
||||
(merge
|
||||
@@ -547,7 +553,10 @@
|
||||
"Choose nodes"
|
||||
:else
|
||||
"Choose node")
|
||||
:show-new-when-not-exact-match? (if (and parent-property? (contains? (set children-pages) (:db/id block)))
|
||||
:show-new-when-not-exact-match? (if (or (and parent-property? (contains? (set children-pages) (:db/id block)))
|
||||
;; Don't allow creating private tags
|
||||
(seq (set/intersection (set (map :db/ident classes))
|
||||
ldb/private-tags)))
|
||||
false
|
||||
true)
|
||||
:extract-chosen-fn :value
|
||||
@@ -815,6 +824,9 @@
|
||||
(= value :logseq.property/empty-placeholder)
|
||||
(property-empty-btn-value property)
|
||||
|
||||
closed-values?
|
||||
(closed-value-item value opts)
|
||||
|
||||
(or (ldb/page? value)
|
||||
(and (seq (:block/tags value))
|
||||
;; FIXME: page-cp should be renamed to node-cp and
|
||||
@@ -832,9 +844,6 @@
|
||||
(when-let [reference (state/get-component :block/reference)]
|
||||
(reference {} (:block/uuid value)))
|
||||
|
||||
closed-values?
|
||||
(closed-value-item value opts)
|
||||
|
||||
(de/entity? value)
|
||||
(when-some [content (str (db-property/property-value-content value))]
|
||||
(inline-text-cp content))
|
||||
@@ -966,7 +975,9 @@
|
||||
(let [type (get schema :type :default)
|
||||
date? (= type :date)
|
||||
*el (rum/use-ref nil)
|
||||
items (if (de/entity? v) #{v} v)]
|
||||
items (cond->> (if (de/entity? v) #{v} v)
|
||||
(= (:db/ident property) :block/tags)
|
||||
(remove (fn [v] (contains? ldb/hidden-tags (:db/ident v)))))]
|
||||
(rum/use-effect!
|
||||
(fn []
|
||||
(when editing?
|
||||
@@ -1002,7 +1013,7 @@
|
||||
(do (some-> (rum/deref *el) (.click))
|
||||
(util/stop e))
|
||||
:dune))
|
||||
:class "flex flex-1 flex-row items-center flex-wrap gap-x-2 gap-y-2 pr-4"}
|
||||
:class "flex flex-1 flex-row items-center flex-wrap gap-x-2 gap-y-2"}
|
||||
(let [not-empty-value? (not= (map :db/ident items) [:logseq.property/empty-placeholder])]
|
||||
(if (and (seq items) not-empty-value?)
|
||||
(concat
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
db-based? (config/db-based-graph? repo)]
|
||||
(rum/use-effect!
|
||||
(fn []
|
||||
(let [result (db-model/get-all-classes repo {:except-root-class? true})]
|
||||
(let [result (db-model/get-all-readable-classes repo {:except-root-class? true})]
|
||||
(set-values! result)))
|
||||
[])
|
||||
(let [items (->> values
|
||||
|
||||
@@ -307,8 +307,8 @@
|
||||
db-based? (config/db-based-graph? current-repo)
|
||||
repos (sort-repos-with-metadata-local repos)
|
||||
repos (distinct
|
||||
(if (and (or (seq remotes) (seq rtc-graphs)) login?)
|
||||
(repo-handler/combine-local-&-remote-graphs repos (concat remotes rtc-graphs)) repos))
|
||||
(if (and (or (seq remotes) (seq rtc-graphs)) login?)
|
||||
(repo-handler/combine-local-&-remote-graphs repos (concat remotes rtc-graphs)) repos))
|
||||
items-fn #(repos-dropdown-links repos current-repo downloading-graph-id opts)
|
||||
header-fn #(when (> (count repos) 1) ; show switch to if there are multiple repos
|
||||
[:div.font-medium.text-sm.opacity-50.px-1.py-1.flex.flex-row.justify-between.items-center
|
||||
@@ -318,14 +318,14 @@
|
||||
(if remotes-loading?
|
||||
(ui/loading "")
|
||||
(shui/button
|
||||
{:variant :ghost
|
||||
:size :sm
|
||||
:title "Refresh remote graphs"
|
||||
:class "!h-6 !px-1 relative right-[-4px]"
|
||||
:on-click (fn []
|
||||
(file-sync/load-session-graphs)
|
||||
(rtc-handler/<get-remote-graphs))}
|
||||
(ui/icon "refresh" {:size 15}))))])
|
||||
{:variant :ghost
|
||||
:size :sm
|
||||
:title "Refresh remote graphs"
|
||||
:class "!h-6 !px-1 relative right-[-4px]"
|
||||
:on-click (fn []
|
||||
(file-sync/load-session-graphs)
|
||||
(rtc-handler/<get-remote-graphs))}
|
||||
(ui/icon "refresh" {:size 15}))))])
|
||||
_remote? (and current-repo (:remote? (first (filter #(= current-repo (:url %)) repos))))
|
||||
_repo-name (when current-repo (db/get-repo-name current-repo))]
|
||||
|
||||
@@ -339,23 +339,23 @@
|
||||
(if hr
|
||||
(shui/dropdown-menu-separator)
|
||||
(shui/dropdown-menu-item
|
||||
(assoc options
|
||||
:title hover-detail
|
||||
:on-click (fn [^js e]
|
||||
(when on-click'
|
||||
(when-not (false? (on-click' e))
|
||||
(shui/popup-hide! contentid)))))
|
||||
(or item
|
||||
(if href'
|
||||
[:a.flex.items-center.w-full
|
||||
{:href href' :on-click #(shui/popup-hide! contentid)
|
||||
:style {:color "inherit"}} title]
|
||||
[:span.flex.items-center.gap-1.w-full
|
||||
icon [:div title]]))))))]
|
||||
(assoc options
|
||||
:title hover-detail
|
||||
:on-click (fn [^js e]
|
||||
(when on-click'
|
||||
(when-not (false? (on-click' e))
|
||||
(shui/popup-hide! contentid)))))
|
||||
(or item
|
||||
(if href'
|
||||
[:a.flex.items-center.w-full
|
||||
{:href href' :on-click #(shui/popup-hide! contentid)
|
||||
:style {:color "inherit"}} title]
|
||||
[:span.flex.items-center.gap-1.w-full
|
||||
icon [:div title]]))))))]
|
||||
(repos-footer multiple-windows? db-based?)]))
|
||||
|
||||
(rum/defcs repos-dropdown < rum/reactive
|
||||
(rum/local false ::electron-multiple-windows?)
|
||||
(rum/local false ::electron-multiple-windows?)
|
||||
[state & {:as opts}]
|
||||
(let [current-repo (state/sub :git/current-repo)
|
||||
login? (boolean (state/sub :auth/id-token))]
|
||||
@@ -365,38 +365,38 @@
|
||||
db-based? (config/db-based-graph? current-repo)
|
||||
repos (sort-repos-with-metadata-local repos)
|
||||
repos (distinct
|
||||
(if (and (or (seq remotes) (seq rtc-graphs)) login?)
|
||||
(repo-handler/combine-local-&-remote-graphs repos (concat remotes rtc-graphs)) repos))]
|
||||
(if (and (or (seq remotes) (seq rtc-graphs)) login?)
|
||||
(repo-handler/combine-local-&-remote-graphs repos (concat remotes rtc-graphs)) repos))]
|
||||
(let [remote? (and current-repo (:remote? (first (filter #(= current-repo (:url %)) repos))))
|
||||
repo-name (when current-repo (db/get-repo-name current-repo))
|
||||
short-repo-name (if current-repo
|
||||
(db/get-short-repo-name repo-name)
|
||||
"Select a Graph")]
|
||||
(shui/trigger-as :a
|
||||
{:tab-index 0
|
||||
:class "item cp__repos-select-trigger"
|
||||
:on-pointer-down
|
||||
(fn [^js e]
|
||||
(check-multiple-windows? state)
|
||||
(some-> (.-target e)
|
||||
(.closest "a.item")
|
||||
(shui/popup-show!
|
||||
(fn [{:keys [id]}] (repos-dropdown-content (assoc opts :contentid id)))
|
||||
{:as-dropdown? true
|
||||
:auto-focus? false
|
||||
:align "start"
|
||||
:content-props {:class "repos-list"
|
||||
:data-mode (when db-based? "db")}})))
|
||||
:title repo-name} ;; show full path on hover
|
||||
[:div.flex.relative.graph-icon.rounded
|
||||
(shui/tabler-icon "database" {:size 15})]
|
||||
{:tab-index 0
|
||||
:class "item cp__repos-select-trigger"
|
||||
:on-pointer-down
|
||||
(fn [^js e]
|
||||
(check-multiple-windows? state)
|
||||
(some-> (.-target e)
|
||||
(.closest "a.item")
|
||||
(shui/popup-show!
|
||||
(fn [{:keys [id]}] (repos-dropdown-content (assoc opts :contentid id)))
|
||||
{:as-dropdown? true
|
||||
:auto-focus? false
|
||||
:align "start"
|
||||
:content-props {:class "repos-list"
|
||||
:data-mode (when db-based? "db")}})))
|
||||
:title repo-name} ;; show full path on hover
|
||||
[:div.flex.relative.graph-icon.rounded
|
||||
(shui/tabler-icon "database" {:size 15})]
|
||||
|
||||
[:div.repo-switch.pr-2.whitespace-nowrap
|
||||
[:span.repo-name.font-medium
|
||||
[:span.repo-text.overflow-hidden.text-ellipsis
|
||||
(if (config/demo-graph? short-repo-name) "Demo" short-repo-name)]
|
||||
(when remote? [:span.pl-1 (ui/icon "cloud")])]
|
||||
[:span.dropdown-caret]])))))
|
||||
[:div.repo-switch.pr-2.whitespace-nowrap
|
||||
[:span.repo-name.font-medium
|
||||
[:span.repo-text.overflow-hidden.text-ellipsis
|
||||
(if (config/demo-graph? short-repo-name) "Demo" short-repo-name)]
|
||||
(when remote? [:span.pl-1 (ui/icon "cloud")])]
|
||||
[:span.dropdown-caret]])))))
|
||||
|
||||
(rum/defcs graphs-selector < rum/reactive
|
||||
[_state]
|
||||
@@ -413,10 +413,10 @@
|
||||
[:a.item.flex.items-center.gap-1.select-none
|
||||
{:on-click (fn [^js e]
|
||||
(shui/popup-show! (.closest (.-target e) "a")
|
||||
(fn [{:keys [id]}] (repos-dropdown-content {:contentid id}))
|
||||
{:as-dropdown? true
|
||||
:content-props {:class "repos-list"}
|
||||
:align :start}))}
|
||||
(fn [{:keys [id]}] (repos-dropdown-content {:contentid id}))
|
||||
{:as-dropdown? true
|
||||
:content-props {:class "repos-list"}
|
||||
:align :start}))}
|
||||
[:span.thumb (shui/tabler-icon (if remote? "cloud" (if db-based? "database" "folder")) {:size 16})]
|
||||
[:strong short-repo-name]
|
||||
(shui/tabler-icon "selector" {:size 18})]]))
|
||||
|
||||
@@ -97,8 +97,8 @@
|
||||
(let [lookup (if (integer? db-id) db-id [:block/uuid db-id])
|
||||
page (db/entity repo lookup)]
|
||||
(if (ldb/page? page)
|
||||
[[:.flex.items-center.page-title
|
||||
(icon/get-node-icon-cp page {:class "text-md mr-2"})
|
||||
[[:.flex.items-center.page-title.gap-1
|
||||
(icon/get-node-icon-cp page {:class "text-md"})
|
||||
[:span.overflow-hidden.text-ellipsis (:block/title page)]]
|
||||
(page-cp repo (str (:block/uuid page)))]
|
||||
(block-with-breadcrumb repo page idx [repo db-id block-type] false)))
|
||||
|
||||
@@ -38,7 +38,3 @@ html[data-theme=light] {
|
||||
@apply opacity-100;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-panel-content {
|
||||
@apply pt-3;
|
||||
}
|
||||
@@ -29,7 +29,8 @@
|
||||
(when multiple-choices?
|
||||
(ui/checkbox {:checked (boolean (selected-choices (:value result)))
|
||||
:on-click (fn [e]
|
||||
(.preventDefault e))}))
|
||||
(.preventDefault e))
|
||||
:disabled (:disabled? result)}))
|
||||
value]
|
||||
(when (and (map? result) (:id result))
|
||||
[:div.tip.flex
|
||||
@@ -67,7 +68,7 @@
|
||||
:will-unmount (fn [state]
|
||||
(shui/dialog-close! :ls-select-modal)
|
||||
state)}
|
||||
[state {:keys [items limit on-chosen empty-placeholder
|
||||
[state {:keys [items limit on-chosen empty-placeholder grouped?
|
||||
prompt-key input-default-placeholder close-modal?
|
||||
extract-fn extract-chosen-fn host-opts on-input input-opts
|
||||
item-cp transform-fn tap-*input-val
|
||||
@@ -138,7 +139,8 @@
|
||||
[:div.item-results-wrap
|
||||
(ui/auto-complete
|
||||
search-result
|
||||
{:item-render (or item-cp (fn [result chosen?]
|
||||
{:grouped? grouped?
|
||||
:item-render (or item-cp (fn [result chosen?]
|
||||
(render-item result chosen? multiple-choices? *selected-choices)))
|
||||
:class "cp__select-results"
|
||||
:on-chosen (fn [raw-chosen e]
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
(ns frontend.components.title
|
||||
(:require [clojure.string :as string]
|
||||
[frontend.db :as db]
|
||||
[logseq.db :as ldb]
|
||||
[datascript.impl.entity :as de]))
|
||||
|
||||
(defn block-unique-title
|
||||
"Multiple pages/objects may have the same `:block/title`.
|
||||
Notice: this doesn't prevent for pages/objects that have the same tag or created by different clients."
|
||||
[block]
|
||||
(let [block-e (cond
|
||||
(de/entity? block)
|
||||
block
|
||||
(uuid? (:block/uuid block))
|
||||
(db/entity [:block/uuid (:block/uuid block)])
|
||||
:else
|
||||
block)
|
||||
tags (remove (fn [t] (some-> (:block/raw-title block-e) (ldb/inline-tag? t)))
|
||||
(map (fn [tag] (if (number? tag) (db/entity tag) tag)) (:block/tags block)))]
|
||||
(if (and (seq tags)
|
||||
(not (ldb/journal? block)))
|
||||
(str (:block/title block)
|
||||
" "
|
||||
(string/join
|
||||
", "
|
||||
(keep (fn [tag]
|
||||
(when-let [title (:block/title tag)]
|
||||
(str "#" title)))
|
||||
tags)))
|
||||
(:block/title block))))
|
||||
@@ -80,11 +80,11 @@
|
||||
{:variant "text"
|
||||
:class "h-8 !pl-4 !px-2 !py-0 hover:text-foreground w-full justify-start"
|
||||
:on-click #(column-toggle-sorting! column)}
|
||||
(let [title (str (:name column))]
|
||||
[:span {:title title
|
||||
:class "max-w-full overflow-hidden text-ellipsis"}
|
||||
title])
|
||||
(case asc?
|
||||
(let [title (str (:name column))]
|
||||
[:span {:title title
|
||||
:class "max-w-full overflow-hidden text-ellipsis"}
|
||||
title])
|
||||
(case asc?
|
||||
true
|
||||
(ui/icon "arrow-up")
|
||||
false
|
||||
@@ -529,7 +529,6 @@
|
||||
(defn- get-property-values
|
||||
[rows property]
|
||||
(let [property-ident (:db/ident property)
|
||||
block-type? (= property-ident :block/type)
|
||||
values (->> (mapcat (fn [e] (let [e' (db/entity (:db/id e))
|
||||
v (get e' property-ident)]
|
||||
(if (set? v) v #{v}))) rows)
|
||||
@@ -537,9 +536,8 @@
|
||||
(distinct))]
|
||||
(->>
|
||||
(map (fn [e]
|
||||
(let [label (get-property-value-content e)
|
||||
label' (if (and block-type? (= label "class")) "tag" label)]
|
||||
{:label (str label') :value e}))
|
||||
(let [label (get-property-value-content e)]
|
||||
{:label (str label) :value e}))
|
||||
values)
|
||||
(sort-by :label))))
|
||||
|
||||
@@ -607,7 +605,7 @@
|
||||
(do
|
||||
(shui/popup-hide!)
|
||||
(let [property internal-property
|
||||
new-filter [(:db/ident property) (if (= (:db/ident property) :block/type) :is :text-contains)]
|
||||
new-filter [(:db/ident property) :text-contains]
|
||||
filters' (if (seq filters)
|
||||
(conj filters new-filter)
|
||||
[new-filter])]
|
||||
@@ -685,15 +683,14 @@
|
||||
[:before :after]
|
||||
(concat
|
||||
[:is :is-not]
|
||||
(when-not (= :block/type (:db/ident property))
|
||||
(case (get-in property [:block/schema :type])
|
||||
(:default :url :node)
|
||||
[:text-contains :text-not-contains]
|
||||
(:date)
|
||||
[:date-before :date-after]
|
||||
:number
|
||||
[:number-gt :number-lt :number-gte :number-lte :between]
|
||||
nil)))))
|
||||
(case (get-in property [:block/schema :type])
|
||||
(:default :url :node)
|
||||
[:text-contains :text-not-contains]
|
||||
(:date)
|
||||
[:date-before :date-after]
|
||||
:number
|
||||
[:number-gt :number-lt :number-gte :number-lte :between]
|
||||
nil))))
|
||||
|
||||
(defn- get-filter-with-changed-operator
|
||||
[_property operator value]
|
||||
@@ -821,16 +818,11 @@
|
||||
{:class "!px-2 rounded-none border-r"
|
||||
:variant "ghost"
|
||||
:size :sm}
|
||||
(let [block-type? (= (:db/ident property) :block/type)
|
||||
value (cond
|
||||
(let [value (cond
|
||||
(uuid? value)
|
||||
(db/entity [:block/uuid value])
|
||||
(and (coll? value) (every? uuid? value))
|
||||
(set (map #(db/entity [:block/uuid %]) value))
|
||||
(and block-type? (coll? value))
|
||||
(map (fn [v] (if (= v "class") "tag" v)) value)
|
||||
(and block-type? (= value "class"))
|
||||
"tag"
|
||||
:else
|
||||
value)]
|
||||
[:div.flex.flex-row.items-center.gap-1.text-xs
|
||||
@@ -1012,15 +1004,15 @@
|
||||
(rum/defc new-record-button < rum/static
|
||||
[table view-entity]
|
||||
(let [asset? (and (:logseq.property/built-in? view-entity)
|
||||
(= (:block/name view-entity) "asset"))]
|
||||
(= (:block/name view-entity) "asset"))]
|
||||
(ui/tooltip
|
||||
(shui/button
|
||||
{:variant "ghost"
|
||||
:class "!px-1 text-muted-foreground"
|
||||
:size :sm
|
||||
:on-click (get-in table [:data-fns :add-new-object!])}
|
||||
(ui/icon (if asset? "upload" "plus")))
|
||||
[:div "New record"])))
|
||||
(shui/button
|
||||
{:variant "ghost"
|
||||
:class "!px-1 text-muted-foreground"
|
||||
:size :sm
|
||||
:on-click (get-in table [:data-fns :add-new-object!])}
|
||||
(ui/icon (if asset? "upload" "plus")))
|
||||
[:div "New record"])))
|
||||
|
||||
(rum/defc add-new-row < rum/static
|
||||
[table]
|
||||
@@ -1084,8 +1076,8 @@
|
||||
*rows-wrap (rum/use-ref nil)]
|
||||
|
||||
(rum/use-effect!
|
||||
(fn [] (set-ready? true))
|
||||
[])
|
||||
(fn [] (set-ready? true))
|
||||
[])
|
||||
|
||||
(shui/table
|
||||
(let [columns' (:columns table)
|
||||
@@ -1097,17 +1089,17 @@
|
||||
(table-header table columns' option selected-rows)
|
||||
|
||||
(ui/virtualized-list
|
||||
{:ref #(reset! *scroller-ref %)
|
||||
:custom-scroll-parent (or (some-> (rum/deref *rows-wrap) (.closest ".sidebar-item-list"))
|
||||
(gdom/getElement "main-content-container"))
|
||||
:increase-viewport-by {:top 300 :bottom 300}
|
||||
:compute-item-key (fn [idx]
|
||||
(let [block (nth rows idx)]
|
||||
(str "table-row-" (:db/id block))))
|
||||
:total-count (count rows)
|
||||
:item-content (fn [idx]
|
||||
(let [row (nth rows idx)]
|
||||
(table-row table row columns' {} option)))})
|
||||
{:ref #(reset! *scroller-ref %)
|
||||
:custom-scroll-parent (or (some-> (rum/deref *rows-wrap) (.closest ".sidebar-item-list"))
|
||||
(gdom/getElement "main-content-container"))
|
||||
:increase-viewport-by {:top 300 :bottom 300}
|
||||
:compute-item-key (fn [idx]
|
||||
(let [block (nth rows idx)]
|
||||
(str "table-row-" (:db/id block))))
|
||||
:total-count (count rows)
|
||||
:item-content (fn [idx]
|
||||
(let [row (nth rows idx)]
|
||||
(table-row table row columns' {} option)))})
|
||||
|
||||
(when add-new-object!
|
||||
(shui/table-footer (add-new-row table)))])]))))
|
||||
@@ -1245,39 +1237,42 @@
|
||||
|
||||
[:div.flex.flex-col.gap-2.grid
|
||||
{:ref *view-ref}
|
||||
[:div.flex.flex-wrap.items-center.justify-between.gap-1
|
||||
(when-not render-empty-title?
|
||||
[:div.flex.flex-row.items-center.gap-2
|
||||
(or
|
||||
views-title
|
||||
[:div.font-medium.opacity-50.text-sm
|
||||
(t (or title-key :views.table/default-title)
|
||||
(count (:rows table)))])])
|
||||
[:div.view-actions.flex.items-center.gap-1
|
||||
(ui/foldable
|
||||
[:div.flex.flex-1.flex-wrap.items-center.justify-between.gap-1
|
||||
(when-not render-empty-title?
|
||||
[:div.flex.flex-row.items-center.gap-2
|
||||
(or
|
||||
views-title
|
||||
[:div.font-medium.opacity-50.text-sm
|
||||
(t (or title-key :views.table/default-title)
|
||||
(count (:rows table)))])])
|
||||
[:div.view-actions.flex.items-center.gap-1
|
||||
|
||||
(filter-properties columns table)
|
||||
(filter-properties columns table)
|
||||
|
||||
(search input {:on-change set-input!
|
||||
:set-input! set-input!})
|
||||
(search input {:on-change set-input!
|
||||
:set-input! set-input!})
|
||||
|
||||
[:div.text-muted-foreground.text-sm
|
||||
(pv/property-value view-entity (db/entity :logseq.property.view/type)
|
||||
(db/entity display-type) {})]
|
||||
[:div.text-muted-foreground.text-sm
|
||||
(pv/property-value view-entity (db/entity :logseq.property.view/type)
|
||||
(db/entity display-type) {})]
|
||||
|
||||
(more-actions columns table)
|
||||
(more-actions columns table)
|
||||
|
||||
(when add-new-object! (new-record-button table view-entity))]]
|
||||
(when add-new-object! (new-record-button table view-entity))]]
|
||||
(fn []
|
||||
[:div.ls-view-body.flex.flex-col.gap-2.grid
|
||||
(filters-row table)
|
||||
|
||||
(filters-row table)
|
||||
(case display-type
|
||||
:logseq.property.view/type.list
|
||||
(list-view (:config option) view-entity (:rows table))
|
||||
|
||||
(case display-type
|
||||
:logseq.property.view/type.list
|
||||
(list-view (:config option) view-entity (:rows table))
|
||||
:logseq.property.view/type.gallery
|
||||
(gallery-view (:config option) table view-entity (:rows table) *scroller-ref)
|
||||
|
||||
:logseq.property.view/type.gallery
|
||||
(gallery-view (:config option) table view-entity (:rows table) *scroller-ref)
|
||||
|
||||
(table-view table option row-selection add-new-object! *scroller-ref))]))
|
||||
(table-view table option row-selection add-new-object! *scroller-ref))])
|
||||
{:title-trigger? false})]))
|
||||
|
||||
(rum/defcs view
|
||||
"Provides a view for data like query results and tagged objects, multiple
|
||||
|
||||
@@ -31,10 +31,10 @@
|
||||
(defn tt
|
||||
[& keys]
|
||||
(some->
|
||||
(medley/find-first
|
||||
#(not (string/starts-with? (t %) "{Missing key"))
|
||||
keys)
|
||||
t))
|
||||
(medley/find-first
|
||||
#(not (string/starts-with? (t %) "{Missing key"))
|
||||
keys)
|
||||
t))
|
||||
|
||||
(defn- fetch-local-language []
|
||||
(.. js/window -navigator -language))
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
[graph & {:keys [remove-built-in-property? remove-non-queryable-built-in-property?]
|
||||
:or {remove-built-in-property? true
|
||||
remove-non-queryable-built-in-property? false}}]
|
||||
(let [result (->> (d/datoms (db/get-db graph) :avet :block/type "property")
|
||||
(let [result (->> (d/datoms (db/get-db graph) :avet :block/tags :logseq.class/Property)
|
||||
(map (fn [datom] (db/entity (:e datom))))
|
||||
(sort-by (juxt ldb/built-in? :block/title)))]
|
||||
(cond->> result
|
||||
|
||||
@@ -383,10 +383,10 @@ independent of format as format specific heading characters are stripped"
|
||||
|
||||
(defn page-exists?
|
||||
"Whether a page exists."
|
||||
[page-name type]
|
||||
[page-name tags]
|
||||
(let [repo (state/get-current-repo)]
|
||||
(when-let [db (conn/get-db repo)]
|
||||
(ldb/page-exists? db page-name type))))
|
||||
(ldb/page-exists? db page-name tags))))
|
||||
|
||||
(defn page-empty?
|
||||
"Whether a page is empty. Does it has a non-page block?
|
||||
@@ -531,14 +531,23 @@ independent of format as format specific heading characters are stripped"
|
||||
(defn get-journals-length
|
||||
[]
|
||||
(let [today (date-time-util/date->int (js/Date.))]
|
||||
(d/q '[:find (count ?page) .
|
||||
:in $ ?today
|
||||
:where
|
||||
[?page :block/type "journal"]
|
||||
[?page :block/journal-day ?journal-day]
|
||||
[(<= ?journal-day ?today)]]
|
||||
(conn/get-db (state/get-current-repo))
|
||||
today)))
|
||||
(if (config/db-based-graph?)
|
||||
(d/q '[:find (count ?page) .
|
||||
:in $ ?today
|
||||
:where
|
||||
[?page :block/tags :logseq.class/Journal]
|
||||
[?page :block/journal-day ?journal-day]
|
||||
[(<= ?journal-day ?today)]]
|
||||
(conn/get-db (state/get-current-repo))
|
||||
today)
|
||||
(d/q '[:find (count ?page) .
|
||||
:in $ ?today
|
||||
:where
|
||||
[?page :block/type "journal"]
|
||||
[?page :block/journal-day ?journal-day]
|
||||
[(<= ?journal-day ?today)]]
|
||||
(conn/get-db (state/get-current-repo))
|
||||
today))))
|
||||
|
||||
(defn get-latest-journals
|
||||
([n]
|
||||
@@ -749,17 +758,29 @@ independent of format as format specific heading characters are stripped"
|
||||
|
||||
(defn get-all-whiteboards
|
||||
[repo]
|
||||
(d/q
|
||||
'[:find [(pull ?page [:db/id
|
||||
:block/uuid
|
||||
:block/name
|
||||
:block/title
|
||||
:block/created-at
|
||||
:block/updated-at]) ...]
|
||||
:where
|
||||
[?page :block/name]
|
||||
[?page :block/type "whiteboard"]]
|
||||
(conn/get-db repo)))
|
||||
(if (config/db-based-graph?)
|
||||
(d/q
|
||||
'[:find [(pull ?page [:db/id
|
||||
:block/uuid
|
||||
:block/name
|
||||
:block/title
|
||||
:block/created-at
|
||||
:block/updated-at]) ...]
|
||||
:where
|
||||
[?page :block/name]
|
||||
[?page :block/tags :logseq.class/Whiteboard]]
|
||||
(conn/get-db repo))
|
||||
(d/q
|
||||
'[:find [(pull ?page [:db/id
|
||||
:block/uuid
|
||||
:block/name
|
||||
:block/title
|
||||
:block/created-at
|
||||
:block/updated-at]) ...]
|
||||
:where
|
||||
[?page :block/name]
|
||||
[?page :block/type "whiteboard"]]
|
||||
(conn/get-db repo))))
|
||||
|
||||
(defn get-whiteboard-id-nonces
|
||||
[repo page-id]
|
||||
@@ -777,16 +798,27 @@ independent of format as format specific heading characters are stripped"
|
||||
:nonce (:nonce shape)}))))))
|
||||
|
||||
(defn get-all-classes
|
||||
[repo & {:keys [except-root-class?]
|
||||
:or {except-root-class? false}}]
|
||||
[repo & {:keys [except-root-class? except-private-tags?]
|
||||
:or {except-root-class? false
|
||||
except-private-tags? true}}]
|
||||
(let [db (conn/get-db repo)
|
||||
classes (->> (d/datoms db :avet :block/type "class")
|
||||
classes (->> (d/datoms db :avet :block/tags :logseq.class/Tag)
|
||||
(map (fn [d]
|
||||
(db-utils/entity db (:e d)))))]
|
||||
(db-utils/entity db (:e d))))
|
||||
(remove (fn [d]
|
||||
(and except-private-tags?
|
||||
(contains? ldb/private-tags (:db/ident d))))))]
|
||||
(if except-root-class?
|
||||
(keep (fn [e] (when-not (= :logseq.class/Root (:db/ident e)) e)) classes)
|
||||
classes)))
|
||||
|
||||
(defn get-all-readable-classes
|
||||
"Gets all classes that are used in a read only context e.g. querying or used
|
||||
for property value selection. This should _not_ be used in a write context e.g.
|
||||
adding a tag to a node or creating a new node with a tag"
|
||||
[repo opts]
|
||||
(get-all-classes repo (merge opts {:except-private-tags? false})))
|
||||
|
||||
(defn get-structured-children
|
||||
[repo eid]
|
||||
(->>
|
||||
@@ -802,13 +834,15 @@ independent of format as format specific heading characters are stripped"
|
||||
(defn get-class-objects
|
||||
[repo class-id]
|
||||
(when-let [class (db-utils/entity repo class-id)]
|
||||
(if (first (:logseq.property/_parent class)) ; has children classes
|
||||
(let [all-classes (conj (->> (get-structured-children repo class-id)
|
||||
(map #(db-utils/entity repo %)))
|
||||
class)]
|
||||
(->> (mapcat :block/_tags all-classes)
|
||||
distinct))
|
||||
(:block/_tags class))))
|
||||
(->>
|
||||
(if (first (:logseq.property/_parent class)) ; has children classes
|
||||
(let [all-classes (conj (->> (get-structured-children repo class-id)
|
||||
(map #(db-utils/entity repo %)))
|
||||
class)]
|
||||
(->> (mapcat :block/_tags all-classes)
|
||||
distinct))
|
||||
(:block/_tags class))
|
||||
(remove ldb/hidden?))))
|
||||
|
||||
(defn sub-class-objects
|
||||
[repo class-id]
|
||||
@@ -829,7 +863,8 @@ independent of format as format specific heading characters are stripped"
|
||||
(rules/extract-rules rules/db-query-dsl-rules [:has-property-or-default-value]
|
||||
{:deps rules/rules-dependencies})
|
||||
(:db/ident property))
|
||||
(map #(db-utils/entity repo %)))))
|
||||
(map #(db-utils/entity repo %))
|
||||
(remove ldb/hidden?))))
|
||||
|
||||
(defn get-all-namespace-relation
|
||||
[repo]
|
||||
@@ -849,18 +884,30 @@ independent of format as format specific heading characters are stripped"
|
||||
(defn get-pages-relation
|
||||
[repo with-journal?]
|
||||
(when-let [db (conn/get-db repo)]
|
||||
(let [q (if with-journal?
|
||||
'[:find ?p ?ref-page
|
||||
:where
|
||||
[?block :block/page ?p]
|
||||
[?block :block/refs ?ref-page]]
|
||||
'[:find ?p ?ref-page
|
||||
:where
|
||||
[?block :block/page ?p]
|
||||
[(get-else $ ?p :block/type "N/A") ?type]
|
||||
[(not= ?type "journal")]
|
||||
[?block :block/refs ?ref-page]])]
|
||||
(d/q q db))))
|
||||
(if (config/db-based-graph?)
|
||||
(let [q (if with-journal?
|
||||
'[:find ?p ?ref-page
|
||||
:where
|
||||
[?block :block/page ?p]
|
||||
[?block :block/refs ?ref-page]]
|
||||
'[:find ?p ?ref-page
|
||||
:where
|
||||
[?block :block/page ?p]
|
||||
[?p :block/tags]
|
||||
(not [?p :block/tags :logseq.class/Journal])
|
||||
[?block :block/refs ?ref-page]])]
|
||||
(d/q q db))
|
||||
(let [q (if with-journal?
|
||||
'[:find ?p ?ref-page
|
||||
:where
|
||||
[?block :block/page ?p]
|
||||
[?block :block/refs ?ref-page]]
|
||||
'[:find ?p ?ref-page
|
||||
:where
|
||||
[?block :block/page ?p]
|
||||
(not [?p :block/type "journal"])
|
||||
[?block :block/refs ?ref-page]])]
|
||||
(d/q q db)))))
|
||||
|
||||
(defn get-namespace-pages
|
||||
"Accepts both sanitized and unsanitized namespaces"
|
||||
|
||||
@@ -323,7 +323,7 @@
|
||||
(or (some->> (name property-name)
|
||||
(db-utils/q '[:find [(pull ?b [:db/ident]) ...]
|
||||
:in $ ?title
|
||||
:where [?b :block/type "property"] [?b :block/title ?title]])
|
||||
:where [?b :block/tags :logseq.class/Property] [?b :block/title ?title]])
|
||||
first
|
||||
:db/ident)
|
||||
;; Don't return nil as that incorrectly matches all properties
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
[frontend.handler.property.util :as pu]
|
||||
[dommy.core :as dom]
|
||||
[goog.object :as gobj]
|
||||
[promesa.core :as p]))
|
||||
[promesa.core :as p]
|
||||
[datascript.impl.entity :as de]))
|
||||
|
||||
;; Fns
|
||||
|
||||
@@ -188,6 +189,32 @@
|
||||
(-> (property-util/remove-built-in-properties format content)
|
||||
(drawer/remove-logbook))))
|
||||
|
||||
(defn block-unique-title
|
||||
"Multiple pages/objects may have the same `:block/title`.
|
||||
Notice: this doesn't prevent for pages/objects that have the same tag or created by different clients."
|
||||
[block]
|
||||
(let [block-e (cond
|
||||
(de/entity? block)
|
||||
block
|
||||
(uuid? (:block/uuid block))
|
||||
(db/entity [:block/uuid (:block/uuid block)])
|
||||
:else
|
||||
block)
|
||||
tags (remove (fn [t]
|
||||
(or (some-> (:block/raw-title block-e) (ldb/inline-tag? t))
|
||||
(ldb/private-tags (:db/ident t))))
|
||||
(map (fn [tag] (if (number? tag) (db/entity tag) tag)) (:block/tags block)))]
|
||||
(if (seq tags)
|
||||
(str (:block/title block)
|
||||
" "
|
||||
(string/join
|
||||
", "
|
||||
(keep (fn [tag]
|
||||
(when-let [title (:block/title tag)]
|
||||
(str "#" title)))
|
||||
tags)))
|
||||
(:block/title block))))
|
||||
|
||||
(defn edit-block!
|
||||
[block pos & {:keys [_container-id custom-content tail-len save-code-editor?]
|
||||
:or {tail-len 0
|
||||
|
||||
@@ -4,15 +4,16 @@
|
||||
[frontend.db :as db]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.handler.common.page :as page-common-handler]
|
||||
[frontend.handler.db-based.property :as db-property-handler]
|
||||
[frontend.handler.notification :as notification]
|
||||
[frontend.state :as state]
|
||||
[frontend.modules.outliner.ui :as ui-outliner-tx]
|
||||
[logseq.outliner.validate :as outliner-validate]
|
||||
[logseq.db.frontend.class :as db-class]
|
||||
[logseq.common.util :as common-util]
|
||||
[logseq.common.util.page-ref :as page-ref]
|
||||
[datascript.impl.entity :as de]
|
||||
[promesa.core :as p]))
|
||||
[promesa.core :as p]
|
||||
[logseq.db]))
|
||||
|
||||
(defn- valid-tag?
|
||||
"Returns a boolean indicating whether the new tag passes all valid checks.
|
||||
@@ -32,27 +33,23 @@
|
||||
(throw e)))))
|
||||
|
||||
(defn add-tag [repo block-id tag-entity]
|
||||
(ui-outliner-tx/transact!
|
||||
{:outliner-op :save-block}
|
||||
(p/do!
|
||||
(editor-handler/save-current-block!)
|
||||
;; Check after save-current-block to get most up to date block content
|
||||
(when (valid-tag? repo (db/entity repo [:block/uuid block-id]) tag-entity)
|
||||
(let [tx-data [[:db/add [:block/uuid block-id] :block/tags (:db/id tag-entity)]
|
||||
;; TODO: Move this to outliner.core to consistently add refs for tags
|
||||
[:db/add [:block/uuid block-id] :block/refs (:db/id tag-entity)]]]
|
||||
(db/transact! repo tx-data {:outliner-op :save-block}))))))
|
||||
(p/do!
|
||||
(editor-handler/save-current-block!)
|
||||
;; Check after save-current-block to get most up to date block content
|
||||
(when (valid-tag? repo (db/entity repo [:block/uuid block-id]) tag-entity)
|
||||
(db-property-handler/set-block-property! block-id :block/tags (:db/id tag-entity)))))
|
||||
|
||||
(defn convert-to-tag!
|
||||
[page-entity]
|
||||
(if (db/page-exists? (:block/title page-entity) "class")
|
||||
(if (db/page-exists? (:block/title page-entity) #{:logseq.class/Tag})
|
||||
(notification/show! (str "A tag with the name \"" (:block/title page-entity) "\" already exists.") :warning false)
|
||||
(let [class (db-class/build-new-class (db/get-db)
|
||||
{:db/id (:db/id page-entity)
|
||||
:block/title (:block/title page-entity)
|
||||
:block/created-at (:block/created-at page-entity)})]
|
||||
(let [txs [(db-class/build-new-class (db/get-db)
|
||||
{:db/id (:db/id page-entity)
|
||||
:block/title (:block/title page-entity)
|
||||
:block/created-at (:block/created-at page-entity)})
|
||||
[:db/retract (:db/id page-entity) :block/tags :logseq.class/Page]]]
|
||||
|
||||
(db/transact! (state/get-current-repo) [class] {:outliner-op :save-block}))))
|
||||
(db/transact! (state/get-current-repo) txs {:outliner-op :save-block}))))
|
||||
|
||||
(defn <create-class!
|
||||
"Creates a class page and provides class-specific error handling"
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
[frontend.storage :as storage]
|
||||
[logseq.graph-parser.db :as gp-db]
|
||||
[logseq.db.sqlite.create-graph :as sqlite-create-graph]
|
||||
[logseq.db :as ldb]
|
||||
[frontend.components.title :as title]))
|
||||
[logseq.db :as ldb]))
|
||||
|
||||
(defn- build-links
|
||||
[links]
|
||||
@@ -45,7 +44,7 @@
|
||||
size (int (* 8 (max 1.0 (js/Math.cbrt n))))]
|
||||
(cond->
|
||||
{:id (str (:db/id p))
|
||||
:label (title/block-unique-title p)
|
||||
:label page-title
|
||||
:size size
|
||||
:color color
|
||||
:block/created-at (:block/created-at p)}
|
||||
@@ -72,8 +71,8 @@
|
||||
(defn- normalize-page-name
|
||||
[{:keys [nodes links]}]
|
||||
(let [nodes' (->> (remove-uuids-and-files! nodes)
|
||||
(util/distinct-by (fn [node] (:id node)))
|
||||
(remove nil?))]
|
||||
(util/distinct-by (fn [node] (:id node)))
|
||||
(remove nil?))]
|
||||
{:nodes nodes'
|
||||
:links links}))
|
||||
|
||||
@@ -207,15 +206,15 @@
|
||||
(let [search-nodes (fn [forward?]
|
||||
(let [links (group-by (if forward? :source :target) links)]
|
||||
(loop [nodes nodes
|
||||
level level]
|
||||
(if (zero? level)
|
||||
nodes
|
||||
(recur (distinct (apply concat nodes
|
||||
(map
|
||||
(fn [id]
|
||||
(->> (get links id) (map (if forward? :target :source))))
|
||||
nodes)))
|
||||
(dec level))))))
|
||||
level level]
|
||||
(if (zero? level)
|
||||
nodes
|
||||
(recur (distinct (apply concat nodes
|
||||
(map
|
||||
(fn [id]
|
||||
(->> (get links id) (map (if forward? :target :source))))
|
||||
nodes)))
|
||||
(dec level))))))
|
||||
nodes (concat (search-nodes true) (search-nodes false))
|
||||
nodes (set nodes)]
|
||||
(update graph :nodes
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
(when (and page (state/enable-journals? (state/get-current-repo)))
|
||||
(p/do!
|
||||
(db-async/<get-block (state/get-current-repo) page :children? false)
|
||||
(if (db-model/page-exists? page "journal")
|
||||
(if (db-model/page-exists? page #{:logseq.class/Journal})
|
||||
(route-handler/redirect! {:to :page
|
||||
:path-params {:name page}})
|
||||
(page-handler/<create! page)))))
|
||||
|
||||
@@ -425,7 +425,6 @@
|
||||
(<create! title {:redirect? false
|
||||
:split-namespace? false
|
||||
:create-first-block? (not template)
|
||||
:journal? true
|
||||
:today-journal? true})
|
||||
(state/pub-event! [:journal/insert-template today-page])
|
||||
(ui-handler/re-render-root!)
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
{:db/id (:db/id page-entity)
|
||||
:block/title page-name
|
||||
:block/name (util/page-name-sanity-lc page-name)
|
||||
:block/type "whiteboard"
|
||||
:block/tags :logseq.class/Whiteboard
|
||||
:block/format :markdown
|
||||
:logseq.property/ls-type :whiteboard-page
|
||||
:logseq.property.tldraw/page tldraw-page
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
[frontend.db.transact]
|
||||
[frontend.db.conn]
|
||||
[logseq.outliner.op]
|
||||
[frontend.modules.outliner.op])))
|
||||
[frontend.modules.outliner.op]
|
||||
[logseq.db])))
|
||||
|
||||
(defmacro transact!
|
||||
[opts & body]
|
||||
|
||||
@@ -511,42 +511,53 @@
|
||||
empty-placeholder
|
||||
item-render
|
||||
class
|
||||
header]}]
|
||||
header
|
||||
grouped?]}]
|
||||
(let [*current-idx (get state ::current-idx)
|
||||
*groups (atom #{})]
|
||||
*groups (atom #{})
|
||||
render-f (fn [matched]
|
||||
(for [[idx item] (medley/indexed matched)]
|
||||
(let [react-key (str idx)
|
||||
item-cp
|
||||
[:div.menu-link-wrap
|
||||
{:key react-key
|
||||
;; mouse-move event to indicate that cursor moved by user
|
||||
:on-mouse-move #(reset! *current-idx idx)}
|
||||
(let [chosen? (= @*current-idx idx)]
|
||||
(menu-link
|
||||
{:id (str "ac-" react-key)
|
||||
:tab-index "0"
|
||||
:class (when chosen? "chosen")
|
||||
;; TODO: should have more tests on touch devices
|
||||
;:on-pointer-down #(util/stop %)
|
||||
:on-click (fn [e]
|
||||
(util/stop e)
|
||||
(when-not (:disabled? item)
|
||||
(if (and (gobj/get e "shiftKey") on-shift-chosen)
|
||||
(on-shift-chosen item)
|
||||
(on-chosen item e))))}
|
||||
(if item-render (item-render item chosen?) item)))]]
|
||||
|
||||
(let [group-name (and (fn? get-group-name) (get-group-name item))]
|
||||
(if (and group-name (not (contains? @*groups group-name)))
|
||||
(do
|
||||
(swap! *groups conj group-name)
|
||||
[:div
|
||||
[:div.ui__ac-group-name group-name]
|
||||
item-cp])
|
||||
item-cp)))))]
|
||||
[:div#ui__ac {:class class}
|
||||
(if (seq matched)
|
||||
[:div#ui__ac-inner.hide-scrollbar
|
||||
(when header header)
|
||||
(for [[idx item] (medley/indexed matched)]
|
||||
(let [react-key (str idx)
|
||||
item-cp
|
||||
[:div.menu-link-wrap
|
||||
{:key react-key
|
||||
;; mouse-move event to indicate that cursor moved by user
|
||||
:on-mouse-move #(reset! *current-idx idx)}
|
||||
(let [chosen? (= @*current-idx idx)]
|
||||
(menu-link
|
||||
{:id (str "ac-" react-key)
|
||||
:tab-index "0"
|
||||
:class (when chosen? "chosen")
|
||||
;; TODO: should have more tests on touch devices
|
||||
;:on-pointer-down #(util/stop %)
|
||||
:on-click (fn [e]
|
||||
(util/stop e)
|
||||
(if (and (gobj/get e "shiftKey") on-shift-chosen)
|
||||
(on-shift-chosen item)
|
||||
(on-chosen item e)))}
|
||||
(if item-render (item-render item chosen?) item)))]]
|
||||
|
||||
(let [group-name (and (fn? get-group-name) (get-group-name item))]
|
||||
(if (and group-name (not (contains? @*groups group-name)))
|
||||
(do
|
||||
(swap! *groups conj group-name)
|
||||
[:div
|
||||
[:div.ui__ac-group-name group-name]
|
||||
item-cp])
|
||||
item-cp))))]
|
||||
(if grouped?
|
||||
(for [[group matched] (group-by :group matched)]
|
||||
(if group
|
||||
[:div
|
||||
[:div.ui__ac-group-name group]
|
||||
(render-f matched)]
|
||||
(render-f matched)))
|
||||
(render-f matched))]
|
||||
(when empty-placeholder
|
||||
empty-placeholder))]))
|
||||
|
||||
@@ -593,28 +604,27 @@
|
||||
(rum/local false ::control?)
|
||||
[state {:keys [on-pointer-down header title-trigger? collapsed?]}]
|
||||
(let [control? (get state ::control?)]
|
||||
[:div.content
|
||||
[:div.ls-foldable-title.content
|
||||
[:div.flex-1.flex-row.foldable-title (cond->
|
||||
{:on-mouse-over #(reset! control? true)
|
||||
:on-mouse-out #(reset! control? false)}
|
||||
title-trigger?
|
||||
(assoc :on-pointer-down on-pointer-down
|
||||
:class "cursor"))
|
||||
[:div.flex.flex-row.items-center.ls-foldable-header
|
||||
[:div.flex.flex-row.items-center.ls-foldable-header.gap-1
|
||||
{:on-click (fn [^js e]
|
||||
(let [^js target (.-target e)]
|
||||
(when (some-> target (.closest ".as-toggle"))
|
||||
(reset! collapsed? (not @collapsed?)))))}
|
||||
(when-not (mobile-util/native-platform?)
|
||||
[:a.block-control.opacity-50.hover:opacity-100.mr-2
|
||||
(cond->
|
||||
{:style {:width 14
|
||||
:height 16
|
||||
:margin-left -30}}
|
||||
(not title-trigger?)
|
||||
(assoc :on-pointer-down on-pointer-down))
|
||||
[:span {:class (if (or @control? @collapsed?) "control-show cursor-pointer" "control-hide")}
|
||||
(rotating-arrow @collapsed?)]])
|
||||
(let [style {:width 14 :height 16}]
|
||||
[:a.ls-foldable-title-control.block-control.opacity-50.hover:opacity-100
|
||||
(cond->
|
||||
{:style style}
|
||||
(not title-trigger?)
|
||||
(assoc :on-pointer-down on-pointer-down))
|
||||
[:span {:class (if (or @control? @collapsed?) "control-show cursor-pointer" "control-hide")}
|
||||
(rotating-arrow @collapsed?)]]))
|
||||
(if (fn? header)
|
||||
(header @collapsed?)
|
||||
header)]]]))
|
||||
|
||||
@@ -329,4 +329,8 @@ input[type='range'] {
|
||||
.as-toggle {
|
||||
@apply opacity-60 cursor-pointer select-none active:opacity-50;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ls-foldable-title-control {
|
||||
margin-left: -27px;
|
||||
}
|
||||
|
||||
@@ -417,6 +417,34 @@
|
||||
:block/title title'})))))
|
||||
datoms)))
|
||||
|
||||
(defn- replace-block-type-with-tags
|
||||
[conn _search-db]
|
||||
(let [db @conn
|
||||
block-type-entity (d/entity db :block/type)
|
||||
;; Not using (d/datoms db :avet :block/type) here because some old graphs
|
||||
;; don't have :block/type indexed
|
||||
datoms (->> (d/datoms db :eavt)
|
||||
(filter (fn [d] (= :block/type (:a d)))))
|
||||
journal-entity (d/entity db :logseq.class/Journal)
|
||||
tx-data (mapcat (fn [{:keys [e _a v]}]
|
||||
(let [tag (case v
|
||||
"page" :logseq.class/Page
|
||||
"class" :logseq.class/Tag
|
||||
"property" :logseq.class/Property
|
||||
"journal" :logseq.class/Journal
|
||||
"whiteboard" :logseq.class/Whiteboard
|
||||
"closed value" nil
|
||||
(throw (ex-info "unsupported block/type" {:type v})))]
|
||||
(cond->
|
||||
[[:db/retract e :block/type]]
|
||||
(some? tag)
|
||||
(conj [:db/add e :block/tags tag])))) datoms)]
|
||||
(concat
|
||||
;; set journal's tag to `#Page`
|
||||
[[:db/add (:db/id journal-entity) :block/tags :logseq.class/Page]]
|
||||
tx-data
|
||||
[[:db/retractEntity (:db/id block-type-entity)]])))
|
||||
|
||||
(defn- deprecate-logseq-user-ns
|
||||
[conn _search-db]
|
||||
(let [db @conn]
|
||||
@@ -514,9 +542,10 @@
|
||||
[47 {:fix replace-hidden-type-with-schema}]
|
||||
[48 {:properties [:logseq.property/default-value :logseq.property/scalar-default-value]}]
|
||||
[49 {:fix replace-special-id-ref-with-id-ref}]
|
||||
[50 {:properties [:logseq.property.user/name :logseq.property.user/email :logseq.property.user/avatar]}]
|
||||
[51 {:properties [:logseq.property.user/name :logseq.property.user/email :logseq.property.user/avatar]
|
||||
:fix deprecate-logseq-user-ns}]])
|
||||
[50 {:properties [:logseq.property.user/name :logseq.property.user/email :logseq.property.user/avatar]
|
||||
:fix deprecate-logseq-user-ns}]
|
||||
[51 {:classes [:logseq.class/Property :logseq.class/Tag :logseq.class/Page :logseq.class/Whiteboard]}]
|
||||
[52 {:fix replace-block-type-with-tags}]])
|
||||
|
||||
(let [max-schema-version (apply max (map first schema-version->updates))]
|
||||
(assert (<= db-schema/version max-schema-version))
|
||||
@@ -534,17 +563,20 @@
|
||||
(into {})
|
||||
sqlite-create-graph/build-initial-properties*
|
||||
(map (fn [b] (assoc b :logseq.property/built-in? true))))
|
||||
new-classes (->> (select-keys db-class/built-in-classes classes)
|
||||
;; class already exists, this should never happen
|
||||
(remove (fn [[k _]]
|
||||
(when (d/entity db k)
|
||||
(assert (str "DB migration: class already exists " k)))))
|
||||
classes' (->> (concat [:logseq.class/Property :logseq.class/Tag :logseq.class/Page :logseq.class/Journal :logseq.class/Whiteboard] classes)
|
||||
distinct)
|
||||
new-classes (->> (select-keys db-class/built-in-classes classes')
|
||||
;; class already exists, this should never happen
|
||||
(remove (fn [[k _]] (d/entity db k)))
|
||||
(into {})
|
||||
(#(sqlite-create-graph/build-initial-classes* % (zipmap properties properties)))
|
||||
(map (fn [b] (assoc b :logseq.property/built-in? true))))
|
||||
new-class-idents (keep (fn [class]
|
||||
(when-let [db-ident (:db/ident class)]
|
||||
{:db/ident db-ident})) new-classes)
|
||||
fixes (when (fn? fix)
|
||||
(fix conn search-db))
|
||||
tx-data (if db-based? (concat new-properties new-classes fixes) fixes)
|
||||
tx-data (if db-based? (concat new-class-idents new-properties new-classes fixes) fixes)
|
||||
tx-data' (concat
|
||||
[(sqlite-util/kv :logseq.kv/schema-version version)]
|
||||
tx-data)]
|
||||
@@ -555,30 +587,31 @@
|
||||
"Migrate 'frontend' datascript schema and data. To add a new migration,
|
||||
add an entry to schema-version->updates and bump db-schema/version"
|
||||
[conn search-db]
|
||||
(let [db @conn
|
||||
version-in-db (or (:kv/value (d/entity db :logseq.kv/schema-version)) 0)]
|
||||
(cond
|
||||
(= version-in-db db-schema/version)
|
||||
nil
|
||||
(when (ldb/db-based-graph? @conn)
|
||||
(let [db @conn
|
||||
version-in-db (or (:kv/value (d/entity db :logseq.kv/schema-version)) 0)]
|
||||
(cond
|
||||
(= version-in-db db-schema/version)
|
||||
nil
|
||||
|
||||
(< db-schema/version version-in-db) ; outdated client, db version could be synced from server
|
||||
(< db-schema/version version-in-db) ; outdated client, db version could be synced from server
|
||||
;; FIXME: notify users to upgrade to the latest version asap
|
||||
nil
|
||||
nil
|
||||
|
||||
(> db-schema/version version-in-db)
|
||||
(try
|
||||
(let [db-based? (ldb/db-based-graph? @conn)
|
||||
updates (keep (fn [[v updates]]
|
||||
(when (and (< version-in-db v) (<= v db-schema/version))
|
||||
[v updates]))
|
||||
schema-version->updates)]
|
||||
(println "DB schema migrated from" version-in-db)
|
||||
(doseq [[v m] updates]
|
||||
(upgrade-version! conn search-db db-based? v m)))
|
||||
(catch :default e
|
||||
(prn :error (str "DB migration failed to migrate to " db-schema/version " from " version-in-db ":"))
|
||||
(js/console.error e)
|
||||
(throw e))))))
|
||||
(> db-schema/version version-in-db)
|
||||
(try
|
||||
(let [db-based? (ldb/db-based-graph? @conn)
|
||||
updates (keep (fn [[v updates]]
|
||||
(when (and (< version-in-db v) (<= v db-schema/version))
|
||||
[v updates]))
|
||||
schema-version->updates)]
|
||||
(println "DB schema migrated from" version-in-db)
|
||||
(doseq [[v m] updates]
|
||||
(upgrade-version! conn search-db db-based? v m)))
|
||||
(catch :default e
|
||||
(prn :error (str "DB migration failed to migrate to " db-schema/version " from " version-in-db ":"))
|
||||
(js/console.error e)
|
||||
(throw e)))))))
|
||||
|
||||
;; Backend migrations
|
||||
;; ==================
|
||||
|
||||
@@ -318,7 +318,7 @@
|
||||
|
||||
(when-not db-based?
|
||||
(try
|
||||
(when-not (ldb/page-exists? @conn common-config/views-page-name "page")
|
||||
(when-not (ldb/page-exists? @conn common-config/views-page-name #{:logseq.class/Page})
|
||||
(ldb/transact! conn (sqlite-create-graph/build-initial-views)))
|
||||
(catch :default _e)))
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
[logseq.graph-parser.property :as gp-property]
|
||||
[logseq.outliner.tree :as otree]
|
||||
[cljs-bean.core :as bean]
|
||||
[logseq.db.sqlite.util :as sqlite-util]
|
||||
[clojure.string :as string]))
|
||||
[logseq.db.sqlite.util :as sqlite-util]))
|
||||
|
||||
(defn- safe-keywordize
|
||||
[block]
|
||||
@@ -64,23 +63,17 @@
|
||||
(:keys result)))))
|
||||
(group-by first)
|
||||
(mapcat (fn [[_id col]]
|
||||
(let [type (some (fn [[_e a v _t]]
|
||||
(when (= a :block/type)
|
||||
v)) col)
|
||||
ident (some (fn [[_e a v _t]]
|
||||
(let [ident (some (fn [[_e a v _t]]
|
||||
(when (= a :db/ident)
|
||||
v)) col)]
|
||||
v)) col)
|
||||
journal (some (fn [[_e a v _t]]
|
||||
(when (= a :block/journal-day)
|
||||
v)) col)]
|
||||
(map
|
||||
(fn [[e a v t]]
|
||||
(cond
|
||||
(and (contains? #{:block/title :block/name} a)
|
||||
(or
|
||||
;; normal page or block
|
||||
(not (contains? #{"class" "property" "journal" "closed value"} type))
|
||||
;; class/property created by user
|
||||
(and ident
|
||||
(contains? #{"class" "property"} type)
|
||||
(not (string/starts-with? (namespace ident) "logseq")))))
|
||||
(not (or ident journal)))
|
||||
[e a (str "debug " e) t]
|
||||
|
||||
(= a :block/uuid)
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
|
||||
* :create-first-block? - when true, create an empty block if the page is empty.
|
||||
* :uuid - when set, use this uuid instead of generating a new one.
|
||||
* :class? - when true, adds a :block/type 'class'
|
||||
* :whiteboard? - when true, adds a :block/type 'whiteboard'
|
||||
* :class? - when true, adds a :block/tags ':logseq.class/Tag'
|
||||
* :whiteboard? - when true, adds a :block/tags ':logseq.class/Whiteboard'
|
||||
* :tags - tag uuids that are added to :block/tags
|
||||
* :persist-op? - when true, add an update-page op
|
||||
* :properties - properties to add to the page
|
||||
|
||||
@@ -13,30 +13,30 @@
|
||||
[logseq.db.sqlite.util :as sqlite-util]
|
||||
[logseq.graph-parser.block :as gp-block]
|
||||
[logseq.graph-parser.text :as text]
|
||||
[logseq.outliner.validate :as outliner-validate]))
|
||||
[logseq.outliner.validate :as outliner-validate]
|
||||
[logseq.db.frontend.entity-util :as entity-util]
|
||||
[logseq.db.frontend.malli-schema :as db-malli-schema]))
|
||||
|
||||
(defn- build-page-tx [conn properties page {:keys [whiteboard? class? tags]}]
|
||||
(when (:block/uuid page)
|
||||
(let [page (assoc page :block/type (cond class? "class"
|
||||
whiteboard? "whiteboard"
|
||||
(:block/type page) (:block/type page)
|
||||
:else "page"))
|
||||
page' (cond-> page
|
||||
(seq tags)
|
||||
(update :block/tags
|
||||
(fnil into [])
|
||||
(mapv (fn [tag]
|
||||
(let [v (if (uuid? tag)
|
||||
(d/entity @conn [:block/uuid tag])
|
||||
tag)]
|
||||
(cond
|
||||
(de/entity? v)
|
||||
(:db/id v)
|
||||
(map? v)
|
||||
(:db/id v)
|
||||
:else
|
||||
v)))
|
||||
tags)))
|
||||
(let [type-tag (cond class? :logseq.class/Tag
|
||||
whiteboard? :logseq.class/Whiteboard
|
||||
:else :logseq.class/Page)
|
||||
tags' (if (:block/journal-day page) tags (conj tags type-tag))
|
||||
page' (update page :block/tags
|
||||
(fnil into [])
|
||||
(mapv (fn [tag]
|
||||
(let [v (if (uuid? tag)
|
||||
(d/entity @conn [:block/uuid tag])
|
||||
tag)]
|
||||
(cond
|
||||
(de/entity? v)
|
||||
(:db/id v)
|
||||
(map? v)
|
||||
(:db/id v)
|
||||
:else
|
||||
v)))
|
||||
tags'))
|
||||
property-vals-tx-m
|
||||
;; Builds property values for built-in properties like logseq.property.pdf/file
|
||||
(db-property-build/build-property-values-tx-m
|
||||
@@ -95,10 +95,12 @@
|
||||
|
||||
(defn- split-namespace-pages
|
||||
[db page date-formatter]
|
||||
(let [{:block/keys [title] block-uuid :block/uuid block-type :block/type} page]
|
||||
(let [{:block/keys [title] block-uuid :block/uuid} page]
|
||||
(->>
|
||||
(if (and (contains? #{"page" "class"} block-type) (ns-util/namespace-page? title))
|
||||
(let [class? (= block-type "class")
|
||||
(if (and (or (entity-util/class? page)
|
||||
(entity-util/page? page))
|
||||
(ns-util/namespace-page? title))
|
||||
(let [class? (entity-util/class? page)
|
||||
parts (->> (string/split title ns-util/parent-re)
|
||||
(map string/trim)
|
||||
(remove string/blank?))
|
||||
@@ -168,15 +170,24 @@
|
||||
(let [db @conn
|
||||
date-formatter (:logseq.property.journal/title-format (d/entity db :logseq.class/Journal))
|
||||
title (sanitize-title title*)
|
||||
type (cond class?
|
||||
"class"
|
||||
whiteboard?
|
||||
"whiteboard"
|
||||
today-journal?
|
||||
"journal"
|
||||
:else
|
||||
"page")]
|
||||
(when-not (ldb/page-exists? db title type)
|
||||
types (cond class?
|
||||
#{:logseq.class/Tag}
|
||||
whiteboard?
|
||||
#{:logseq.class/Whiteboard}
|
||||
today-journal?
|
||||
#{:logseq.class/Journal}
|
||||
:else
|
||||
#{:logseq.class/Page})]
|
||||
(if-let [existing-page-id (first (ldb/page-exists? db title types))]
|
||||
(let [existing-page (d/entity db existing-page-id)
|
||||
tx-meta {:persist-op? persist-op?
|
||||
:outliner-op :save-block}]
|
||||
(when (and class?
|
||||
(not (ldb/class? existing-page))
|
||||
(or (ldb/property? existing-page) (ldb/internal-page? existing-page)))
|
||||
;; Convert existing user property or page to class
|
||||
(let [tx-data (db-class/build-new-class db (select-keys existing-page [:block/title :block/uuid :db/ident :block/created-at]))]
|
||||
(ldb/transact! conn tx-data tx-meta))))
|
||||
(let [format :markdown
|
||||
page (-> (gp-block/page-name->map title @conn true date-formatter
|
||||
{:class? class?
|
||||
@@ -189,9 +200,12 @@
|
||||
(let [pages (split-namespace-pages db page date-formatter)]
|
||||
[(last pages) (butlast pages)])
|
||||
[page nil])]
|
||||
(when page
|
||||
(when (and page (or (nil? (:db/ident page))
|
||||
;; New page creation must not override built-in entities
|
||||
(not (db-malli-schema/internal-ident? (:db/ident page)))))
|
||||
;; Don't validate journal names because they can have '/'
|
||||
(when (not= "journal" type)
|
||||
(when-not (or (contains? types :logseq.class/Journal)
|
||||
(contains? (set (:block/tags page)) :logseq.class/Journal))
|
||||
(outliner-validate/validate-page-title-characters (str (:block/title page)) {:node page})
|
||||
(doseq [parent parents]
|
||||
(outliner-validate/validate-page-title-characters (str (:block/title parent)) {:node parent})))
|
||||
@@ -205,7 +219,8 @@
|
||||
page-txs)
|
||||
(build-first-block-tx (:block/uuid (first page-txs)) format))
|
||||
txs (concat
|
||||
parents
|
||||
;; transact doesn't support entities
|
||||
(remove de/entity? parents)
|
||||
page-txs
|
||||
first-block-tx)]
|
||||
(when (seq txs)
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
(def ^:private watched-attrs
|
||||
#{:block/title :block/created-at :block/updated-at :block/alias
|
||||
:block/tags :block/type :block/schema :block/link :block/journal-day
|
||||
:block/tags :block/schema :block/link :block/journal-day
|
||||
:property/schema.classes :property.value/content
|
||||
:db/index :db/valueType :db/cardinality})
|
||||
|
||||
|
||||
@@ -366,7 +366,6 @@
|
||||
:block/updated-at
|
||||
:block/created-at
|
||||
:block/alias
|
||||
:block/type
|
||||
:block/schema
|
||||
:block/tags
|
||||
:block/link
|
||||
|
||||
@@ -332,13 +332,6 @@ DROP TRIGGER IF EXISTS blocks_au;
|
||||
(drop-tables-and-triggers! db)
|
||||
(create-tables-and-triggers! db))
|
||||
|
||||
(comment
|
||||
(defn- property-value-when-closed
|
||||
"Returns property value if the given entity is type 'closed value' or nil"
|
||||
[ent]
|
||||
(when (= (:block/type ent) "closed value")
|
||||
(:block/title ent))))
|
||||
|
||||
(comment
|
||||
(defn- get-db-properties-str
|
||||
"Similar to db-pu/readable-properties but with a focus on making property values searchable"
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
[frontend.db :as db]
|
||||
[frontend.test.helper :as test-helper]
|
||||
[datascript.core :as d]
|
||||
[logseq.outliner.property :as outliner-property]
|
||||
[logseq.db.frontend.class :as db-class]))
|
||||
[logseq.db.frontend.class :as db-class]
|
||||
[logseq.db :as ldb]
|
||||
[logseq.db.test.helper :as db-test]
|
||||
[frontend.db.conn :as conn]))
|
||||
|
||||
(def repo test-helper/test-db-name-db-version)
|
||||
|
||||
@@ -27,7 +29,9 @@
|
||||
_ (test-helper/create-page! "class2" opts)]
|
||||
(is (= (set
|
||||
(concat
|
||||
(map :title (vals db-class/built-in-classes))
|
||||
(map :title (vals (remove (fn [[ident _]]
|
||||
(contains? ldb/private-tags ident))
|
||||
db-class/built-in-classes)))
|
||||
["class1" "class2"]))
|
||||
(set (map :block/title (model/get-all-classes repo)))))))
|
||||
|
||||
@@ -51,19 +55,16 @@
|
||||
(:db/id (db/entity [:block/uuid sbid]))])))))
|
||||
|
||||
(deftest get-classes-with-property-test
|
||||
(let [opts {:redirect? false :create-first-block? false :class? true}
|
||||
_ (test-helper/create-page! "class1" opts)
|
||||
_ (test-helper/create-page! "class2" opts)
|
||||
class1 (db/get-case-page "class1")
|
||||
class2 (db/get-case-page "class2")
|
||||
conn (db/get-db false)]
|
||||
(outliner-property/upsert-property! conn :user.property/property-1 {:type :node} {})
|
||||
(outliner-property/class-add-property! conn (:db/id class1) :user.property/property-1)
|
||||
(outliner-property/class-add-property! conn (:db/id class2) :user.property/property-1)
|
||||
(let [property (db/entity :user.property/property-1)
|
||||
classes (model/get-classes-with-property (:db/ident property))]
|
||||
(is (= (set (map :db/id classes))
|
||||
#{(:db/id class1) (:db/id class2)})))))
|
||||
(let [conn (db-test/create-conn-with-blocks
|
||||
{:properties {:prop1 {:block/schema {:type :default}}}
|
||||
:classes
|
||||
{:Class1 {:build/schema-properties [:prop1]}
|
||||
:Class2 {:build/schema-properties [:prop1]}}})
|
||||
property (d/entity @conn :user.property/prop1)
|
||||
classes (with-redefs [conn/get-db (constantly @conn)]
|
||||
(model/get-classes-with-property (:db/ident property)))]
|
||||
(is (= ["Class1" "Class2"]
|
||||
(map :block/title classes)))))
|
||||
|
||||
(deftest hidden-page-test
|
||||
(let [opts {:redirect? false :create-first-block? false}
|
||||
|
||||
@@ -9,12 +9,8 @@
|
||||
(let [conn (db-test/create-conn)
|
||||
_ (worker-db-page/create! conn "movie" {:class? true})
|
||||
_ (worker-db-page/create! conn "Movie" {:class? true})
|
||||
movie-class (->> (d/q '[:find [(pull ?b [*]) ...] :in $ ?title :where [?b :block/title ?title]]
|
||||
@conn "movie")
|
||||
first)
|
||||
Movie-class (->> (d/q '[:find [(pull ?b [*]) ...] :in $ ?title :where [?b :block/title ?title]]
|
||||
@conn "Movie")
|
||||
first)]
|
||||
movie-class (ldb/get-case-page @conn "movie")
|
||||
Movie-class (ldb/get-case-page @conn "Movie")]
|
||||
|
||||
(is (ldb/class? movie-class) "Creates a class")
|
||||
(is (ldb/class? Movie-class) "Creates another class with a different case sensitive name")
|
||||
@@ -44,9 +40,12 @@
|
||||
"Child class with new parent has correct parents")
|
||||
|
||||
(worker-db-page/create! conn "foo/class1/baz3" {:split-namespace? true})
|
||||
(is (= #{"class" "page"}
|
||||
(set (d/q '[:find [?type ...]
|
||||
:where [?b :block/type ?type] [?b :block/title "class1"]] @conn)))
|
||||
(is (= #{"Tag" "Page"}
|
||||
(set (d/q '[:find [?tag-title ...]
|
||||
:where
|
||||
[?b :block/title "class1"]
|
||||
[?b :block/tags ?t]
|
||||
[?t :block/title ?tag-title]] @conn)))
|
||||
"Using an existing class page in a multi-parent namespace doesn't allow a page to have a class parent and instead creates a new page")))
|
||||
|
||||
(testing "Child pages with same name and different parents"
|
||||
@@ -79,10 +78,23 @@
|
||||
(let [conn (db-test/create-conn)
|
||||
[_ page-uuid] (worker-db-page/create! conn "fooz" {})]
|
||||
(is (= "fooz" (:block/title (d/entity @conn [:block/uuid page-uuid])))
|
||||
"Valid page created")
|
||||
"Page created correctly")
|
||||
|
||||
(is (thrown-with-msg?
|
||||
js/Error
|
||||
#"can't include \"/"
|
||||
(worker-db-page/create! conn "foo/bar" {}))
|
||||
"Page can't have '/'n title")))
|
||||
"Page can't have '/'n title")))
|
||||
|
||||
(deftest create-journal
|
||||
(let [conn (db-test/create-conn)
|
||||
[_ page-uuid] (worker-db-page/create! conn "Dec 16th, 2024" {})]
|
||||
|
||||
(is (= "Dec 16th, 2024" (:block/title (d/entity @conn [:block/uuid page-uuid])))
|
||||
"Journal created correctly")
|
||||
|
||||
(is (= [:logseq.class/Journal]
|
||||
(->> (d/entity @conn [:block/uuid page-uuid])
|
||||
:block/tags
|
||||
(map #(:db/ident (d/entity @conn (:db/id %))))))
|
||||
"New journal only has Journal tag")))
|
||||
@@ -9,16 +9,14 @@
|
||||
[frontend.worker.rtc.db-listener :as subject]
|
||||
[frontend.worker.rtc.fixture :as r.fixture]
|
||||
[frontend.worker.state :as worker-state]
|
||||
[logseq.db.frontend.schema :as db-schema]
|
||||
[logseq.outliner.batch-tx :as batch-tx]
|
||||
[logseq.outliner.core :as outliner-core]))
|
||||
[logseq.outliner.core :as outliner-core]
|
||||
[logseq.db.test.helper :as db-test]))
|
||||
|
||||
(t/use-fixtures :each
|
||||
test-helper/db-based-start-and-destroy-db-map-fixture
|
||||
r.fixture/listen-test-db-to-gen-rtc-ops-fixture)
|
||||
|
||||
(def empty-db (d/empty-db db-schema/schema-for-db-based-graph))
|
||||
|
||||
(defn- tx-data=>e->a->add?->v->t
|
||||
[tx-data]
|
||||
(let [datom-vec-coll (map vec tx-data)
|
||||
@@ -27,11 +25,11 @@
|
||||
|
||||
(deftest entity-datoms=>ops-test
|
||||
(testing "remove whiteboard page-block"
|
||||
(let [conn (d/conn-from-db empty-db)
|
||||
(let [conn (db-test/create-conn)
|
||||
block-uuid (random-uuid)
|
||||
_create-whiteboard-page-block
|
||||
(d/transact! conn [{:block/uuid block-uuid
|
||||
:block/type "whiteboard"
|
||||
:block/tags :logseq.class/Whiteboard
|
||||
:block/name "block-name"
|
||||
:block/title "BLOCK-NAME"}])
|
||||
remove-whiteboard-page-block
|
||||
@@ -44,20 +42,20 @@
|
||||
(map (fn [[op-type _t op-value]] [op-type op-value]) r)))))
|
||||
|
||||
(testing "update-schema op"
|
||||
(let [conn (d/conn-from-db empty-db)
|
||||
tx-data [[:db/add 69 :db/index true]
|
||||
[:db/add 69 :block/uuid #uuid "66558abf-6512-469d-9e83-8f1ba0be9305"]
|
||||
[:db/add 69 :db/valueType :db.type/ref]
|
||||
[:db/add 69 :block/updated-at 1716882111476]
|
||||
[:db/add 69 :block/created-at 1716882111476]
|
||||
[:db/add 69 :block/schema {:type :number}]
|
||||
[:db/add 69 :block/format :markdown]
|
||||
[:db/add 69 :db/cardinality :db.cardinality/one]
|
||||
[:db/add 69 :db/ident :user.property/qqq]
|
||||
[:db/add 69 :block/type "property"]
|
||||
[:db/add 69 :block/order "b0T"]
|
||||
[:db/add 69 :block/name "qqq"]
|
||||
[:db/add 69 :block/title "qqq"]]
|
||||
(let [conn (db-test/create-conn)
|
||||
tx-data [[:db/add 1000000 :db/index true]
|
||||
[:db/add 1000000 :block/uuid #uuid "66558abf-6512-469d-9e83-8f1ba0be9305"]
|
||||
[:db/add 1000000 :db/valueType :db.type/ref]
|
||||
[:db/add 1000000 :block/updated-at 1716882111476]
|
||||
[:db/add 1000000 :block/created-at 1716882111476]
|
||||
[:db/add 1000000 :block/schema {:type :number}]
|
||||
[:db/add 1000000 :block/format :markdown]
|
||||
[:db/add 1000000 :db/cardinality :db.cardinality/one]
|
||||
[:db/add 1000000 :db/ident :user.property/qqq]
|
||||
[:db/add 1000000 :block/tags :logseq.class/Property]
|
||||
[:db/add 1000000 :block/order "b0T"]
|
||||
[:db/add 1000000 :block/name "qqq"]
|
||||
[:db/add 1000000 :block/title "qqq"]]
|
||||
{:keys [db-before db-after tx-data]} (d/transact! conn tx-data)
|
||||
ops (#'subject/entity-datoms=>ops db-before db-after
|
||||
(tx-data=>e->a->add?->v->t tx-data)
|
||||
@@ -72,10 +70,11 @@
|
||||
[:block/updated-at "[\"~#'\",1716882111476]"]
|
||||
[:block/created-at "[\"~#'\",1716882111476]"]
|
||||
[:block/schema "[\"^ \",\"~:type\",\"~:number\"]"]
|
||||
[:block/tags #uuid "00000002-1038-7670-4800-000000000000"]
|
||||
[:block/title "[\"~#'\",\"qqq\"]"]
|
||||
[:db/cardinality "[\"~#'\",\"~:db.cardinality/one\"]"]
|
||||
;; [:db/ident "[\"~#'\",\"~:user.property/qqq\"]"]
|
||||
[:block/type "[\"~#'\",\"property\"]"]]}]]
|
||||
]}]]
|
||||
(map (fn [[op-type _t op-value]]
|
||||
[op-type (cond-> op-value
|
||||
(:av-coll op-value)
|
||||
@@ -83,16 +82,16 @@
|
||||
ops)))))
|
||||
|
||||
(testing "create user-class"
|
||||
(let [conn (d/conn-from-db empty-db)
|
||||
tx-data [[:db/add 62 :block/uuid #uuid "66856a29-6eb3-4122-af97-8580a853c6a6" 536870954]
|
||||
[:db/add 62 :block/updated-at 1720019497643 536870954]
|
||||
[:db/add 62 :logseq.property/parent 4 536870954]
|
||||
[:db/add 62 :block/created-at 1720019497643 536870954]
|
||||
[:db/add 62 :block/format :markdown 536870954]
|
||||
[:db/add 62 :db/ident :user.class/zzz 536870954]
|
||||
[:db/add 62 :block/type "class" 536870954]
|
||||
[:db/add 62 :block/name "zzz" 536870954]
|
||||
[:db/add 62 :block/title "zzz" 536870954]]
|
||||
(let [conn (db-test/create-conn)
|
||||
tx-data [[:db/add 1000000 :block/uuid #uuid "66856a29-6eb3-4122-af97-8580a853c6a6" 536870954]
|
||||
[:db/add 1000000 :block/updated-at 1720019497643 536870954]
|
||||
[:db/add 1000000 :logseq.property/parent :logseq.class/Root 536870954]
|
||||
[:db/add 1000000 :block/created-at 1720019497643 536870954]
|
||||
[:db/add 1000000 :block/format :markdown 536870954]
|
||||
[:db/add 1000000 :db/ident :user.class/zzz 536870954]
|
||||
[:db/add 1000000 :block/tags :logseq.class/Tag 536870954]
|
||||
[:db/add 1000000 :block/name "zzz" 536870954]
|
||||
[:db/add 1000000 :block/title "zzz" 536870954]]
|
||||
{:keys [db-before db-after tx-data]} (d/transact! conn tx-data)
|
||||
ops (#'subject/entity-datoms=>ops db-before db-after
|
||||
(tx-data=>e->a->add?->v->t tx-data)
|
||||
@@ -103,9 +102,9 @@
|
||||
:av-coll
|
||||
[[:block/updated-at "[\"~#'\",1720019497643]"]
|
||||
[:block/created-at "[\"~#'\",1720019497643]"]
|
||||
[:block/tags #uuid "00000002-5389-0208-3000-000000000000"]
|
||||
[:block/title "[\"~#'\",\"zzz\"]"]
|
||||
[:block/type "[\"~#'\",\"class\"]"]
|
||||
[:logseq.property/parent "[\"~#'\",4]"]
|
||||
[:logseq.property/parent #uuid "00000002-2737-8382-7000-000000000000"]
|
||||
;;1. shouldn't have :db/ident, :db/ident is special, will be handled later
|
||||
]}]]
|
||||
(map (fn [[op-type _t op-value]]
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
(deftest remote-op-value->tx-data-test
|
||||
(let [[block-uuid ref-uuid1 ref-uuid2] (repeatedly random-uuid)
|
||||
db (d/db-with (d/empty-db db-schema/schema-for-db-based-graph)
|
||||
(sqlite-create-graph/build-db-initial-data ""))]
|
||||
(sqlite-create-graph/build-db-initial-data "{}" {}))]
|
||||
(testing ":block/title"
|
||||
(let [db (d/db-with db [{:block/uuid block-uuid
|
||||
:block/title "local-content"}])
|
||||
|
||||
Reference in New Issue
Block a user