enhance: show custom title instead of live query for custom queries (#9026)

* enhance(ui): replace live query with custom title if exists

also moved query related components to frontend.components.query

* fix: simplify query components' state
pull/8974/head
Tienson Qin 2023-04-12 11:12:05 +08:00 committed by GitHub
parent 5cce967192
commit b17c4ea37e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 395 additions and 365 deletions

View File

@ -16,17 +16,15 @@
[frontend.components.lazy-editor :as lazy-editor]
[frontend.components.macro :as macro]
[frontend.components.plugins :as plugins]
[frontend.components.query-table :as query-table]
[frontend.components.query.builder :as query-builder-component]
[frontend.components.svg :as svg]
[frontend.components.query :as query]
[frontend.config :as config]
[frontend.context.i18n :refer [t]]
[frontend.date :as date]
[frontend.db :as db]
[frontend.db-mixins :as db-mixins]
[frontend.db.model :as model]
[frontend.db.query-dsl :as query-dsl]
[frontend.db.utils :as db-utils]
[frontend.extensions.highlight :as highlight]
[frontend.extensions.latex :as latex]
[frontend.extensions.lightbox :as lightbox]
@ -53,7 +51,6 @@
[frontend.handler.export.common :as export-common-handler]
[frontend.mobile.util :as mobile-util]
[frontend.modules.outliner.tree :as tree]
[frontend.search :as search]
[frontend.security :as security]
[frontend.state :as state]
[frontend.template :as template]
@ -84,17 +81,7 @@
[datascript.impl.entity :as e]
[logseq.common.path :as path]))
(defn safe-read-string
([s]
(safe-read-string s true))
([s warn?]
(try
(reader/read-string s)
(catch :default e
(log/error :read-string-error e :string s)
(when warn?
[:div.warning {:title "read-string failed"}
s])))))
;; local state
(defonce *dragging?
@ -443,7 +430,7 @@
(defn image-link [config url href label metadata full_text]
(let [metadata (if (string/blank? metadata)
nil
(safe-read-string metadata false))
(gp-util/safe-read-string metadata))
title (second (first label))]
(ui/catch-error
[:span.warning full_text]
@ -985,12 +972,10 @@
(not (= (:id config) "contents")))
[:span.text-gray-500 page-ref/right-brackets])]))
(declare custom-query)
(defn- show-link?
[config metadata s full-text]
(let [media-formats (set (map name config/media-formats))
metadata-show (:show (safe-read-string metadata))
metadata-show (:show (gp-util/safe-read-string metadata))
format (get-in config [:block :block/format])]
(or
(and
@ -1228,15 +1213,25 @@
(assoc :title title))
(map-inline config label)))))))
(declare ->hiccup)
(defn wrap-query-components
[config]
(merge config
{:->hiccup ->hiccup
:->elem ->elem
:page-cp page-cp
:inline-text inline-text
:map-inline map-inline}))
;;;; Macro component render functions
(defn- macro-query-cp
[config arguments]
[:div.dsl-query.pr-3.sm:pr-0
(let [query (->> (string/join ", " arguments)
(string/trim))]
(custom-query (assoc config :dsl-query? true)
{:title (rum/with-key (query-builder-component/builder query config)
query)
(query/custom-query (wrap-query-components (assoc config :dsl-query? true))
{:builder (query-builder-component/builder query config)
:query query}))])
(defn- macro-function-cp
@ -1544,9 +1539,13 @@
(defn hiccup->html
[s]
(-> (safe-read-string s)
(let [result (gp-util/safe-read-string s)
result' (if (seq result) result
[:div.warning {:title "Invalid hiccup"}
s])]
(-> result'
(hiccups.core/html)
(security/sanitize-html)))
(security/sanitize-html))))
(defn inline
[{:keys [html-export?] :as config} item]
@ -2265,6 +2264,29 @@
[:a.fade-link
summary]])]))))
(defn- block-content-inner
[config block body plugin-slotted? collapsed? block-ref-with-title?]
(if plugin-slotted?
[:div.block-slotted-body
(plugins/hook-block-slot
:block-content-slotted
(-> block (dissoc :block/children :block/page)))]
(let [title-collapse-enabled? (:outliner/block-title-collapse-enabled? (state/get-config))]
(when (and (not block-ref-with-title?)
(seq body)
(or (not title-collapse-enabled?)
(and title-collapse-enabled?
(or (not collapsed?)
(some? (mldoc/extract-first-query-from-ast body))))))
[:div.block-body
;; TODO: consistent id instead of the idx (since it could be changed later)
(let [body (block/trim-break-lines! (:block/body block))]
(for [[idx child] (medley/indexed body)]
(when-let [block (markup-element-cp config child)]
(rum/with-key (block-child block)
(str uuid "-" idx)))))]))))
(rum/defc block-content < rum/reactive
[config {:block/keys [uuid content children properties scheduled deadline format pre-block?] :as block} edit-input-id block-id slide?]
(let [{:block/keys [title body] :as block} (if (:block/title block) block
@ -2342,24 +2364,7 @@
(not= block-type :whiteboard-shape))
(properties-cp config block))
(if plugin-slotted?
[:div.block-slotted-body
(plugins/hook-block-slot
:block-content-slotted
(-> block (dissoc :block/children :block/page)))]
(let [title-collapse-enabled? (:outliner/block-title-collapse-enabled? (state/get-config))]
(when (and (not block-ref-with-title?)
(seq body)
(or (not title-collapse-enabled?)
(and title-collapse-enabled? (not collapsed?))))
[:div.block-body
;; TODO: consistent id instead of the idx (since it could be changed later)
(let [body (block/trim-break-lines! (:block/body block))]
(for [[idx child] (medley/indexed body)]
(when-let [block (markup-element-cp config child)]
(rum/with-key (block-child block)
(str uuid "-" idx)))))])))
(block-content-inner config block body plugin-slotted? collapsed? block-ref-with-title?)
(case (:block/warning block)
:multiple-blocks
@ -3058,320 +3063,6 @@
[config col]
(map #(inline config %) col))
(declare ->hiccup)
(defn built-in-custom-query?
[title]
(let [queries (get-in (state/sub-config) [:default-queries :journals])]
(when (seq queries)
(boolean (some #(= % title) (map :title queries))))))
;; TODO: move query related fns/components to components.query
(defn- trigger-custom-query!
[state *query-error *query-triggered?]
(let [[config query _query-result] (:rum/args state)
repo (state/get-current-repo)
result-atom (or (:query-atom state) (atom nil))
current-block-uuid (or (:block/uuid (:block config))
(:block/uuid config))
_ (reset! *query-error nil)
query-atom (try
(cond
(:dsl-query? config)
(let [q (:query query)
form (safe-read-string q false)]
(cond
;; Searches like 'foo' or 'foo bar' come back as symbols
;; and are meant to go directly to full text search
(and (util/electron?) (symbol? form)) ; full-text search
(p/let [blocks (search/block-search repo (string/trim (str form)) {:limit 30})]
(when (seq blocks)
(let [result (db/pull-many (state/get-current-repo) '[*] (map (fn [b] [:block/uuid (uuid (:block/uuid b))]) blocks))]
(reset! result-atom result))))
(symbol? form)
(atom nil)
:else
(query-dsl/query (state/get-current-repo) q)))
:else
(db/custom-query query {:current-block-uuid current-block-uuid}))
(catch :default e
(reset! *query-error e)
(atom nil)))]
(when *query-triggered?
(reset! *query-triggered? true))
(if (instance? Atom query-atom)
query-atom
result-atom)))
(rum/defc query-refresh-button
[query-time {:keys [on-mouse-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-mouse-down on-mouse-down}
(ui/icon "refresh" {:style {:font-size 20}})]))
(defn- get-query-result
[state config *query-error *query-triggered? current-block-uuid q not-grouped-by-page? query-result-atom]
(or (when-let [*result (:query-result config)] @*result)
(let [query-atom (trigger-custom-query! state *query-error *query-triggered?)
query-result (and query-atom (rum/react query-atom))
;; exclude the current one, otherwise it'll loop forever
remove-blocks (if current-block-uuid [current-block-uuid] nil)
transformed-query-result (when query-result
(db/custom-query-result-transform query-result remove-blocks q))
result (if (and (:block/uuid (first transformed-query-result)) (not not-grouped-by-page?))
(let [result (db-utils/group-by-page transformed-query-result)]
(if (map? result)
(dissoc result nil)
result))
transformed-query-result)]
(when query-result-atom
(reset! query-result-atom (util/safe-with-meta result (meta @query-atom))))
(when-let [query-result (:query-result config)]
(let [result (remove (fn [b] (some? (get-in b [:block/properties :template]))) result)]
(reset! query-result result)))
result)))
(rum/defcs custom-query-inner < rum/reactive db-mixins/query
[state config {:keys [query children? breadcrumb-show?] :as q}
{:keys [query-result-atom
query-error-atom
query-triggered-atom
current-block
current-block-uuid
table?
dsl-query?
page-list?
view-f]}]
(let [*query-error query-error-atom
*query-triggered? query-triggered-atom
not-grouped-by-page? (or table?
(boolean (:result-transform q))
(and (string? query) (string/includes? query "(by-page false)")))
result (get-query-result state config *query-error *query-triggered? current-block-uuid q not-grouped-by-page? query-result-atom)
only-blocks? (:block/uuid (first result))
blocks-grouped-by-page? (and (seq result)
(not not-grouped-by-page?)
(coll? (first result))
(:block/name (ffirst result))
(:block/uuid (first (second (first result))))
true)]
(if @*query-error
(do
(log/error :exception @*query-error)
[:div.warning.my-1 "Query failed: "
[:p (.-message @*query-error)]])
[:div.custom-query-results
(cond
(and (seq result) view-f)
(let [result (try
(sci/call-fn view-f result)
(catch :default error
(log/error :custom-view-failed {:error error
:result result})
[:div "Custom view failed: "
(str error)]))]
(util/hiccup-keywordize result))
page-list?
(query-table/result-table config current-block result {:page? true} map-inline page-cp ->elem inline-text)
table?
(query-table/result-table config current-block result {:page? false} map-inline page-cp ->elem inline-text)
(and (seq result) (or only-blocks? blocks-grouped-by-page?))
(->hiccup result (cond-> (assoc config
:custom-query? true
:dsl-query? dsl-query?
:query query
:breadcrumb-show? (if (some? breadcrumb-show?)
breadcrumb-show?
true)
:group-by-page? blocks-grouped-by-page?
:ref? true)
children?
(assoc :ref? true))
{:style {:margin-top "0.25rem"
:margin-left "0.25rem"}})
(seq result)
(let [result (->>
(for [record result]
(if (map? record)
(str (util/pp-str record) "\n")
record))
(remove nil?))]
(when (seq result)
[:ul
(for [item result]
[:li (str item)])]))
(or (string/blank? query)
(= query "(and)"))
nil
:else
[:div.text-sm.mt-2.opacity-90 "No matched result"])])))
(rum/defc query-title
[config title {:keys [result-count]}]
[:div.custom-query-title.flex.justify-between.w-full
[:span.title-text (cond
(vector? title) title
(string? title) (inline-text config
(get-in config [:block :block/format] :markdown)
title)
:else title)]
(when result-count
[: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/local nil ::query-result)
(rum/local false ::query-triggered?)
{:init (fn [state] (assoc state :query-error (atom nil)))}
[state config {:keys [title query view collapsed? table-view?] :as q}]
(let [*query-error (:query-error state)
*query-triggered? (::query-triggered? state)
built-in? (built-in-custom-query? title)
*query-result (::query-result state)
result (rum/react *query-result)
dsl-query? (:dsl-query? config)
current-block-uuid (or (:block/uuid (:block config))
(:block/uuid config))
current-block (db/entity [:block/uuid current-block-uuid])
temp-collapsed? (state/sub-collapsed current-block-uuid)
collapsed?' (if (some? temp-collapsed?)
temp-collapsed?
(or
collapsed?
(:block/collapsed? current-block)))
table? (or table-view?
(get-in current-block [:block/properties :query-table])
(and (string? query) (string/ends-with? (string/trim query) "table")))
query-time (:query-time (meta @*query-result))
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)))
page-list? (and (seq result)
(some? (:block/name (first result))))
dsl-page-query? (and dsl-query?
(false? (:blocks? (query-dsl/parse-query query))))
full-text-search? (and dsl-query?
(util/electron?)
(symbol? (safe-read-string query false)))
opts {:query-result-atom *query-result
:query-error-atom *query-error
:query-triggered-atom *query-triggered?
:current-block current-block
:dsl-query? dsl-query?
:current-block-uuid current-block-uuid
:table? table?
:view-f view-f
:page-list? page-list?}]
(if (:custom-query? config)
[:code (if dsl-query?
(util/format "{{query %s}}" query)
"{{query hidden}}")]
(if-not @*query-triggered?
;; trigger custom query
(custom-query-inner config q opts)
(when-not (and built-in? (empty? @*query-result))
[:div.custom-query (get config :attr {})
(when-not built-in?
[:div.th
[:div.flex.flex-1.flex-row
(ui/icon "search" {:size 14})
[:div.ml-1 (str "Live query" (when dsl-page-query? " for pages"))]]
(when (or (not dsl-query?) (not collapsed?'))
[:div.flex.flex-row.items-center.fade-in
(when (> (count result) 0)
[:span.results-count
(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 [] (editor-handler/set-block-property! 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 [] (editor-handler/set-block-property! 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-keys 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-mouse-down (fn [e]
(util/stop e)
(trigger-custom-query! state *query-error *query-triggered?))}))]])])
(if (or built-in? (not dsl-query?))
[:div {:style {:margin-left 2}}
(ui/foldable
(query-title config title (when built-in? {:result-count (count result)}))
(fn []
(custom-query-inner config q opts))
{:default-collapsed? collapsed?
:title-trigger? true})]
[:div.bd
(query-title config title {})
(when-not collapsed?'
(custom-query-inner config q opts))])])))))
(rum/defc custom-query
[config q]
(ui/catch-error
(ui/block-error "Query Error:" {:content (:query q)})
(ui/lazy-visible
(fn [] (custom-query* config q))
{:debug-id q
:trigger-once? false})))
;; TODO: move to mldoc
;; (defn- convert-md-src-to-custom-block
;; [item]
;; (let [{:keys [language options lines] :as options} (second item)
;; lang (string/lower-case (or language ""))]
;; (cond
;; (= lang "quote")
;; (let [content (string/trim (string/join "\n" lines))]
;; ["Quote" (first (mldoc/->edn content (gp-mldoc/default-config :markdown)))])
;; (contains? #{"query" "note" "tip" "important" "caution" "warning" "pinned"} lang)
;; (let [content (string/trim (string/join "\n" lines))]
;; ["Custom" lang nil (first (mldoc/->edn content (gp-mldoc/default-config :markdown))) content])
;; :else
;; ["Src" options])))
(rum/defc src-cp < rum/static
[config options html-export?]
(when options
@ -3500,7 +3191,7 @@
["Custom" "query" _options _result content]
(try
(let [query (reader/read-string content)]
(custom-query config query))
(query/custom-query (wrap-query-components config) query))
(catch :default e
(log/error :read-string-error e)
(ui/block-error "Invalid query:" {:content content})))

View File

@ -1,6 +1,7 @@
(ns frontend.components.page
(:require [clojure.string :as string]
[frontend.components.block :as component-block]
[frontend.components.query :as query]
[frontend.components.content :as content]
[frontend.components.editor :as editor]
[frontend.components.hierarchy :as hierarchy]
@ -186,9 +187,11 @@
(rum/with-key
(ui/catch-error
(ui/component-error "Failed default query:" {:content (pr-str query)})
(component-block/custom-query {:attr {:class "mt-10"}
(query/custom-query (component-block/wrap-query-components
{:attr {:class "mt-10"}
:editor-box editor/box
:page page} query))
:page page})
query))
(str repo "-custom-query-" (:query query))))]))))
(defn tagged-pages

View File

@ -0,0 +1,307 @@
(ns frontend.components.query
(:require [rum.core :as rum]
[frontend.ui :as ui]
[frontend.util :as util]
[frontend.state :as state]
[frontend.search :as search]
[frontend.db :as db]
[frontend.db-mixins :as db-mixins]
[clojure.string :as string]
[frontend.db.query-dsl :as query-dsl]
[frontend.components.query-table :as query-table]
[frontend.db.utils :as db-utils]
[lambdaisland.glogi :as log]
[frontend.extensions.sci :as sci]
[frontend.handler.editor :as editor-handler]
[logseq.graph-parser.util :as gp-util]
[promesa.core :as p]))
(defn built-in-custom-query?
[title]
(let [queries (get-in (state/sub-config) [:default-queries :journals])]
(when (seq queries)
(boolean (some #(= % title) (map :title queries))))))
(defn- trigger-custom-query!
[state *query-error *query-triggered?]
(let [[config query] (:rum/args state)
repo (state/get-current-repo)
result-atom (atom nil)
current-block-uuid (or (:block/uuid (:block config))
(:block/uuid config))
_ (reset! *query-error nil)
query-atom (try
(cond
(:dsl-query? config)
(let [q (:query query)
form (gp-util/safe-read-string q)]
(cond
;; Searches like 'foo' or 'foo bar' come back as symbols
;; and are meant to go directly to full text search
(and (util/electron?) (symbol? form)) ; full-text search
(p/let [blocks (search/block-search repo (string/trim (str form)) {:limit 30})]
(when (seq blocks)
(let [result (db/pull-many (state/get-current-repo) '[*] (map (fn [b] [:block/uuid (uuid (:block/uuid b))]) blocks))]
(reset! result-atom result))))
(symbol? form)
(atom nil)
:else
(query-dsl/query (state/get-current-repo) q)))
:else
(db/custom-query query {:current-block-uuid current-block-uuid}))
(catch :default e
(reset! *query-error e)
(atom nil)))]
(when *query-triggered?
(reset! *query-triggered? true))
(if (instance? Atom query-atom)
query-atom
result-atom)))
(rum/defc query-refresh-button
[query-time {:keys [on-mouse-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-mouse-down on-mouse-down}
(ui/icon "refresh" {:style {:font-size 20}})]))
(defn- get-query-result
[state config *query-error *query-triggered? current-block-uuid q not-grouped-by-page? ]
(or (when-let [*result (:query-result config)] @*result)
(let [query-atom (trigger-custom-query! state *query-error *query-triggered?)
query-result (and query-atom (rum/react query-atom))
;; exclude the current one, otherwise it'll loop forever
remove-blocks (if current-block-uuid [current-block-uuid] nil)
transformed-query-result (when query-result
(db/custom-query-result-transform query-result remove-blocks q))
result (if (and (:block/uuid (first transformed-query-result)) (not not-grouped-by-page?))
(let [result (db-utils/group-by-page transformed-query-result)]
(if (map? result)
(dissoc result nil)
result))
transformed-query-result)]
(when query-atom
(util/safe-with-meta result (meta @query-atom))))))
(rum/defcs custom-query-inner < rum/reactive db-mixins/query
[state config {:keys [query children? breadcrumb-show?]}
{:keys [query-error-atom
current-block
table?
dsl-query?
page-list?
view-f
result]}]
(let [{:keys [->hiccup ->elem inline-text page-cp map-inline]} config
*query-error query-error-atom
not-grouped-by-page? (or table?
(and (string? query) (string/includes? query "(by-page false)")))
only-blocks? (:block/uuid (first result))
blocks-grouped-by-page? (and (seq result)
(not not-grouped-by-page?)
(coll? (first result))
(:block/name (ffirst result))
(:block/uuid (first (second (first result))))
true)]
(if @*query-error
(do
(log/error :exception @*query-error)
[:div.warning.my-1 "Query failed: "
[:p (.-message @*query-error)]])
[:div.custom-query-results
(cond
(and (seq result) view-f)
(let [result (try
(sci/call-fn view-f result)
(catch :default error
(log/error :custom-view-failed {:error error
:result result})
[:div "Custom view failed: "
(str error)]))]
(util/hiccup-keywordize result))
page-list?
(query-table/result-table config current-block result {:page? true} map-inline page-cp ->elem inline-text)
table?
(query-table/result-table config current-block result {:page? false} map-inline page-cp ->elem inline-text)
(and (seq result) (or only-blocks? blocks-grouped-by-page?))
(->hiccup result
(cond-> (assoc config
:custom-query? true
:dsl-query? dsl-query?
:query query
:breadcrumb-show? (if (some? breadcrumb-show?)
breadcrumb-show?
true)
:group-by-page? blocks-grouped-by-page?
:ref? true)
children?
(assoc :ref? true))
{:style {:margin-top "0.25rem"
:margin-left "0.25rem"}})
(seq result)
(let [result (->>
(for [record result]
(if (map? record)
(str (util/pp-str record) "\n")
record))
(remove nil?))]
(when (seq result)
[:ul
(for [item result]
[:li (str item)])]))
(or (string/blank? query)
(= query "(and)"))
nil
:else
[:div.text-sm.mt-2.opacity-90 "No matched result"])])))
(rum/defc query-title
[config title {:keys [result-count]}]
(let [inline-text (:inline-text config)]
[:div.custom-query-title.flex.justify-between.w-full
[:span.title-text (cond
(vector? title) title
(string? title) (inline-text config
(get-in config [:block :block/format] :markdown)
title)
:else title)]
(when result-count
[: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
(rum/local nil ::query-result)
{:init (fn [state] (assoc state :query-error (atom nil)))}
[state config {:keys [title builder query view collapsed? table-view?] :as q} *query-triggered?]
(let [*query-error (:query-error state)
built-in? (built-in-custom-query? title)
dsl-query? (:dsl-query? config)
current-block-uuid (or (:block/uuid (:block config))
(:block/uuid config))
current-block (db/entity [:block/uuid current-block-uuid])
temp-collapsed? (state/sub-collapsed current-block-uuid)
collapsed?' (if (some? temp-collapsed?)
temp-collapsed?
(or
collapsed?
(:block/collapsed? current-block)))
table? (or table-view?
(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?
(util/electron?)
(symbol? (gp-util/safe-read-string query)))
not-grouped-by-page? (or table?
(and (string? query) (string/includes? query "(by-page false)")))
result (when-not collapsed?'
(get-query-result state config *query-error *query-triggered? current-block-uuid q not-grouped-by-page?))
query-time (:query-time (meta result))
page-list? (and (seq result)
(some? (:block/name (first result))))
opts {:query-error-atom *query-error
:current-block current-block
:dsl-query? dsl-query?
:table? table?
:view-f view-f
:page-list? page-list?
:result result}]
(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 built-in?
[:div.th
(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
(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 [] (editor-handler/set-block-property! 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 [] (editor-handler/set-block-property! 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-keys 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-mouse-down (fn [e]
(util/stop e)
(trigger-custom-query! state *query-error *query-triggered?))}))]])])
(when dsl-query? builder)
(if built-in?
[:div {:style {:margin-left 2}}
(ui/foldable
(query-title config title {:result-count (count result)})
(fn []
(custom-query-inner config q opts))
{:default-collapsed? collapsed?
:title-trigger? true})]
[:div.bd
(when-not collapsed?'
(custom-query-inner config q opts))])]))))
(rum/defcs custom-query < rum/static
(rum/local false ::query-triggered?)
[state config q]
(ui/catch-error
(ui/block-error "Query Error:" {:content (:query q)})
(ui/lazy-visible
(fn []
(custom-query* config q (::query-triggered? state)))
{:debug-id q
:trigger-once? false})))

View File

@ -144,7 +144,11 @@
(defn get-query-cached-result
[k]
(:result (get @query-state k)))
(when-let [result (get @query-state k)]
(when (satisfies? IWithMeta @(:result result))
(set! (.-state (:result result))
(with-meta @(:result result) {:query-time (:query-time result)})))
(:result result)))
(defn q
[repo k {:keys [use-cache? transform-fn query-fn inputs-fn disable-reactive?]
@ -179,7 +183,7 @@
transform-fn))
result-atom (or result-atom (atom nil))]
;; Don't notify watches now
(set! (.-state result-atom) (util/safe-with-meta result {:query-time time}))
(set! (.-state result-atom) result)
(if disable-reactive?
result-atom
(add-q! k query time inputs result-atom transform-fn query-fn inputs-fn))))))))

View File

@ -7,7 +7,8 @@
[lambdaisland.glogi :as log]
["mldoc" :as mldoc :refer [Mldoc]]
[logseq.graph-parser.mldoc :as gp-mldoc]
[logseq.graph-parser.util :as gp-util]))
[logseq.graph-parser.util :as gp-util]
[clojure.walk :as walk]))
(defonce anchorLink (gobj/get Mldoc "anchorLink"))
(defonce parseOPML (gobj/get Mldoc "parseOPML"))
@ -79,3 +80,15 @@
[ast typ]
(and (contains? #{"Drawer"} (ffirst ast))
(= typ (second (first ast)))))
(defn extract-first-query-from-ast [ast]
(let [*result (atom nil)]
(walk/postwalk
(fn [f]
(if (and (vector? f)
(= "Custom" (first f))
(= "query" (second f)))
(reset! *result (last f))
f))
ast)
@*result))

View File

@ -3338,6 +3338,17 @@
(catch :default _e
nil)))))))))
(defn- valid-custom-query-block?
"Whether block has a valid customl query."
[block]
(let [entity (db/entity (:db/id block))
content (:block/content entity)]
(when (and (string/includes? content "#+BEGIN_QUERY")
(string/includes? content "#+END_QUERY"))
(let [ast (mldoc/->edn (string/trim content) (gp-mldoc/default-config (or (:block/format entity) :markdown)))
q (mldoc/extract-first-query-from-ast ast)]
(some? (:query (gp-util/safe-read-string q)))))))
(defn collapsable?
([block-id]
(collapsable? block-id {}))
@ -3347,6 +3358,7 @@
(if-let [block (db-model/query-block-by-uuid block-id)]
(or (db-model/has-children? block-id)
(valid-dsl-query-block? block)
(valid-custom-query-block? block)
(and
(:outliner/block-title-collapse-enabled? (state/get-config))
(block-with-title? (:block/format block)