mirror of
https://github.com/logseq/logseq.git
synced 2026-06-01 19:01:22 +00:00
fix: debounce search results
This commit is contained in:
@@ -197,7 +197,8 @@
|
||||
group-key
|
||||
(if (= group-key :create)
|
||||
(count group-items)
|
||||
(count (get-in results [group-key :items])))
|
||||
(or (get-in results [group-key :matched-count])
|
||||
(count (get-in results [group-key :items]))))
|
||||
(mapv #(assoc % :group group-key :item-index (vswap! index inc)) group-items)])))
|
||||
|
||||
(defn state->highlighted-item
|
||||
@@ -383,19 +384,31 @@
|
||||
(= page-id current-page-uuid))
|
||||
:source-block block}))
|
||||
|
||||
(defn- block-search-result->items
|
||||
[result]
|
||||
(if (map? result)
|
||||
{:blocks (:items result)
|
||||
:matched-count (or (:matched-count result)
|
||||
(count (:items result)))}
|
||||
{:blocks result
|
||||
:matched-count (count result)}))
|
||||
|
||||
;; The blocks search action uses an existing handler
|
||||
(defmethod load-results :nodes [group state]
|
||||
(let [!input (::input state)
|
||||
!results (::results state)
|
||||
repo (state/get-current-repo)
|
||||
current-page-uuid (page-util/get-current-page-uuid)
|
||||
expanded? (::expanded? state)
|
||||
opts (cmdk-state/cmdk-block-search-options
|
||||
{:filter-group :nodes
|
||||
:dev? config/dev?
|
||||
:action (get-action)})]
|
||||
:action (get-action)
|
||||
:expanded? expanded?})]
|
||||
(swap! !results assoc-in [group :status] :loading)
|
||||
(swap! !results assoc-in [:current-page :status] :loading)
|
||||
(p/let [blocks (search/block-search repo @!input opts)
|
||||
(p/let [search-result (search/block-search repo @!input opts)
|
||||
{:keys [blocks matched-count]} (block-search-result->items search-result)
|
||||
blocks (remove nil? blocks)
|
||||
items (keep (fn [block]
|
||||
(if (:page? block)
|
||||
@@ -403,8 +416,14 @@
|
||||
(block-item repo block current-page-uuid @!input))) blocks)]
|
||||
(if (= group :current-page)
|
||||
(let [items-on-current-page (filter :current-page? items)]
|
||||
(swap! !results update group merge {:status :success :items items-on-current-page}))
|
||||
(swap! !results update group merge {:status :success :items items})))))
|
||||
(swap! !results update group merge {:status :success
|
||||
:items items-on-current-page
|
||||
:matched-count (count items-on-current-page)
|
||||
:has-more? false}))
|
||||
(swap! !results update group merge {:status :success
|
||||
:items items
|
||||
:matched-count matched-count
|
||||
:has-more? (> matched-count (count items))})))))
|
||||
|
||||
(defmethod load-results :codes [group state]
|
||||
(let [!input (::input state)
|
||||
@@ -478,13 +497,16 @@
|
||||
(let [!results (::results state)
|
||||
!input (::input state)
|
||||
repo (state/get-current-repo)
|
||||
expanded? (::expanded? state)
|
||||
opts (cmdk-state/cmdk-block-search-options
|
||||
{:filter-group :current-page
|
||||
:dev? config/dev?
|
||||
:page-uuid (:block/uuid current-page)})]
|
||||
:page-uuid (:block/uuid current-page)
|
||||
:expanded? expanded?})]
|
||||
(swap! !results assoc-in [group :status] :loading)
|
||||
(swap! !results assoc-in [:current-page :status] :loading)
|
||||
(p/let [blocks (search/block-search repo @!input opts)
|
||||
(p/let [search-result (search/block-search repo @!input opts)
|
||||
{:keys [blocks matched-count]} (block-search-result->items search-result)
|
||||
blocks (remove nil? blocks)
|
||||
items (map (fn [block]
|
||||
(let [id (if (uuid? (:block/uuid block))
|
||||
@@ -497,7 +519,10 @@
|
||||
:result-type (if (:page? block) :page :block)
|
||||
:current-page? true
|
||||
:source-block block})) blocks)]
|
||||
(swap! !results update :current-page merge {:status :success :items items})))
|
||||
(swap! !results update :current-page merge {:status :success
|
||||
:items items
|
||||
:matched-count matched-count
|
||||
:has-more? (> matched-count (count items))})))
|
||||
(reset! (::filter state) nil)))
|
||||
|
||||
;; The default load-results function triggers all the other load-results function
|
||||
@@ -838,14 +863,20 @@
|
||||
nil)]
|
||||
[:div {:data-item-index item-idx}
|
||||
(if (= group :nodes)
|
||||
(ui/lazy-visible (fn [] item) {:root scroll-root
|
||||
:root-margin "500px 0px"})
|
||||
item)]))
|
||||
(ui/lazy-visible (fn [] item) {:root scroll-root
|
||||
:root-margin "500px 0px"})
|
||||
item)]))
|
||||
|
||||
(defn- show-more-results!
|
||||
[state group]
|
||||
(swap! (::results state) assoc-in [group :show] :more)
|
||||
(when (contains? #{:nodes :current-page} group)
|
||||
(load-results group (assoc state ::expanded? true))))
|
||||
|
||||
(rum/defcs result-group
|
||||
< rum/reactive
|
||||
[state' state title group visible-items first-item sidebar?]
|
||||
(let [{:keys [show items]} (some-> state ::results deref group)
|
||||
(let [{:keys [show items matched-count has-more?]} (some-> state ::results deref group)
|
||||
focus-source @(::focus-source state)
|
||||
highlighted-item (or @(::highlighted-item state)
|
||||
(when (= :keyboard focus-source) first-item))
|
||||
@@ -853,9 +884,10 @@
|
||||
input @(::input state)
|
||||
filter' @(::filter state)
|
||||
can-show-less? (< (get-group-limit group) (count visible-items))
|
||||
can-show-more? (< (count visible-items) (count items))
|
||||
can-show-more? (or has-more?
|
||||
(< (count visible-items) (count items)))
|
||||
show-less #(swap! (::results state) assoc-in [group :show] :less)
|
||||
show-more #(swap! (::results state) assoc-in [group :show] :more)]
|
||||
show-more #(show-more-results! state group)]
|
||||
[:div {:class (if (= group :create)
|
||||
"border-b border-gray-06 last:border-b-0"
|
||||
"border-b border-gray-06 pb-1 last:border-b-0")}
|
||||
@@ -864,15 +896,17 @@
|
||||
[:div {:class "font-bold text-gray-11 pl-0.5 cursor-pointer select-none"
|
||||
:on-click (fn [_e]
|
||||
;; change :less to :more or :more to :less
|
||||
(swap! (::results state) update-in [group :show] {:more :less
|
||||
:less :more}))}
|
||||
(if (= show :more)
|
||||
(show-less)
|
||||
(show-more)))}
|
||||
title]
|
||||
(when (not= group :create)
|
||||
[:div {:class "pl-1.5 text-gray-12 rounded-full"
|
||||
:style {:font-size "0.7rem"}}
|
||||
(if (<= 100 (count items))
|
||||
"99+"
|
||||
(count items))])
|
||||
(let [display-count (or matched-count (count items))]
|
||||
[:div {:class "pl-1.5 text-gray-12 rounded-full"
|
||||
:style {:font-size "0.7rem"}}
|
||||
(if (<= 99 display-count)
|
||||
"99+"
|
||||
display-count)]))
|
||||
|
||||
[:div {:class "flex-1"}]
|
||||
|
||||
@@ -946,9 +980,15 @@
|
||||
(reset! (::pending-scroll-item-idx state) nil)
|
||||
(reset! (::highlighted-item state) nil)))))
|
||||
|
||||
(defn- refresh-results!
|
||||
[state]
|
||||
(persist-cmdk-query-state! state)
|
||||
(load-results :default state))
|
||||
|
||||
(defn handle-input-change
|
||||
([state e] (handle-input-change state e (.. e -target -value)))
|
||||
([state e input]
|
||||
([state e] (handle-input-change state e (.. e -target -value) true))
|
||||
([state e input] (handle-input-change state e input true))
|
||||
([state e input refresh?]
|
||||
(let [composing? (util/native-event-is-composing? e)
|
||||
e-type (gobj/getValueByKeys e "type")
|
||||
composing-end? (= e-type "compositionend")
|
||||
@@ -964,9 +1004,8 @@
|
||||
(when container
|
||||
(set! (.-scrollTop container) 0))
|
||||
;; retrieve the load-results function and update all the results
|
||||
(when (or (not composing?) composing-end?)
|
||||
(persist-cmdk-query-state! state)
|
||||
(load-results :default state)))))
|
||||
(when (and refresh? (or (not composing?) composing-end?))
|
||||
(refresh-results! state)))))
|
||||
|
||||
(defn- open-current-item-link
|
||||
"Opens a link for the current item if a page or block. For pages, opens the
|
||||
@@ -1013,7 +1052,7 @@
|
||||
(swap! (::results state) assoc-in [highlighted-group :show] :less)))
|
||||
show-more (fn []
|
||||
(when highlighted-group
|
||||
(swap! (::results state) assoc-in [highlighted-group :show] :more)))
|
||||
(show-more-results! state highlighted-group)))
|
||||
input @(::input state)
|
||||
as-keydown? (or (= keyname "ArrowDown") (and ctrl? (= keyname "n")))
|
||||
as-keyup? (or (= keyname "ArrowUp") (and ctrl? (= keyname "p")))]
|
||||
@@ -1098,17 +1137,11 @@
|
||||
(let [highlighted-item @(::highlighted-item state)
|
||||
input @(::input state)
|
||||
input-ref (::input-ref state)
|
||||
debounced-on-change (hooks/use-callback
|
||||
(gfun/debounce
|
||||
(fn [e]
|
||||
(let [new-value (.-value (.-target e))]
|
||||
(handle-input-change state e)
|
||||
(when-let [on-change (:on-input-change opts)]
|
||||
(on-change new-value))))
|
||||
200)
|
||||
[])
|
||||
debounced-composition-end (hooks/use-callback
|
||||
(gfun/debounce (fn [e] (handle-input-change state e)) 100)
|
||||
debounced-refresh-results (hooks/use-callback
|
||||
(gfun/debounce
|
||||
(fn []
|
||||
(refresh-results! state))
|
||||
150)
|
||||
[])]
|
||||
(hooks/use-effect! (fn []
|
||||
(reset! (::all-items-cache state) (vec all-items))
|
||||
@@ -1142,11 +1175,20 @@
|
||||
:autoCapitalize "off"
|
||||
:placeholder (input-placeholder)
|
||||
:ref #(when-not @input-ref (reset! input-ref %))
|
||||
:on-change debounced-on-change
|
||||
:on-change (fn [e]
|
||||
(let [new-value (.-value (.-target e))
|
||||
composing? (util/native-event-is-composing? e)]
|
||||
(handle-input-change state e new-value false)
|
||||
(when-not composing?
|
||||
(debounced-refresh-results))
|
||||
(when-let [on-change (:on-input-change opts)]
|
||||
(on-change new-value))))
|
||||
:on-blur (fn [_e]
|
||||
(when-let [on-blur (:on-input-blur opts)]
|
||||
(on-blur input)))
|
||||
:on-composition-end debounced-composition-end
|
||||
:on-composition-end (fn [e]
|
||||
(handle-input-change state e (.. e -target -value) false)
|
||||
(debounced-refresh-results))
|
||||
:default-value input}]]))
|
||||
|
||||
(defn- tip-with-shortcut
|
||||
|
||||
@@ -70,21 +70,27 @@
|
||||
(save-last-cmdk-search! repo input (:group filter-state))))
|
||||
|
||||
(defn cmdk-block-search-options
|
||||
[{:keys [filter-group dev? action page-uuid]}]
|
||||
(let [nodes-base {:limit 100
|
||||
[{:keys [filter-group dev? action page-uuid expanded?]}]
|
||||
(let [nodes-limit (if expanded? 100 10)
|
||||
nodes-base {:limit nodes-limit
|
||||
:search-limit 100
|
||||
:dev? dev?
|
||||
:built-in? true
|
||||
:enable-snippet? true}]
|
||||
:enable-snippet? true
|
||||
:include-matched-count? true}]
|
||||
(case filter-group
|
||||
:code
|
||||
(assoc nodes-base
|
||||
:limit 20
|
||||
;; larger limit for code search since most of the results will be filtered out
|
||||
:search-limit 300
|
||||
:code-only? true)
|
||||
|
||||
:current-page
|
||||
(cond-> {:limit 100
|
||||
:enable-snippet? true}
|
||||
(cond-> {:limit nodes-limit
|
||||
:search-limit 100
|
||||
:enable-snippet? true
|
||||
:include-matched-count? true}
|
||||
page-uuid
|
||||
(assoc :page (str page-uuid)))
|
||||
|
||||
|
||||
@@ -307,12 +307,23 @@
|
||||
;; TODO: use rum/use-effect instead
|
||||
(rum/defcs block-search-auto-complete < rum/reactive
|
||||
{:init (fn [state]
|
||||
(let [result (atom nil)]
|
||||
(search-blocks! state result)
|
||||
(assoc state ::result result)))
|
||||
(let [result (atom nil)
|
||||
[debounced-search stop-search!] (util/cancelable-debounce search-blocks! 150)]
|
||||
(debounced-search state result)
|
||||
(assoc state
|
||||
::result result
|
||||
::debounced-search debounced-search
|
||||
::stop-search! stop-search!)))
|
||||
:did-update (fn [state]
|
||||
(search-blocks! state (::result state))
|
||||
state)}
|
||||
(let [[_edit-block _ _ q] (:rum/args state)]
|
||||
(if (string/blank? q)
|
||||
(reset! (::result state) nil)
|
||||
((::debounced-search state) state (::result state))))
|
||||
state)
|
||||
:will-unmount (fn [state]
|
||||
(when-let [stop-search! (::stop-search! state)]
|
||||
(stop-search!))
|
||||
state)}
|
||||
[state _edit-block input id q format selected-text]
|
||||
(let [result (->> (rum/react (get state ::result))
|
||||
(remove (fn [b] (nil? (:block/uuid b)))))
|
||||
|
||||
@@ -1542,6 +1542,8 @@
|
||||
[q & [{:keys [nlp-pages? page-only?]}]]
|
||||
(p/let [block (state/get-edit-block)
|
||||
result (search/block-search (state/get-current-repo) q {:built-in? false
|
||||
:limit 20
|
||||
:search-limit 100
|
||||
:enable-snippet? false
|
||||
:page-only? page-only?})
|
||||
matched (remove (fn [b] (= (:block/uuid b) (:block/uuid block))) result)
|
||||
|
||||
@@ -584,21 +584,42 @@ DROP TRIGGER IF EXISTS blocks_au;
|
||||
(when (include-search-block? conn block code-class option)
|
||||
(let [display-title (if (:enable-snippet? option)
|
||||
(ensure-highlighted-snippet snippet title q)
|
||||
(or snippet title))]
|
||||
{:db/id (:db/id block)
|
||||
:block/uuid (:block/uuid block)
|
||||
:block/title display-title
|
||||
:block.temp/original-title (:block/title block)
|
||||
:block/page (or
|
||||
(:block/uuid (:block/page block))
|
||||
(when (and page (common-util/uuid-string? page))
|
||||
(uuid page)))
|
||||
:block/parent (:db/id (:block/parent block))
|
||||
:block/tags (seq (map :db/id (:block/tags block)))
|
||||
:logseq.property/icon (:logseq.property/icon block)
|
||||
:page? (ldb/page? block)
|
||||
:alias (some-> (first (:block/_alias block))
|
||||
(select-keys [:block/uuid :block/title]))})))))
|
||||
(or snippet title))
|
||||
block-page (or
|
||||
(:block/uuid (:block/page block))
|
||||
(when (and page (common-util/uuid-string? page))
|
||||
(uuid page)))
|
||||
parent-id (:db/id (:block/parent block))
|
||||
tag-ids (seq (map :db/id (:block/tags block)))
|
||||
icon (:logseq.property/icon block)
|
||||
alias (some-> (first (:block/_alias block))
|
||||
(select-keys [:block/uuid :block/title]))]
|
||||
(cond-> {:db/id (:db/id block)
|
||||
:block/uuid (:block/uuid block)
|
||||
:block/title display-title
|
||||
:block.temp/original-title (:block/title block)
|
||||
:page? (ldb/page? block)}
|
||||
block-page
|
||||
(assoc :block/page block-page)
|
||||
|
||||
parent-id
|
||||
(assoc :block/parent parent-id)
|
||||
|
||||
tag-ids
|
||||
(assoc :block/tags tag-ids)
|
||||
|
||||
icon
|
||||
(assoc :logseq.property/icon icon)
|
||||
|
||||
alias
|
||||
(assoc :alias alias)))))))
|
||||
|
||||
(defn- search-result-visible?
|
||||
[conn code-class option {:keys [id] :as result}]
|
||||
(let [block-id (uuid id)]
|
||||
(when-let [block (or (get result search-result-block-key)
|
||||
(d/entity @conn [:block/uuid block-id]))]
|
||||
(include-search-block? conn block code-class option))))
|
||||
|
||||
(defn search-blocks
|
||||
"Options:
|
||||
@@ -609,7 +630,7 @@ DROP TRIGGER IF EXISTS blocks_au;
|
||||
* :dev? - Allow all nodes to be seen for development. Defaults to false
|
||||
* :code-only? - Whether to return only code blocks. Defaults to false
|
||||
* :built-in? - Whether to return public built-in nodes for db graphs. Defaults to false"
|
||||
[conn search-db q {:keys [limit search-limit page enable-snippet? page-only? code-only?]
|
||||
[conn search-db q {:keys [limit search-limit page enable-snippet? page-only? code-only? include-matched-count?]
|
||||
:as option
|
||||
:or {enable-snippet? true}}]
|
||||
(when-not (string/blank? q)
|
||||
@@ -639,10 +660,15 @@ DROP TRIGGER IF EXISTS blocks_au;
|
||||
combined-result (combine-results @conn (concat fuzzy-result matched-result non-match-result))
|
||||
code-class (when code-only?
|
||||
(d/entity @conn :logseq.class/Code-block))
|
||||
matched-count (when include-matched-count?
|
||||
(count (filter #(search-result-visible? conn code-class option %) combined-result)))
|
||||
result (->> combined-result
|
||||
(common-util/distinct-by :id)
|
||||
(keep #(search-result->block-result conn q code-class option %)))]
|
||||
(take limit result))))
|
||||
(if include-matched-count?
|
||||
{:items (take limit result)
|
||||
:matched-count matched-count}
|
||||
(take limit result)))))
|
||||
|
||||
(defn truncate-table!
|
||||
[db]
|
||||
|
||||
Reference in New Issue
Block a user