Browse Source

fix: searching in page defers while entering Chinese character using a Pinyin IME (#8399)

fix: `electron-find-in-page!` not being delayed when using IME
situ2001 2 years ago
parent
commit
b3557bc6df

+ 31 - 18
src/main/frontend/components/find_in_page.cljs

@@ -3,30 +3,43 @@
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.state :as state]
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.util :as util]
-            [frontend.handler.search :as search-handler :refer [debounced-search]]
+            [frontend.handler.search :as search-handler :refer [debounced-search, stop-debounced-search!]]
+            [goog.object :as gobj]
             [goog.dom :as gdom]
             [goog.dom :as gdom]
             [frontend.mixins :as mixins]
             [frontend.mixins :as mixins]
             [clojure.string :as string]))
             [clojure.string :as string]))
 
 
 (rum/defc search-input
 (rum/defc search-input
   [q matches]
   [q matches]
-  [:div.flex.w-48.relative
-   [:input#search-in-page-input.form-input.block.sm:text-sm.sm:leading-5.my-2.border-none.mr-4.outline-none
-    {:auto-focus true
-     :placeholder "Find in page"
-     :aria-label "Find in page"
-     :value q
-     :on-change (fn [e]
-                  (let [value (util/evalue e)]
-                    (state/set-state! [:ui/find-in-page :q] value)
-                    (debounced-search)))}]
-   (when-not (string/blank? q)
-     (when-let [total (:matches matches)]
-      [:div.text-sm.absolute.top-2.right-0.py-2.px-4
-       (:activeMatchOrdinal matches 0)
-       "/"
-       total]))
-   [:div#search-in-page-placeholder.absolute.top-2.left-0.p-2.sm:text-sm]])
+  (let [*composing? (rum/use-ref false)
+        on-change-fn (fn [e]
+                       (let [value (util/evalue e)
+                             e-type (gobj/getValueByKeys e "type")]
+                         (state/set-state! [:ui/find-in-page :q] value)
+                         (cond (= e-type "compositionstart")
+                               (do (rum/set-ref! *composing? true)
+                                   (stop-debounced-search!))
+
+                               (= e-type "compositionend")
+                               (rum/set-ref! *composing? false))
+                         (when-not (rum/deref *composing?)
+                           (debounced-search))))]
+    [:div.flex.w-48.relative
+     [:input#search-in-page-input.form-input.block.sm:text-sm.sm:leading-5.my-2.border-none.mr-4.outline-none
+      {:auto-focus true
+       :placeholder "Find in page"
+       :aria-label "Find in page"
+       :value q
+       :on-composition-start on-change-fn
+       :on-composition-end on-change-fn
+       :on-change on-change-fn}]
+     (when-not (string/blank? q)
+       (when-let [total (:matches matches)]
+         [:div.text-sm.absolute.top-2.right-0.py-2.px-4
+          (:activeMatchOrdinal matches 0)
+          "/"
+          total]))
+     [:div#search-in-page-placeholder.absolute.top-2.left-0.p-2.sm:text-sm]]))
 
 
 (rum/defc search-inner < rum/static
 (rum/defc search-inner < rum/static
   (mixins/event-mixin
   (mixins/event-mixin

+ 3 - 2
src/main/frontend/handler/search.cljs

@@ -10,7 +10,6 @@
             [promesa.core :as p]
             [promesa.core :as p]
             [logseq.graph-parser.text :as text]
             [logseq.graph-parser.text :as text]
             [electron.ipc :as ipc]
             [electron.ipc :as ipc]
-            [goog.functions :refer [debounce]]
             [dommy.core :as dom]))
             [dommy.core :as dom]))
 
 
 (defn add-search-to-recent!
 (defn add-search-to-recent!
@@ -87,7 +86,9 @@
                           (str " " (subs q 1)))))
                           (str " " (subs q 1)))))
         (ipc/ipc "find-in-page" q option)))))
         (ipc/ipc "find-in-page" q option)))))
 
 
-(defonce debounced-search (debounce electron-find-in-page! 500))
+(let [cancelable-debounce-search (util/cancelable-debounce electron-find-in-page! 500)]
+  (defonce debounced-search (first cancelable-debounce-search))
+  (defonce stop-debounced-search! (second cancelable-debounce-search)))
 
 
 (defn loop-find-in-page!
 (defn loop-find-in-page!
   [backward?]
   [backward?]

+ 15 - 0
src/main/frontend/util.cljc

@@ -30,6 +30,7 @@
             [cljs.core.async.impl.channels :refer [ManyToManyChannel]]
             [cljs.core.async.impl.channels :refer [ManyToManyChannel]]
             [medley.core :as medley]
             [medley.core :as medley]
             [frontend.pubsub :as pubsub]))
             [frontend.pubsub :as pubsub]))
+  #?(:cljs (:import [goog.async Debouncer]))
   (:require
   (:require
    [clojure.pprint]
    [clojure.pprint]
    [clojure.string :as string]
    [clojure.string :as string]
@@ -296,6 +297,20 @@
                                       (reset! t nil)
                                       (reset! t nil)
                                       (apply f args))
                                       (apply f args))
                                    threshold)))))))
                                    threshold)))))))
+#?(:cljs 
+   (defn cancelable-debounce
+     "Create a stateful debounce function with specified interval
+   
+      Returns [fire-fn, cancel-fn]
+   
+      Use `fire-fn` to call the function(debounced)
+   
+      Use `cancel-fn` to cancel pending callback if there is"
+     [f interval]
+     (let [debouncer (Debouncer. f interval)]
+       (js/console.log debouncer)
+       [(fn [& args] (.apply (.-fire debouncer) debouncer (to-array args)))
+        (fn [] (.stop debouncer))])))
 
 
 (defn nth-safe [c i]
 (defn nth-safe [c i]
   (if (or (< i 0) (>= i (count c)))
   (if (or (< i 0) (>= i (count c)))