Sfoglia il codice sorgente

Merge branch 'feat/db' into feat/capacitor-new

charlie 6 mesi fa
parent
commit
0a61981c48
36 ha cambiato i file con 684 aggiunte e 637 eliminazioni
  1. 2 1
      clj-e2e/deps.edn
  2. 73 0
      clj-e2e/dev/user.clj
  3. 22 0
      clj-e2e/src/logseq/e2e/assert.clj
  4. 5 0
      clj-e2e/src/logseq/e2e/config.clj
  5. 24 0
      clj-e2e/src/logseq/e2e/playwright_page.clj
  6. 0 38
      clj-e2e/src/logseq/e2e/repl.clj
  7. 28 18
      clj-e2e/src/logseq/e2e/util.clj
  8. 32 19
      clj-e2e/test/logseq/e2e/fixtures.clj
  9. 23 0
      clj-e2e/test/logseq/e2e/multi_tabs_test.clj
  10. 1 8
      clj-e2e/test/logseq/e2e/rtc_basic_test.clj
  11. 7 6
      deps/shui/src/logseq/shui/dialog/core.cljs
  12. 0 1
      deps/shui/src/logseq/shui/popup/core.cljs
  13. 2 1
      deps/shui/src/logseq/shui/util.cljs
  14. 1 0
      gulpfile.js
  15. 1 1
      packages/ui/.storybook/preview.js
  16. 1 0
      packages/ui/@/components/ui/dialog.tsx
  17. 1 0
      packages/ui/@/components/ui/toaster.tsx
  18. 2 2
      packages/ui/examples/index.tsx
  19. 20 24
      packages/ui/package.json
  20. 2 1
      packages/ui/src/Demo.story.tsx
  21. 382 449
      packages/ui/yarn.lock
  22. 2 1
      resources/marketplace.html
  23. 3 4
      src/main/frontend/components/cmdk/core.cljs
  24. 5 9
      src/main/frontend/components/container.cljs
  25. 1 1
      src/main/frontend/components/plugins.cljs
  26. 1 2
      src/main/frontend/components/repo.cljs
  27. 1 1
      src/main/frontend/db.cljs
  28. 0 7
      src/main/frontend/db/model.cljs
  29. 6 7
      src/main/frontend/fs/watcher_handler.cljs
  30. 1 1
      src/main/frontend/handler/events.cljs
  31. 4 4
      src/main/frontend/handler/file_based/nfs.cljs
  32. 0 1
      src/main/frontend/modules/shortcut/config.cljs
  33. 2 2
      src/main/frontend/persist_db.cljs
  34. 4 2
      src/main/frontend/util.cljc
  35. 4 6
      src/main/frontend/worker/db_worker.cljs
  36. 21 20
      src/main/frontend/worker/shared_service.cljs

+ 2 - 1
clj-e2e/deps.edn

@@ -15,4 +15,5 @@
          :main-opts ["-m" "cognitect.test-runner"]
          :extra-deps {org.clojure/test.check {:mvn/version "1.1.1"}
                       io.github.cognitect-labs/test-runner
-                      {:git/tag "v0.5.1" :git/sha "dfb30dd"}}}}}
+                      {:git/tag "v0.5.1" :git/sha "dfb30dd"}}}
+  :dev {:extra-paths ["dev" "test"]}}}

+ 73 - 0
clj-e2e/dev/user.clj

@@ -0,0 +1,73 @@
+(ns user
+  "fns used on repl"
+  (:require [clojure.test :refer [run-tests run-test]]
+            [logseq.e2e.config :as config]
+            [logseq.e2e.editor-test]
+            [logseq.e2e.fixtures :as fixtures]
+            [logseq.e2e.multi-tabs-test]
+            [logseq.e2e.outliner-test]
+            [logseq.e2e.rtc-basic-test]
+            [logseq.e2e.util :as util]
+            [wally.main :as w]
+            [wally.repl :as repl]))
+
+;; Use port 3001 for local testing
+(reset! config/*port 3001)
+;; show ui
+(reset! config/*headless false)
+
+(def *futures (atom {}))
+
+(defn cancel
+  [test-name]
+  (some-> (get @*futures test-name) future-cancel)
+  (swap! *futures dissoc test-name))
+
+(defn run-editor-test
+  []
+  (->> (future (run-tests 'logseq.e2e.editor-test))
+       (swap! *futures assoc :editor-test)))
+
+(defn run-outliner-test
+  []
+  (->> (future (run-tests 'logseq.e2e.outliner-test))
+       (swap! *futures assoc :outliner-test)))
+
+(defn run-rtc-basic-test
+  []
+  (->> (future (run-tests 'logseq.e2e.rtc-basic-test))
+       (swap! *futures assoc :rtc-basic-test)))
+
+(defn run-multi-tabs-test
+  []
+  (->> (future (run-tests 'logseq.e2e.multi-tabs-test))
+       (swap! *futures assoc :multi-tabs-test)))
+
+(comment
+
+  (future
+    (fixtures/open-page
+     repl/pause
+     {:headless false}))
+
+  ;; You can put `(repl/pause)` in any test to pause the tests,
+  ;; this allows us to continue experimenting with the current page.
+  (repl/pause)
+
+  ;; To resume the tests, close the page/context/browser
+  (repl/resume)
+
+  ;; Run specific test
+  (future (run-test logseq.e2e.editor-test/commands-test))
+
+  ;; after the test has been paused, you can do anything with the current page like this
+  (repl/with-page
+    (w/wait-for (first (util/get-edit-block-container))
+                {:state :detached}))
+
+  (run-tests 'logseq.e2e.editor-test
+             'logseq.e2e.multi-tabs-test
+             'logseq.e2e.outliner-test
+             'logseq.e2e.rtc-basic-test)
+  ;;
+  )

+ 22 - 0
clj-e2e/src/logseq/e2e/assert.clj

@@ -0,0 +1,22 @@
+(ns logseq.e2e.assert
+  (:import [com.microsoft.playwright.assertions PlaywrightAssertions])
+  (:require [wally.main :as w]))
+
+(def assert-that PlaywrightAssertions/assertThat)
+
+(defn assert-is-visible
+  [q]
+  (-> (w/-query q) assert-that .isVisible))
+
+(defn assert-is-hidden
+  [q]
+  (-> (w/-query q) assert-that .isHidden))
+
+(defn assert-in-normal-mode?
+  "- not editing mode
+  - no action bar
+  - no search(cmdk) modal"
+  []
+  (assert-is-hidden (w/get-by-label "editing block"))
+  (assert-is-hidden ".selection-action-bar")
+  (assert-is-visible "#search-button"))

+ 5 - 0
clj-e2e/src/logseq/e2e/config.clj

@@ -0,0 +1,5 @@
+(ns logseq.e2e.config)
+
+(defonce *port (atom 3002))
+(defonce *headless (atom true))
+(defonce *slow-mo (atom 100))                  ; Set `slow-mo` lower to find more flaky tests

+ 24 - 0
clj-e2e/src/logseq/e2e/playwright_page.clj

@@ -0,0 +1,24 @@
+(ns logseq.e2e.playwright-page
+  "operations on playwright pages."
+  (:require [logseq.e2e.config :as config]
+            [wally.main :as w]))
+
+(defn get-pages
+  [pw-ctx]
+  (.pages pw-ctx))
+
+(defn open-pages
+  "Pages in same pw-ctx share cookies and storages"
+  [pw-ctx n]
+  (let [url (str "http://localhost:" @config/*port)]
+    (dotimes [_i n]
+      (let [page (.newPage pw-ctx)]
+        (.navigate page url)
+        ;; wait the demo graph loaded
+        (w/with-page page
+          (w/wait-for "span.block-title-wrap"))))))
+
+(defn close-pages
+  [pages]
+  (doseq [p pages]
+    (.close p)))

+ 0 - 38
clj-e2e/src/logseq/e2e/repl.clj

@@ -1,38 +0,0 @@
-(ns logseq.e2e.repl
-  "fns used on repl"
-  (:require [clojure.test :refer [run-tests run-test]]
-            [logseq.e2e.util :as util]
-            [wally.main :as w]
-            [wally.repl :as repl]))
-
-(comment
-
-  (future
-    (w/with-page-open
-      (w/make-page {:headless false
-                    :persistent false
-                    :slow-mo 20})
-      (w/navigate "http://localhost:3001")
-      (repl/pause)))
-
-  ;; You can put `(repl/pause)` in any test to pause the tests,
-  ;; this allows us to continue experimenting with the current page.
-  (repl/pause)
-
-  ;; To resume the tests, close the page/context/browser
-  (repl/resume)
-
-  ;; Run all the tests in specific ns with `future` to not block repl
-  (future (run-tests 'logseq.e2e.editor-test))
-  (future (run-tests 'logseq.e2e.outliner-test))
-
-  ;; Run specific test
-  (future (run-test logseq.e2e.editor-test/commands-test))
-
-  ;; after the test has been paused, you can do anything with the current page like this
-  (repl/with-page
-    (w/wait-for (first (util/get-edit-block-container))
-                {:state :detached}))
-
-  ;;
-  )

+ 28 - 18
clj-e2e/src/logseq/e2e/util.clj

@@ -2,13 +2,11 @@
   (:refer-clojure :exclude [type])
   (:require [clojure.string :as string]
             [clojure.test :refer [is]]
+            [logseq.e2e.assert :as assert]
+            [logseq.e2e.keyboard :as k]
             [wally.main :as w]
-            [wally.selectors :as ws]
-            [logseq.e2e.keyboard :as k])
-  (:import [com.microsoft.playwright TimeoutError]
-           [com.microsoft.playwright.assertions PlaywrightAssertions]))
-
-(def assert-that PlaywrightAssertions/assertThat)
+            [wally.selectors :as ws])
+  (:import [com.microsoft.playwright TimeoutError]))
 
 (defn wait-timeout
   [ms]
@@ -39,8 +37,16 @@
   (let [input-node (w/-query "*:focus")]
     (.type input-node text)))
 
+(defn double-esc
+  "Exits editing mode and ensure there's no action bar"
+  []
+  (k/esc)
+  (k/esc))
+
 (defn search
   [text]
+  (double-esc)
+  (assert/assert-in-normal-mode?)
   (w/click :#search-button)
   (w/fill ".cp__cmdk-search-input" text))
 
@@ -48,6 +54,7 @@
   [title]
   ;; Question: what's the best way to close all the popups?
   ;; close popup, exit editing
+  ;; (repl/pause)
   (search title)
   (w/click [(ws/text "Create page") (ws/nth= "0")])
   (w/wait-for ".editor-wrapper textarea"))
@@ -96,18 +103,6 @@
   (when-let [editor (get-editor)]
     (get-text editor)))
 
-;; TODO: support tree
-(defn new-blocks
-  [titles]
-  (let [value (get-edit-content)]
-    (if (string/blank? value)           ; empty block
-      (do
-        (save-block (first titles))
-        (doseq [title (rest titles)]
-          (new-block title)))
-      (doseq [title titles]
-        (new-block title)))))
-
 (defn bounding-xy
   [locator]
   (let [box (.boundingBox locator)]
@@ -133,8 +128,23 @@
 
 (defn open-last-block
   []
+  (double-esc)
+  (assert/assert-in-normal-mode?)
   (w/click (last (w/query ".ls-page-blocks .ls-block .block-content"))))
 
+;; TODO: support tree
+(defn new-blocks
+  [titles]
+  (open-last-block)
+  (let [value (get-edit-content)]
+    (if (string/blank? value)           ; empty block
+      (do
+        (save-block (first titles))
+        (doseq [title (rest titles)]
+          (new-block title)))
+      (doseq [title titles]
+        (new-block title)))))
+
 (defn repeat-keyboard
   [n shortcut]
   (dotimes [_i n]

+ 32 - 19
clj-e2e/test/logseq/e2e/fixtures.clj

@@ -1,20 +1,18 @@
 (ns logseq.e2e.fixtures
-  (:require [wally.main :as w]))
+  (:require [wally.main :as w]
+            [logseq.e2e.config :as config]
+            [logseq.e2e.playwright-page :as pw-page]))
 
 ;; TODO: save trace
 ;; TODO: parallel support
 (defn open-page
-  [f & {:keys [headless port]
-        :or {headless true
-             port 3002}}]
+  [f & {:keys [headless port]}]
   (w/with-page-open
-    (w/make-page {:headless headless
+    (w/make-page {:headless (or headless @config/*headless)
                   :persistent false
-                  :slow-mo 100
-                  ;; Set `slow-mo` lower to find more flaky tests
-                  ;; :slow-mo 30
+                  :slow-mo @config/*slow-mo
                   })
-    (w/navigate (str "http://localhost:" port))
+    (w/navigate (str "http://localhost:" (or port @config/*port)))
     (f)))
 
 (def *page1 (atom nil))
@@ -22,18 +20,17 @@
 
 (defn open-2-pages
   "Use `*page1` and `*page2` in `f`"
-  [f & {:keys [headless port]
-        :or {headless true
-             port 3002}}]
-  (let [p1 (w/make-page {:headless headless
-                         :persistent false
-                         :slow-mo 100})
-        p2 (w/make-page {:headless headless
-                         :persistent false
-                         :slow-mo 100})]
+  [f & {:keys [headless port]}]
+  (let [headless (or headless @config/*headless)
+        page-opts {:headless headless
+                   :persistent false
+                   :slow-mo @config/*slow-mo}
+        p1 (w/make-page page-opts)
+        p2 (w/make-page page-opts)
+        port' (or port @config/*port)]
     (run!
      #(w/with-page %
-        (w/navigate (str "http://localhost:" port)))
+        (w/navigate (str "http://localhost:" port')))
      [p1 p2])
 
     (reset! *page1 p1)
@@ -42,3 +39,19 @@
       (f))
     (w/with-page-open p1)
     (w/with-page-open p2)))
+
+(def ^:dynamic *pw-ctx* nil)
+(defn open-new-context
+  "create a new playwright-context in `*pw-ctx*`"
+  [f]
+  (let [page-opts {:headless @config/*headless
+                   :persistent false
+                   :slow-mo @config/*slow-mo}
+        p @(w/make-page page-opts)
+        ctx (.newContext (.browser (.context p)))]
+    ;; context for p is no longer needed
+    (.close (.context p))
+    (w/with-page-open p)              ; use with-page-open to close playwright instance
+    (binding [*pw-ctx* ctx]
+      (f)
+      (.close (.browser *pw-ctx*)))))

+ 23 - 0
clj-e2e/test/logseq/e2e/multi_tabs_test.clj

@@ -0,0 +1,23 @@
+(ns logseq.e2e.multi-tabs-test
+  (:require
+   [clojure.test :refer [deftest testing is use-fixtures]]
+   [logseq.e2e.fixtures :as fixtures]
+   [logseq.e2e.playwright-page :as pw-page]
+   [logseq.e2e.util :as util]
+   [wally.main :as w]
+   [wally.repl :as repl]))
+
+(use-fixtures :once fixtures/open-new-context)
+
+(deftest multi-tabs-test
+  (testing "edit on one tab, check all tab's blocks are same"
+    (pw-page/open-pages fixtures/*pw-ctx* 3)
+    (let [[p1 p2 p3 :as pages] (pw-page/get-pages fixtures/*pw-ctx*)
+          blocks-to-add (map #(str "b" %) (range 10))]
+      (is (= 3 (count pages)))
+      (w/with-page p1
+        (util/new-blocks blocks-to-add))
+      (w/with-page p2
+        (is (= (util/page-blocks-count) (count blocks-to-add))))
+      (w/with-page p3
+        (is (= (util/page-blocks-count) (count blocks-to-add)))))))

+ 1 - 8
clj-e2e/test/logseq/e2e/rtc_basic_test.clj

@@ -4,13 +4,10 @@
    [com.climate.claypoole :as cp]
    [logseq.e2e.fixtures :as fixtures :refer [*page1 *page2]]
    [logseq.e2e.util :as util]
-   [wally.main :as w]
-   [wally.repl :as repl]))
+   [wally.main :as w]))
 
 (use-fixtures :once fixtures/open-2-pages)
 
-;; (use-fixtures :once #(fixtures/open-2-pages % :headless false :port 3001))
-
 (deftest rtc-basic-test
   (let [graph-name (str "rtc-graph-" (.toEpochMilli (java.time.Instant/now)))]
     (testing "open 2 app instances"
@@ -23,7 +20,3 @@
         (util/new-graph graph-name true))
       (w/with-page @*page2
         (util/wait-for-remote-graph graph-name)))))
-
-(comment
-  (def xxx (future (clojure.test/run-tests)))
-  (future-cancel xxx))

+ 7 - 6
deps/shui/src/logseq/shui/dialog/core.cljs

@@ -134,8 +134,7 @@
     (hooks/use-effect!
      (fn []
        (when (false? open?)
-         (let [timeout (js/setTimeout #(detach-modal! id) 128)]
-           #(js/clearTimeout timeout))))
+         (detach-modal! id)))
      [open?])
 
     (dialog
@@ -159,12 +158,14 @@
         (cond-> (merge props content-props)
           auto-width? (assoc :data-auto-width true)
           (false? close-btn?) (assoc :data-close-btn false))
-        (when title
-          (dialog-header
-           (when title (dialog-title title))
-           (when description (dialog-description description))))
+
+        ;; nested title component is required for radix dialog content
+        (dialog-title {:class (when (nil? title) "hidden")} title)
+        (when description (dialog-description description))
+
         (when content
           [:div.ui__dialog-main-content content])
+
         (when footer
           (dialog-footer footer)))))))
 

+ 0 - 1
deps/shui/src/logseq/shui/popup/core.cljs

@@ -178,7 +178,6 @@
                          :top y
                          :left x}} ""))
        (let [content-props (cond-> (merge content-props {:onEscapeKeyDown handle-key-escape!
-                                                         :disableOutsideScroll false
                                                          :onPointerDownOutside handle-pointer-outside!})
                              (and (not force-popover?)
                                   (not as-dropdown?))

+ 2 - 1
deps/shui/src/logseq/shui/util.cljs

@@ -27,7 +27,8 @@
   [data & {:keys [html-props]}]
   (let [convert-to-camel (fn [[key value]]
                            (let [k (name key)]
-                             [(if-not (s/starts-with? k "data-")
+                             [(if-not (or (s/starts-with? k "data-")
+                                        (s/starts-with? k "aria-"))
                                 (kebab-case->camel-case k) k) value]))]
     (w/postwalk (fn [x]
                   (if (map? x)

+ 1 - 0
gulpfile.js

@@ -80,6 +80,7 @@ const common = {
         'node_modules/react-dom/umd/react-dom.production.min.js',
         'node_modules/react-dom/umd/react-dom.development.js',
         'node_modules/prop-types/prop-types.min.js',
+        'node_modules/dompurify/dist/purify.js',
       ]).pipe(gulp.dest(path.join(outputPath, 'js'))),
       () => gulp.src([
         'node_modules/@tabler/icons-react/dist/umd/tabler-icons-react.min.js',

+ 1 - 1
packages/ui/.storybook/preview.js

@@ -5,7 +5,7 @@ import '../src/index.css'
 import { useEffect } from 'react'
 
 // require in this file to keep app state when HMR
-const { setupGlobals } = require('../src')
+const { setupGlobals } = require('../src/ui')
 
 setupGlobals()
 

+ 1 - 0
packages/ui/@/components/ui/dialog.tsx

@@ -36,6 +36,7 @@ const DialogContent = React.forwardRef<
     <DialogOverlay {...overlayProps}>
       <DialogPrimitive.Content
         ref={ref}
+        aria-describedby={undefined}
         className={cn(
           'ui__dialog-content',
           'relative grid w-full max-w-2xl lg:max-w-3xl gap-4 border sm:rounded-lg ' +

+ 1 - 0
packages/ui/@/components/ui/toaster.tsx

@@ -22,6 +22,7 @@ export function Toaster() {
         description,
         action,
         icon,
+        onDismiss, // remove from toast props
         ...props
       }) {
         const duration = props?.duration

+ 2 - 2
packages/ui/examples/index.tsx

@@ -1,5 +1,5 @@
-import 'src/index.css'
-import { setupGlobals } from '../src'
+import '../src/index.css'
+import { setupGlobals } from '../src/ui'
 import * as React from 'react'
 import * as ReactDOM from 'react-dom'
 

+ 20 - 24
packages/ui/package.json

@@ -8,33 +8,30 @@
     "build:ui:only": "parcel build --target ui",
     "build:ui": "rm -rf .parcel-cache && yarn build:ui:only",
     "watch:storybook": "storybook dev -p 6006",
-    "install:primitives": "rm -rf primitives/ && git clone -b x https://github.com/xyhp915/primitives.git --depth=1",
-    "build:primitives": "yarn install:primitives && cd primitives/ && yarn install && yarn build",
-    "preinstall": "test -f primitives/package.json || yarn build:primitives",
     "postinstall": "yarn build:ui"
   },
   "dependencies": {
     "@hookform/resolvers": "^3.3.2",
     "@radix-ui/colors": "^3.0.0",
-    "@radix-ui/react-alert-dialog": "^1.0.5",
-    "@radix-ui/react-avatar": "^1.0.4",
-    "@radix-ui/react-checkbox": "^1.0.4",
-    "@radix-ui/react-context-menu": "^2.1.5",
-    "@radix-ui/react-dialog": "^1.1.2",
-    "@radix-ui/react-dropdown-menu": "^2.0.6",
-    "@radix-ui/react-label": "^2.0.2",
-    "@radix-ui/react-popover": "^1.0.7",
-    "@radix-ui/react-radio-group": "^1.1.3",
-    "@radix-ui/react-select": "^2.0.0",
-    "@radix-ui/react-separator": "^1.1.0",
-    "@radix-ui/react-slider": "^1.1.2",
-    "@radix-ui/react-slot": "^1.1.0",
-    "@radix-ui/react-switch": "^1.0.3",
-    "@radix-ui/react-tabs": "^1.1.1",
-    "@radix-ui/react-toast": "^1.1.5",
-    "@radix-ui/react-toggle": "^1.0.3",
-    "@radix-ui/react-toggle-group": "^1.0.4",
-    "@radix-ui/react-tooltip": "^1.1.4",
+    "@radix-ui/react-alert-dialog": "^1.1.11",
+    "@radix-ui/react-avatar": "^1.1.7",
+    "@radix-ui/react-checkbox": "^1.2.3",
+    "@radix-ui/react-context-menu": "^2.2.12",
+    "@radix-ui/react-dialog": "^1.1.11",
+    "@radix-ui/react-dropdown-menu": "^2.1.12",
+    "@radix-ui/react-label": "^2.1.4",
+    "@radix-ui/react-popover": "^1.1.11",
+    "@radix-ui/react-radio-group": "^1.3.4",
+    "@radix-ui/react-select": "^2.2.2",
+    "@radix-ui/react-separator": "^1.1.4",
+    "@radix-ui/react-slider": "^1.3.2",
+    "@radix-ui/react-slot": "^1.2.0",
+    "@radix-ui/react-switch": "^1.2.2",
+    "@radix-ui/react-tabs": "^1.1.9",
+    "@radix-ui/react-toast": "^1.2.11",
+    "@radix-ui/react-toggle": "^1.1.6",
+    "@radix-ui/react-toggle-group": "^1.1.7",
+    "@radix-ui/react-tooltip": "^1.2.4",
     "class-variance-authority": "^0.7.1",
     "clsx": "^2.0.0",
     "cmdk": "^0.2.0",
@@ -89,8 +86,7 @@
     },
     "react/jsx-dev-runtime": "./node_modules/react/jsx-dev-runtime.js",
     "react/jsx-runtime": "./node_modules/react/jsx-runtime.js",
-    "@/*": "./@/$1",
-    "@radix-ui/react-*": "./primitives/packages/react/$1"
+    "@/*": "./@/$1"
   },
   "targets": {
     "main": false,

+ 2 - 1
packages/ui/src/Demo.story.tsx

@@ -1,4 +1,4 @@
-import { Button } from '../@/components/ui/button'
+import { Button } from '@/components/ui/button'
 import { Meta, StoryObj } from '@storybook/react'
 import { DropdownMenuCheckboxItemProps } from '@radix-ui/react-dropdown-menu'
 
@@ -73,6 +73,7 @@ export const Primary: StoryObj =
         <div className={'p-20'}>
           <DropdownMenuCheckboxes/>
           <Button onClick={() => {
+            // @ts-ignore
             const p = toast({
               title: 'hello',
               description: <Button onClick={() => p.dismiss()}>hello</Button>

File diff suppressed because it is too large
+ 382 - 449
packages/ui/yarn.lock


+ 2 - 1
resources/marketplace.html

@@ -69,6 +69,7 @@
 <body>
 <div id="app"></div>
 <script src="./js/marked.min.js"></script>
+<script src="./js/purify.js"></script>
 <script>
   ;(async function () {
     const app = document.getElementById('app')
@@ -131,7 +132,7 @@
       }
 
       content = marked.parse(content).replace('src="./', `src="${fixLink('')}`)
-      setContent(content)
+      setContent(DOMPurify.sanitize(content))
     }
 
     // load default

+ 3 - 4
src/main/frontend/components/cmdk/core.cljs

@@ -495,12 +495,11 @@
 
 (defmethod handle-action :trigger [_ state _event]
   (let [command (some-> state state->highlighted-item :source-command)
-        dont-close-commands #{:graph/open :graph/remove :dev/replace-graph-with-db-file
-                              :ui/toggle-settings :go/flashcards :misc/import-edn-data}]
+        dont-close-commands #{:graph/open :graph/remove :dev/replace-graph-with-db-file :misc/import-edn-data}]
     (when-let [action (:action command)]
-      (action)
       (when-not (contains? dont-close-commands (:id command))
-        (shui/dialog-close! :ls-dialog-cmdk)))))
+        (shui/dialog-close! :ls-dialog-cmdk))
+      (util/schedule #(action) 32))))
 
 (defmethod handle-action :create [_ state _event]
   (let [item (state->highlighted-item state)

+ 5 - 9
src/main/frontend/components/container.cljs

@@ -708,7 +708,6 @@
   (let [default-home (get-default-home-if-valid)
         current-repo (state/sub :git/current-repo)
         loading-files? (when current-repo (state/sub [:repo/loading-files? current-repo]))
-        latest-journals (db/get-latest-journals (state/get-current-repo) 1)
         graph-parsing-state (state/sub [:graph/parsing-state current-repo])]
     (cond
       (or
@@ -727,20 +726,17 @@
               (:page default-home))
          (route-handler/redirect-to-page! (:page default-home))
 
-         (and config/publishing?
-              (not default-home)
-              (empty? latest-journals))
+         (let [latest-journals (db/get-latest-journals (state/get-current-repo) 1)]
+           (and config/publishing?
+                (not default-home)
+                (empty? latest-journals)))
          (route-handler/redirect! {:to :all-pages})
 
          loading-files?
          (ui/loading (t :loading-files))
 
-         (seq latest-journals)
-         (journal/all-journals)
-
-         ;; FIXME: why will this happen?
          :else
-         [:div])])))
+         (journal/all-journals))])))
 
 (defn- hide-context-menu-and-clear-selection
   [e]

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

@@ -202,7 +202,7 @@
 
 (rum/defc remote-readme-display
   [{:keys [repo]} _content]
-  (let [src (str (if (string/includes? js/location.href "logseq")
+  (let [src (str (if (string/includes? js/location.host "logseq")
                    "./static/" "./") "marketplace.html?repo=" repo)]
     [:iframe.lsp-frame-readme {:src src}]))
 

+ 1 - 2
src/main/frontend/components/repo.cljs

@@ -235,8 +235,7 @@
                                                 (state/pub-event! [:graph/open-new-window url])
                                                 (cond
                                                   ;; exists locally?
-                                                  (or (:root graph)
-                                                      (and db-only? (not rtc-graph?)))
+                                                  (or (:root graph) (not rtc-graph?))
                                                   (state/pub-event! [:graph/switch url])
 
                                                   (and rtc-graph? remote?)

+ 1 - 1
src/main/frontend/db.cljs

@@ -31,7 +31,7 @@
   get-block-parent get-block-parents parents-collapsed? get-block-referenced-blocks
   get-block-immediate-children get-block-page
   get-custom-css
-  get-file-last-modified-at get-file get-file-page get-file-page-id file-exists?
+  get-file get-file-page get-file-page-id file-exists?
   get-files-blocks get-files-full get-journals-length
   get-latest-journals get-page get-case-page get-page-alias-names
   get-page-blocks-count get-page-blocks-no-cache get-page-file get-page-format

+ 0 - 7
src/main/frontend/db/model.cljs

@@ -82,13 +82,6 @@
              (conn/get-db repo-url) pred)
         db-utils/seq-flatten)))
 
-(defn get-file-last-modified-at
-  [repo path]
-  (when (and repo path)
-    (when-let [db (conn/get-db repo)]
-      (-> (db-utils/entity db [:file/path path])
-          :file/last-modified-at))))
-
 (defn file-exists?
   [repo path]
   (when (and repo path)

+ 6 - 7
src/main/frontend/fs/watcher_handler.cljs

@@ -4,9 +4,9 @@
             [clojure.string :as string]
             [frontend.config :as config]
             [frontend.db :as db]
+            [frontend.db.async :as db-async]
             [frontend.db.model :as model]
             [frontend.fs :as fs]
-            [logseq.common.path :as path]
             [frontend.handler.file-based.file :as file-handler]
             [frontend.handler.file-based.property :as file-property-handler]
             [frontend.handler.global-config :as global-config-handler]
@@ -17,9 +17,9 @@
             [frontend.util.fs :as fs-util]
             [lambdaisland.glogi :as log]
             [logseq.common.config :as common-config]
+            [logseq.common.path :as path]
             [logseq.common.util.block-ref :as block-ref]
-            [promesa.core :as p]
-            [frontend.db.async :as db-async]))
+            [promesa.core :as p]))
 
 ;; all IPC paths must be normalized! (via common-util/path-normalize)
 
@@ -42,12 +42,11 @@
 (defn- handle-add-and-change!
   [repo path content db-content ctime mtime backup?]
   (let [config (state/get-config repo)
-        path-hidden-patterns (:hidden config)
-        db-last-modified-at (db/get-file-last-modified-at repo path)]
+        path-hidden-patterns (:hidden config)]
     (when-not (or (and (seq path-hidden-patterns)
-                    (common-config/hidden? path path-hidden-patterns))
+                       (common-config/hidden? path path-hidden-patterns))
                   ;; File not changed
-                  (= db-last-modified-at mtime))
+                  (= content db-content))
       (p/let [;; save the previous content in a versioned bak file to avoid data overwritten.
               _ (when backup?
                   (-> (when-let [repo-dir (config/get-local-dir repo)]

+ 1 - 1
src/main/frontend/handler/events.cljs

@@ -376,7 +376,7 @@
                             :path-params {:name link}}))
 
 (defmethod handle :graph/save-db-to-disk [[_ _opts]]
-  (persist-db/export-current-graph! {:succ-notification? true}))
+  (persist-db/export-current-graph! {:succ-notification? true :force-save? true}))
 
 (defmethod handle :ui/re-render-root [[_]]
   (ui-handler/re-render-root!))

+ 4 - 4
src/main/frontend/handler/file_based/nfs.cljs

@@ -8,20 +8,20 @@
             [frontend.fs :as fs]
             [frontend.fs.nfs :as nfs]
             [frontend.handler.common :as common-handler]
+            [frontend.handler.file-based.repo :as file-repo-handler]
             [frontend.handler.global-config :as global-config-handler]
             [frontend.handler.repo :as repo-handler]
-            [frontend.handler.file-based.repo :as file-repo-handler]
             [frontend.handler.route :as route-handler]
             [frontend.idb :as idb]
             [frontend.mobile.util :as mobile-util]
+            [frontend.persist-db :as persist-db]
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.util.fs :as util-fs]
             [goog.object :as gobj]
             [lambdaisland.glogi :as log]
             [logseq.common.util :as common-util]
-            [promesa.core :as p]
-            [frontend.persist-db :as persist-db]))
+            [promesa.core :as p]))
 
 (defn remove-ignore-files
   [files dir-name nfs?]
@@ -273,7 +273,7 @@
     (when repo
       (p/do!
        (repo-handler/remove-repo! {:url repo} :switch-graph? false)
-       (ls-dir-files-with-path! graph-dir)
+       (ls-dir-files-with-path! graph-dir {:re-index? true})
        (when (fn? ok-handler) (ok-handler))))))
 
 ;; TODO: move to frontend.handler.repo

+ 0 - 1
src/main/frontend/modules/shortcut/config.cljs

@@ -466,7 +466,6 @@
 
    :graph/db-save                           {:fn #(state/pub-event! [:graph/save-db-to-disk])
                                              :inactive (not (util/electron?))
-                                             :db-graph? true
                                              :binding "mod+s"}
 
    :graph/re-index                          {:fn      (fn []

+ 2 - 2
src/main/frontend/persist_db.cljs

@@ -42,10 +42,10 @@
     (<export-db repo {})))
 
 (defn export-current-graph!
-  [& {:keys [succ-notification?]}]
+  [& {:keys [succ-notification? force-save?]}]
   (when (util/electron?)
     (when-let [repo (state/get-current-repo)]
-      (when (config/db-based-graph? repo)
+      (when (or (config/db-based-graph? repo) force-save?)
         (println :debug :save-db-to-disk repo)
         (->
          (p/do!

+ 4 - 2
src/main/frontend/util.cljc

@@ -1248,7 +1248,9 @@
 #?(:cljs
    (defn scroll-editor-cursor
      [^js/HTMLElement el & {:keys [to-vw-one-quarter?]}]
-     (when (and el (or (mobile-util/native-platform?) (mobile?)))
+     (when (and el (or web-platform?
+                       (mobile-util/native-platform?)
+                       (mobile?)))
        (let [box-rect    (.getBoundingClientRect el)
              box-top     (.-top box-rect)
              box-bottom  (.-bottom box-rect)
@@ -1260,7 +1262,7 @@
              scroll-top'  (.-scrollTop main-node)
 
              current-pos (get-selection-start el)
-             grapheme-pos (get-graphemes-pos (.-value (.textContent el)) current-pos)
+             grapheme-pos (get-graphemes-pos (.-value el) current-pos)
              mock-text   (some-> (gdom/getElement "mock-text")
                                  gdom/getChildren
                                  array-seq

+ 4 - 6
src/main/frontend/worker/db_worker.cljs

@@ -880,13 +880,11 @@
                                 (= :thread-api/create-or-open-db method-k)
                                 ;; because shared-service operates at the graph level,
                                 ;; creating a new database or switching to another one requires re-initializing the service.
-                                (let [[graph opts] (ldb/read-transit-str (last args))]
-                                  (if (:import-type opts)
-                                    (start-db! graph opts)
-                                    (p/let [service (<init-service! graph)]
-                                      (get-in service [:status :ready])
+                                (let [[graph _opts] (ldb/read-transit-str (last args))]
+                                  (p/let [service (<init-service! graph)]
+                                    (get-in service [:status :ready])
                                       ;; wait for service ready
-                                      (js-invoke (:proxy service) k args))))
+                                    (js-invoke (:proxy service) k args)))
 
                                 (or (contains? #{:thread-api/sync-app-state} method-k)
                                     (nil? service))

+ 21 - 20
src/main/frontend/worker/shared_service.cljs

@@ -328,26 +328,27 @@
 
     {:proxy (js/Proxy. target
                        #js {:get (fn [target method]
-                                   (assert (identical? "remoteInvoke" method) method)
-                                   (fn [args]
-                                     (cond
-                                       @*master-client?
-                                       (<apply-target-f! target method args)
-
-                                       :else
-                                       (let [request-id (next-request-id)
-                                             client-channel (ensure-client-channel client-id service-name)]
-                                         (p/create
-                                          (fn [resolve-fn reject-fn]
-                                            (vswap! *requests-in-flight assoc request-id {:method method
-                                                                                          :args args
-                                                                                          :resolve-fn resolve-fn
-                                                                                          :reject-fn reject-fn})
-                                            (.postMessage client-channel (bean/->js
-                                                                          {:id request-id
-                                                                           :type "request"
-                                                                           :method method
-                                                                           :args args}))))))))})
+                                   (if (= "remoteInvoke" method)
+                                     (fn [args]
+                                       (cond
+                                         @*master-client?
+                                         (<apply-target-f! target method args)
+
+                                         :else
+                                         (let [request-id (next-request-id)
+                                               client-channel (ensure-client-channel client-id service-name)]
+                                           (p/create
+                                            (fn [resolve-fn reject-fn]
+                                              (vswap! *requests-in-flight assoc request-id {:method method
+                                                                                            :args args
+                                                                                            :resolve-fn resolve-fn
+                                                                                            :reject-fn reject-fn})
+                                              (.postMessage client-channel (bean/->js
+                                                                            {:id request-id
+                                                                             :type "request"
+                                                                             :method method
+                                                                             :args args})))))))
+                                     (log/error :invalid-invoke-method method)))})
      :status status}))
 
 (defn broadcast-to-clients!

Some files were not shown because too many files changed in this diff