Feature: tag multiple views (#11511)

Add tag multiple views support
pull/11514/head
Tienson Qin 2024-09-10 16:12:25 +08:00 committed by GitHub
parent ad910fa77a
commit 19f3f287fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 141 additions and 69 deletions

View File

@ -524,7 +524,7 @@
:block/parent [:block/uuid page-id]
:block/order (db-order/gen-key nil)
:block/page [:block/uuid page-id]
:logseq.property/view-for :all-pages})])))
:logseq.property/view-for [:block/uuid page-id]})])))
;; TODO: why not generate a UUID for all local graphs?
;; And prefer this local graph UUID when picking an ID for new rtc graph?
@ -588,8 +588,7 @@
[db]
(when (db-based-graph? db)
(when-let [page (get-page db common-config/views-page-name)]
(->> (:block/_parent page)
(filter (fn [b] (= :all-pages (:logseq.property/view-for b))))))))
(:logseq.property/_view-for page))))
(defn inline-tag?
[block-raw-title tag]

View File

@ -219,7 +219,7 @@
:hide? true
:public? false}}
:logseq.property/view-for {:schema
{:type :keyword
{:type :node
:hide? true
:public? false}}
:logseq.property.asset/remote-metadata {:schema

View File

@ -2,7 +2,7 @@
"Main datascript schemas for the Logseq app"
(:require [clojure.set :as set]))
(def version 19)
(def version 20)
;; A page is a special block, a page can corresponds to multiple files with the same ":block/name".
(def ^:large-vars/data-var schema
{:db/ident {:db/unique :db.unique/identity}

View File

@ -14,7 +14,11 @@
[rum.core :as rum]
[frontend.modules.outliner.ui :as ui-outliner-tx]
[frontend.modules.outliner.op :as outliner-op]
[frontend.db.react :as react]))
[frontend.db.react :as react]
[logseq.shui.ui :as shui]
[frontend.util :as util]
[frontend.ui :as ui]
[logseq.common.config :as common-config]))
(defn- get-class-objects
[class]
@ -33,15 +37,72 @@
(defn- get-views
[ent]
(let [class (db/entity (:db/id ent))]
(-> (filter (fn [b]
(= (:db/ident class) (:logseq.property/view-for b)))
(:block/_parent class))
(ldb/sort-by-order))))
(-> (:logseq.property/_view-for class)
(ldb/sort-by-order))))
(defn- create-view!
[class view-title views set-view-entity! set-views!]
(when-let [page (db/get-case-page common-config/views-page-name)]
(p/let [result (editor-handler/api-insert-new-block! view-title {:page (:block/uuid page)
:properties {:logseq.property/view-for (:db/id class)}})
view (db/entity [:block/uuid (:block/uuid result)])]
(set-view-entity! view)
(set-views! (concat views [view])))))
(rum/defc class-views < rum/reactive db-mixins/query
[class views current-view {:keys [set-view-entity! set-views!]}]
[:div.views.flex.flex-row.items-center.flex-wrap.gap-1
(for [view* views]
(let [view (db/sub-block (:db/id view*))
current-view? (= (:db/id current-view) (:db/id view))]
(shui/button
{:variant :ghost
:size :sm
:class (str "text-sm px-2 py-0 h-6 " (when-not current-view? "text-muted-foreground"))
:on-click (fn [e]
(if (and current-view? (not= (:db/id view) (:db/id class)))
(shui/popup-show!
(.-target e)
(fn []
[:<>
(shui/dropdown-menu-sub
(shui/dropdown-menu-sub-trigger
"Rename")
(shui/dropdown-menu-sub-content
(when-let [block-container (state/get-component :block/container)]
(block-container {} view))))
(shui/dropdown-menu-item
{:key "Delete"
:on-click (fn []
(p/do!
(editor-handler/delete-block-aux! view)
(let [views' (remove (fn [v] (= (:db/id v) (:db/id view))) views)]
(set-views! views')
(set-view-entity! (first views'))
(shui/popup-hide!))))}
"Delete")])
{:as-dropdown? true
:align "start"
:content-props {:onClick shui/popup-hide!}})
(set-view-entity! view)))}
(if (= (:db/id view) (:db/id class))
"All"
(let [title (:block/title view)]
(if (= title "")
"New view"
title))))))
(shui/button
{:variant :text
:size :sm
:class "!px-1 text-muted-foreground hover:text-foreground"
:on-click (fn [] (create-view! class "" views set-view-entity! set-views!))}
(ui/icon "plus" {}))])
(rum/defc class-objects-inner < rum/static
[config class objects properties]
(let [[loading? set-loading?] (rum/use-state nil)
[view-entity set-view-entity!] (rum/use-state class)
[views set-views!] (rum/use-state [class])
[data set-data!] (rum/use-state objects)
columns (views/build-columns config properties)]
@ -55,7 +116,10 @@
(when (nil? loading?)
(set-loading? true)
(p/let [_result (db-async/<get-views (state/get-current-repo) (:db/id class))
views (get-views class)]
views (get-views class)
views (->> (concat [class] views)
(util/distinct-by :db/id))]
(set-views! views)
(when-let [view (first views)]
(set-view-entity! view))
(p/let [_result (db-async/<get-tag-objects (state/get-current-repo) (:db/id class))]
@ -66,36 +130,35 @@
[])
(when (false? loading?)
(views/view view-entity {:data data
:set-data! set-data!
:title-key :views.table/tagged-nodes
:columns columns
:add-new-object! #(add-new-class-object! class set-data!)
:create-view! (fn []
(p/let [result (editor-handler/api-insert-new-block! "" {:page (:block/uuid class)
:properties {:logseq.property/view-for (:db/ident class)}})]
(let [view (db/entity [:block/uuid (:block/uuid result)])]
(set-view-entity! view)
view)))
: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 {})))))}))))
[:div.flex.flex-col.gap-2.mt-2
[:div.font-medium.opacity-50 "Tagged nodes"]
(views/view view-entity {: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! #(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]
@ -156,12 +219,6 @@
:title-key :views.table/property-nodes
:columns columns
:add-new-object! #(add-new-property-object! property set-data!)
:create-view! (fn []
(p/let [result (editor-handler/api-insert-new-block! "" {:page (:block/uuid property)
:properties {:logseq.property/view-for (:db/ident property)}})]
(let [view (db/entity [:block/uuid (:block/uuid result)])]
(set-view-entity! view)
view)))
;; TODO: Add support for adding column
:show-add-property? false
:on-delete-rows (fn [table selected-rows]

View File

@ -21,8 +21,7 @@
[logseq.db.frontend.property :as db-property]
[logseq.db.frontend.property.type :as db-property-type]
[logseq.shui.ui :as shui]
[rum.core :as rum]
[promesa.core :as p]))
[rum.core :as rum]))
(rum/defc header-checkbox < rum/static
[{:keys [selected-all? selected-some? toggle-selected-all!]}]
@ -892,33 +891,29 @@
filters))
(defn- db-set-table-state!
[entity {:keys [set-sorting! set-filters! set-visible-columns! set-ordered-columns! create-view!]}]
[entity {:keys [set-sorting! set-filters! set-visible-columns! set-ordered-columns!]}]
(let [repo (state/get-current-repo)]
{:set-sorting!
(fn [sorting]
(set-sorting! sorting)
(p/let [entity (or entity (create-view!))]
(property-handler/set-block-property! repo (:db/id entity) :logseq.property.table/sorting sorting)))
(property-handler/set-block-property! repo (:db/id entity) :logseq.property.table/sorting sorting))
:set-filters!
(fn [filters]
(let [filters (table-filters->persist-state filters)]
(set-filters! filters)
(p/let [entity (or entity (create-view!))]
(property-handler/set-block-property! repo (:db/id entity) :logseq.property.table/filters filters))))
(property-handler/set-block-property! repo (:db/id entity) :logseq.property.table/filters filters)))
:set-visible-columns!
(fn [columns]
(let [hidden-columns (vec (keep (fn [[column visible?]]
(when (false? visible?)
column)) columns))]
(set-visible-columns! columns)
(p/let [entity (or entity (create-view!))]
(property-handler/set-block-property! repo (:db/id entity) :logseq.property.table/hidden-columns hidden-columns))))
(property-handler/set-block-property! repo (:db/id entity) :logseq.property.table/hidden-columns hidden-columns)))
:set-ordered-columns!
(fn [ordered-columns]
(let [ids (vec (remove #{:select} ordered-columns))]
(set-ordered-columns! ordered-columns)
(p/let [entity (or entity (create-view!))]
(property-handler/set-block-property! repo (:db/id entity) :logseq.property.table/ordered-columns ids))))}))
(property-handler/set-block-property! repo (:db/id entity) :logseq.property.table/ordered-columns ids)))}))
(rum/defc table-view < rum/static
[table option row-selection add-new-object! ready?]
@ -963,7 +958,7 @@
:ref? true)))))
(rum/defc view-inner < rum/static
[view-entity {:keys [data set-data! columns add-new-object! create-view! title-key render-empty-title?] :as option
[view-entity {:keys [data set-data! columns add-new-object! views-title title-key render-empty-title?] :as option
:or {render-empty-title? false}}]
(let [[input set-input!] (rum/use-state "")
sorting (:logseq.property.table/sorting view-entity)
@ -978,8 +973,7 @@
(db-set-table-state! view-entity {:set-sorting! set-sorting!
:set-filters! set-filters!
:set-visible-columns! set-visible-columns!
:set-ordered-columns! set-ordered-columns!
:create-view! create-view!})
:set-ordered-columns! set-ordered-columns!})
row-filter-fn (fn []
(fn [row]
(row-matched? row input filters)))
@ -1020,9 +1014,11 @@
[:div.flex.flex-wrap.items-center.justify-between.gap-1
(when-not render-empty-title?
[:div.flex.flex-row.items-center.gap-2
[:div.font-medium.opacity-50.text-sm
(t (or title-key :views.table/default-title)
(count (:rows table)))]])
(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)
@ -1057,4 +1053,5 @@
(rum/defc view < rum/reactive
[view-entity option]
(let [view-entity' (db/sub-block (:db/id view-entity))]
(view-inner view-entity' option)))
(rum/with-key (view-inner view-entity' option)
(str "view-" (:db/id view-entity')))))

View File

@ -280,8 +280,7 @@
'[:find [(pull ?b [*]) ...]
:in $ ?class-id
:where
[?class-id :db/ident ?ident]
[?b :logseq.property/view-for ?ident]]
[?b :logseq.property/view-for ?class-id]]
class-id))
(defn <get-tags

View File

@ -8,7 +8,8 @@
[logseq.db.frontend.schema :as db-schema]
[frontend.worker.search :as search]
[cljs-bean.core :as bean]
[logseq.db.sqlite.util :as sqlite-util]))
[logseq.db.sqlite.util :as sqlite-util]
[logseq.common.config :as common-config]))
;; TODO: fixes/rollback
@ -171,6 +172,24 @@
:public? true
:classes #{:logseq.class/Root}}]]))))
(defn- fix-view-for
[conn _search-db]
(let [db @conn]
(when (ldb/db-based-graph? db)
(let [datoms (d/datoms db :avet :logseq.property/view-for)
e (d/entity db :logseq.property/view-for)
fix-schema [:db/add (:db/id e) :block/schema {:type :node
:hide? true
:public? false}]
fix-data (map
(fn [d]
(let [id (if (= :all-pages (:v d))
(:db/id (ldb/get-case-page db common-config/views-page-name))
(:db/id (d/entity db (:v d))))]
[:db/add (:e d) :logseq.property/view-for id]))
datoms)]
(cons fix-schema fix-data)))))
(defn- add-addresses-in-kvs-table
[^Object sqlite-db]
(let [columns (->> (.exec sqlite-db #js {:sql "SELECT NAME FROM PRAGMA_TABLE_INFO('kvs')"
@ -230,7 +249,8 @@
[16 {:properties [:logseq.property.class/hide-from-node]}]
[17 {:fix update-db-attrs-type}]
[18 {:properties [:logseq.property.view/type]}]
[19 {:classes [:logseq.class/Query]}]])
[19 {:classes [:logseq.class/Query]}]
[20 {:fix fix-view-for}]])
(let [max-schema-version (apply max (map first schema-version->updates))]
(assert (<= db-schema/version max-schema-version))