Просмотр исходного кода

feat(query dsl): add custom component

Tienson Qin 5 лет назад
Родитель
Сommit
378d692eaa

+ 10 - 2
src/main/api.cljs

@@ -2,9 +2,17 @@
   (:require [frontend.db :as db]
             [frontend.state :as state]
             [datascript.core :as d]
-            [cljs.reader]))
+            [cljs.reader]
+            [frontend.db.query-dsl :as query-dsl]))
 
-(defn ^:export query
+(defn ^:export q
+  [query-string]
+  (when-let [repo (state/get-current-repo)]
+    (when-let [conn (db/get-conn repo)]
+      (when-let [result (query-dsl/query query-string)]
+        @result))))
+
+(defn ^:export datascript_query
   [query & inputs]
   (when-let [repo (state/get-current-repo)]
     (when-let [conn (db/get-conn repo)]

+ 7 - 7
src/main/frontend/commands.cljs

@@ -49,14 +49,14 @@
 (defn embed-page
   []
   (conj
-   [[:editor/input "{{{embed [[]]}}}" {:last-pattern slash
-                                       :backward-pos 5}]]
+   [[:editor/input "{{embed [[]]}}" {:last-pattern slash
+                                     :backward-pos 5}]]
    [:editor/search-page :embed]))
 
 (defn embed-block
   []
-  [[:editor/input "{{{embed (())}}}" {:last-pattern slash
-                                      :backward-pos 5}]
+  [[:editor/input "{{embed (())}}" {:last-pattern slash
+                                    :backward-pos 5}]
    [:editor/search-block :embed]])
 
 ;; Stop now!!
@@ -100,7 +100,7 @@
                   [:editor/show-date-picker]]]
      ["Scheduled" [[:editor/clear-current-slash]
                    [:editor/show-date-picker]]]
-     ["Query" [[:editor/input "{{{query }}}" {:backward-pos 3}]]]
+     ["Query" [[:editor/input "{{query }}" {:backward-pos 3}]]]
      ["Draw" [[:editor/input "/draw "]
               [:editor/show-input [{:command :draw
                                     :id :title
@@ -125,8 +125,8 @@
      ["Image Link" link-steps]
      (when (state/logged?)
        ["Upload an image" [[:editor/click-hidden-file-input :id]]])
-     ["Embed Youtube Video" [[:editor/input "{{{youtube }}}" {:last-pattern slash
-                                                              :backward-pos 3}]]]
+     ["Embed Youtube Video" [[:editor/input "{{youtube }}" {:last-pattern slash
+                                                            :backward-pos 3}]]]
      ["Html Inline " (->inline "html")]
 
      ;; TODO:

+ 26 - 7
src/main/frontend/components/block.cljs

@@ -8,6 +8,7 @@
             [frontend.state :as state]
             [frontend.db :as db]
             [frontend.db.model :as model]
+            [frontend.db.query-dsl :as query-dsl]
             [dommy.core :as d]
             [datascript.core :as dc]
             [goog.dom :as gdom]
@@ -443,6 +444,8 @@
                 (not (= (:id config) "contents")))
        [:span.text-gray-500 "]]"])]))
 
+(declare custom-query)
+
 (defn inline
   [{:keys [html-export?] :as config} item]
   (match item
@@ -655,7 +658,10 @@
                       arguments)]
       (cond
         (= name "query")
-        [:div "TBD"]
+        [:div.dsl-query
+         (custom-query (assoc config :dsl-query? true)
+                       {:title [:code (str "Query: " (first arguments))]
+                        :query (first arguments)})]
 
         (= name "youtube")
         (let [url (first arguments)]
@@ -1549,30 +1555,42 @@
       (when (seq queries)
         (boolean (some #(= % title) (map :title queries)))))))
 
+(defn- trigger-custom-query!
+  [state]
+  (let [[config query] (:rum/args state)
+        query-atom (if (:dsl-query? config)
+                     (query-dsl/query (:query query))
+                     (db/custom-query query))]
+    (assoc state :query-atom query-atom)))
+
 (rum/defcs custom-query < rum/reactive
-  {:will-mount (fn [state]
-                 (let [[config query] (:rum/args state)
-                       query-atom (db/custom-query query)]
-                   (assoc state :query-atom query-atom)))
+  {:will-mount trigger-custom-query!
    :did-mount (fn [state]
                 (when-let [query (last (:rum/args state))]
                   (state/add-custom-query-component! query (:rum/react-component state)))
                 state)
+   :did-remount (fn [_old_state state]
+                  (trigger-custom-query! state))
    :will-unmount (fn [state]
                    (when-let [query (last (:rum/args state))]
                      (state/remove-custom-query-component! query)
                      (db/remove-custom-query! (state/get-current-repo) query))
                    state)}
   [state config {:keys [title query inputs view collapsed? children?] :as q}]
-  (let [query-atom (:query-atom state)]
+  (let [dsl-query? (:dsl-query? config)
+        query-atom (:query-atom state)]
     (let [current-block-uuid (or (:block/uuid (:block config))
                                  (:block/uuid config))
           ;; exclude the current one, otherwise it'll loop forever
           remove-blocks (if current-block-uuid [current-block-uuid] nil)
           query-result (and query-atom (rum/react query-atom))
+
+          result (if (and query-result dsl-query?)
+                   query-result
+                   (db/custom-query-result-transform query-result remove-blocks q))
           result (if query-result
                    (db/custom-query-result-transform query-result remove-blocks q))
-          view-f (sci/eval-string (pr-str view))
+          view-f (and view (sci/eval-string (pr-str view)))
           only-blocks? (:block/uuid (first result))
           blocks-grouped-by-page? (and (seq result)
                                        (coll? (first result))
@@ -1599,6 +1617,7 @@
             (and (seq result)
                  (or only-blocks? blocks-grouped-by-page?))
             (->hiccup result (cond-> (assoc config
+                                            ;; :editor-box editor/box
                                             :custom-query? true
                                             :group-by-page? blocks-grouped-by-page?)
                                children?

+ 1 - 1
src/main/frontend/components/editor.cljs

@@ -643,7 +643,7 @@
                   (when-let [element (gdom/getElement id)]
                     (.focus element)))
                 state)
-   :did-remount (fn [state]
+   :did-remount (fn [_old-state state]
                   (keyboards-handler/esc-save! state)
                   state)
    :will-unmount (fn [state]

+ 27 - 31
src/main/frontend/db/query_custom.cljs

@@ -37,37 +37,6 @@
     :else
     input))
 
-(defn- custom-query-aux
-  [{:keys [query inputs] :as query'} query-opts]
-  (try
-    (let [inputs (map resolve-input inputs)
-          repo (state/get-current-repo)
-          k [:custom query']]
-      (apply react/q repo k query-opts query inputs))
-    (catch js/Error e
-      (println "Custom query failed: ")
-      (js/console.dir e))))
-
-(defn custom-query
-  ([query]
-   (custom-query query {}))
-  ([query query-opts]
-   (when-let [query' (cond
-                       (and (string? query)
-                            (not (string/blank? query)))
-                       (reader/read-string query)
-
-                       (map? query)
-                       query
-
-                       :else
-                       nil)]
-     (custom-query-aux query' query-opts))))
-
-(defn simple-custom-query
-  [query-string]
-  (custom-query-aux query-string {}))
-
 (defn custom-query-result-transform
   [query-result remove-blocks q]
   (let [repo (state/get-current-repo)
@@ -96,3 +65,30 @@
         (if block?
           (db-utils/group-by-page result)
           result)))))
+
+(defn react-query
+  [{:keys [query inputs] :as query'} query-opts]
+  (try
+    (let [inputs (map resolve-input inputs)
+          repo (state/get-current-repo)
+          k [:custom query']]
+      (apply react/q repo k query-opts query inputs))
+    (catch js/Error e
+      (println "Custom query failed: ")
+      (js/console.dir e))))
+
+(defn custom-query
+  ([query]
+   (custom-query query {}))
+  ([query query-opts]
+   (when-let [query' (cond
+                       (and (string? query)
+                            (not (string/blank? query)))
+                       (reader/read-string query)
+
+                       (map? query)
+                       query
+
+                       :else
+                       nil)]
+     (react-query query' query-opts))))

+ 78 - 64
src/main/frontend/db/query_dsl.cljs

@@ -5,8 +5,10 @@
             [lambdaisland.glogi :as log]
             [clojure.string :as string]
             [frontend.db :as db]
+            [frontend.db.query-custom :as query-custom]
             [cljs-time.core :as t]
-            [frontend.util :as util]))
+            [frontend.util :as util]
+            [medley.core :as medley]))
 
 ;; Query fields:
 
@@ -18,16 +20,16 @@
 ;; property (block)
 ;; todo (block)
 ;; priority (block)
-;; page-property (page, TBD)
+;; page_tag (page, TBD)
+;; page_property (page, TBD)
 ;; project (block, TBD)
 
 ;; Sort by (field, asc/desc):
 
-;; block-title
-;; page-title
-;; created-at
-;; updated-at
+;; created_at
+;; last_modified_at
 
+;; (sort-by last_modified_at asc)
 
 (defonce remove-nil? (partial remove nil?))
 
@@ -65,9 +67,9 @@
         (db-utils/date->int (t/plus (t/today) (tf duration)))))))
 
 (defn build-query
-  ([e]
-   (build-query e 0))
-  ([e level]
+  ([e sort-by]
+   (build-query e sort-by 0))
+  ([e sort-by level]
    ;; TODO: replace with multi-methods for extensibility.
    (let [fe (first e)]
      (cond
@@ -75,7 +77,7 @@
        nil
 
        (contains? #{'and 'or} fe)
-       (let [clauses (->> (map #(build-query % (inc level)) (rest e))
+       (let [clauses (->> (map #(build-query % sort-by (inc level)) (rest e))
                           remove-nil?
                           (apply concat))]
          (when (seq clauses)
@@ -84,8 +86,8 @@
                result
                [result]))))
 
-       (= 'not fe)                       ; or
-       (let [clauses (->> (map build-query (rest e))
+       (= 'not fe)
+       (let [clauses (->> (map #(build-query % sort-by) (rest e))
                           remove-nil?
                           (apply concat))]
          (when (seq clauses)
@@ -108,16 +110,16 @@
         [(list 'get '?p (name (nth e 1))) '?v]
         [(list '= '?v (name (nth e 2)))]]
 
-       (and (= 'todo fe))
+       (= 'todo fe)
        (let [markers (if (coll? (first (rest e)))
                        (first (rest e))
                        (rest e))]
          (when (seq markers)
-           (let [markers (set (map name markers))]
+           (let [markers (set (map (comp string/upper-case name) markers))]
              [['?b :block/marker '?marker]
               [(list 'contains? markers '?marker)]])))
 
-       (and (= 'priority fe))
+       (= 'priority fe)
        (let [priorities (if (coll? (first (rest e)))
                           (first (rest e))
                           (rest e))]
@@ -137,6 +139,24 @@
                     (some? (db-utils/entity [:page/name page-name])))
            [['?b :block/ref-pages [:page/name page-name]]]))
 
+       (= 'sort-by fe)
+       (let [[k order] (rest e)
+             order (if (and order (contains? #{:asc :desc}
+                                             (keyword (string/lower-case (name order)))))
+                     (keyword (string/lower-case (name order)))
+                     :desc)
+             k (-> (string/lower-case (name k))
+                   (string/replace "-" "_"))]
+         (when (contains? #{"created_at" "last_modified_at"} k)
+           (do
+             (reset! sort-by
+                     (fn [result]
+                       (->> result
+                            flatten
+                            (clojure.core/sort-by #(get-in % [:block/properties k])
+                                                  (if :desc >= <=)))))
+             nil)))
+
        :else
        nil))))
 
@@ -147,65 +167,59 @@
   (when (and (string? s)
              (not (string/blank? s)))
     (try
-      (let [s (string/replace s link-re "\"[[$1]]\"")
-            result (some->
-                    s
-                    (reader/read-string)
-                    (build-query))]
-        (when (seq result)
-          (let [key (if (coll? (first result))
-                      (keyword (ffirst result))
-                      (keyword (first result)))]
-            (case key
-              :and
-              (rest result)
-
-              :not
-              (cons ['?b :block/uuid] result)
-
-              :or
-              [['?b :block/uuid] result]
-
-              result))))
+      (let [form (some-> (string/replace s link-re "\"[[$1]]\"")
+                         (reader/read-string))
+
+            sort-by (atom nil)
+            result (when form (build-query form sort-by))
+            result (when (seq result)
+                     (let [key (if (coll? (first result))
+                                 (keyword (ffirst result))
+                                 (keyword (first result)))]
+                       (case key
+                         :and
+                         (rest result)
+
+                         :not
+                         (cons ['?b :block/uuid] result)
+
+                         :or
+                         [['?b :block/uuid] result]
+
+                         result)))]
+        {:query result
+         :sort-by @sort-by})
       (catch js/Error e
         (log/error :query-dsl/parse-error e)))))
 
-(comment
-  (require '[frontend.db :as db])
+(defn query
+  [query-string]
+  (when query-string
+    (let [{:keys [query sort-by]} (parse query-string)]
+      (when query
+        (let [query (query-wrapper query)]
+          (query-custom/react-query {:query query}
+                                    (if sort-by
+                                      {:transform-fn sort-by})))))))
 
-  (d/q
-   (query-wrapper (parse "(and [[foo]] [[bar]])"))
-   (db/get-conn))
+(comment
+  (query "(and [[foo]] [[bar]])")
 
-  (d/q
-   (query-wrapper (parse "(or [[foo]] [[bar]])"))
-   (db/get-conn))
+  (query "(or [[foo]] [[bar]])")
 
-  (d/q
-   (query-wrapper (parse "(not [[foo]])"))
-   (db/get-conn))
+  (query "(not [[foo]])")
 
-  (d/q
-   (query-wrapper (parse "(between :-7d :+7d)"))
-   (db/get-conn))
+  (query "(between :-7d :+7d)")
 
-  (d/q
-   (query-wrapper (parse "(between :-7d :today)"))
-   (db/get-conn))
+  (query "(between :-7d :today)")
 
-  (d/q
-   (query-wrapper (parse "(and [[some page]] (property foo bar))"))
-   (db/get-conn))
+  (query "(and [[some page]] (property foo bar))")
 
-  (d/q
-   (query-wrapper (parse "(and [[some page]] (todo now later))"))
-   (db/get-conn))
+  (query "(and [[some page]] (todo now later))")
 
-  (d/q
-   (query-wrapper (parse "(and [[some page]] (priority A))"))
-   (db/get-conn))
+  (query "(and [[some page]] (priority A))")
 
   ;; nested query
-  (d/q
-   (query-wrapper (parse "(and [[baz]] (or [[foo]] [[bar]]))"))
-   (db/get-conn)))
+  (query "(and [[baz]] (or [[foo]] [[bar]]))")
+
+  (query "(and [[some page]] (sort-by created-at))"))

+ 15 - 14
src/main/frontend/db/react.cljs

@@ -51,7 +51,7 @@
   (let [state @query-state
         state (->> (filter (fn [[[_repo k] v]]
                              (contains? #{:blocks :block/block :custom} k)) state)
-                (into {}))]
+                   (into {}))]
     (reset! query-state state)))
 
 ;; TODO: Add components which subscribed to a specific query
@@ -72,21 +72,21 @@
 (defn add-query-component!
   [key component]
   (swap! query-components update key
-    (fn [components]
-      (distinct (conj components component)))))
+         (fn [components]
+           (distinct (conj components component)))))
 
 (defn remove-query-component!
   [component]
   (reset!
-    query-components
-    (->> (for [[k components] @query-components
-               :let [new-components (remove #(= component %) components)]]
-           (if (empty? new-components) ; no subscribed components
-             (do (remove-q! k)
-                 nil)
-             [k new-components]))
-      (keep identity)
-      (into {}))))
+   query-components
+   (->> (for [[k components] @query-components
+              :let [new-components (remove #(= component %) components)]]
+          (if (empty? new-components) ; no subscribed components
+            (do (remove-q! k)
+                nil)
+            [k new-components]))
+        (keep identity)
+        (into {}))))
 
 (defn get-page-blocks-cache-atom
   [repo page-id]
@@ -160,6 +160,7 @@
 
 ;; TODO: Extract several parts to handlers
 
+
 (defn get-current-page
   []
   (let [match (:route-match @state/state)
@@ -179,8 +180,8 @@
     (when page
       (let [page-name (util/url-decode (string/lower-case page))]
         (db-utils/entity (if tag?
-                        [:tag/name page-name]
-                        [:page/name page-name]))))))
+                           [:tag/name page-name]
+                           [:page/name page-name]))))))
 
 (defn get-current-priority
   []

+ 37 - 37
src/main/frontend/extensions/code.cljs

@@ -107,42 +107,42 @@
     (if @editor-atom
       @editor-atom
       (let [[config id attr code pos_meta] (:rum/args state)
-           original-mode (get attr :data-lang)
-           mode (or original-mode "javascript")
-           clojure? (contains? #{"clojure" "clj" "text/x-clojure" "cljs" "cljc"} mode)
-           mode (if clojure? "clojure" (text->cm-mode mode))
-           lisp? (or clojure?
-                     (contains? #{"scheme" "racket" "lisp"} mode))
-           textarea (gdom/getElement id)
-           editor (or
-                   @(:editor-atom state)
-                   (when textarea
-                     (from-textarea textarea
-                                    #js {:mode mode
-                                         :matchBrackets lisp?
-                                         :autoCloseBrackets true
-                                         :lineNumbers true
-                                         :extraKeys #js {"Esc" (fn [cm]
-                                                                 (let [save! #(save-file-or-block-when-blur-or-esc! cm textarea config state)]
-                                                                   (if-let [block-id (:block/uuid config)]
-                                                                     (let [block (db/pull [:block/uuid block-id])
-                                                                           value (.getValue cm)
-                                                                           textarea-value (gobj/get textarea "value")
-                                                                           changed? (not= value textarea-value)]
-                                                                       (if changed?
-                                                                         (save!)
-                                                                         (editor-handler/edit-block! block :max (:block/format block) block-id)))
-                                                                     (save!))))}})))]
-       (when editor
-         (let [element (.getWrapperElement editor)]
-           (.on editor "blur" (fn []
-                                (save-file-or-block-when-blur-or-esc! editor textarea config state)))
-           (.addEventListener element "click"
-                              (fn [e]
-                                (util/stop e)))
-           (.save editor)
-           (.refresh editor)))
-       editor))))
+            original-mode (get attr :data-lang)
+            mode (or original-mode "javascript")
+            clojure? (contains? #{"clojure" "clj" "text/x-clojure" "cljs" "cljc"} mode)
+            mode (if clojure? "clojure" (text->cm-mode mode))
+            lisp? (or clojure?
+                      (contains? #{"scheme" "racket" "lisp"} mode))
+            textarea (gdom/getElement id)
+            editor (or
+                    @(:editor-atom state)
+                    (when textarea
+                      (from-textarea textarea
+                                     #js {:mode mode
+                                          :matchBrackets lisp?
+                                          :autoCloseBrackets true
+                                          :lineNumbers true
+                                          :extraKeys #js {"Esc" (fn [cm]
+                                                                  (let [save! #(save-file-or-block-when-blur-or-esc! cm textarea config state)]
+                                                                    (if-let [block-id (:block/uuid config)]
+                                                                      (let [block (db/pull [:block/uuid block-id])
+                                                                            value (.getValue cm)
+                                                                            textarea-value (gobj/get textarea "value")
+                                                                            changed? (not= value textarea-value)]
+                                                                        (if changed?
+                                                                          (save!)
+                                                                          (editor-handler/edit-block! block :max (:block/format block) block-id)))
+                                                                      (save!))))}})))]
+        (when editor
+          (let [element (.getWrapperElement editor)]
+            (.on editor "blur" (fn []
+                                 (save-file-or-block-when-blur-or-esc! editor textarea config state)))
+            (.addEventListener element "click"
+                               (fn [e]
+                                 (util/stop e)))
+            (.save editor)
+            (.refresh editor)))
+        editor))))
 
 (defn- load-and-render!
   [state]
@@ -158,7 +158,7 @@
    :did-mount (fn [state]
                 (load-and-render! state)
                 state)
-   :did-remount (fn [state]
+   :did-remount (fn [old_state state]
                   (load-and-render! state)
                   state)}
   [state config id attr code pos_meta]

+ 1 - 1
src/main/frontend/external/roam.cljs

@@ -39,7 +39,7 @@
                                            (let [name (case name
                                                         "[[embed]]" "embed"
                                                         name)]
-                                             (util/format "{{{%s %s}}}" name arg))
+                                             (util/format "{{%s %s}}" name arg))
                                            original)))))
 
 (defn load-all-refed-uids!