diff --git a/deps/db/src/logseq/db/frontend/property.cljs b/deps/db/src/logseq/db/frontend/property.cljs index 0fc5df5180..491c6bf7ce 100644 --- a/deps/db/src/logseq/db/frontend/property.cljs +++ b/deps/db/src/logseq/db/frontend/property.cljs @@ -67,15 +67,6 @@ :hide? true}} :logseq.property/built-in? {:schema {:type :checkbox :hide? true}} - :logseq.property/query-table {:schema {:type :checkbox - :hide? true}} - ;; query-properties is a coll of property db-idents and keywords where keywords are special frontend keywords - :logseq.property/query-properties {:schema {:type :coll - :hide? true}} - :logseq.property/query-sort-by {:schema {:type :keyword - :hide? true}} - :logseq.property/query-sort-desc {:schema {:type :checkbox - :hide? true}} :logseq.property/ls-type {:schema {:type :keyword :hide? true}} :logseq.property/hl-type {:schema {:type :keyword :hide? true}} diff --git a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs index 3bf6a43694..23afc57787 100644 --- a/deps/graph-parser/src/logseq/graph_parser/exporter.cljs +++ b/deps/graph-parser/src/logseq/graph_parser/exporter.cljs @@ -295,8 +295,8 @@ (def all-built-in-property-names "All built-in property names as a set of keywords" (-> built-in-property-name-to-idents keys set - ;; :filters is not in built-in-properties because it maps to 2 new properties - (conj :filters))) + ;; built-in-properties that map to new properties + (set/union #{:filters :query-table :query-properties :query-sort-by :query-sort-desc}))) (def all-built-in-names "All built-in properties and classes as a set of keywords" @@ -321,7 +321,7 @@ (->> props (keep (fn [[prop val]] ;; FIXME: Migrate :filters to :logseq.property.linked-references/* properties - (if (#{:icon :filters} prop) + (if (#{:icon :filters :query-sort-by :query-sort-desc :query-properties :query-table} prop) (do (swap! ignored-properties conj {:property prop :value val :location (if name {:page name} {:block title})}) diff --git a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs index 8ef4e310b0..84ada701c1 100644 --- a/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs +++ b/deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs @@ -148,7 +148,7 @@ (is (empty? (map :entity (:errors (db-validate/validate-db! @conn)))) "Created graph has no validation errors") - (is (= 0 (count @(:ignored-properties import-state))) "No ignored properties"))) + (is (= 16 (count @(:ignored-properties import-state))) "No ignored properties"))) (deftest-async export-basic-graph ;; This graph will contain basic examples of different features to import @@ -176,7 +176,7 @@ (filter #(= "page" (:block/type %)))))) "Correct number of pages with block content") (is (= 4 (count (d/datoms @conn :avet :block/type "whiteboard")))) - (is (= 1 (count @(:ignored-properties import-state))) ":filters should be the only ignored property") + (is (= 6 (count @(:ignored-properties import-state))) ":filters should be the only ignored property") (is (= 1 (count @assets)))) (testing "logseq files" @@ -285,11 +285,12 @@ (is (= #{"gpt"} (:block/alias (readable-properties @conn (find-page-by-name @conn "chat-gpt"))))) - (is (= {:logseq.property/query-sort-by :user.property/prop-num - :logseq.property/query-properties [:block :page :user.property/prop-string :user.property/prop-num] - :logseq.property/query-table true} - (readable-properties @conn (find-block-by-content @conn "{{query (property :prop-string)}}"))) - "query block has correct query properties")) + ;; FIXME: Enable when new properties are imported + #_(is (= {:logseq.property/query-sort-by :user.property/prop-num + :logseq.property/query-properties [:block :page :user.property/prop-string :user.property/prop-num] + :logseq.property/query-table true} + (readable-properties @conn (find-block-by-content @conn "{{query (property :prop-string)}}"))) + "query block has correct query properties")) (testing "db attributes" (is (= true diff --git a/src/main/frontend/components/file_based/query.cljs b/src/main/frontend/components/file_based/query.cljs new file mode 100644 index 0000000000..3b530c9648 --- /dev/null +++ b/src/main/frontend/components/file_based/query.cljs @@ -0,0 +1,85 @@ +(ns frontend.components.file-based.query + (:require [frontend.components.query.result :as query-result] + [frontend.components.file-based.query-table :as query-table] + [frontend.db.query-dsl :as query-dsl] + [frontend.handler.property :as property-handler] + [frontend.state :as state] + [frontend.ui :as ui] + [frontend.util :as util] + [rum.core :as rum])) + +(rum/defc query-refresh-button + [query-time {:keys [on-pointer-down full-text-search?]}] + (ui/tippy + {:html [:div + [:p + (if full-text-search? + [:span "Full-text search results will not be refreshed automatically."] + [:span (str "This query takes " (int query-time) "ms to finish, it's a bit slow so that auto refresh is disabled.")])] + [:p + "Click the refresh button instead if you want to see the latest result."]] + :interactive true + :popperOptions {:modifiers {:preventOverflow + {:enabled true + :boundariesElement "viewport"}}} + :arrow true} + [:a.fade-link.flex + {:on-pointer-down on-pointer-down} + (ui/icon "refresh" {:style {:font-size 20}})])) + +;; Custom query header only used by file graphs +(rum/defc custom-query-header + [{:keys [dsl-query?] :as config} + {:keys [title query] :as q} + {:keys [collapsed? result table? current-block view-f page-list? query-error-atom fulltext-query-result-atom]}] + (let [dsl-page-query? (and dsl-query? + (false? (:blocks? (query-dsl/parse-query query)))) + full-text-search? (and dsl-query? + (string? query) + (re-matches #"\".*\"" query)) + query-time (:query-time (meta result)) + current-block-uuid (:block/uuid current-block)] + [:div.th + {:title (str "Query: " query)} + (if dsl-query? + [:div.flex.flex-1.flex-row + (ui/icon "search" {:size 14}) + [:div.ml-1 (str "Live query" (when dsl-page-query? " for pages"))]] + [:div {:style {:font-size "initial"}} title]) + + (when (or (not dsl-query?) (not collapsed?)) + [:div.flex.flex-row.items-center.fade-in + (when (> (count result) 0) + [:span.results-count.pl-2 + (let [result-count (if (and (not table?) (map? result)) + (apply + (map (comp count val) result)) + (count result))] + (str result-count (if (> result-count 1) " results" " result")))]) + + (when (and current-block (not view-f) (not page-list?)) + (if table? + [:a.flex.ml-1.fade-link {:title "Switch to list view" + :on-click (fn [] (property-handler/set-block-property! (state/get-current-repo) current-block-uuid + :query-table + false))} + (ui/icon "list" {:style {:font-size 20}})] + [:a.flex.ml-1.fade-link {:title "Switch to table view" + :on-click (fn [] (property-handler/set-block-property! (state/get-current-repo) current-block-uuid + :query-table + true))} + (ui/icon "table" {:style {:font-size 20}})])) + + [:a.flex.ml-1.fade-link + {:title "Setting properties" + :on-click (fn [] + (let [all-keys (query-table/get-all-columns-for-result result page-list?)] + (state/pub-event! [:modal/set-query-properties current-block all-keys])))} + (ui/icon "settings" {:style {:font-size 20}})] + + [:div.ml-1 + (when (or full-text-search? + (and query-time (> query-time 50))) + (query-refresh-button query-time {:full-text-search? full-text-search? + :on-pointer-down (fn [e] + (util/stop e) + (query-result/trigger-custom-query! config q query-error-atom fulltext-query-result-atom))}))]])])) \ No newline at end of file diff --git a/src/main/frontend/components/file_based/query_table.cljs b/src/main/frontend/components/file_based/query_table.cljs index df8c6b3bd0..ee05f3c2ba 100644 --- a/src/main/frontend/components/file_based/query_table.cljs +++ b/src/main/frontend/components/file_based/query_table.cljs @@ -241,4 +241,4 @@ ;; as user needs to know if there result is sorted sort-state (get-sort-state current-block) sort-result' (sort-result result' (assoc sort-state :page? page?))] - (result-table-v1 config current-block sort-result' sort-state columns options map-inline page-cp ->elem inline-text)))) + (result-table-v1 config current-block sort-result' sort-state columns options map-inline page-cp ->elem inline-text)))) \ No newline at end of file diff --git a/src/main/frontend/components/query.cljs b/src/main/frontend/components/query.cljs index 0baabd3dd0..85d71b7586 100644 --- a/src/main/frontend/components/query.cljs +++ b/src/main/frontend/components/query.cljs @@ -1,21 +1,19 @@ (ns frontend.components.query (:require [clojure.string :as string] [frontend.components.file-based.query-table :as query-table] + [frontend.components.file-based.query :as file-query] [frontend.components.query.result :as query-result] [frontend.components.query.view :as query-view] [frontend.context.i18n :refer [t]] [frontend.db :as db] [frontend.db-mixins :as db-mixins] - [frontend.db.query-dsl :as query-dsl] [frontend.extensions.sci :as sci] [frontend.handler.editor :as editor-handler] - [frontend.handler.property :as property-handler] [frontend.state :as state] [frontend.ui :as ui] [frontend.util :as util] [lambdaisland.glogi :as log] [rum.core :as rum] - [frontend.handler.property.util :as pu] [frontend.config :as config])) (defn built-in-custom-query? @@ -24,25 +22,7 @@ (when (seq queries) (boolean (some #(= % title) (map :title queries)))))) -(rum/defc query-refresh-button - [query-time {:keys [on-pointer-down full-text-search?]}] - (ui/tippy - {:html [:div - [:p - (if full-text-search? - [:span "Full-text search results will not be refreshed automatically."] - [:span (str "This query takes " (int query-time) "ms to finish, it's a bit slow so that auto refresh is disabled.")])] - [:p - "Click the refresh button instead if you want to see the latest result."]] - :interactive true - :popperOptions {:modifiers {:preventOverflow - {:enabled true - :boundariesElement "viewport"}}} - :arrow true} - [:a.fade-link.flex - {:on-pointer-down on-pointer-down} - (ui/icon "refresh" {:style {:font-size 20}})])) - +;; TODO: Split this into file and DB graph versions. DB graph needlessly coupled too file graph args (rum/defcs custom-query-inner < rum/reactive [state config {:keys [query breadcrumb-show?]} {:keys [query-error-atom @@ -80,8 +60,7 @@ (util/hiccup-keywordize result)) (and (config/db-based-graph? (state/get-current-repo)) - (not (:built-in? config)) - (or page-list? only-blocks? blocks-grouped-by-page? table?)) + (not (:built-in? config))) (query-view/query-result (assoc config :id (:db/id current-block)) current-block result) @@ -137,7 +116,7 @@ [:span.opacity-60.text-sm.ml-2.results-count (str result-count (if (> result-count 1) " results" " result"))])])) -(rum/defcs ^:large-vars/cleanup-todo custom-query* < rum/reactive rum/static db-mixins/query +(rum/defcs custom-query* < rum/reactive rum/static db-mixins/query {:init (fn [state] (let [[config {:keys [title collapsed?]}] (:rum/args state) built-in? (built-in-custom-query? title) @@ -149,7 +128,7 @@ (editor-handler/collapse-block! current-block-uuid)))) (assoc state :query-error (atom nil) :fulltext-query-result (atom nil)))} - [state config {:keys [title builder query view collapsed? table-view?] :as q}] + [state config {:keys [title builder query view collapsed?] :as q}] (let [*query-error (:query-error state) *fulltext-query-result (:fulltext-query-result state) built-in? (built-in-custom-query? title) @@ -165,20 +144,14 @@ collapsed? (:block/collapsed? current-block))) built-in-collapsed? (and collapsed? built-in?) - query-table? (pu/get-block-property-value current-block :logseq.property/query-table) - table? (or table-view? - query-table? - (and (string? query) (string/ends-with? (string/trim query) "table"))) + db-based? (config/db-based-graph? (state/get-current-repo)) + table? (when-not db-based? + (or (get-in current-block [:block/properties :query-table]) + (and (string? query) (string/ends-with? (string/trim query) "table")))) view-fn (if (keyword? view) (get-in (state/sub-config) [:query/views view]) view) view-f (and view-fn (sci/eval-string (pr-str view-fn))) - dsl-page-query? (and dsl-query? - (false? (:blocks? (query-dsl/parse-query query)))) - full-text-search? (and dsl-query? - (string? query) - (re-matches #"\".*\"" query)) result (when (or built-in-collapsed? (not collapsed?')) (query-result/get-query-result config q *query-error *fulltext-query-result current-block-uuid {:table? table?})) - query-time (:query-time (meta result)) page-list? (and (seq result) (some? (:block/name (first result)))) opts {:query-error-atom *query-error @@ -188,59 +161,24 @@ :view-f view-f :page-list? page-list? :result result - :group-by-page? (query-result/get-group-by-page q {:table? table?})} - db-based? (config/db-based-graph? (state/get-current-repo))] + :group-by-page? (query-result/get-group-by-page q {:table? table?})}] (if (:custom-query? config) [:code (if dsl-query? (util/format "{{query %s}}" query) "{{query hidden}}")] (when-not (and built-in? (empty? result)) [:div.custom-query (get config :attr {}) - (when-not (or built-in? db-based?) - [:div.th - {:title (str "Query: " query)} - (if dsl-query? - [:div.flex.flex-1.flex-row - (ui/icon "search" {:size 14}) - [:div.ml-1 (str "Live query" (when dsl-page-query? " for pages"))]] - [:div {:style {:font-size "initial"}} title]) - - (when (or (not dsl-query?) (not collapsed?')) - [:div.flex.flex-row.items-center.fade-in - (when (> (count result) 0) - [:span.results-count.pl-2 - (let [result-count (if (and (not table?) (map? result)) - (apply + (map (comp count val) result)) - (count result))] - (str result-count (if (> result-count 1) " results" " result")))]) - - (when (and current-block (not view-f) (nil? table-view?) (not page-list?)) - (if table? - [:a.flex.ml-1.fade-link {:title "Switch to list view" - :on-click (fn [] (property-handler/set-block-property! (state/get-current-repo) current-block-uuid - (pu/get-pid :logseq.property/query-table) - false))} - (ui/icon "list" {:style {:font-size 20}})] - [:a.flex.ml-1.fade-link {:title "Switch to table view" - :on-click (fn [] (property-handler/set-block-property! (state/get-current-repo) current-block-uuid - (pu/get-pid :logseq.property/query-table) - true))} - (ui/icon "table" {:style {:font-size 20}})])) - - [:a.flex.ml-1.fade-link - {:title "Setting properties" - :on-click (fn [] - (let [all-keys (query-table/get-all-columns-for-result result page-list?)] - (state/pub-event! [:modal/set-query-properties current-block all-keys])))} - (ui/icon "settings" {:style {:font-size 20}})] - - [:div.ml-1 - (when (or full-text-search? - (and query-time (> query-time 50))) - (query-refresh-button query-time {:full-text-search? full-text-search? - :on-pointer-down (fn [e] - (util/stop e) - (query-result/trigger-custom-query! config q *query-error *fulltext-query-result))}))]])]) + (when (and (not db-based?) (not built-in?)) + (file-query/custom-query-header config + q + {:query-error-atom *query-error + :fulltext-query-result-atom *fulltext-query-result + :current-block current-block + :table? table? + :view-f view-f + :page-list? page-list? + :result result + :collapsed? collapsed?'})) (when dsl-query? builder) @@ -264,4 +202,4 @@ (ui/lazy-visible (fn [] (custom-query* config q)) - {:debug-id q}))) + {:debug-id q}))) \ No newline at end of file diff --git a/src/main/frontend/handler/editor.cljs b/src/main/frontend/handler/editor.cljs index 493cc71f57..7f89f4b2d2 100644 --- a/src/main/frontend/handler/editor.cljs +++ b/src/main/frontend/handler/editor.cljs @@ -893,31 +893,6 @@ "")] (when edit-block-f (edit-block-f)))))))) -(defn set-block-query-properties! - [block-id all-properties key add?] - (when-let [block (db/entity [:block/uuid block-id])] - (let [query-properties (get block (pu/get-pid :logseq.property/query-properties)) - repo (state/get-current-repo) - db-based? (config/db-based-graph? repo) - query-properties (if db-based? - query-properties - (some-> query-properties - (common-handler/safe-read-string "Parsing query properties failed"))) - query-properties (if (seq query-properties) - query-properties - all-properties) - query-properties (if add? - (distinct (conj query-properties key)) - (remove #{key} query-properties)) - query-properties (vec query-properties)] - (if (seq query-properties) - (property-handler/set-block-property! repo block-id - (pu/get-pid :logseq.property/query-properties) - (if db-based? - query-properties - (str query-properties))) - (property-handler/remove-block-property! repo block-id (pu/get-pid :logseq.property/query-properties)))))) - (defn set-block-timestamp! [block-id key value] (let [key (string/lower-case (str key)) diff --git a/src/main/frontend/handler/events.cljs b/src/main/frontend/handler/events.cljs index bc207613d0..aee3f91a5b 100644 --- a/src/main/frontend/handler/events.cljs +++ b/src/main/frontend/handler/events.cljs @@ -8,7 +8,6 @@ [cljs-bean.core :as bean] [clojure.core.async :as async] [clojure.core.async.interop :refer [p->c]] - [clojure.set :as set] [clojure.string :as string] [frontend.commands :as commands] [frontend.components.cmdk.core :as cmdk] @@ -38,7 +37,6 @@ [frontend.fs.nfs :as nfs] [frontend.fs.sync :as sync] [frontend.fs.watcher-handler :as fs-watcher] - [frontend.handler.common :as common-handler] [frontend.handler.editor :as editor-handler] [frontend.handler.file :as file-handler] [frontend.handler.file-sync :as file-sync-handler] @@ -53,9 +51,6 @@ [frontend.handler.shell :as shell-handler] [frontend.handler.ui :as ui-handler] [frontend.handler.user :as user-handler] - [frontend.handler.property.util :as pu] - [frontend.handler.db-based.property.util :as db-pu] - [frontend.handler.property :as property-handler] [frontend.handler.file-based.nfs :as nfs-handler] [frontend.handler.code :as code-handler] [frontend.handler.db-based.rtc :as rtc-handler] @@ -293,64 +288,6 @@ (some-> (ask-permission repo) (shui/dialog-open! {:align :top})))) -(defonce *query-properties (atom {})) -(rum/defc query-properties-settings-inner < rum/reactive - {:will-unmount (fn [state] - (reset! *query-properties {}) - state)} - [block shown-properties all-properties] - (let [query-properties (rum/react *query-properties) - db-graph? (config/db-based-graph? (state/get-current-repo))] - [:div - [:h1.font-semibold.-mt-2.mb-2.text-lg (t :query/config-property-settings)] - [:a.flex - {:title "Refresh list of columns" - :on-click - (fn [] - (reset! *query-properties {}) - (let [k (pu/get-pid :logseq.property/query-properties)] - (property-handler/remove-block-property! (state/get-current-repo) (:block/uuid block) k)))} - (ui/icon "refresh")] - (for [property all-properties] - (let [property-value (get query-properties property) - shown? (if (nil? property-value) - (contains? shown-properties property) - property-value)] - [:div.flex.flex-row.my-2.justify-between.align-items - [:div (if (and db-graph? (qualified-keyword? property)) - (db-pu/get-property-name property) - (name property))] - [:div.mt-1 (ui/toggle shown? - (fn [] - (let [value (not shown?)] - (swap! *query-properties assoc property value) - (editor-handler/set-block-query-properties! - (:block/uuid block) - all-properties - property - value))) - true)]]))])) - -(defn query-properties-settings - [block shown-properties all-properties] - (fn [_close-fn] - (query-properties-settings-inner block shown-properties all-properties))) - -(defmethod handle :modal/set-query-properties [[_ block all-properties]] - (let [properties (:block/properties block) - query-properties (pu/lookup properties :logseq.property/query-properties) - block-properties (if (config/db-based-graph? (state/get-current-repo)) - query-properties - (some-> query-properties - (common-handler/safe-read-string "Parsing query properties failed"))) - shown-properties (if (seq block-properties) - (set block-properties) - (set all-properties)) - shown-properties (set/intersection (set all-properties) shown-properties)] - (shui/dialog-open! - (query-properties-settings block shown-properties all-properties) - {}))) - (defmethod handle :modal/show-cards [_] (shui/dialog-open! srs/global-cards diff --git a/src/main/frontend/handler/file_based/events.cljs b/src/main/frontend/handler/file_based/events.cljs index 57d70b811d..cb87891a39 100644 --- a/src/main/frontend/handler/file_based/events.cljs +++ b/src/main/frontend/handler/file_based/events.cljs @@ -1,17 +1,22 @@ (ns frontend.handler.file-based.events "Events that are only for file graphs" (:require [clojure.core.async :as async] + [clojure.set :as set] [frontend.context.i18n :refer [t]] + [frontend.db :as db] [frontend.handler.events :as events] [frontend.handler.page :as page-handler] [frontend.handler.repo :as repo-handler] [frontend.handler.file-based.nfs :as nfs-handler] + [frontend.handler.common :as common-handler] + [frontend.handler.property :as property-handler] [frontend.fs.sync :as sync] [frontend.state :as state] [frontend.ui :as ui] [frontend.util :as util] [frontend.config :as config] - [logseq.shui.ui :as shui])) + [logseq.shui.ui :as shui] + [rum.core :as rum])) (defmethod events/handle :graph/ask-for-re-index [[_ *multiple-windows? ui]] ;; *multiple-windows? - if the graph is opened in multiple windows, boolean atom @@ -44,3 +49,74 @@ nfs-handler/rebuild-index! #(do (page-handler/create-today-journal!) (events/file-sync-restart!)))))) + +(defn set-block-query-properties! + [block-id all-properties key add?] + (when-let [block (db/entity [:block/uuid block-id])] + (let [query-properties (get-in block [:block/properties :query-properties]) + repo (state/get-current-repo) + query-properties (some-> query-properties + (common-handler/safe-read-string "Parsing query properties failed")) + query-properties (if (seq query-properties) + query-properties + all-properties) + query-properties (if add? + (distinct (conj query-properties key)) + (remove #{key} query-properties)) + query-properties (vec query-properties)] + (if (seq query-properties) + (property-handler/set-block-property! repo block-id + :query-properties + (str query-properties)) + (property-handler/remove-block-property! repo block-id :query-properties))))) + +(defonce *query-properties (atom {})) +(rum/defc query-properties-settings-inner < rum/reactive + {:will-unmount (fn [state] + (reset! *query-properties {}) + state)} + [block shown-properties all-properties] + (let [query-properties (rum/react *query-properties)] + [:div + [:h1.font-semibold.-mt-2.mb-2.text-lg (t :query/config-property-settings)] + [:a.flex + {:title "Refresh list of columns" + :on-click + (fn [] + (reset! *query-properties {}) + (property-handler/remove-block-property! (state/get-current-repo) (:block/uuid block) :query-properties))} + (ui/icon "refresh")] + (for [property all-properties] + (let [property-value (get query-properties property) + shown? (if (nil? property-value) + (contains? shown-properties property) + property-value)] + [:div.flex.flex-row.my-2.justify-between.align-items + [:div (name property)] + [:div.mt-1 (ui/toggle shown? + (fn [] + (let [value (not shown?)] + (swap! *query-properties assoc property value) + (set-block-query-properties! + (:block/uuid block) + all-properties + property + value))) + true)]]))])) + +(defn query-properties-settings + [block shown-properties all-properties] + (fn [_close-fn] + (query-properties-settings-inner block shown-properties all-properties))) + +(defmethod events/handle :modal/set-query-properties [[_ block all-properties]] + (let [query-properties (get-in block [:block/properties :query-properties]) + block-properties (some-> query-properties + (common-handler/safe-read-string "Parsing query properties failed")) + shown-properties (if (seq block-properties) + (set block-properties) + (set all-properties)) + shown-properties (set/intersection (set all-properties) shown-properties)] + (shui/dialog-open! + (query-properties-settings block shown-properties all-properties) + {})))