Browse Source

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

charlie 8 months ago
parent
commit
bf040e07db
100 changed files with 814 additions and 2078 deletions
  1. 3 2
      .clj-kondo/config.edn
  2. 1 1
      .github/workflows/build-android.yml
  3. 1 1
      .github/workflows/build-demo.yml
  4. 39 34
      .github/workflows/build-desktop-release.yml
  5. 1 1
      .github/workflows/build-ios-release.yml
  6. 1 1
      .github/workflows/build-ios.yml
  7. 1 1
      .github/workflows/build-stage.yml
  8. 1 1
      .github/workflows/build.yml
  9. 1 1
      .github/workflows/clj-e2e.yml
  10. 1 1
      .github/workflows/db.yml
  11. 1 1
      .github/workflows/deploy-db-test-pages.yml
  12. 1 1
      .github/workflows/e2e.yml
  13. 1 1
      .github/workflows/graph-parser.yml
  14. 1 1
      .github/workflows/logseq-common.yml
  15. 1 1
      .github/workflows/outliner.yml
  16. 1 1
      .github/workflows/publishing.yml
  17. 1 1
      bb.edn
  18. 1 1
      clj-e2e/deps.edn
  19. 1 1
      deps.edn
  20. 1 1
      deps/common/package.json
  21. 21 21
      deps/common/src/logseq/common/path.cljs
  22. 8 8
      deps/common/yarn.lock
  23. 2 2
      deps/db/deps.edn
  24. 2 2
      deps/db/package.json
  25. 2 2
      deps/db/src/logseq/db/common/view.cljs
  26. 41 22
      deps/db/src/logseq/db/sqlite/export.cljs
  27. 53 1
      deps/db/test/logseq/db/sqlite/export_test.cljs
  28. 12 12
      deps/db/yarn.lock
  29. 8 7
      deps/graph-parser/deps.edn
  30. 1 0
      deps/graph-parser/nbb.edn
  31. 2 2
      deps/graph-parser/package.json
  32. 22 7
      deps/graph-parser/src/logseq/graph_parser/exporter.cljs
  33. 2 2
      deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs
  34. 22 1
      deps/graph-parser/test/resources/exporter-test-graph/journals/2024_08_07.md
  35. 0 2
      deps/graph-parser/test/resources/exporter-test-graph/logseq/config.edn
  36. 12 12
      deps/graph-parser/yarn.lock
  37. 4 2
      deps/outliner/deps.edn
  38. 2 2
      deps/outliner/package.json
  39. 12 12
      deps/outliner/yarn.lock
  40. 2 2
      deps/publishing/package.json
  41. 12 12
      deps/publishing/yarn.lock
  42. 5 4
      package.json
  43. 1 1
      resources/forge.config.js
  44. 0 45
      resources/js/preload.js
  45. 15 15
      resources/package.json
  46. 2 2
      scripts/package.json
  47. 5 5
      scripts/src/logseq/tasks/lang.clj
  48. 12 12
      scripts/yarn.lock
  49. 1 1
      src/main/frontend/commands.cljs
  50. 2 4
      src/main/frontend/components/block.cljs
  51. 12 11
      src/main/frontend/components/block/macros.cljs
  52. 12 14
      src/main/frontend/components/container.cljs
  53. 1 1
      src/main/frontend/components/content.cljs
  54. 2 2
      src/main/frontend/components/file_sync.cljs
  55. 29 29
      src/main/frontend/components/file_sync.css
  56. 1 1
      src/main/frontend/components/imports.cljs
  57. 10 10
      src/main/frontend/components/plugins.cljs
  58. 2 1
      src/main/frontend/components/property/value.cljs
  59. 1 1
      src/main/frontend/components/query/builder.cljs
  60. 2 2
      src/main/frontend/components/repo.cljs
  61. 1 1
      src/main/frontend/components/right_sidebar.css
  62. 23 15
      src/main/frontend/components/rtc/indicator.cljs
  63. 3 3
      src/main/frontend/components/theme.cljs
  64. 31 21
      src/main/frontend/components/views.cljs
  65. 6 16
      src/main/frontend/db/persist.cljs
  66. 7 28
      src/main/frontend/encrypt.cljs
  67. 20 23
      src/main/frontend/extensions/excalidraw.cljs
  68. 6 5
      src/main/frontend/extensions/pdf/assets.cljs
  69. 1 1
      src/main/frontend/extensions/zotero/handler.cljs
  70. 1 2
      src/main/frontend/flows.cljs
  71. 14 41
      src/main/frontend/fs.cljs
  72. 0 404
      src/main/frontend/fs/capacitor_fs.cljs
  73. 0 366
      src/main/frontend/fs/nfs.cljs
  74. 74 300
      src/main/frontend/fs/sync.cljs
  75. 0 5
      src/main/frontend/handler.cljs
  76. 2 19
      src/main/frontend/handler/assets.cljs
  77. 2 1
      src/main/frontend/handler/db_based/rtc_flows.cljs
  78. 1 1
      src/main/frontend/handler/dnd.cljs
  79. 12 10
      src/main/frontend/handler/draw.cljs
  80. 17 36
      src/main/frontend/handler/editor.cljs
  81. 13 85
      src/main/frontend/handler/events.cljs
  82. 1 38
      src/main/frontend/handler/events/ui.cljs
  83. 22 48
      src/main/frontend/handler/file_based/editor.cljs
  84. 6 6
      src/main/frontend/handler/file_based/events.cljs
  85. 41 54
      src/main/frontend/handler/file_based/file.cljs
  86. 5 7
      src/main/frontend/handler/file_based/native_fs.cljs
  87. 15 51
      src/main/frontend/handler/file_based/repo.cljs
  88. 13 13
      src/main/frontend/handler/global_config.cljs
  89. 2 2
      src/main/frontend/handler/page.cljs
  90. 23 16
      src/main/frontend/handler/plugin.cljs
  91. 2 2
      src/main/frontend/handler/plugin_config.cljs
  92. 6 3
      src/main/frontend/handler/user.cljs
  93. 8 7
      src/main/frontend/idb.cljs
  94. 1 1
      src/main/frontend/mobile/action_bar.cljs
  95. 4 51
      src/main/frontend/mobile/core.cljs
  96. 1 1
      src/main/frontend/mobile/graph_picker.cljs
  97. 1 1
      src/main/frontend/mobile/intent.cljs
  98. 10 22
      src/main/frontend/mobile/util.cljs
  99. 2 9
      src/main/frontend/persist_db/browser.cljs
  100. 0 15
      src/main/frontend/state.cljs

+ 3 - 2
.clj-kondo/config.edn

@@ -59,6 +59,7 @@
   :consistent-alias
   {:aliases {"/electron/utils" js-utils
              "path" node-path
+             borkdude.rewrite-edn rewrite
              cljs-time.coerce tc
              cljs-time.core t
              cljs.reader reader
@@ -98,9 +99,7 @@
              frontend.format.block block
              frontend.format.mldoc mldoc
              frontend.fs fs
-             frontend.fs.capacitor-fs capacitor-fs
              frontend.fs.memory-fs memory-fs
-             frontend.fs.nfs nfs
              frontend.handler.common common-handler
              frontend.handler.common.developer dev-common-handler
              frontend.handler.common.page page-common-handler
@@ -115,6 +114,7 @@
              frontend.handler.events events
              frontend.handler.extract extract
              frontend.handler.file-based.file file-handler
+             frontend.handler.file-based.native-fs nfs-handler
              frontend.handler.file-based.page file-page-handler
              frontend.handler.file-based.page-property file-page-property
              frontend.handler.file-based.property file-property-handler
@@ -158,6 +158,7 @@
              frontend.util.text text-util
              frontend.util.thingatpt thingatpt
              frontend.util.url url-util
+             frontend.util.ref ref
              frontend.worker.shared-service shared-service
              frontend.worker.handler.page worker-page
              frontend.worker.pipeline worker-pipeline

+ 1 - 1
.github/workflows/build-android.yml

@@ -42,7 +42,7 @@ on:
 
 env:
   CLOJURE_VERSION: '1.11.1.1413'
-  NODE_VERSION: '20'
+  NODE_VERSION: '22'
   JAVA_VERSION: '17'
 
 jobs:

+ 1 - 1
.github/workflows/build-demo.yml

@@ -19,7 +19,7 @@ on:
 
 env:
   CLOJURE_VERSION: '1.11.1.1413'
-  NODE_VERSION: '20'
+  NODE_VERSION: '22'
   JAVA_VERSION: '17'
 
 jobs:

+ 39 - 34
.github/workflows/build-desktop-release.yml

@@ -38,17 +38,17 @@ on:
         type: boolean
         required: true
         default: true
-      build-android:
-        description: 'Build Android App'
-        type: boolean
-        required: true
-        default: true
+      # build-android:
+      #   description: 'Build Android App'
+      #   type: boolean
+      #   required: true
+      #   default: true
   schedule: # Every workday at the 2 P.M. (UTC) we run a scheduled nightly build
     - cron: '0 14 * * MON-FRI'
 
 env:
   CLOJURE_VERSION: '1.11.1.1413'
-  NODE_VERSION: '20'
+  NODE_VERSION: '22'
   JAVA_VERSION: '11'
 
 jobs:
@@ -307,6 +307,8 @@ jobs:
       - name: Build/Release Electron app
         run: yarn electron:make
         working-directory: ./static
+        env:
+          DEBUG: electron-packager
         #env:
         #  CODE_SIGN_CERTIFICATE_FILE: ../codesign.pfx
         #  CODE_SIGN_CERTIFICATE_PASSWORD: ${{ secrets.CODE_SIGN_CERTIFICATE_PASSWORD }}
@@ -393,7 +395,7 @@ jobs:
       #     key: ${{ runner.os }}-node-modules
 
       - name: Build/Release Electron App for x64
-        run: yarn install && yarn electron:make
+        run: yarn install && yarn rebuild:all && yarn electron:make
         working-directory: ./static
         env:
           APPLE_ID: ${{ secrets.APPLE_ID_EMAIL }}
@@ -466,7 +468,7 @@ jobs:
       #     key: ${{ runner.os }}-node-modules
 
       - name: Fetch deps and fix dugit arch for arm64
-        run: yarn install --ignore-platform && cd node_modules/dugite && npm_config_arch=arm64 node script/download-git.js
+        run: yarn install --ignore-platform && yarn rebuild:all && cd node_modules/dugite && npm_config_arch=arm64 node script/download-git.js
         working-directory: ./static
 
       - name: Build/Release Electron App for arm64
@@ -490,23 +492,26 @@ jobs:
           path: builds
 
   # reuse workflow via workflow_call
-  build-android:
-    uses: ./.github/workflows/build-android.yml
-    if: ${{ github.event_name == 'schedule' || github.event.inputs.build-android == 'true' }}
-    with:
-      build-target: "${{ github.event.inputs.build-target }}"
-      # if scheduled, use production mode
-      enable-file-sync-production: "${{ github.event_name == 'schedule' || github.event.inputs.enable-file-sync-production == 'true' }}"
-    secrets:
-      ANDROID_KEYSTORE: "${{ secrets.ANDROID_KEYSTORE }}"
-      ANDROID_KEYSTORE_PASSWORD: "${{ secrets.ANDROID_KEYSTORE_PASSWORD }}"
-      SENTRY_AUTH_TOKEN: "${{ secrets.SENTRY_AUTH_TOKEN }}"
+  # build-android:
+  #   uses: ./.github/workflows/build-android.yml
+  #   if: ${{ github.event_name == 'schedule' || github.event.inputs.build-android == 'true' }}
+  #   with:
+  #     build-target: "${{ github.event.inputs.build-target }}"
+  #     # if scheduled, use production mode
+  #     enable-file-sync-production: "${{ github.event_name == 'schedule' || github.event.inputs.enable-file-sync-production == 'true' }}"
+  #   secrets:
+  #     ANDROID_KEYSTORE: "${{ secrets.ANDROID_KEYSTORE }}"
+  #     ANDROID_KEYSTORE_PASSWORD: "${{ secrets.ANDROID_KEYSTORE_PASSWORD }}"
+  #     SENTRY_AUTH_TOKEN: "${{ secrets.SENTRY_AUTH_TOKEN }}"
 
   codesign-windows:
     if: ${{ github.event_name == 'schedule' || github.event.inputs.build-target == 'nightly' || github.event.inputs.build-target == 'beta' }}
     needs: [ build-windows ]
     runs-on: [self-hosted, macos, token]
     steps:
+      - name: Remove old builds
+        run: rm -rf ./builds && mkdir ./builds
+
       - name: Download Windows Artifact
         uses: actions/download-artifact@v4
         with:
@@ -528,7 +533,7 @@ jobs:
 
   nightly-release:
     if: ${{ github.event_name == 'schedule' || github.event.inputs.build-target == 'nightly' }}
-    needs: [ build-macos-x64, build-macos-arm64, build-linux-x64, build-linux-arm64, codesign-windows, build-android]
+    needs: [ build-macos-x64, build-macos-arm64, build-linux-x64, build-linux-arm64, codesign-windows]
     runs-on: ubuntu-22.04
     steps:
       - name: Download MacOS x64 Artifacts
@@ -567,11 +572,11 @@ jobs:
           name: logseq-win64-builds
           path: ./
 
-      - name: Download Android Artifacts
-        uses: actions/download-artifact@v4
-        with:
-          name: logseq-android-builds
-          path: ./
+      # - name: Download Android Artifacts
+      #   uses: actions/download-artifact@v4
+      #   with:
+      #     name: logseq-android-builds
+      #     path: ./
 
       - name: Generate SHA256 checksums
         run: |
@@ -590,7 +595,7 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         with:
           tag_name: nightly
-          name: 'Desktop/Android APP Nightly Release $$'
+          name: 'Desktop app Nightly Release $$'
           draft: false
           prerelease: ${{ (github.event_name == 'workflow_dispatch' && github.event.inputs.is-pre-release) || (github.event_name == 'schedule')}}
           body: |
@@ -608,7 +613,7 @@ jobs:
   release:
     # NOTE: For now, we only have beta channel to be released on Github
     if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.build-target == 'beta' }}
-    needs: [ build-macos-x64, build-macos-arm64, build-linux-x64, build-linux-arm64, codesign-windows, build-android]
+    needs: [ build-macos-x64, build-macos-arm64, build-linux-x64, build-linux-arm64, codesign-windows]
     runs-on: ubuntu-22.04
     steps:
       - name: Download MacOS x64 Artifacts
@@ -647,12 +652,12 @@ jobs:
           name: logseq-win64-builds
           path: ./
 
-      - name: Download Android Artifacts
-        uses: actions/download-artifact@v4
-        if: ${{ github.event_name == 'schedule' || github.event.inputs.build-android == 'true' }}
-        with:
-          name: logseq-android-builds
-          path: ./
+      # - name: Download Android Artifacts
+      #   uses: actions/download-artifact@v4
+      #   if: ${{ github.event_name == 'schedule' || github.event.inputs.build-android == 'true' }}
+      #   with:
+      #     name: logseq-android-builds
+      #     path: ./
 
       - name: List files
         run: ls -rl
@@ -682,7 +687,7 @@ jobs:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         with:
           tag_name: ${{ steps.ref.outputs.version }}
-          name: Desktop/Android APP ${{ steps.ref.outputs.version }} (Beta Testing)
+          name: Desktop APP ${{ steps.ref.outputs.version }} (Beta Testing)
           body: "TODO: Fill this changelog. Sorry for the inconvenience!"
           draft: ${{ github.event.inputs.is-draft }}
           prerelease: ${{ github.event.inputs.is-pre-release }}

+ 1 - 1
.github/workflows/build-ios-release.yml

@@ -12,7 +12,7 @@ on:
 
 env:
   CLOJURE_VERSION: '1.11.1.1413'
-  NODE_VERSION: '20'
+  NODE_VERSION: '22'
   JAVA_VERSION: '11'
 
 jobs:

+ 1 - 1
.github/workflows/build-ios.yml

@@ -17,7 +17,7 @@ on:
 
 env:
   CLOJURE_VERSION: '1.11.1.1413'
-  NODE_VERSION: '20'
+  NODE_VERSION: '22'
   JAVA_VERSION: '11'
 
 jobs:

+ 1 - 1
.github/workflows/build-stage.yml

@@ -19,7 +19,7 @@ on:
 
 env:
   CLOJURE_VERSION: '1.11.1.1413'
-  NODE_VERSION: '20'
+  NODE_VERSION: '22'
   JAVA_VERSION: '17'
 
 jobs:

+ 1 - 1
.github/workflows/build.yml

@@ -14,7 +14,7 @@ env:
   CLOJURE_VERSION: '1.11.1.1413'
   JAVA_VERSION: '11'
   # This is the latest node version we can run.
-  NODE_VERSION: '20'
+  NODE_VERSION: '22'
   BABASHKA_VERSION: '1.0.168'
 
 jobs:

+ 1 - 1
.github/workflows/clj-e2e.yml

@@ -21,7 +21,7 @@ on:
 env:
   CLOJURE_VERSION: '1.11.1.1413'
   # This is the latest node version we can run.
-  NODE_VERSION: '20'
+  NODE_VERSION: '22'
   BABASHKA_VERSION: '1.0.168'
 
 jobs:

+ 1 - 1
.github/workflows/db.yml

@@ -23,7 +23,7 @@ env:
   CLOJURE_VERSION: '1.11.1.1413'
   JAVA_VERSION: '11'
   # This is the latest node version we can run.
-  NODE_VERSION: '20'
+  NODE_VERSION: '22'
   BABASHKA_VERSION: '1.0.168'
 
 jobs:

+ 1 - 1
.github/workflows/deploy-db-test-pages.yml

@@ -6,7 +6,7 @@ on:
 
 env:
   CLOJURE_VERSION: "1.11.1.1413"
-  NODE_VERSION: '20'
+  NODE_VERSION: '22'
   JAVA_VERSION: "11"
 
 jobs:

+ 1 - 1
.github/workflows/e2e.yml

@@ -19,7 +19,7 @@ env:
   CLOJURE_VERSION: '1.11.1.1413'
   JAVA_VERSION: '11'
   # This is the latest node version we can run.
-  NODE_VERSION: '20'
+  NODE_VERSION: '22'
   BABASHKA_VERSION: '1.0.168'
 
 jobs:

+ 1 - 1
.github/workflows/graph-parser.yml

@@ -28,7 +28,7 @@ env:
   # This is the same as 1.8.
   JAVA_VERSION: '11'
   # This is the latest node version we can run.
-  NODE_VERSION: '20'
+  NODE_VERSION: '22'
   BABASHKA_VERSION: '1.0.168'
 
 jobs:

+ 1 - 1
.github/workflows/logseq-common.yml

@@ -23,7 +23,7 @@ env:
   CLOJURE_VERSION: '1.11.1.1413'
   JAVA_VERSION: '11'
   # This is the latest node version we can run.
-  NODE_VERSION: '20'
+  NODE_VERSION: '22'
   BABASHKA_VERSION: '1.0.168'
 
 jobs:

+ 1 - 1
.github/workflows/outliner.yml

@@ -28,7 +28,7 @@ env:
   # This is the same as 1.8.
   JAVA_VERSION: '11'
   # This is the latest node version we can run.
-  NODE_VERSION: '20'
+  NODE_VERSION: '22'
   BABASHKA_VERSION: '1.0.168'
 
 jobs:

+ 1 - 1
.github/workflows/publishing.yml

@@ -28,7 +28,7 @@ env:
   # This is the same as 1.8.
   JAVA_VERSION: '11'
   # This is the latest node version we can run.
-  NODE_VERSION: '20'
+  NODE_VERSION: '22'
   BABASHKA_VERSION: '1.0.168'
 
 jobs:

+ 1 - 1
bb.edn

@@ -2,7 +2,7 @@
  :deps
  {metosin/malli
   {:mvn/version "0.16.1"}
-  borkdude/rewrite-edn {:mvn/version "0.4.8"}
+  borkdude/rewrite-edn {:mvn/version "0.4.9"}
   logseq/bb-tasks
   #_{:local/root "../bb-tasks"}
   {:git/url "https://github.com/logseq/bb-tasks"

+ 1 - 1
clj-e2e/deps.edn

@@ -2,7 +2,7 @@
  :deps {org.clojure/clojure {:mvn/version "1.12.0"}
         ;; io.github.pfeodrippe/wally {:local/root "../../../wally"}
         io.github.pfeodrippe/wally {:git/url "https://github.com/logseq/wally"
-                                    :sha "d84a2e068ec2e56fc7028fc61e694ccd1ddea03b"}
+                                    :sha "8571fae7c51400ac61c8b1026cbfba68279bc461"}
         ;; io.github.zmedelis/bosquet {:mvn/version "2025.03.28"}
         org.clj-commons/claypoole          {:mvn/version "1.2.2"}
         clj-time/clj-time                  {:mvn/version "0.15.2"}}

+ 1 - 1
deps.edn

@@ -8,7 +8,7 @@
                                          :sha     "4b1f15f05a6b4a718a62c247956206480e361ea6"}
 
   datascript-transit/datascript-transit {:mvn/version "0.3.0"}
-  borkdude/rewrite-edn                  {:mvn/version "0.4.7"}
+  borkdude/rewrite-edn                  {:mvn/version "0.4.9"}
   funcool/promesa                       {:mvn/version "11.0.678"}
   medley/medley                         {:mvn/version "1.4.0"}
   metosin/reitit-frontend               {:mvn/version "0.3.10"}

+ 1 - 1
deps/common/package.json

@@ -3,7 +3,7 @@
   "version": "1.0.0",
   "private": true,
   "devDependencies": {
-    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v20"
+    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v23"
   },
   "scripts": {
     "test": "yarn nbb-logseq -cp test -m nextjournal.test-runner"

+ 21 - 21
deps/common/src/logseq/common/path.cljs

@@ -14,12 +14,11 @@
 (defn is-file-url?
   [s]
   (and (string? s)
-       (or (string/starts-with? s "file://") ;; mobile platform
-           (string/starts-with? s "content://") ;; android only
-           (string/starts-with? s "assets://") ;; Electron asset, urlencoded
-           (string/starts-with? s "logseq://") ;; reserved for future fs protocol
-           (string/starts-with? s "memory://") ;; special memory fs
-           (string/starts-with? s "s3://"))))
+       (or
+        (string/starts-with? s "memory://") ;; special memory fs
+        (string/starts-with? s "assets://") ;; Electron asset, urlencoded
+        (string/starts-with? s "file://") ;; Electron files
+        )))
 
 (defn filename
   "File name of a path or URL.
@@ -63,7 +62,6 @@
                 (re-find #"(?i)^(COM[0-9]|CON|LPT[0-9]|NUL|PRN|AUX|com[0-9]|con|lpt[0-9]|nul|prn|aux)\..+" fname)
                 (re-find #"[\u0000-\u001f\u0080-\u009f]" fname)))))
 
-
 (defn- path-join-internal
   "Joins the given path segments into a single path, handling relative paths,
   '..' and '.' normalization."
@@ -144,16 +142,16 @@
          (join-fn))))
 
 (defn url-join
-  "Segments are not URL-ecoded"
+  "Segments are not URL-encoded"
   [base-url & segments]
-  (let [^js url (js/URL. base-url)
+  (let [^js url (js/URL. (safe-decode-uri-component base-url))
         scheme (.-protocol url)
-        domain (or (not-empty (.-host url)) "")
-        path (safe-decode-uri-component (.-pathname url))
+        path (.-pathname url)
+        domain (or (not-empty (.-host url))
+                 (if (string/starts-with? path "/") "" "/"))
         encoded-new-path (apply uri-path-join-internal path segments)]
     (str scheme "//" domain encoded-new-path)))
 
-
 (defn path-join
   "Join path segments, or URL base and path segments"
   [base & segments]
@@ -191,19 +189,17 @@
     :else
     (path-join (str protocol "//") path)))
 
-
 (defn- path-normalize-internal
   "Normalize path using path-join, break into segment and re-join"
   [path]
   (path-join path))
 
-
 (defn url-normalize
   [origin-url]
-  (let [^js url (js/URL. origin-url)
+  (let [^js url (js/URL. (safe-decode-uri-component origin-url))
         scheme (.-protocol url)
-        domain (or (not-empty (.-host url)) "")
-        path (safe-decode-uri-component (.-pathname url))
+        domain (or (not-empty (.-host url)) "/")
+        path (.-pathname url)
         encoded-new-path (uri-path-join-internal path)]
     (str scheme "//" domain encoded-new-path)))
 
@@ -223,8 +219,14 @@
   (if (is-file-url? original-url)
     ;; NOTE: URL type is not consistent across all protocols
     ;; Check file:// and assets://, pathname behavior is different
-    (let [^js url (js/URL. (string/replace original-url "assets://" "file://"))
-          path (safe-decode-uri-component (.-pathname url))
+    (let [^js url (try
+                    (js/URL. (string/replace (safe-decode-uri-component original-url) "assets://" "file://"))
+                    (catch :default e
+                      (js/console.error "invalid URL:"
+                                        (str "original-url: " original-url
+                                             " url: " (string/replace (safe-decode-uri-component original-url) "assets://" "file://")))
+                      (throw e)))
+          path (.-pathname url)
           host (.-host url)
           path (if (string/starts-with? path "///")
                  (subs path 2)
@@ -276,7 +278,6 @@
           (safe-decode-uri-component (str base-prefix (string/join "/" remain-segs)))
           (str base-prefix (string/join "/" remain-segs)))))))
 
-
 (defn parent
   "Parent, containing directory"
   [path]
@@ -285,7 +286,6 @@
     (path-normalize (str path "/.."))
     nil))
 
-
 (defn resolve-relative-path
   "Assume current-path is a file"
   [current-path rel-path]

+ 8 - 8
deps/common/yarn.lock

@@ -2,13 +2,13 @@
 # yarn lockfile v1
 
 
-"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v20":
-  version "1.2.173-feat-db-v20"
-  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/7fb63423ab566717cac79e5f76084d89b845fbd6"
+"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v23":
+  version "1.2.173-feat-db-v23"
+  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/810782c4ddba6346c4ab8ae6740b60438c07cd01"
   dependencies:
-    import-meta-resolve "^2.1.0"
+    import-meta-resolve "^4.1.0"
 
-import-meta-resolve@^2.1.0:
-  version "2.2.2"
-  resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-2.2.2.tgz#75237301e72d1f0fbd74dbc6cca9324b164c2cc9"
-  integrity sha512-f8KcQ1D80V7RnqVm+/lirO9zkOxjGxhaTC1IPrBGd3MEfNgmNG67tSUO9gTi2F3Blr2Az6g1vocaxzkVnWl9MA==
+import-meta-resolve@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#f9db8bead9fafa61adb811db77a2bf22c5399706"
+  integrity sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==

+ 2 - 2
deps/db/deps.edn

@@ -1,5 +1,5 @@
 {:deps
- ;; These deps are kept in sync with https://github.com/logseq/nbb-logseq/blob/main/bb.edn
+ ;; These nbb-logseq deps are kept in sync with https://github.com/logseq/nbb-logseq/blob/main/bb.edn
  {datascript/datascript {:git/url "https://github.com/logseq/datascript" ;; fork
                          :sha     "4b1f15f05a6b4a718a62c247956206480e361ea6"}
   datascript-transit/datascript-transit {:mvn/version "0.3.0"
@@ -8,7 +8,7 @@
   com.cognitect/transit-cljs   {:mvn/version "0.8.280"}
   org.flatland/ordered         {:mvn/version "1.15.11"}
 
-  ;; New deps should be added here and to nbb.edn
+  ;; Any other deps should be added here and to nbb.edn
   logseq/common                {:local/root "../common"}
   logseq/clj-fractional-indexing        {:git/url "https://github.com/logseq/clj-fractional-indexing"
                                          :sha     "7182b7878410f78536dc2b6df35ed32ef9cd6b61"}

+ 2 - 2
deps/db/package.json

@@ -3,10 +3,10 @@
   "version": "1.0.0",
   "private": true,
   "devDependencies": {
-    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v20"
+    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v23"
   },
   "dependencies": {
-    "better-sqlite3": "9.3.0"
+    "better-sqlite3": "11.10.0"
   },
   "scripts": {
     "test": "yarn nbb-logseq -cp test -m nextjournal.test-runner",

+ 2 - 2
deps/db/src/logseq/db/common/view.cljs

@@ -506,7 +506,7 @@
      (common-util/distinct-by :label))))
 
 (defn ^:api ^:large-vars/cleanup-todo get-view-data
-  [db view-id {:keys [journals? _view-for-id view-feature-type input query-entity-ids filters sorting]
+  [db view-id {:keys [journals? _view-for-id view-feature-type group-by-property-ident input query-entity-ids filters sorting]
                :as opts}]
   ;; TODO: create a view for journals maybe?
   (cond
@@ -519,7 +519,7 @@
     (let [view (d/entity db view-id)
           group-by-property (:logseq.property.view/group-by-property view)
           list-view? (= :logseq.property.view/type.list (:db/ident (:logseq.property.view/type view)))
-          group-by-property-ident (:db/ident group-by-property)
+          group-by-property-ident (or (:db/ident group-by-property) group-by-property-ident)
           group-by-closed-values? (some? (:property/closed-values group-by-property))
           ref-property? (= (:db/valueType group-by-property) :db.type/ref)
           filters (or (:logseq.property.table/filters view) filters)

+ 41 - 22
deps/db/src/logseq/db/sqlite/export.cljs

@@ -847,29 +847,52 @@
 ;; Import fns
 ;; ==========
 (defn- add-uuid-to-page-if-exists
-  [db m]
+  [db import-to-existing-page-uuids m]
   (if-let [ent (some->> (:build/journal m)
                         (d/datoms db :avet :block/journal-day)
                         first
                         :e
                         (d/entity db))]
-    (assoc m :block/uuid (:block/uuid ent))
+    (do
+      (swap! import-to-existing-page-uuids assoc (:block/uuid m) (:block/uuid ent))
+      (assoc m :block/uuid (:block/uuid ent)))
     ;; TODO: For now only check page uniqueness by title. Could handle more uniqueness checks later
     (if-let [ent (some->> (:block/title m) (ldb/get-case-page db))]
-      (assoc m :block/uuid (:block/uuid ent))
+      (do
+        (swap! import-to-existing-page-uuids assoc (:block/uuid m) (:block/uuid ent))
+        (assoc m :block/uuid (:block/uuid ent)))
       m)))
 
+(defn- update-existing-properties
+  "Updates existing properties by ident. Also check imported and existing properties have
+   the same cardinality and type to avoid failure after import"
+  [db property-conflicts properties]
+  (->> properties
+       (map (fn [[k v]]
+              (if-let [ent (d/entity db k)]
+                (do
+                  (when (not= (select-keys ent [:logseq.property/type :db/cardinality])
+                              (select-keys v [:logseq.property/type :db/cardinality]))
+                    (swap! property-conflicts conj
+                           {:property-id k
+                            :actual (select-keys v [:logseq.property/type :db/cardinality])
+                            :expected (select-keys ent [:logseq.property/type :db/cardinality])}))
+                  [k (assoc v :block/uuid (:block/uuid ent))])
+                [k v])))
+       (into {})))
+
 (defn- check-for-existing-entities
   "Checks export map for existing entities and adds :block/uuid to them if they exist in graph to import.
    Also checks for property conflicts between existing properties and properties to be imported"
   [db {:keys [pages-and-blocks classes properties] ::keys [export-type] :as export-map} property-conflicts]
-  (let [export-map
+  (let [import-to-existing-page-uuids (atom {})
+        export-map
         (cond-> {:build-existing-tx? true
                  :extract-content-refs? false}
           (seq pages-and-blocks)
           (assoc :pages-and-blocks
                  (mapv (fn [m]
-                         (update m :page (partial add-uuid-to-page-if-exists db)))
+                         (update m :page (partial add-uuid-to-page-if-exists db import-to-existing-page-uuids)))
                        pages-and-blocks))
           (seq classes)
           (assoc :classes
@@ -880,20 +903,7 @@
                                [k v])))
                       (into {})))
           (seq properties)
-          (assoc :properties
-                 (->> properties
-                      (map (fn [[k v]]
-                             (if-let [ent (d/entity db k)]
-                               (do
-                                 (when (not= (select-keys ent [:logseq.property/type :db/cardinality])
-                                             (select-keys v [:logseq.property/type :db/cardinality]))
-                                   (swap! property-conflicts conj
-                                          {:property-id k
-                                           :actual (select-keys v [:logseq.property/type :db/cardinality])
-                                           :expected (select-keys ent [:logseq.property/type :db/cardinality])}))
-                                 [k (assoc v :block/uuid (:block/uuid ent))])
-                               [k v])))
-                      (into {})))
+          (assoc :properties (update-existing-properties db property-conflicts properties))
           ;; Graph export doesn't use :build/page so this speeds up build
           (= :graph export-type)
           (assoc :translate-property-values? false)
@@ -904,10 +914,19 @@
                       export-map
                       (walk/postwalk (fn [f]
                                        (if (and (vector? f) (= :build/page (first f)))
-                                         [:build/page (add-uuid-to-page-if-exists db (second f))]
+                                         [:build/page
+                                          (add-uuid-to-page-if-exists db import-to-existing-page-uuids (second f))]
                                          f))
-                                     export-map))]
-    export-map'))
+                                     export-map))
+        ;; Update uuid references of all pages that had their uuids updated to reference an existing page
+        export-map''
+        (walk/postwalk (fn [f]
+                         (if-let [new-uuid (and (vector? f) (= :block/uuid (first f))
+                                                (get @import-to-existing-page-uuids (second f)))]
+                           [:block/uuid new-uuid]
+                           f))
+                       export-map')]
+    export-map''))
 
 (defn- build-block-import-options
   "Builds options for sqlite-build to import into current-block"

+ 53 - 1
deps/db/test/logseq/db/sqlite/export_test.cljs

@@ -824,4 +824,56 @@
     (is (= (expand-properties (:properties original-data)) (:properties imported-graph)))
     (is (= (expand-classes (:classes original-data))
            (-> (:classes imported-graph)
-               (medley/dissoc-in [:user.property/p1 :build/properties]))))))
+               (medley/dissoc-in [:user.property/p1 :build/properties]))))))
+
+(deftest build-import-can-import-existing-page-with-different-uuid
+  (let [original-data
+        {:properties {:user.property/node {:logseq.property/type :node
+                                           :db/cardinality :db.cardinality/many}}
+         :pages-and-blocks
+         [{:page {:block/title "page1"
+                  :build/properties {:user.property/node #{[:build/page {:block/title "node1"}]}}}}]}
+        conn (db-test/create-conn-with-blocks original-data)
+        page-uuid (:block/uuid (db-test/find-page-by-title @conn "node1"))
+        _ (validate-db @conn)
+        ;; This is just a temp uuid used to link to the page during import
+        temp-uuid (random-uuid)
+        existing-data
+        {:properties {:user.property/node {:logseq.property/type :node
+                                           :db/cardinality :db.cardinality/many}}
+         :pages-and-blocks
+         [{:page {:block/title "node1"
+                  :block/uuid temp-uuid
+                  :build/keep-uuid? true}}
+          {:page {:block/title "page2"
+                  :build/properties {:user.property/node #{[:block/uuid temp-uuid]}}}}]}
+        {:keys [init-tx block-props-tx] :as _txs}
+        (sqlite-export/build-import existing-data @conn {})
+        ;; _ (cljs.pprint/pprint _txs)
+        _ (d/transact! conn init-tx)
+        _ (d/transact! conn block-props-tx)
+        _ (validate-db @conn)
+        expected-pages-and-blocks
+        [{:page
+          {:block/uuid page-uuid
+           :build/keep-uuid? true,
+           :block/title "node1"},
+          :blocks []}
+         {:page
+          {:build/properties
+           {:user.property/node
+            #{[:block/uuid page-uuid]}},
+           :block/title "page1"},
+          :blocks []}
+         {:page
+          {:build/properties
+           {:user.property/node
+            #{[:block/uuid page-uuid]}},
+           :block/title "page2"},
+          :blocks []}],
+        exported-graph (sqlite-export/build-export @conn {:export-type :graph
+                                                          :graph-options {:exclude-built-in-pages? true}})]
+    (is (= expected-pages-and-blocks
+           (:pages-and-blocks exported-graph))
+        "page uuid ('node1') is preserved across imports even when its assigned a temporary
+         uuid to relate it to other nodes")))

+ 12 - 12
deps/db/yarn.lock

@@ -2,21 +2,21 @@
 # yarn lockfile v1
 
 
-"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v20":
-  version "1.2.173-feat-db-v20"
-  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/7fb63423ab566717cac79e5f76084d89b845fbd6"
+"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v23":
+  version "1.2.173-feat-db-v23"
+  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/810782c4ddba6346c4ab8ae6740b60438c07cd01"
   dependencies:
-    import-meta-resolve "^2.1.0"
+    import-meta-resolve "^4.1.0"
 
 base64-js@^1.3.1:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
   integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
 
-better-sqlite3@9.3.0:
-  version "9.3.0"
-  resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-9.3.0.tgz#2a8aaad65fa0210a4df5e8a0bcbc9156f6138d56"
-  integrity sha512-ww73jVpQhRRdS9uMr761ixlkl4bWoXi8hMQlBGhoN6vPNlUHpIsNmw4pKN6kjknlt/wopdvXHvLk1W75BI+n0Q==
+better-sqlite3@11.10.0:
+  version "11.10.0"
+  resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-11.10.0.tgz#2b1b14c5acd75a43fd84d12cc291ea98cef57d98"
+  integrity sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==
   dependencies:
     bindings "^1.5.0"
     prebuild-install "^7.1.1"
@@ -99,10 +99,10 @@ ieee754@^1.1.13:
   resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
   integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
 
-import-meta-resolve@^2.1.0:
-  version "2.2.2"
-  resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-2.2.2.tgz#75237301e72d1f0fbd74dbc6cca9324b164c2cc9"
-  integrity sha512-f8KcQ1D80V7RnqVm+/lirO9zkOxjGxhaTC1IPrBGd3MEfNgmNG67tSUO9gTi2F3Blr2Az6g1vocaxzkVnWl9MA==
+import-meta-resolve@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#f9db8bead9fafa61adb811db77a2bf22c5399706"
+  integrity sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==
 
 inherits@^2.0.3, inherits@^2.0.4:
   version "2.0.4"

+ 8 - 7
deps/graph-parser/deps.edn

@@ -1,16 +1,17 @@
 {:paths ["src"]
  :deps
- ;; External deps should be kept in sync with https://github.com/logseq/nbb-logseq/blob/main/bb.edn
+ ;; These nbb-logseq deps are kept in sync with https://github.com/logseq/nbb-logseq/blob/main/bb.edn
  {com.andrewmcveigh/cljs-time {:git/url "https://github.com/logseq/cljs-time" ;; fork
                                :sha     "5704fbf48d3478eedcf24d458c8964b3c2fd59a9"}
-  ;; local deps
+  funcool/promesa             {:mvn/version "11.0.678"}
+  cljs-bean/cljs-bean         {:mvn/version "1.5.0"}
+
+  ;; Any other deps should be added here and to nbb.edn
+  borkdude/rewrite-edn {:mvn/version "0.4.9"}
   logseq/db                   {:local/root "../db"}
   logseq/common               {:local/root "../common"}
-  ;; stubbed in nbb
-  com.lambdaisland/glogi      {:mvn/version "1.1.144"}
-  ;; built in to nbb
-  funcool/promesa             {:mvn/version "11.0.678"}
-  cljs-bean/cljs-bean         {:mvn/version "1.5.0"}}
+  ;; stubbed via logseq.common.log
+  com.lambdaisland/glogi      {:mvn/version "1.1.144"}}
 
  :aliases
  ;; This runs tests with nodejs. Would be nice to run this with in a browser env

+ 1 - 0
deps/graph-parser/nbb.edn

@@ -6,4 +6,5 @@
   {:local/root "../db"}
   io.github.nextjournal/nbb-test-runner
   {:git/sha "60ed57aa04bca8d604f5ba6b28848bd887109347"}
+  borkdude/rewrite-edn {:mvn/version "0.4.9"}
   io.github.pez/baldr {:mvn/version "1.0.9"}}}

+ 2 - 2
deps/graph-parser/package.json

@@ -3,8 +3,8 @@
   "version": "1.0.0",
   "private": true,
   "devDependencies": {
-    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v20",
-    "better-sqlite3": "9.3.0"
+    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v23",
+    "better-sqlite3": "11.10.0"
   },
   "dependencies": {
     "mldoc": "^1.5.9"

+ 22 - 7
deps/graph-parser/src/logseq/graph_parser/exporter.cljs

@@ -1,7 +1,8 @@
 (ns logseq.graph-parser.exporter
   "Exports a file graph to DB graph. Used by the File to DB graph importer and
   by nbb-logseq CLIs"
-  (:require [cljs-time.coerce :as tc]
+  (:require [borkdude.rewrite-edn :as rewrite]
+            [cljs-time.coerce :as tc]
             [cljs.pprint]
             [clojure.edn :as edn]
             [clojure.set :as set]
@@ -17,6 +18,7 @@
             [logseq.common.uuid :as common-uuid]
             [logseq.db :as ldb]
             [logseq.db.common.order :as db-order]
+            [logseq.db.common.property-util :as db-property-util]
             [logseq.db.frontend.class :as db-class]
             [logseq.db.frontend.content :as db-content]
             [logseq.db.frontend.db-ident :as db-ident]
@@ -29,8 +31,7 @@
             [logseq.graph-parser.block :as gp-block]
             [logseq.graph-parser.extract :as extract]
             [logseq.graph-parser.property :as gp-property]
-            [promesa.core :as p]
-            [logseq.db.common.property-util :as db-property-util]))
+            [promesa.core :as p]))
 
 (defn- add-missing-timestamps
   "Add updated-at or created-at timestamps if they doesn't exist"
@@ -762,6 +763,23 @@
         block'' (replace-namespace-with-parent block' page-names-to-uuids)]
     {:block block'' :properties-tx properties-tx}))
 
+(defn- pretty-print-dissoc
+  [s dissoc-keys]
+  (-> (reduce rewrite/dissoc
+              (rewrite/parse-string s)
+              dissoc-keys)
+      str))
+
+(defn- migrate-advanced-query-string [query-str]
+  (try
+    (pretty-print-dissoc query-str [:title :group-by-page? :collapsed?])
+    (catch :default _e
+      ;; rewrite/parse-string can fail on some queries in Advanced Queries in docs graph
+      (js/console.error "Failed to parse advanced query string. Falling back to full query string: " (pr-str query-str))
+      (if-let [query-map (not-empty (common-util/safe-read-map-string query-str))]
+        (pr-str (dissoc query-map :title :group-by-page? :collapsed?))
+        query-str))))
+
 (defn- handle-block-properties
   "Does everything page properties does and updates a couple of block specific attributes"
   [{:block/keys [title] :as block*}
@@ -777,10 +795,7 @@
                                                string/trim)
                                       title))
                            (seq advanced-query)
-                           (assoc :logseq.property/query
-                                  (if-let [query-map (not-empty (common-util/safe-read-map-string advanced-query))]
-                                    (pr-str (dissoc query-map :title :group-by-page? :collapsed?))
-                                    advanced-query)))
+                           (assoc :logseq.property/query (migrate-advanced-query-string advanced-query)))
         {:keys [block-properties pvalues-tx]}
         (when (seq additional-props)
           (build-properties-and-values additional-props db page-names-to-uuids

+ 2 - 2
deps/graph-parser/test/logseq/graph_parser/exporter_test.cljs

@@ -173,7 +173,7 @@
       (is (= 25 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Journal]] @conn))))
 
       (is (= 4 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Task]] @conn))))
-      (is (= 3 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Query]] @conn))))
+      (is (= 4 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Query]] @conn))))
       (is (= 2 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Card]] @conn))))
 
       ;; Properties and tags aren't included in this count as they aren't a Page
@@ -520,7 +520,7 @@
         "Correct number of user classes")
 
     (is (= 4 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Task]] @conn))))
-    (is (= 3 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Query]] @conn))))
+    (is (= 4 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Query]] @conn))))
     (is (= 2 (count (d/q '[:find ?b :where [?b :block/tags :logseq.class/Card]] @conn))))
 
     (testing "replacing refs in :block/title when :remove-inline-tags? set"

+ 22 - 1
deps/graph-parser/test/resources/exporter-test-graph/journals/2024_08_07.md

@@ -1,5 +1,5 @@
 - For example, here's a query with title text:
-{{query (property type book)}}
+  {{query (property type book)}}
 - test multilines in this page
 - |markdown| table|
   |some|thing|
@@ -21,3 +21,24 @@
   :query (task todo doing)}
   #+END_QUERY
   Text after
+- collapsed:: true
+  #+BEGIN_QUERY
+  {:title "⌛ Pretty print advanced query test"
+    :query [:find (pull ?b [*])
+       :in $ ?today
+       :where
+       [?b :block/page ?p]
+       [?p :page/journal? true]
+       [?p :page/journal-day ?jd]
+       [(str ?jd) ?jds]
+       [(subs ?jds 4 8) ?md1]
+       [(str ?today) ?td]
+       [(subs ?td 4 8) ?md2]
+       [(= ?md1 ?md2)]
+       [(< ?jd ?today)]
+    ]
+    :inputs [:today]
+    :breadcrumb-show? true
+    :collapsed? False
+  }
+  #+END_QUERY

+ 0 - 2
deps/graph-parser/test/resources/exporter-test-graph/logseq/config.edn

@@ -208,7 +208,6 @@
             [?h :block/marker ?marker]
             [(contains? #{"NOW" "DOING"} ?marker)]
             [?h :block/page ?p]
-            [?p :block/journal? true]
             [?p :block/journal-day ?d]
             [(>= ?d ?start)]
             [(<= ?d ?today)]]
@@ -225,7 +224,6 @@
             [?h :block/marker ?marker]
             [(contains? #{"NOW" "LATER" "TODO"} ?marker)]
             [?h :block/page ?p]
-            [?p :block/journal? true]
             [?p :block/journal-day ?d]
             [(> ?d ?start)]
             [(< ?d ?next)]]

+ 12 - 12
deps/graph-parser/yarn.lock

@@ -2,11 +2,11 @@
 # yarn lockfile v1
 
 
-"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v20":
-  version "1.2.173-feat-db-v20"
-  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/7fb63423ab566717cac79e5f76084d89b845fbd6"
+"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v23":
+  version "1.2.173-feat-db-v23"
+  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/810782c4ddba6346c4ab8ae6740b60438c07cd01"
   dependencies:
-    import-meta-resolve "^2.1.0"
+    import-meta-resolve "^4.1.0"
 
 ansi-regex@^2.0.0:
   version "2.1.1"
@@ -23,10 +23,10 @@ base64-js@^1.3.1:
   resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
   integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
 
-better-sqlite3@9.3.0:
-  version "9.3.0"
-  resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-9.3.0.tgz#2a8aaad65fa0210a4df5e8a0bcbc9156f6138d56"
-  integrity sha512-ww73jVpQhRRdS9uMr761ixlkl4bWoXi8hMQlBGhoN6vPNlUHpIsNmw4pKN6kjknlt/wopdvXHvLk1W75BI+n0Q==
+better-sqlite3@11.10.0:
+  version "11.10.0"
+  resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-11.10.0.tgz#2b1b14c5acd75a43fd84d12cc291ea98cef57d98"
+  integrity sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==
   dependencies:
     bindings "^1.5.0"
     prebuild-install "^7.1.1"
@@ -176,10 +176,10 @@ ieee754@^1.1.13:
   resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
   integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
 
-import-meta-resolve@^2.1.0:
-  version "2.2.2"
-  resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-2.2.2.tgz#75237301e72d1f0fbd74dbc6cca9324b164c2cc9"
-  integrity sha512-f8KcQ1D80V7RnqVm+/lirO9zkOxjGxhaTC1IPrBGd3MEfNgmNG67tSUO9gTi2F3Blr2Az6g1vocaxzkVnWl9MA==
+import-meta-resolve@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#f9db8bead9fafa61adb811db77a2bf22c5399706"
+  integrity sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==
 
 inherits@^2.0.3, inherits@^2.0.4:
   version "2.0.4"

+ 4 - 2
deps/outliner/deps.edn

@@ -1,10 +1,12 @@
 {:deps
- ;; External deps should be kept in sync with https://github.com/logseq/nbb-logseq/blob/main/bb.edn
+ ;; These nbb-logseq deps are kept in sync with https://github.com/logseq/nbb-logseq/blob/main/bb.edn
  {datascript/datascript {:git/url "https://github.com/logseq/datascript" ;; fork
                          :sha     "4b1f15f05a6b4a718a62c247956206480e361ea6"}
+  com.cognitect/transit-cljs {:mvn/version "0.8.280"}
+
+  ;; Any other deps should be added here and to nbb.edn
   logseq/db             {:local/root "../db"}
   logseq/graph-parser   {:local/root "../db"}
-  com.cognitect/transit-cljs {:mvn/version "0.8.280"}
   metosin/malli {:mvn/version "0.16.1"}}
  :aliases
  {:clj-kondo

+ 2 - 2
deps/outliner/package.json

@@ -3,10 +3,10 @@
   "version": "1.0.0",
   "private": true,
   "devDependencies": {
-    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v20"
+    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v23"
   },
   "dependencies": {
-    "better-sqlite3": "9.3.0",
+    "better-sqlite3": "11.10.0",
     "mldoc": "^1.5.9"
   },
   "scripts": {

+ 12 - 12
deps/outliner/yarn.lock

@@ -2,11 +2,11 @@
 # yarn lockfile v1
 
 
-"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v20":
-  version "1.2.173-feat-db-v20"
-  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/7fb63423ab566717cac79e5f76084d89b845fbd6"
+"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v23":
+  version "1.2.173-feat-db-v23"
+  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/810782c4ddba6346c4ab8ae6740b60438c07cd01"
   dependencies:
-    import-meta-resolve "^2.1.0"
+    import-meta-resolve "^4.1.0"
 
 ansi-regex@^2.0.0:
   version "2.1.1"
@@ -23,10 +23,10 @@ base64-js@^1.3.1:
   resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
   integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
 
-better-sqlite3@9.3.0:
-  version "9.3.0"
-  resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-9.3.0.tgz#2a8aaad65fa0210a4df5e8a0bcbc9156f6138d56"
-  integrity sha512-ww73jVpQhRRdS9uMr761ixlkl4bWoXi8hMQlBGhoN6vPNlUHpIsNmw4pKN6kjknlt/wopdvXHvLk1W75BI+n0Q==
+better-sqlite3@11.10.0:
+  version "11.10.0"
+  resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-11.10.0.tgz#2b1b14c5acd75a43fd84d12cc291ea98cef57d98"
+  integrity sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==
   dependencies:
     bindings "^1.5.0"
     prebuild-install "^7.1.1"
@@ -176,10 +176,10 @@ ieee754@^1.1.13:
   resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
   integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
 
-import-meta-resolve@^2.1.0:
-  version "2.2.2"
-  resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-2.2.2.tgz#75237301e72d1f0fbd74dbc6cca9324b164c2cc9"
-  integrity sha512-f8KcQ1D80V7RnqVm+/lirO9zkOxjGxhaTC1IPrBGd3MEfNgmNG67tSUO9gTi2F3Blr2Az6g1vocaxzkVnWl9MA==
+import-meta-resolve@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#f9db8bead9fafa61adb811db77a2bf22c5399706"
+  integrity sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==
 
 inherits@^2.0.3, inherits@^2.0.4:
   version "2.0.4"

+ 2 - 2
deps/publishing/package.json

@@ -3,11 +3,11 @@
   "version": "1.0.0",
   "private": true,
   "devDependencies": {
-    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v20",
+    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v23",
     "mldoc": "^1.5.9"
   },
   "dependencies": {
-    "better-sqlite3": "9.3.0",
+    "better-sqlite3": "11.10.0",
     "fs-extra": "9.1.0"
   },
   "scripts": {

+ 12 - 12
deps/publishing/yarn.lock

@@ -2,11 +2,11 @@
 # yarn lockfile v1
 
 
-"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v20":
-  version "1.2.173-feat-db-v20"
-  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/7fb63423ab566717cac79e5f76084d89b845fbd6"
+"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v23":
+  version "1.2.173-feat-db-v23"
+  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/810782c4ddba6346c4ab8ae6740b60438c07cd01"
   dependencies:
-    import-meta-resolve "^2.1.0"
+    import-meta-resolve "^4.1.0"
 
 ansi-regex@^2.0.0:
   version "2.1.1"
@@ -28,10 +28,10 @@ base64-js@^1.3.1:
   resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
   integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
 
-better-sqlite3@9.3.0:
-  version "9.3.0"
-  resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-9.3.0.tgz#2a8aaad65fa0210a4df5e8a0bcbc9156f6138d56"
-  integrity sha512-ww73jVpQhRRdS9uMr761ixlkl4bWoXi8hMQlBGhoN6vPNlUHpIsNmw4pKN6kjknlt/wopdvXHvLk1W75BI+n0Q==
+better-sqlite3@11.10.0:
+  version "11.10.0"
+  resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-11.10.0.tgz#2b1b14c5acd75a43fd84d12cc291ea98cef57d98"
+  integrity sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==
   dependencies:
     bindings "^1.5.0"
     prebuild-install "^7.1.1"
@@ -196,10 +196,10 @@ ieee754@^1.1.13:
   resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
   integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
 
-import-meta-resolve@^2.1.0:
-  version "2.2.2"
-  resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-2.2.2.tgz#75237301e72d1f0fbd74dbc6cca9324b164c2cc9"
-  integrity sha512-f8KcQ1D80V7RnqVm+/lirO9zkOxjGxhaTC1IPrBGd3MEfNgmNG67tSUO9gTi2F3Blr2Az6g1vocaxzkVnWl9MA==
+import-meta-resolve@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#f9db8bead9fafa61adb811db77a2bf22c5399706"
+  integrity sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==
 
 inherits@^2.0.3, inherits@^2.0.4:
   version "2.0.4"

+ 5 - 4
package.json

@@ -3,6 +3,9 @@
     "version": "0.0.1",
     "private": true,
     "main": "static/electron.js",
+    "engines" : {
+      "node" : ">=22.15.0"
+    },
     "devDependencies": {
         "@axe-core/playwright": "=4.4.4",
         "@capacitor/cli": "7.2.0",
@@ -118,8 +121,7 @@
         "@js-joda/core": "3.2.0",
         "@js-joda/locale_en-us": "3.1.1",
         "@js-joda/timezone": "2.5.0",
-        "@logseq/capacitor-file-sync": "5.0.2",
-        "@logseq/diff-merge": "0.2.2",
+        "@logseq/diff-merge": "^0.2.2",
         "@logseq/react-tweet-embed": "1.3.1-1",
         "@logseq/sqlite-wasm": "=0.1.0",
         "@radix-ui/colors": "^0.1.8",
@@ -138,7 +140,7 @@
         "d3-force": "3.0.0",
         "diff": "5.0.0",
         "dompurify": "2.4.0",
-        "electron": "35.0.1",
+        "electron": "36.2.0",
         "electron-dl": "^4.0.0",
         "emoji-mart": "^5.5.2",
         "fs": "0.0.1-security",
@@ -171,7 +173,6 @@
         "react-transition-group": "4.3.0",
         "react-virtuoso": "4.12.5",
         "remove-accents": "0.4.2",
-        "reveal.js": "^4.5.0",
         "sanitize-filename": "1.6.3",
         "send-intent": "^5.0.0",
         "shepherd.js": "^9.1.0",

+ 1 - 1
resources/forge.config.js

@@ -5,7 +5,7 @@ module.exports = {
   packagerConfig: {
     name: 'Logseq',
     icon: './icons/logseq_big_sur.icns',
-    buildVersion: 84,
+    buildVersion: "85",
     protocols: [
       {
         "protocol": "logseq",

+ 0 - 45
resources/js/preload.js

@@ -107,51 +107,6 @@ contextBridge.exposeInMainWorld('apis', {
     )
   },
 
-  /**
-   * When from is empty. The resource maybe from
-   * client paste or screenshoot.
-   * @param repoPathRoot
-   * @param to
-   * @param from?
-   * @returns {Promise<void>}
-   */
-  async copyFileToAssets (repoPathRoot, to, from) {
-    if (from && fs.statSync(from).isDirectory()) {
-      throw new Error('not support copy directory')
-    }
-
-    const dest = path.join(repoPathRoot, to)
-    const assetsRoot = path.dirname(dest)
-
-    await fs.promises.mkdir(assetsRoot, { recursive: true })
-
-    from = from || getFilePathFromClipboard()
-
-    if (from) {
-      try {
-        // console.debug('copy file: ', from, dest)
-        await fs.promises.copyFile(from, dest)
-        return path.basename(from)
-      } catch (e) {
-        from = decodeURIComponent(from)
-        await fs.promises.copyFile(from, dest)
-        return path.basename(from)
-      }
-    }
-
-    // support image
-    // console.debug('read image: ', from, dest)
-    const nImg = clipboard.readImage()
-
-    if (nImg && !nImg.isEmpty()) {
-      const rawExt = path.extname(dest)
-      return await fs.promises.writeFile(
-        dest.replace(rawExt, '.png'),
-        nImg.toPNG()
-      )
-    }
-  },
-
   toggleMaxOrMinActiveWindow (isToggleMin = false) {
     ipcRenderer.invoke('toggle-max-or-min-active-win', isToggleMin)
   },

+ 15 - 15
resources/package.json

@@ -14,14 +14,14 @@
     "electron:make-linux-arm64": "electron-forge make --platform=linux --arch=arm64",
     "electron:make-macos-arm64": "electron-forge make --platform=darwin --arch=arm64",
     "electron:publish:github": "electron-forge publish",
-    "rebuild:all": "electron-rebuild -v 35.0.1 -f",
+    "rebuild:all": "electron-rebuild -v 36.2.0 -f",
     "postinstall": "install-app-deps"
   },
   "config": {
     "forge": "./forge.config.js"
   },
   "dependencies": {
-    "@fastify/cors": "10.0.1",
+    "@fastify/cors": "11.0.1",
     "@logseq/rsapi": "0.0.91",
     "@sentry/electron": "2.5.1",
     "abort-controller": "3.0.0",
@@ -35,7 +35,7 @@
     "electron-squirrel-startup": "1.0.0",
     "electron-window-state": "5.0.3",
     "extract-zip": "2.0.1",
-    "fastify": "5.1.0",
+    "fastify": "5.3.2",
     "fs-extra": "9.1.0",
     "https-proxy-agent": "7.0.2",
     "node-fetch": "2.6.7",
@@ -46,22 +46,22 @@
     "update-electron-app": "2.0.1"
   },
   "devDependencies": {
-    "@electron-forge/cli": "^7.3.1",
-    "@electron-forge/maker-deb": "^7.3.1",
-    "@electron-forge/maker-dmg": "^7.3.1",
-    "@electron-forge/maker-wix": "^7.3.1",
-    "@electron-forge/maker-rpm": "^7.3.1",
-    "@electron-forge/maker-squirrel": "^7.3.1",
-    "@electron-forge/maker-zip": "^7.3.1",
-    "@electron/rebuild": "3.2.10",
-    "electron": "35.0.1",
-    "electron-builder": "25.1.8",
+    "@electron-forge/cli": "^7.8.0",
+    "@electron-forge/maker-deb": "^7.8.0",
+    "@electron-forge/maker-dmg": "^7.8.0",
+    "@electron-forge/maker-wix": "^7.8.0",
+    "@electron-forge/maker-rpm": "^7.8.0",
+    "@electron-forge/maker-squirrel": "^7.8.0",
+    "@electron-forge/maker-zip": "^7.8.0",
+    "@electron/rebuild": "4.0.1",
+    "electron": "36.2.0",
+    "electron-builder": "26.0.12",
     "electron-forge-maker-appimage": "https://github.com/logseq/electron-forge-maker-appimage.git",
     "electron-devtools-installer": "^3.2.0"
   },
   "resolutions": {
-    "**/electron": "35.0.1",
-    "**/node-abi": "3.74.0",
+    "**/electron": "36.2.0",
+    "**/node-abi": "3.75.0",
     "**/node-gyp": "11.1.0",
     "string-width": "4.2.0",
     "wrap-ansi": "^7.0.0",

+ 2 - 2
scripts/package.json

@@ -3,10 +3,10 @@
   "version": "1.0.0",
   "private": true,
   "devDependencies": {
-    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v20"
+    "@logseq/nbb-logseq": "logseq/nbb-logseq#feat-db-v23"
   },
   "dependencies": {
-    "better-sqlite3": "9.3.0",
+    "better-sqlite3": "11.10.0",
     "fs-extra": "9.1.0",
     "mldoc": "^1.5.9"
   }

+ 5 - 5
scripts/src/logseq/tasks/lang.clj

@@ -7,7 +7,7 @@
             [babashka.cli :as cli]
             [babashka.process :refer [shell]]
             [babashka.fs :as fs]
-            [borkdude.rewrite-edn :as r]))
+            [borkdude.rewrite-edn :as rewrite]))
 
 (defn- get-dicts
   []
@@ -78,10 +78,10 @@
   [invalid-keys-by-lang]
   (doseq [[lang invalid-keys] invalid-keys-by-lang]
     (let [path (fs/path "src/resources/dicts" (str (name lang) ".edn"))
-          result (r/parse-string (String. (fs/read-all-bytes path)))
+          result (rewrite/parse-string (String. (fs/read-all-bytes path)))
           new-content (str (reduce
                             (fn [result k]
-                              (r/dissoc result k))
+                              (rewrite/dissoc result k))
                             result invalid-keys))]
       (spit (fs/file path) new-content))))
 
@@ -148,10 +148,10 @@
   [invalid-keys]
   (let [paths (fs/list-dir "src/resources/dicts")]
     (doseq [path paths]
-      (let [result (r/parse-string (String. (fs/read-all-bytes path)))
+      (let [result (rewrite/parse-string (String. (fs/read-all-bytes path)))
             new-content (str (reduce
                               (fn [result k]
-                                (r/dissoc result k))
+                                (rewrite/dissoc result k))
                               result invalid-keys))]
         (spit (fs/file path) new-content)))))
 

+ 12 - 12
scripts/yarn.lock

@@ -2,11 +2,11 @@
 # yarn lockfile v1
 
 
-"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v20":
-  version "1.2.173-feat-db-v20"
-  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/7fb63423ab566717cac79e5f76084d89b845fbd6"
+"@logseq/nbb-logseq@logseq/nbb-logseq#feat-db-v23":
+  version "1.2.173-feat-db-v23"
+  resolved "https://codeload.github.com/logseq/nbb-logseq/tar.gz/810782c4ddba6346c4ab8ae6740b60438c07cd01"
   dependencies:
-    import-meta-resolve "^2.1.0"
+    import-meta-resolve "^4.1.0"
 
 ansi-regex@^2.0.0:
   version "2.1.1"
@@ -28,10 +28,10 @@ base64-js@^1.3.1:
   resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
   integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
 
-better-sqlite3@9.3.0:
-  version "9.3.0"
-  resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-9.3.0.tgz#2a8aaad65fa0210a4df5e8a0bcbc9156f6138d56"
-  integrity sha512-ww73jVpQhRRdS9uMr761ixlkl4bWoXi8hMQlBGhoN6vPNlUHpIsNmw4pKN6kjknlt/wopdvXHvLk1W75BI+n0Q==
+better-sqlite3@11.10.0:
+  version "11.10.0"
+  resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-11.10.0.tgz#2b1b14c5acd75a43fd84d12cc291ea98cef57d98"
+  integrity sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==
   dependencies:
     bindings "^1.5.0"
     prebuild-install "^7.1.1"
@@ -196,10 +196,10 @@ ieee754@^1.1.13:
   resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
   integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
 
-import-meta-resolve@^2.1.0:
-  version "2.2.2"
-  resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-2.2.2.tgz#75237301e72d1f0fbd74dbc6cca9324b164c2cc9"
-  integrity sha512-f8KcQ1D80V7RnqVm+/lirO9zkOxjGxhaTC1IPrBGd3MEfNgmNG67tSUO9gTi2F3Blr2Az6g1vocaxzkVnWl9MA==
+import-meta-resolve@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz#f9db8bead9fafa61adb811db77a2bf22c5399706"
+  integrity sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==
 
 inherits@^2.0.3, inherits@^2.0.4:
   version "2.0.4"

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

@@ -13,7 +13,7 @@
             [frontend.handler.notification :as notification]
             [frontend.handler.plugin :as plugin-handler]
             [frontend.handler.property.file :as property-file]
-            [frontend.ref :as ref]
+            [frontend.util.ref :as ref]
             [frontend.search :as search]
             [frontend.state :as state]
             [frontend.util :as util]

+ 2 - 4
src/main/frontend/components/block.cljs

@@ -62,7 +62,7 @@
             [frontend.mobile.util :as mobile-util]
             [frontend.modules.outliner.tree :as tree]
             [frontend.modules.shortcut.utils :as shortcut-utils]
-            [frontend.ref :as ref]
+            [frontend.util.ref :as ref]
             [frontend.security :as security]
             [frontend.state :as state]
             [frontend.template :as template]
@@ -458,7 +458,7 @@
                  (let [repo (state/get-current-repo)
                        file-rpath (string/replace s #"^[.\/\\]*assets[\/\\]+" "assets/")
                        dir (config/get-repo-dir repo)]
-                   (-> (fs/write-file! repo dir file-rpath content nil)
+                   (-> (fs/write-plain-text-file! repo dir file-rpath content nil)
                        (p/then load$)))))
              (js/console.error _e)))))))
 
@@ -467,11 +467,9 @@
   [state config title href metadata full_text]
   (let [src (::src state)
         repo (state/get-current-repo)
-        granted? (state/sub [:nfs/user-granted? repo])
         href (config/get-local-asset-absolute-path href)
         db-based? (config/db-based-graph? repo)]
     (when (and (or db-based?
-                   granted?
                    (util/electron?)
                    (mobile-util/native-platform?))
                (nil? @src))

+ 12 - 11
src/main/frontend/components/block/macros.cljs

@@ -13,16 +13,18 @@
 
 (defn- properties-by-name
   "Given a block from a query result, returns a map of its properties indexed by
-  property names"
+  property idents and titles"
   [db block]
   (->> (db-property/properties block)
-       (map (fn [[k v]]
-              [(:block/title (d/entity db k))
-               ;; For now just support cardinality :one
-               (when-not (set? v)
-                 (some->> (:db/id v)
-                          (d/entity db)
-                          db-property/property-value-content))]))
+       (mapcat (fn [[k v]]
+                 ;; For now just support cardinality :one
+                 (when-not (set? v)
+                   (let [prop-val (some->> (:db/id v)
+                                           (d/entity db)
+                                           db-property/property-value-content)
+                         property (d/entity db k)]
+                     [[(keyword (:block/title property)) prop-val]
+                      [(:db/ident property) prop-val]]))))
        (into {})))
 
 (defn- normalize-query-function
@@ -55,11 +57,10 @@
          (keyword? f)
          (if-let [kw (and (not db-based-graph?) (get special-file-graph-keywords f))]
            kw
-           (let [prop-key (if db-based-graph? (name f) f)
-                 vals (map #(get-in % [:block/properties prop-key]) result)
+           (let [vals (map #(get-in % [:block/properties f]) result)
                  int? (some integer? vals)]
              `(~'fn [~'b]
-                    (~'let [~'result-str (~'get-in ~'b [:block/properties ~prop-key])
+                    (~'let [~'result-str (~'get-in ~'b [:block/properties ~f])
                             ~'result-num (~'parseFloat ~'result-str)
                             ~'result (if (~'isNaN ~'result-num) ~'result-str ~'result-num)]
                            (~'or ~'result (~'when ~int? 0))))))

+ 12 - 14
src/main/frontend/components/container.cljs

@@ -262,17 +262,17 @@
             :shortcut :go/home})
 
           (when enable-journals?
-           (sidebar-item
-            {:class "journals-nav"
-             :active (and (not srs-open?)
-                          (or (= route-name :all-journals) (= route-name :home)))
-             :title (t :left-side-bar/journals)
-             :on-click-handler (fn [e]
-                                 (if (gobj/get e "shiftKey")
-                                   (route-handler/sidebar-journals!)
-                                   (route-handler/go-to-journals!)))
-             :icon "calendar"
-             :shortcut :go/journals}))))
+            (sidebar-item
+             {:class "journals-nav"
+              :active (and (not srs-open?)
+                           (or (= route-name :all-journals) (= route-name :home)))
+              :title (t :left-side-bar/journals)
+              :on-click-handler (fn [e]
+                                  (if (gobj/get e "shiftKey")
+                                    (route-handler/sidebar-journals!)
+                                    (route-handler/go-to-journals!)))
+              :icon "calendar"
+              :shortcut :go/journals}))))
 
       (for [nav checked-navs]
         (cond
@@ -955,7 +955,6 @@
                       (state/set-state! :editor/latest-shortcut nil)))))
   [state route-match main-content']
   (let [current-repo (state/sub :git/current-repo)
-        granted? (state/sub [:nfs/user-granted? (state/get-current-repo)])
         theme (state/sub :ui/theme)
         accent-color (some-> (state/sub :ui/radix-color) (name))
         editor-font (some-> (state/sub :ui/editor-font) (name))
@@ -992,7 +991,7 @@
       :route route-match
       :current-repo current-repo
       :edit? edit?
-      :nfs-granted? granted?
+
       :db-restoring? db-restoring?
       :sidebar-open? sidebar-open?
       :settings-open? settings-open?
@@ -1067,7 +1066,6 @@
       (plugins/custom-js-installer
        {:t t
         :current-repo current-repo
-        :nfs-granted? granted?
         :db-restoring? db-restoring?})
       (app-context-menu-observer)
 

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

@@ -19,7 +19,7 @@
             [frontend.handler.property :as property-handler]
             [frontend.handler.property.util :as pu]
             [frontend.modules.shortcut.core :as shortcut]
-            [frontend.ref :as ref]
+            [frontend.util.ref :as ref]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]

+ 2 - 2
src/main/frontend/components/file_sync.cljs

@@ -9,11 +9,11 @@
             [frontend.components.onboarding.quick-tour :as quick-tour]
             [frontend.components.page :as page]
             [frontend.config :as config]
-            [frontend.db.model :as db-model]
             [frontend.db.file-based.model :as file-model]
+            [frontend.db.model :as db-model]
             [frontend.fs :as fs]
             [frontend.fs.sync :as fs-sync]
-            [frontend.handler.file-based.nfs :as nfs-handler]
+            [frontend.handler.file-based.native-fs :as nfs-handler]
             [frontend.handler.file-sync :refer [*beta-unavailable?] :as file-sync-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.page :as page-handler]

+ 29 - 29
src/main/frontend/components/file_sync.css

@@ -6,37 +6,37 @@
 
 .cp__file-sync, .cp__rtc-sync {
   &-indicator {
-    .ui__button.cloud {
-      @apply relative;
-
-      &.on {
-        @apply after:content-['*'];
-
-        &:after {
-          @apply absolute bottom-2.5 right-1 w-[7px] h-[7px] rounded-full opacity-100 text-transparent;
-
-          background-color: var(--ls-color-file-sync-pending);
-        }
-
-        &.syncing {
-          &:after {
-            background-color: var(--ls-color-file-sync-pending);
+      .ui__button.cloud {
+          @apply relative;
+
+          &.on {
+              @apply after:content-['*'];
+
+              &:after {
+                  @apply absolute bottom-2.5 right-1 w-[7px] h-[7px] rounded-full opacity-100 text-transparent;
+
+                  background-color: var(--ls-color-file-sync-pending);
+              }
+
+              &.syncing {
+                  &:after {
+                      background-color: var(--ls-color-file-sync-pending);
+                  }
+              }
+
+              &.queuing {
+                  &:after {
+                      background-color: var(--ls-color-file-sync-pending);
+                  }
+              }
+
+              &.idle {
+                  &:after {
+                      background-color: var(--ls-color-file-sync-idle);
+                  }
+              }
           }
-        }
-
-        &.queuing {
-          &:after {
-            background-color: var(--ls-color-file-sync-pending);
-          }
-        }
-
-        &.idle {
-          &:after {
-            background-color: var(--ls-color-file-sync-idle);
-          }
-        }
       }
-    }
 
     .debug-status {
       position: absolute;

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

@@ -345,7 +345,7 @@
                       parent-dir (path/path-join repo-dir (path/dirname (:path file)))]
                   (p/do!
                    (fs/mkdir-if-not-exists parent-dir)
-                   (fs/write-file! repo repo-dir (:path file) content {:skip-transact? true})))))))
+                   (fs/write-plain-text-file! repo repo-dir (:path file) content {:skip-transact? true})))))))
 
 (defn- import-file-graph
   [*files

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

@@ -586,13 +586,13 @@
       (when (and develop-mode? (util/electron?) (not market?))
         [:div
          (ui/tooltip
-           (ui/button
-             (t :plugin/load-unpacked)
-             {:icon "upload"
-              :intent "link"
-              :class "load-unpacked"
-              :on-click plugin-handler/load-unpacked-plugin})
-           [:div (t :plugin/unpacked-tips)])
+          (ui/button
+           (t :plugin/load-unpacked)
+           {:icon "upload"
+            :intent "link"
+            :class "load-unpacked"
+            :on-click plugin-handler/load-unpacked-plugin})
+          [:div (t :plugin/unpacked-tips)])
 
          (when (util/electron?)
            (unpacked-plugin-loader selected-unpacked-pkg))])]
@@ -1524,13 +1524,13 @@
             (bean/->clj (.-settingsSchema pl)) pl)))]]]]))
 
 (rum/defc custom-js-installer
-  [{:keys [t current-repo db-restoring? nfs-granted?]}]
+  [{:keys [t current-repo db-restoring?]}]
   (hooks/use-effect!
    (fn []
      (when (and (not db-restoring?)
-                (or (not util/nfs?) nfs-granted?))
+                (not util/nfs?))
        (ui-handler/exec-js-if-exists-&-allowed! t)))
-   [current-repo db-restoring? nfs-granted?])
+   [current-repo db-restoring?])
   nil)
 
 (rum/defc perf-tip-content

+ 2 - 1
src/main/frontend/components/property/value.cljs

@@ -1460,7 +1460,8 @@
                   (and (:db/id block)
                        (= p-block (:db/id block))
                        (= p-property (:db/id property))))
-              (not= :logseq.class/Tag (:db/ident block)))
+              (not= :logseq.class/Tag
+                    (:db/ident (db/entity (:db/id block)))))
        [:div.flex.flex-row.items-center.gap-1
         [:div.warning "Self reference"]
         (shui/button {:variant :outline

+ 1 - 1
src/main/frontend/components/query/builder.cljs

@@ -13,7 +13,7 @@
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.query.builder :as query-builder]
             [frontend.mixins :as mixins]
-            [frontend.ref :as ref]
+            [frontend.util.ref :as ref]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]

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

@@ -6,7 +6,7 @@
             [frontend.db :as db]
             [frontend.handler.db-based.rtc :as rtc-handler]
             [frontend.handler.db-based.rtc-flows :as rtc-flows]
-            [frontend.handler.file-based.nfs :as nfs-handler]
+            [frontend.handler.file-based.native-fs :as nfs-handler]
             [frontend.handler.file-sync :as file-sync]
             [frontend.handler.graph :as graph]
             [frontend.handler.notification :as notification]
@@ -105,7 +105,7 @@
 
                      :else
                      "Removes Logseq's access to the local file path of your graph. It won't remove your local files.")]
-         (when-not (and only-cloud? (not manager?))
+         (when-not (and db-graph? only-cloud? (not manager?))
            [:a.text-gray-400.ml-4.font-medium.text-sm.whitespace-nowrap
             {:title title
              :on-click (fn []

+ 1 - 1
src/main/frontend/components/right_sidebar.css

@@ -23,7 +23,7 @@ html[data-theme=light] {
 
 .cp__header {
   > .r > div:not(.ui__dropdown-trigger) a, button {
-    @apply opacity-70;
+    @apply opacity-70 text-[inherit];
 
     &:hover {
       @apply opacity-100;

+ 23 - 15
src/main/frontend/components/rtc/indicator.cljs

@@ -4,10 +4,12 @@
             [clojure.pprint :as pprint]
             [frontend.common.missionary :as c.m]
             [frontend.db :as db]
+            [frontend.flows :as flows]
             [frontend.handler.db-based.rtc-flows :as rtc-flows]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
+            [logseq.shui.hooks :as hooks]
             [logseq.shui.ui :as shui]
             [missionary.core :as m]
             [rum.core :as rum]))
@@ -55,10 +57,17 @@
       (reset! *update-detail-info-canceler canceler))))
 (run-task--update-detail-info)
 
-(rum/defc assets-progressing < rum/reactive
+(defn- asset-upload-download-progress-flow
+  [repo]
+  (->> (m/watch (get @state/state :rtc/asset-upload-download-progress))
+       (m/eduction
+        (keep #(get % repo))
+        (dedupe))))
+
+(rum/defc assets-progressing
   []
   (let [repo (state/get-current-repo)
-        progress (state/sub :rtc/asset-upload-download-progress {:path-in-sub-atom [repo]})
+        progress (hooks/use-flow-state (asset-upload-download-progress-flow repo))
         downloading (->>
                      (keep (fn [[id {:keys [direction loaded total]}]]
                              (when (and (= direction :download)
@@ -96,13 +105,12 @@
             (ui/indicator-progress-pie percent)
             (:block/title block)])]])]))
 
-(rum/defcs details < rum/reactive
-  (rum/local false ::expand-debug-info?)
-  [state online?]
-  (let [*expand-debug? (::expand-debug-info? state)
+(rum/defc details
+  [online?]
+  (let [[expand-debug? set-expand-debug!] (hooks/use-state false)
         {:keys [graph-uuid local-tx remote-tx rtc-state
                 download-logs upload-logs misc-logs pending-local-ops pending-server-ops]}
-        (rum/react *detail-info)]
+        (hooks/use-flow-state (m/watch *detail-info))]
     [:div.rtc-info.flex.flex-col.gap-1.p-2.text-gray-11
      [:div.font-medium.mb-2 (if online? "Online" "Offline")]
      [:div [:span.font-medium.mr-1 (or pending-local-ops 0)] "pending local changes"]
@@ -115,9 +123,9 @@
        (when-let [time (:created-at latest-log)]
          [:div.text-sm "Last synced time: "
           (.toLocaleString time)]))
-     [:a.fade-link.text-sm {:on-click #(swap! *expand-debug? not)}
+     [:a.fade-link.text-sm {:on-click #(set-expand-debug! (not expand-debug?))}
       "More debug info"]
-     (when @*expand-debug?
+     (when expand-debug?
        [:div.rtc-info-debug
         [:pre.select-text
          (-> (cond-> {:pending-local-ops pending-local-ops}
@@ -145,13 +153,13 @@
          (> 600
             (/ (- (t/now) created-at) 1000)))))
 
-(rum/defc indicator < rum/reactive
+(rum/defc indicator
   []
-  (let [detail-info                 (rum/react *detail-info)
-        _                           (state/sub :auth/id-token)
-        online?                     (state/sub :network/online?)
-        uploading?'                  (uploading? detail-info)
-        downloading?'                (downloading? detail-info)
+  (let [detail-info                 (hooks/use-flow-state (m/watch *detail-info))
+        _                           (hooks/use-flow-state flows/current-login-user-flow)
+        online?                     (hooks/use-flow-state flows/network-online-event-flow)
+        uploading?'                 (uploading? detail-info)
+        downloading?'               (downloading? detail-info)
         rtc-state                   (:rtc-state detail-info)
         unpushed-block-update-count (:pending-local-ops detail-info)
         {:keys [local-tx remote-tx]} detail-info]

+ 3 - 3
src/main/frontend/components/theme.cljs

@@ -36,7 +36,7 @@
 (defonce *once-theme-loaded? (volatile! false))
 
 (rum/defc ^:large-vars/cleanup-todo container < rum/static
-  [{:keys [route theme accent-color editor-font on-click current-repo nfs-granted? db-restoring?
+  [{:keys [route theme accent-color editor-font on-click current-repo db-restoring?
            settings-open? sidebar-open? system-theme? sidebar-blocks-len onboarding-state preferred-language]} child]
   (let [mounted-fn (use-mounted)
         [restored-sidebar? set-restored-sidebar?] (rum/use-state false)]
@@ -103,9 +103,9 @@
      #(let [db-restored? (false? db-restoring?)]
         (if db-restoring?
           (util/set-title! (t :loading))
-          (when (or nfs-granted? db-restored?)
+          (when db-restored?
             (route-handler/update-page-title! route))))
-     [nfs-granted? db-restoring? route])
+     [db-restoring? route])
 
     (hooks/use-effect!
      (fn []

+ 31 - 21
src/main/frontend/components/views.cljs

@@ -406,7 +406,7 @@
     columns))
 
 (rum/defc more-actions
-  [view-entity columns {:keys [column-visible? rows column-toggle-visibility]}]
+  [view-entity columns {:keys [column-visible? rows column-toggle-visibility]} {:keys [group-by-property-ident]}]
   (let [display-type (:db/ident (:logseq.property.view/type view-entity))
         table? (= display-type :logseq.property.view/type.table)
         group-by-columns (concat (when (or
@@ -455,7 +455,7 @@
              (shui/dropdown-menu-checkbox-item
               {:key (str (:id column))
                :className "capitalize"
-               :checked (= (:id column) (:db/ident (:logseq.property.view/group-by-property view-entity)))
+               :checked (= (:id column) group-by-property-ident)
                :onCheckedChange (fn [result]
                                   (if result
                                     (db-property-handler/set-block-property! (:db/id view-entity) :logseq.property.view/group-by-property
@@ -1828,12 +1828,12 @@
         [:div.text-muted-foreground.text-sm
          (pv/property-value view-entity (db/entity :logseq.property.view/type) {})])
 
-      (when db-based? (more-actions view-entity columns table))
+      (when db-based? (more-actions view-entity columns table option))
 
       (when (and db-based? add-new-object!) (new-record-button table view-entity))]]))
 
 (rum/defc ^:large-vars/cleanup-todo view-inner < rum/static
-  [view-entity {:keys [view-parent data full-data set-data! columns add-new-object! foldable-options input set-input! sorting set-sorting! filters set-filters! view-feature-type] :as option*}
+  [view-entity {:keys [view-parent data full-data set-data! columns add-new-object! foldable-options input set-input! sorting set-sorting! filters set-filters! display-type group-by-property-ident] :as option*}
    *scroller-ref]
   (let [db-based? (config/db-based-graph?)
         option (assoc option* :properties
@@ -1875,7 +1875,8 @@
                                                (remove (fn [column]
                                                          (false? (get visible-columns (:id column))))
                                                        columns))
-        group-by-property (:logseq.property.view/group-by-property view-entity)
+        group-by-property (or (:logseq.property.view/group-by-property view-entity)
+                              (db/entity group-by-property-ident))
         table-map {:view-entity view-entity
                    :data data
                    :full-data full-data
@@ -1901,21 +1902,8 @@
                               :set-last-selected-idx! set-last-selected-idx!}}
         table (shui/table-option table-map)
         *view-ref (rum/use-ref nil)
-        display-type (if (config/db-based-graph?)
-                       (or (:db/ident (get view-entity :logseq.property.view/type))
-                           (when (= (:view-type option) :linked-references)
-                             :logseq.property.view/type.list)
-                           :logseq.property.view/type.table)
-                       (if (= view-feature-type :all-pages)
-                         :logseq.property.view/type.table
-                         :logseq.property.view/type.list))
         gallery? (= display-type :logseq.property.view/type.gallery)
-        list-view? (= display-type :logseq.property.view/type.list)
-        group-by-property-ident (or (:db/ident group-by-property)
-                                    (when (and list-view? (nil? group-by-property))
-                                      :block/page)
-                                    (when (and (not db-based?) (contains? #{:linked-references :unlinked-references} view-feature-type))
-                                      :block/page))]
+        list-view? (= display-type :logseq.property.view/type.list)]
 
     (run-effects! option table-map *scroller-ref gallery?)
 
@@ -1954,6 +1942,7 @@
                    (rum/with-key
                      (ui/foldable
                       [:div
+                       {:class (when-not list-view? "my-4")}
                        (cond
                          group-by-page?
                          (if value
@@ -1974,7 +1963,9 @@
                       {:title-trigger? false})
                      (str (:db/id view-entity) "-group-idx-" idx))))
                (:rows table))])
-           (view-cp view-entity table option view-opts)))]
+           (view-cp view-entity table
+                    (assoc option :group-by-property-ident group-by-property-ident)
+                    view-opts)))]
       (merge {:title-trigger? false} foldable-options))]))
 
 (rum/defcs view-container
@@ -2005,6 +1996,22 @@
 (rum/defc view-aux
   [view-entity {:keys [view-parent view-feature-type data query-entity-ids set-view-entity!] :as option}]
   (let [[input set-input!] (hooks/use-state "")
+        db-based? (config/db-based-graph?)
+        group-by-property (:logseq.property.view/group-by-property view-entity)
+        display-type (if (config/db-based-graph?)
+                       (or (:db/ident (get view-entity :logseq.property.view/type))
+                           (when (= (:view-type option) :linked-references)
+                             :logseq.property.view/type.list)
+                           :logseq.property.view/type.table)
+                       (if (= view-feature-type :all-pages)
+                         :logseq.property.view/type.table
+                         :logseq.property.view/type.list))
+        list-view? (= display-type :logseq.property.view/type.list)
+        group-by-property-ident (or (:db/ident group-by-property)
+                                    (when (and list-view? (nil? group-by-property))
+                                      :block/page)
+                                    (when (and (not db-based?) (contains? #{:linked-references :unlinked-references} view-feature-type))
+                                      :block/page))
         sorting* (:logseq.property.table/sorting view-entity)
         sorting (if (or (= sorting* :logseq.property/empty-placeholder) (empty? sorting*))
                   [{:id :block/updated-at, :asc? false}]
@@ -2035,6 +2042,7 @@
                                                              {:view-for-id (or (:db/id (:logseq.property/view-for view-entity))
                                                                                (:db/id view-parent))
                                                               :view-feature-type view-feature-type
+                                                              :group-by-property-ident group-by-property-ident
                                                               :input input
                                                               :filters filters
                                                               :sorting sorting}
@@ -2052,7 +2060,7 @@
        [(:db/id view-entity)
         (hooks/use-debounced-value input 300)
         sorting-filters
-        (:db/id (:logseq.property.view/group-by-property view-entity))
+        group-by-property-ident
         (:db/id (:logseq.property.view/type view-entity))
         ;; page filters
         (:logseq.property.linked-references/includes view-parent)
@@ -2079,7 +2087,9 @@
                                                          ;; grouped
                                                          (reduce (fn [total [_ col]]
                                                                    (+ total (count col))) 0 data))
+                                          :group-by-property-ident group-by-property-ident
                                           :ref-pages-count ref-pages-count
+                                          :display-type display-type
                                           :load-view-data load-view-data
                                           :set-view-entity! set-view-entity!))])))
 

+ 6 - 16
src/main/frontend/db/persist.cljs

@@ -1,13 +1,13 @@
 (ns frontend.db.persist
   "Handles operations to persisting db to disk or indexedDB"
-  (:require [frontend.util :as util]
-            [frontend.idb :as idb]
+  (:require [cljs-bean.core :as bean]
             [electron.ipc :as ipc]
+            [frontend.config :as config]
             [frontend.db.conn :as db-conn]
-            [promesa.core :as p]
+            [frontend.idb :as idb]
             [frontend.persist-db :as persist-db]
-            [cljs-bean.core :as bean]
-            [frontend.config :as config]))
+            [frontend.util :as util]
+            [promesa.core :as p]))
 
 (defn get-all-graphs
   []
@@ -24,7 +24,7 @@
     (distinct (concat
                repos'
                (map (fn [repo-name] {:name repo-name})
-                 (concat idb-repos (some-> electron-disk-graphs bean/->clj)))))))
+                    (concat idb-repos (some-> electron-disk-graphs bean/->clj)))))))
 
 (defn delete-graph!
   [graph]
@@ -34,13 +34,3 @@
       (if (util/electron?)
         (ipc/ipc "deleteGraph" graph key db-based?)
         (idb/remove-item! key)))))
-
-(defn rename-graph!
-  [old-repo new-repo]
-  (let [old-key (db-conn/get-repo-path old-repo)
-        new-key (db-conn/get-repo-path new-repo)]
-    (if (util/electron?)
-      (do
-        (js/console.error "rename-graph! is not supported in electron")
-        (idb/rename-item! old-key new-key))
-      (idb/rename-item! old-key new-key))))

+ 7 - 28
src/main/frontend/encrypt.cljs

@@ -1,41 +1,20 @@
 (ns frontend.encrypt
   "Encryption related fns for use with encryption feature and file sync"
-  (:require [logseq.graph-parser.utf8 :as utf8]
+  (:require [electron.ipc :as ipc]
             [frontend.util :as util]
-            [promesa.core :as p]
-            [electron.ipc :as ipc]
-            [frontend.mobile.util :as mobile-util]))
+            [logseq.graph-parser.utf8 :as utf8]
+            [promesa.core :as p]))
 
 (defn encrypt-with-passphrase
   [passphrase content]
-  (cond
-    (util/electron?)
+  (when (util/electron?)
     (p/let [raw-content (utf8/encode content)
             encrypted (ipc/ipc "encrypt-with-passphrase" passphrase raw-content)]
-      (utf8/decode encrypted))
-
-    (mobile-util/native-platform?)
-    (p/chain (.encryptWithPassphrase mobile-util/file-sync
-                                     (clj->js {:passphrase passphrase :content content}))
-             #(js->clj % :keywordize-keys true)
-             :data)
-
-    :else
-    nil))
+      (utf8/decode encrypted))))
 
 (defn decrypt-with-passphrase
   [passphrase content]
-  (cond
-    (util/electron?)
+  (when (util/electron?)
     (p/let [raw-content (utf8/encode content)
             decrypted (ipc/ipc "decrypt-with-passphrase" passphrase raw-content)]
-      (utf8/decode decrypted))
-
-    (mobile-util/native-platform?)
-    (p/chain (.decryptWithPassphrase mobile-util/file-sync
-                                     (clj->js {:passphrase passphrase :content content}))
-             #(js->clj % :keywordize-keys true)
-             :data)
-
-    :else
-    nil))
+      (utf8/decode decrypted))))

+ 20 - 23
src/main/frontend/extensions/excalidraw.cljs

@@ -1,22 +1,22 @@
 (ns frontend.extensions.excalidraw
-  (:require [clojure.string :as string]
-            ;; NOTE: Always use production build of excalidraw
+  (:require ;; NOTE: Always use production build of excalidraw
             ;; See-also: https://github.com/excalidraw/excalidraw/pull/3330
-            ["@excalidraw/excalidraw/dist/excalidraw.production.min" :refer [Excalidraw serializeAsJSON]]
-            [frontend.config :as config]
-            [frontend.db :as db]
-            [frontend.handler.editor :as editor-handler]
-            [frontend.handler.draw :as draw]
-            [frontend.handler.notification :as notification]
-            [frontend.handler.ui :as ui-handler]
-            [frontend.rum :as r]
-            [frontend.state :as state]
-            [frontend.ui :as ui]
-            [frontend.util :as util]
-            [goog.object :as gobj]
-            [goog.functions :refer [debounce]]
-            [rum.core :as rum]
-            [frontend.mobile.util :as mobile-util]))
+   ["@excalidraw/excalidraw/dist/excalidraw.production.min" :refer [Excalidraw serializeAsJSON]]
+   [clojure.string :as string]
+   [frontend.config :as config]
+   [frontend.db :as db]
+   [frontend.handler.draw :as draw]
+   [frontend.handler.editor :as editor-handler]
+   [frontend.handler.notification :as notification]
+   [frontend.handler.ui :as ui-handler]
+   [frontend.mobile.util :as mobile-util]
+   [frontend.rum :as r]
+   [frontend.state :as state]
+   [frontend.ui :as ui]
+   [frontend.util :as util]
+   [goog.functions :refer [debounce]]
+   [goog.object :as gobj]
+   [rum.core :as rum]))
 
 (def excalidraw (r/adapt-class Excalidraw))
 
@@ -99,8 +99,8 @@
        [:div.draw-wrap
         {:ref ref
          :on-pointer-down (fn [e]
-                          (util/stop e)
-                          (state/set-block-component-editing-mode! true))
+                            (util/stop e)
+                            (state/set-block-component-editing-mode! true))
          :on-blur #(state/set-block-component-editing-mode! false)
          :style {:width  @*draw-width
                  :height (if wide-mode? 650 500)}}
@@ -162,11 +162,8 @@
 
 (rum/defc draw < rum/reactive
   [option]
-  (let [repo (state/get-current-repo)
-        granted? (state/sub [:nfs/user-granted? repo])]
-    ;; Web granted
+  (let [repo (state/get-current-repo)]
     (when-not (and (config/local-file-based-graph? repo)
-                   (not granted?)
                    (not (util/electron?))
                    (not (mobile-util/native-platform?)))
       (draw-container option))))

+ 6 - 5
src/main/frontend/extensions/pdf/assets.cljs

@@ -19,7 +19,7 @@
             [frontend.handler.property :as property-handler]
             [frontend.handler.property.util :as pu]
             [frontend.handler.route :as route-handler]
-            [frontend.ref :as ref]
+            [frontend.util.ref :as ref]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]
@@ -49,8 +49,9 @@
         url       (if blob-res? href
                       (assets-handler/normalize-asset-resource-url original-path))
         filename' (if (or asset-res? web-link? blob-res?) filename
-                      (some-> (get-in-repo-assets-full-filename url)
-                              (js/decodeURIComponent) (string/replace '"/" "_")))
+                    (some-> url (js/decodeURIComponent)
+                      (get-in-repo-assets-full-filename)
+                      (string/replace '"/" "_")))
         filekey   (util/safe-sanitize-file-name
                    (subs filename' 0 (- (count filename') (inc (count ext-name)))))]
     (when-let [key (and (not (string/blank? filekey))
@@ -216,7 +217,7 @@
     (let [repo-cur (state/get-current-repo)
           repo-dir (config/get-repo-dir repo-cur)
           data     (with-out-str (pprint {:highlights highlights :extra extra}))]
-      (fs/write-file! repo-cur repo-dir hls-file data {:skip-compare? true}))))
+      (fs/write-plain-text-file! repo-cur repo-dir hls-file data {:skip-compare? true}))))
 
 (defn file-based-resolve-hls-data-by-key$
   [target-key]
@@ -242,7 +243,7 @@
           new-fpath  (str fdir "/" fname "_" fstamp ".png")
           old-fpath  (and old-fstamp (str fdir "/" fname "_" old-fstamp ".png"))
           _          (and old-fpath (fs/rename! repo-url old-fpath new-fpath))
-          _          (fs/write-file! repo-url repo-dir new-fpath png {:skip-compare? true})]
+          _          (fs/write-plain-text-file! repo-url repo-dir new-fpath png {:skip-compare? true})]
 
     (js/console.timeEnd :write-area-image)))
 

+ 1 - 1
src/main/frontend/extensions/zotero/handler.cljs

@@ -9,7 +9,7 @@
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.page :as page-handler]
-            [frontend.ref :as ref]
+            [frontend.util.ref :as ref]
             [frontend.state :as state]
             [promesa.core :as p]))
 

+ 1 - 2
src/main/frontend/flows.cljs

@@ -46,5 +46,4 @@
        (m/relieve)))
 
 (def network-online-event-flow
-  (->> (m/watch *network-online?)
-       (m/eduction (filter true?))))
+  (m/watch *network-online?))

+ 14 - 41
src/main/frontend/fs.cljs

@@ -2,39 +2,27 @@
   "System-component-like ns that provides common file operations for all
   platforms by delegating to implementations of the fs protocol"
   (:require [cljs-bean.core :as bean]
+            [clojure.string :as string]
+            [electron.ipc :as ipc]
             [frontend.config :as config]
-            [frontend.fs.nfs :as nfs]
-            [frontend.fs.node :as node]
-            [frontend.fs.capacitor-fs :as capacitor-fs]
             [frontend.fs.memory-fs :as memory-fs]
-            [frontend.mobile.util :as mobile-util]
+            [frontend.fs.node :as node]
             [frontend.fs.protocol :as protocol]
+            [frontend.state :as state]
             [frontend.util :as util]
             [lambdaisland.glogi :as log]
-            [promesa.core :as p]
             [logseq.common.path :as path]
-            [clojure.string :as string]
-            [frontend.state :as state]
             [logseq.common.util :as common-util]
-            [electron.ipc :as ipc]))
+            [promesa.core :as p]))
 
-(defonce nfs-backend (nfs/->Nfs))
 (defonce memory-backend (memory-fs/->MemoryFs))
 (defonce node-backend (node/->Node))
-(defonce mobile-backend (capacitor-fs/->Capacitorfs))
 
 (defn- get-native-backend
   "Native FS backend of current platform"
   []
-  (cond
-    (util/electron?)
-    node-backend
-
-    (mobile-util/native-platform?)
-    mobile-backend
-
-    :else
-    nfs-backend))
+  (when (util/electron?)
+    node-backend))
 
 (defn get-fs
   [dir & {:keys [repo rpath]}]
@@ -62,11 +50,8 @@
       (and (util/electron?) (not bfs-local?))
       node-backend
 
-      (mobile-util/native-platform?)
-      mobile-backend
-
       :else
-      nfs-backend)))
+      nil)))
 
 (defn mkdir!
   [dir]
@@ -99,8 +84,8 @@
     (when (= fs memory-backend)
       (protocol/rmdir! fs dir))))
 
-;; TODO(andelf): distinguish from graph file writing and global file write
-(defn write-file!
+(defn write-plain-text-file!
+  "Use it only for plain-text files, not binary"
   [repo dir rpath content opts]
   (when content
     (let [path (common-util/path-normalize rpath)
@@ -174,7 +159,7 @@
 
     :else
     (let [[old-path new-path]
-          (map #(if (or (util/electron?) (mobile-util/native-platform?))
+          (map #(if (util/electron?)
                   %
                   (str (config/get-repo-dir repo) "/" %))
                [old-path new-path])
@@ -182,8 +167,6 @@
       (p/let [_ (mkdir-if-not-exists new-dir)]
         (protocol/copy! (get-fs old-path) repo old-path new-path)))))
 
-
-
 (defn open-dir
   [dir]
   (let [record (get-native-backend)]
@@ -227,7 +210,7 @@
          true)
        (p/catch
         (fn [_error]
-          (p/let [_ (write-file! repo dir path initial-content nil)]
+          (p/let [_ (write-plain-text-file! repo dir path initial-content nil)]
             false))))))
 
 (defn file-exists?
@@ -256,9 +239,6 @@
     (util/electron?)
     (path/url-to-path path)
 
-    (mobile-util/native-platform?)
-    path
-
     :else
     path))
 
@@ -268,12 +248,5 @@
 
 (defn backup-db-file!
   [repo path db-content disk-content]
-  (cond
-    (util/electron?)
-    (ipc/ipc "backupDbFile" (config/get-local-dir repo) path db-content disk-content)
-
-    (mobile-util/native-platform?)
-    (capacitor-fs/backup-file repo :backup-dir path db-content)
-
-    ;; TODO: nfs
-    ))
+  (when (util/electron?)
+    (ipc/ipc "backupDbFile" (config/get-local-dir repo) path db-content disk-content)))

+ 0 - 404
src/main/frontend/fs/capacitor_fs.cljs

@@ -1,404 +0,0 @@
-(ns frontend.fs.capacitor-fs
-  "Implementation of fs protocol for mobile"
-  (:require ["@capacitor/filesystem" :refer [Encoding Filesystem]]
-            [cljs-bean.core :as bean]
-            [clojure.string :as string]
-            [goog.string :as gstring]
-            [frontend.config :as config]
-            [frontend.db :as db]
-            [frontend.fs.protocol :as protocol]
-            [frontend.mobile.util :as mobile-util]
-            [frontend.state :as state]
-            [frontend.util :as util]
-            [lambdaisland.glogi :as log]
-            [promesa.core :as p]
-            [rum.core :as rum]
-            [logseq.common.path :as path]))
-
-(when (mobile-util/native-ios?)
-  (defn ios-ensure-documents!
-    []
-    (.ensureDocuments mobile-util/ios-file-container)))
-
-(when (mobile-util/native-android?)
-  (defn- android-check-permission []
-    (p/let [permission (.checkPermissions Filesystem)
-            permission (-> permission
-                           bean/->clj
-                           :publicStorage)]
-      (when-not (= permission "granted")
-        (p/do!
-         (.requestPermissions Filesystem))))))
-
-(defn- <dir-exists?
-  [fpath]
-  (p/catch (p/let [fpath (path/path-normalize fpath)
-                   stat (.stat Filesystem (clj->js {:path fpath}))]
-             (-> stat
-                 bean/->clj
-                 :type
-                 (= "directory")))
-           (fn [_error]
-             false)))
-
-(defn <write-file-with-base64
-  "Write a binary file, requires base64 encoding"
-  [path content]
-  (when-not (string/blank? path)
-    (-> (p/chain (.writeFile Filesystem (clj->js {:path path
-                                                  :data content
-                                                  :recursive true}))
-                 #(js->clj % :keywordize-keys true))
-        (p/catch (fn [error]
-                   (js/console.error "writeFile Error: " path ": " error)
-                   nil)))))
-
-(defn- <write-file-with-utf8
-  [path content]
-  (when-not (string/blank? path)
-    (-> (p/chain (.writeFile Filesystem (clj->js {:path path
-                                                  :data content
-                                                  :encoding (.-UTF8 Encoding)
-                                                  :recursive true}))
-                 #(js->clj % :keywordize-keys true))
-        (p/catch (fn [error]
-                   (js/console.error "writeFile Error: " path ": " error)
-                   nil)))))
-
-(defn- <read-file-with-utf8
-  [path]
-  (when-not (string/blank? path)
-    (-> (p/chain (.readFile Filesystem (clj->js {:path path
-                                                 :encoding (.-UTF8 Encoding)}))
-                 #(js->clj % :keywordize-keys true)
-                 #(get % :data nil))
-        (p/catch (fn [error]
-                   (js/console.error "readFile Error: " path ": " error)
-                   nil)))))
-
-(defn- <readdir [path]
-  (-> (p/chain (.readdir Filesystem (clj->js {:path path}))
-               #(js->clj % :keywordize-keys true)
-               :files)
-      (p/catch (fn [error]
-                 (js/console.error "readdir Error: " path ": " error)
-                 nil))))
-
-(defn- get-file-paths
-  "get all file paths recursively"
-  [path]
-  (p/let [result (p/loop [result []
-                          dirs [path]]
-                   (if (empty? dirs)
-                     result
-                     (p/let [d (first dirs)
-                             files (<readdir d)
-                             files (->> files
-                                        (remove (fn [{:keys [name  type]}]
-                                                  (or (string/starts-with? name ".")
-                                                      (and (= type "directory")
-                                                           (or (= name "bak")
-                                                               (= name "version-files")))))))
-                             files-dir (->> files
-                                            (filterv #(= (:type %) "directory"))
-                                            (mapv :uri))
-                             paths-result (->> files
-                                               (filterv #(= (:type %) "file"))
-                                               (mapv :uri))]
-                       (p/recur (concat result paths-result)
-                                (concat (rest dirs) files-dir)))))]
-    result))
-
-(defn- get-files
-  "get all files and contents recursively"
-  [path]
-  (p/let [result (p/loop [result []
-                          dirs [path]]
-                   (if (empty? dirs)
-                     result
-                     (p/let [d (first dirs)
-                             files (<readdir d)
-                             files (->> files
-                                        (remove (fn [{:keys [name  type]}]
-                                                  (or (string/starts-with? name ".")
-                                                      (and (= type "directory")
-                                                           (or (= name "bak")
-                                                               (= name "version-files")))))))
-                             files-dir (->> files
-                                            (filterv #(= (:type %) "directory"))
-                                            (mapv :uri))
-                             files-result
-                             (p/all
-                              (->> files
-                                   (filter #(= (:type %) "file"))
-                                   (filter
-                                    (fn [{:keys [uri]}]
-                                      (some #(string/ends-with? uri %)
-                                            [".md" ".markdown" ".org" ".edn" ".css"])))
-                                   (mapv
-                                    (fn [{:keys [uri] :as file-info}]
-                                      (p/chain (<read-file-with-utf8 uri)
-                                               #(assoc (dissoc file-info :uri)
-                                                       :content %
-                                                       :path uri))))))]
-                       (p/recur (concat result files-result)
-                                (concat (rest dirs) files-dir)))))]
-    (js->clj result :keywordize-keys true)))
-
-(defn- <contents-matched?
-  [disk-content db-content]
-  (when (and (string? disk-content) (string? db-content))
-    (p/resolved (= (string/trim disk-content) (string/trim db-content)))))
-
-(def backup-dir "logseq/bak")
-(def version-file-dir "logseq/version-files/local")
-
-(defn- get-backup-dir
-  [repo-dir path bak-dir ext]
-  (let [relative-path (-> path
-                          (string/replace (re-pattern (str "^" (gstring/regExpEscape repo-dir)))
-                                          "")
-                          (string/replace (re-pattern (str "(?i)" (gstring/regExpEscape (str "." ext)) "$"))
-                                          ""))]
-    (path/path-join repo-dir bak-dir relative-path)))
-
-(defn- <truncate-old-versioned-files!
-  "reserve the latest 6 version files"
-  [dir]
-  (-> (p/let [files (.readdir Filesystem (clj->js {:path dir}))
-
-              files (:files (js->clj files :keywordize-keys true))]
-        (drop 6 (reverse (sort-by :mtime files))))
-      (p/then (fn [old-version-files]
-                (p/all (mapv (fn [file]
-                               (.deleteFile Filesystem (clj->js {:path (:uri file)})))
-                             old-version-files))))
-      (p/catch (fn [_]))))
-
-;; TODO: move this to FS protocol
-(defn backup-file
-  "backup CONTENT under DIR :backup-dir or :version-file-dir
-  :backup-dir = `backup-dir`
-  :version-file-dir = `version-file-dir`"
-  [repo dir path content]
-  {:pre [(contains? #{:backup-dir :version-file-dir} dir)]}
-  (let [repo-dir (config/get-local-dir repo)
-        ext (util/get-file-ext path)
-        dir (case dir
-              :backup-dir (get-backup-dir repo-dir path backup-dir ext)
-              :version-file-dir (get-backup-dir repo-dir path version-file-dir ext))
-        new-path (path/path-join dir (str (string/replace (.toISOString (js/Date.)) ":" "_") "." (mobile-util/platform) "." ext))]
-
-    (<write-file-with-utf8 new-path content)
-    (<truncate-old-versioned-files! dir)))
-
-(defn backup-file-handle-changed!
-  [repo-dir file-path content]
-  (let [divider-schema    "://"
-        file-schema       (string/split file-path divider-schema)
-        file-schema       (if (> (count file-schema) 1) (first file-schema) "")
-        dir-schema?       (and (string? repo-dir)
-                               (string/includes? repo-dir divider-schema))
-        repo-dir          (if-not dir-schema?
-                            (str file-schema divider-schema repo-dir) repo-dir)
-        backup-root       (path/path-join repo-dir backup-dir)
-        backup-dir-parent (util/node-path.dirname file-path)
-        backup-dir-parent (string/replace backup-dir-parent repo-dir "")
-        backup-dir-name (util/node-path.name file-path)
-        file-extname (.extname util/node-path file-path)
-        file-root (path/path-join backup-root backup-dir-parent backup-dir-name)
-        file-path (path/path-join file-root
-                                  (str (string/replace (.toISOString (js/Date.)) ":" "_") "." (mobile-util/platform) file-extname))]
-    (<write-file-with-utf8 file-path content)
-    (<truncate-old-versioned-files! file-root)))
-
-(defn- write-file-impl!
-  [repo dir rpath content {:keys [ok-handler error-handler old-content skip-compare?]} stat]
-  (let [fpath (path/path-join dir rpath)]
-    (if (or (string/blank? repo) skip-compare?)
-      (p/catch
-       (p/let [result (<write-file-with-utf8 fpath content)]
-         (when ok-handler
-           (ok-handler repo fpath result)))
-       (fn [error]
-         (if error-handler
-           (error-handler error)
-           (log/error :write-file-failed error))))
-
-    ;; Compare with disk content and backup if not equal
-      (p/let [disk-content (if (not= stat :not-found)
-                             (<read-file-with-utf8 fpath)
-                             "")
-              disk-content (or disk-content "")
-              repo-dir (config/get-local-dir repo)
-              db-content (or old-content (db/get-file repo rpath) "")
-              contents-matched? (<contents-matched? disk-content db-content)]
-        (->
-         (p/let [result (<write-file-with-utf8 fpath content)
-                 mtime (-> (js->clj stat :keywordize-keys true)
-                           :mtime)]
-           (when-not contents-matched?
-             (backup-file repo-dir :backup-dir fpath disk-content))
-           (db/set-file-last-modified-at! repo rpath mtime)
-           (db/set-file-content! repo rpath content)
-           (when ok-handler
-             (ok-handler repo fpath result))
-           result)
-         (p/catch (fn [error]
-                    (if error-handler
-                      (error-handler error)
-                      (log/error :write-file-failed error)))))))))
-
-(defn ios-force-include-private
-  "iOS sometimes return paths without the private part."
-  [path]
-  (if (mobile-util/native-ios?)
-    (cond
-      (or (string/includes? path "///private/")
-          ;; virtual machine
-          (string/starts-with? path "file:///Users/"))
-      path
-
-      (string/includes? path "///")
-      (let [[prefix others] (string/split path "///")]
-        (str prefix "///private/" others))
-
-      :else
-      path)
-    path))
-
-(defn- local-container-path?
-  "Check whether `path' is logseq's container `localDocumentsPath' on iOS"
-  [path localDocumentsPath]
-  (string/includes? path localDocumentsPath))
-
-(rum/defc instruction
-  []
-  [:div.instruction
-   [:h1.title "Please choose a valid directory!"]
-   [:p.leading-6 "Logseq app can only save or access your graphs stored in a specific directory with a "
-    [:strong "Logseq icon"]
-    " inside, located either in \"iCloud Drive\", \"On My iPhone\" or \"On My iPad\"."]
-   [:p.leading-6 "Please watch the following short instruction video. "
-    [:small.text-gray-500 "(may take few seconds to load...)"]]
-   [:iframe
-    {:src "https://www.loom.com/embed/dae612ae5fd94e508bd0acdf02efb888"
-     :frame-border "0"
-     :position "relative"
-     :allow-full-screen "allowfullscreen"
-     :webkit-allow-full-screen "webkitallowfullscreen"
-     :height "100%"}]])
-
-(defn- open-dir
-  [dir]
-  (p/let [_ (when (mobile-util/native-android?) (android-check-permission))
-          {:keys [path localDocumentsPath]} (-> (.pickFolder mobile-util/folder-picker
-                                                             (clj->js (when (and dir (mobile-util/native-ios?))
-                                                                        {:path dir})))
-                                                (p/then #(js->clj % :keywordize-keys true))
-                                                (p/catch (fn [e]
-                                                           (js/alert (str e))
-                                                           nil))) ;; NOTE: If pick folder fails, let it crash
-          _ (when (and (mobile-util/native-ios?)
-                       (not (or (local-container-path? path localDocumentsPath)
-                                (mobile-util/in-iCloud-container-path? path))))
-              (state/pub-event! [:modal/show-instruction]))
-          exists? (<dir-exists? path)
-          _ (when-not exists?
-              (p/rejected (str "Cannot access selected directory: " path)))
-          _ (when (mobile-util/is-iCloud-container-path? path)
-              (p/rejected (str "Please avoid accessing the top-level iCloud container path: " path)))
-          path (if (mobile-util/native-ios?)
-                 (ios-force-include-private path)
-                 path)
-          _ (js/console.log "Opening or Creating graph at directory: " path)
-          files (get-files path)]
-    {:path path
-     :files (into [] files)}))
-
-(defrecord ^:large-vars/cleanup-todo Capacitorfs []
-  protocol/Fs
-  (mkdir! [_this dir]
-    (-> (<dir-exists? dir)
-        (p/then (fn [exists?]
-                  (if exists?
-                    (p/resolved true)
-                    (.mkdir Filesystem
-                            (clj->js
-                             {:path dir})))))
-        (p/catch (fn [error]
-                   (log/error :mkdir! {:path dir
-                                       :error error})))))
-  (mkdir-recur! [_this dir]
-    (-> (<dir-exists? dir)
-        (p/then (fn [exists?]
-                  (if exists?
-                    (p/resolved true)
-                    (.mkdir Filesystem
-                            (clj->js
-                             {:path dir
-                              :recursive true})))))
-        (p/catch (fn [error]
-                   (log/error :mkdir-recur! {:path dir
-                                             :error error})))))
-  (readdir [_this dir]                  ; recursive
-    (let [dir (path/path-normalize dir)]
-      (get-file-paths dir)))
-  (unlink! [this repo fpath _opts]
-    (p/let [repo-dir (config/get-local-dir repo)
-            recycle-dir (path/path-join repo-dir config/app-name ".recycle") ;; logseq/.recycle
-            ;; convert url to pure path
-            file-name (-> (path/trim-dir-prefix repo-dir fpath)
-                          (string/replace "/" "_"))
-            new-path (path/path-join recycle-dir file-name)
-            _ (protocol/mkdir-recur! this recycle-dir)]
-      (protocol/rename! this repo fpath new-path)))
-  (rmdir! [_this _dir]
-    ;; Too dangerous!!! We'll never implement this.
-    nil)
-  (read-file [_this dir path _options]
-    (let [fpath (path/path-join dir path)]
-      (->
-       (<read-file-with-utf8 fpath)
-       (p/catch (fn [error]
-                  (log/error :read-file-failed error))))))
-  (write-file! [_this repo dir path content opts]
-    (let [fpath (path/path-join dir path)]
-      (p/let [stat (p/catch
-                    (.stat Filesystem (clj->js {:path fpath}))
-                    (fn [_e] :not-found))]
-        ;; `path` is full-path
-        (write-file-impl! repo dir path content opts stat))))
-  (rename! [_this _repo old-fpath new-fpath]
-    (-> (.rename Filesystem
-                 (clj->js
-                  {:from old-fpath
-                   :to new-fpath}))
-        (p/catch (fn [error]
-                   (log/error :rename-file-failed error)))))
-  (copy! [_this _repo old-path new-path]
-    (-> (.copy Filesystem
-               (clj->js
-                {:from old-path
-                 :to new-path}))
-        (p/catch (fn [error]
-                   (log/error :copy-file-failed error)))))
-  (stat [_this fpath]
-    (-> (p/chain (.stat Filesystem (clj->js {:path fpath}))
-                 #(js->clj % :keywordize-keys true))
-        (p/catch (fn [error]
-                   (let [errstr (if error (.toString error) "")]
-                     (when (string/includes? errstr "because you don’t have permission to view it")
-                       (state/pub-event! [:notification/show
-                                          {:content "No permission, please clear cache and re-open graph folder."
-                                           :status :error}]))
-                     (p/rejected error))))))
-  (open-dir [_this dir]
-    (open-dir dir))
-  (get-files [_this dir]
-    (get-files dir))
-  (watch-dir! [_this dir _options]
-    (p/let [_ (.unwatch mobile-util/fs-watcher)]
-      (.watch mobile-util/fs-watcher (clj->js {:path dir}))))
-  (unwatch-dir! [_this _dir]
-    (.unwatch mobile-util/fs-watcher)))

+ 0 - 366
src/main/frontend/fs/nfs.cljs

@@ -1,366 +0,0 @@
-(ns frontend.fs.nfs
-  "Browser File System API based fs implementation.
-
-   Rationale:
-   - nfs-file-handles-cache stores all file & directory handle
-   - idb stores top-level directory handle
-   - readdir/get-files is called by re-index and initial watcher to init all handles"
-  (:require [frontend.fs.protocol :as protocol]
-            [frontend.util :as util]
-            [clojure.string :as string]
-            [frontend.idb :as idb]
-            [promesa.core :as p]
-            [lambdaisland.glogi :as log]
-            [goog.object :as gobj]
-            [frontend.db :as db]
-            [frontend.config :as config]
-            [frontend.state :as state]
-            [frontend.handler.notification :as notification]
-            ["/frontend/utils" :as utils]
-            [logseq.common.util :as common-util]
-            [logseq.common.path :as path]))
-
-;; Cache the file handles in the memory so that
-;; the browser will not keep asking permissions.
-(defonce nfs-file-handles-cache (atom {}))
-
-(defn- get-nfs-file-handle
-  [handle-path]
-  (get @nfs-file-handles-cache handle-path))
-
-(defn add-nfs-file-handle!
-  [handle-path handle]
-  (prn ::DEBUG "add-nfs-file-handle!" handle-path)
-  (swap! nfs-file-handles-cache assoc handle-path handle))
-
-(defn remove-nfs-file-handle!
-  [handle-path]
-  (swap! nfs-file-handles-cache dissoc handle-path))
-
-(defn- nfs-saved-handler
-  [repo path file]
-  (when-let [last-modified (gobj/get file "lastModified")]
-    ;; TODO: extract
-    (let [path (if (= \/ (first path))
-                 (subs path 1)
-                 path)]
-      ;; Bad code
-      (db/set-file-last-modified-at! repo path last-modified))))
-
-(defn- verify-handle-permission
-  [handle read-write?]
-  (utils/verifyPermission handle read-write?))
-
-(defn verify-permission
-  [repo read-write?]
-  (let [repo (or repo (state/get-current-repo))
-        repo-dir (config/get-repo-dir repo)
-        handle-path (str "handle/" repo-dir)
-        handle (get-nfs-file-handle handle-path)]
-    (p/then
-     (utils/verifyPermission handle read-write?)
-     (fn []
-       (state/set-state! [:nfs/user-granted? repo] true)
-       true))))
-
-(defn check-directory-permission!
-  [repo]
-  (when (config/local-file-based-graph? repo)
-    (p/let [repo-dir (config/get-repo-dir repo)
-            handle-path (str "handle/" repo-dir)
-            handle (idb/get-item handle-path)]
-      (when handle
-        (add-nfs-file-handle! handle-path handle)
-        (verify-permission repo true)))))
-
-(defn- contents-matched?
-  [disk-content db-content]
-  (when (and (string? disk-content) (string? db-content))
-    (= (string/trim disk-content) (string/trim db-content))))
-
-(defn- await-permission-granted
-  "Guard against File System Access API permission, avoiding early access before granted"
-  [repo]
-  (if (state/nfs-user-granted? repo)
-    (p/resolved true)
-    (js/Promise. (fn [resolve reject]
-                   (let [timer (atom nil)
-                         timer' (js/setInterval (fn []
-                                                  (when (state/nfs-user-granted? repo)
-                                                    (js/clearInterval @timer)
-                                                    (resolve true)))
-                                                1000)
-                         _ (reset! timer timer')]
-                     (js/setTimeout (fn []
-                                      (js/clearInterval timer)
-                                      (reject false))
-                                    100000))))))
-
-(defn await-get-nfs-file-handle
-  "for accessing File handle outside, ensuring user granted."
-  [repo handle-path]
-  (p/let [_ (await-permission-granted repo)]
-    (get-nfs-file-handle handle-path)))
-
-(defn- readdir-and-reload-all-handles
-  "Return list of filenames"
-  [root-dir root-handle]
-  (p/let [files (utils/getFiles root-handle
-                                true
-                                (fn [path entry]
-                                  (let [handle-path (str "handle/" path)]
-                                    ;; Same for all handles here, even for directories and ignored directories(for backing up)
-                                    ;; FileSystemDirectoryHandle or FileSystemFileHandle
-                                    (when-not (string/includes? path "/.")
-                                      (add-nfs-file-handle! handle-path entry)))))]
-    (->> files
-         (remove  (fn [file]
-                    (let [rpath (string/replace-first (.-webkitRelativePath file) (str root-dir "/") "")
-                          ext (util/get-file-ext rpath)]
-                      (or  (string/blank? rpath)
-                           (string/starts-with? rpath ".")
-                           (string/starts-with? rpath "logseq/bak")
-                           ; (string/starts-with? rpath "logseq/version-files")
-                           (not (contains? #{"md" "org" "excalidraw" "edn" "css"} ext))))))
-         (map (fn [file]
-                (-> (.-webkitRelativePath file)
-                    common-util/path-normalize))))))
-
-
-(defn- get-files-and-reload-all-handles
-  "Return list of file objects"
-  [root-dir root-handle]
-  (p/let [files (utils/getFiles root-handle
-                                true
-                                (fn [path entry]
-                                  (let [handle-path (str "handle/" path)]
-                                    ;; Same for all handles here, even for directories and ignored directories(for backing up)
-                                    ;; FileSystemDirectoryHandle or FileSystemFileHandle
-                                    (when-not (string/includes? path "/.")
-                                      (add-nfs-file-handle! handle-path entry)))))]
-    (p/all (->> files
-                (remove  (fn [file]
-                           (let [rpath (string/replace-first (.-webkitRelativePath file) (str root-dir "/") "")
-                                 ext (util/get-file-ext rpath)]
-                             (or  (string/blank? rpath)
-                                  (string/starts-with? rpath ".")
-                                  (string/starts-with? rpath "logseq/bak")
-                                  (string/starts-with? rpath "logseq/version-files")
-                                  (not (contains? #{"md" "org" "excalidraw" "edn" "css"} ext))))))
-                ;; Read out using .text, Promise<string>
-                (map (fn [file]
-                       (p/let [content (.text file)]
-                         {:name        (.-name file)
-                          :path        (-> (.-webkitRelativePath file)
-                                           common-util/path-normalize)
-                          :mtime       (.-lastModified file)
-                          :size        (.-size file)
-                          :type        (.-kind (.-handle file))
-                          :content     content
-                          :file/file   file})))))))
-
-(defrecord ^:large-vars/cleanup-todo Nfs []
-  protocol/Fs
-  (mkdir! [_this dir]
-    (let [dir (path/path-normalize dir)
-          parent-dir (path/parent dir)
-
-          parent-handle-path (str "handle/" parent-dir)]
-      (-> (p/let [parent-handle (or (get-nfs-file-handle parent-handle-path)
-                                    (idb/get-item parent-handle-path))
-                  _ (when parent-handle (verify-handle-permission parent-handle true))]
-            (when parent-handle
-              (p/let [new-dir-name (path/filename dir)
-                      new-handle (.getDirectoryHandle ^js parent-handle new-dir-name
-                                                      #js {:create true})
-                      handle-path (str "handle/" dir)
-                      _ (idb/set-item! handle-path new-handle)]
-                (add-nfs-file-handle! handle-path new-handle)
-                (println "dir created: " dir))))
-          (p/catch (fn [error]
-                     (js/console.debug "mkdir error: " error ", dir: " dir)
-                     (throw error))))))
-
-  (mkdir-recur! [this dir]
-    (protocol/mkdir! this dir))
-
-  (readdir [_this dir]
-    ;; This method is only used for repo-dir and version-files dir
-    ;; There's no Logseq Sync support for nfs. So assume dir is always a repo dir.
-    (p/let [repo-url (str "logseq_local_" dir)
-            _ (await-permission-granted repo-url)
-            handle-path (str "handle/" dir)
-            handle (or (get-nfs-file-handle handle-path)
-                       (idb/get-item handle-path))
-            _ (when handle
-                (verify-handle-permission handle true))
-            fpaths (if (string/includes? dir "/")
-                     (js/console.error "ERROR: unimpl")
-                     (readdir-and-reload-all-handles dir handle))]
-      fpaths))
-
-  (unlink! [this repo fpath _opts]
-    (let [repo-dir (config/get-repo-dir repo)
-          filename (path/filename fpath)
-          handle-path (str "handle/" fpath)
-          recycle-dir (path/path-join repo-dir config/app-name config/recycle-dir)]
-      (->
-       (p/let [_ (protocol/mkdir! this recycle-dir)
-               handle (get-nfs-file-handle handle-path)
-               file (.getFile handle)
-               content (.text file)
-
-               bak-handle (get-nfs-file-handle (str "handle/" recycle-dir))
-               bak-filename (-> (path/relative-path repo-dir fpath)
-                                (string/replace "/" "_")
-                                (string/replace "\\" "_"))
-               file-handle (.getFileHandle ^js bak-handle bak-filename #js {:create true})
-               _ (utils/writeFile file-handle content)
-
-               parent-dir (path/parent fpath)
-               parent-handle (get-nfs-file-handle (str "handle/" parent-dir))
-               _ (when parent-handle
-                   (.removeEntry ^js parent-handle filename))]
-         (idb/remove-item! handle-path)
-         (remove-nfs-file-handle! handle-path))
-       (p/catch (fn [error]
-                  (log/error :unlink/path {:path fpath
-                                           :error error}))))))
-
-  (rmdir! [_this _dir]
-    nil)
-
-  (read-file [_this dir path _options]
-    (p/let [fpath (path/path-join dir path)
-            handle-path (str "handle/" fpath)]
-      (p/let [handle (or (get-nfs-file-handle handle-path)
-                         (idb/get-item handle-path))
-              local-file (and handle (.getFile handle))]
-        (and local-file (.text local-file)))))
-
-  (write-file! [_this repo dir path content opts]
-    ;; TODO: file backup handling
-    (let [fpath (path/path-join dir path)
-          ext (util/get-file-ext path)
-          file-handle-path (str "handle/" fpath)]
-      (p/let [file-handle (get-nfs-file-handle file-handle-path)]
-        (if file-handle
-          ;; file exist
-          (p/let [local-file (.getFile file-handle)
-                  disk-content (.text local-file)
-                  db-content (db/get-file repo path)
-                  contents-matched?' (contents-matched? disk-content db-content)]
-            (if (and
-                 (not (string/blank? db-content))
-                 (not (:skip-compare? opts))
-                 (not contents-matched?')
-                 (not (contains? #{"excalidraw" "edn" "css"} ext))
-                 (not (string/includes? path "/.recycle/")))
-              (state/pub-event! [:file/not-matched-from-disk path disk-content content])
-              (p/let [_ (verify-permission repo true)
-                      _ (utils/writeFile file-handle content)
-                      file (.getFile file-handle)]
-                (when file
-                  (db/set-file-content! repo path content)
-                  (nfs-saved-handler repo path file)))))
-          ;; file no-exist, write via parent dir handle
-          (p/let [basename (path/filename fpath)
-                  parent-dir (path/parent fpath)
-                  parent-dir-handle-path (str "handle/" parent-dir)
-                  parent-dir-handle (get-nfs-file-handle parent-dir-handle-path)]
-
-            (if parent-dir-handle
-              ;; create from directory handle
-              (p/let [file-handle (.getFileHandle ^js parent-dir-handle basename #js {:create true})
-                      _  (add-nfs-file-handle! file-handle-path file-handle)
-                      file (.getFile file-handle)
-                      text (.text file)]
-                (if (string/blank? text)
-                  (p/let [;; _ (idb/set-item! file-handle-path file-handle)
-                          _ (utils/writeFile file-handle content)
-                          file (.getFile file-handle)]
-                    (when file
-                      (nfs-saved-handler repo path file)))
-                  (do
-                    (notification/show! (str "The file " path " already exists, please append the content if you need it.\n Unsaved content: \n" content)
-                                        :warning
-                                        false)
-                    (state/pub-event! [:file/alter repo path text]))))
-
-              ;; TODO(andelf): Create parent directory and write
-              ;; Normally directory are created layer by layer. So it's safe to leave this unimplemented.
-              (js/console.error "TODO: can not create directory hierarchy")))))))
-
-  (rename! [this repo old-path new-path]
-    (p/let [repo-dir (config/get-repo-dir repo)
-            old-rpath (path/relative-path repo-dir old-path)
-            new-rpath (path/relative-path repo-dir new-path)
-            old-content (protocol/read-file this repo-dir old-rpath nil)
-            _ (protocol/write-file! this repo repo-dir new-rpath old-content nil)
-            _ (protocol/unlink! this repo old-path nil)]))
-
-  (stat [_this fpath]
-    (if-let [handle (get-nfs-file-handle (str "handle/" fpath))]
-      (p/let [_ (verify-handle-permission handle true)
-              file (.getFile handle)]
-        (let [get-attr #(gobj/get file %)]
-          {:last-modified-at (get-attr "lastModified")
-           :size (get-attr "size")
-           :path fpath
-           :type (get-attr "type")}))
-      (p/rejected "File not exists")))
-
-  (open-dir [_this _dir]
-    (p/let [files (utils/openDirectory #js {:recursive true
-                                            :mode "readwrite"}
-                                       (fn [path entry]
-                                         (let [handle-path (str "handle/" path)]
-                                           ;; Same all handles here, even for directories and ignored directories(for backing up)
-                                           ;; FileSystemDirectoryHandle or FileSystemFileHandle
-                                           (when-not (string/includes? path "/.")
-                                             (add-nfs-file-handle! handle-path entry)))))
-            dir-handle (first files) ;; FileSystemDirectoryHandle
-            dir-name (.-name dir-handle)
-            files (->> (next files)
-                       (remove  (fn [file]
-                                  (let [rpath (.-webkitRelativePath file) ;
-                                        ; (string/replace-first (.-webkitRelativePath file) (str dir-name "/") "")
-                                        ext (util/get-file-ext rpath)]
-                                    (or  (string/blank? rpath)
-                                         (string/starts-with? rpath ".")
-                                         (string/starts-with? rpath "logseq/bak")
-                                         (string/starts-with? rpath "logseq/version-files")
-                                         (not (contains? #{"md" "org" "excalidraw" "edn" "css"} ext))))))
-                       ;; Read out using .text, Promise<string>
-                       (map (fn [file]
-                              (js/console.log "handle" file)
-                              (p/let [content (.text file)]
-                                ;; path content size mtime
-                                {:name        (.-name file)
-                                 :path        (-> (.-webkitRelativePath file)
-                                                  common-util/path-normalize)
-                                 :mtime       (.-lastModified file)
-                                 :size        (.-size file)
-                                 :type        (.-kind (.-handle file))
-                                 :content     content
-                                 ;; expose the following, they are used by the file system
-                                 :file/file   file}))))
-            files (p/all files)]
-      (add-nfs-file-handle! (str "handle/" dir-name) dir-handle)
-      (idb/set-item! (str "handle/" dir-name) dir-handle)
-      {:path dir-name
-       :files files}))
-
-  (get-files [_this dir]
-    (when (string/includes? dir "/")
-      (js/console.error "BUG: get-files(nfs) only accepts repo-dir"))
-    (p/let [handle-path (str "handle/" dir)
-            handle (get-nfs-file-handle handle-path)
-            files (get-files-and-reload-all-handles dir handle)]
-      files))
-
-  (watch-dir! [_this _dir _options]
-    nil)
-
-  (unwatch-dir! [_this _dir]
-    nil))

+ 74 - 300
src/main/frontend/fs/sync.cljs

@@ -1,7 +1,6 @@
 (ns frontend.fs.sync
   "Main ns for providing file sync functionality"
-  (:require ["@capawesome/capacitor-background-task" :refer [BackgroundTask]]
-            ["path" :as node-path]
+  (:require ["path" :as node-path]
             [cljs-http.client :as http]
             [cljs-time.coerce :as tc]
             [cljs-time.core :as t]
@@ -23,12 +22,10 @@
             [frontend.diff :as diff]
             [frontend.encrypt :as encrypt]
             [frontend.fs :as fs]
-            [frontend.fs.capacitor-fs :as capacitor-fs]
             [frontend.fs.diff-merge :as diff-merge]
             [frontend.handler.file-based.file :as file-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.user :as user]
-            [frontend.mobile.util :as mobile-util]
             [frontend.pubsub :as pubsub]
             [frontend.state :as state]
             [frontend.util :as util]
@@ -164,8 +161,7 @@
                  :remote->local-full-sync-failed
                  :local->remote-full-sync-failed
                  :get-remote-graph-failed
-                 :get-deletion-logs-failed
-                 })
+                 :get-deletion-logs-failed})
 
 (s/def ::sync-event (s/keys :req-un [::event ::data]))
 
@@ -198,7 +194,6 @@
   [x]
   (when (instance? ExceptionInfo x) x))
 
-
 (def ws-addr config/WS-URL)
 
 ;; Warning: make sure to `persist-var/-load` graphs-txid before using it.
@@ -326,9 +321,9 @@
 (defn <request [api-name & args]
   (let [name (str api-name (.now js/Date))]
     (go (swap! *on-flying-request conj name)
-      (let [r (<! (apply <request* api-name args))]
-        (swap! *on-flying-request disj name)
-        r))))
+        (let [r (<! (apply <request* api-name args))]
+          (swap! *on-flying-request disj name)
+          r))))
 
 (defn- remove-dir-prefix [dir path]
   (let [r (string/replace path (js/RegExp. (str "^" (gstring/regExpEscape dir))) "")]
@@ -417,19 +412,19 @@
   (let [update? (= "update_files" TXType)
         delete? (= "delete_files" TXType)
         update-xf
-                (comp
-                 (remove #(or (empty? (first %))
-                              (empty? (last %))))
-                 (map #(->FileTxn (first %) (first %) update? delete? TXId (last %))))
+        (comp
+         (remove #(or (empty? (first %))
+                      (empty? (last %))))
+         (map #(->FileTxn (first %) (first %) update? delete? TXId (last %))))
         delete-xf
-                (comp
-                 (remove #(empty? (first %)))
-                 (map #(->FileTxn (first %) (first %) update? delete? TXId nil)))
+        (comp
+         (remove #(empty? (first %)))
+         (map #(->FileTxn (first %) (first %) update? delete? TXId nil)))
         rename-xf
-                (comp
-                 (remove #(or (empty? (first %))
-                              (empty? (second %))))
-                 (map #(->FileTxn (second %) (first %) false false TXId nil)))
+        (comp
+         (remove #(or (empty? (first %))
+                      (empty? (second %))))
+         (map #(->FileTxn (second %) (first %) false false TXId nil)))
         xf (case TXType
              "delete_files" delete-xf
              "update_files" update-xf
@@ -560,7 +555,6 @@
    (map-indexed filepath+checksum->diff)
    (diffs->partitioned-filetxns n)))
 
-
 (deftype FileMetadata [size etag path encrypted-path last-modified remote? txid ^:mutable normalized-path]
   Object
   (get-normalized-path [_]
@@ -596,13 +590,10 @@
       :txid txid
       not-found))
 
-
   IPrintWithWriter
   (-pr-writer [_ w _opts]
     (write-all w (str {:size size :etag etag :path path :remote? remote? :txid txid :last-modified last-modified}))))
 
-
-
 (def ^:private higher-priority-remote-files
   "when diff all remote files and local files, following remote files always need to download(when checksum not matched),
   even local-file's last-modified > remote-file's last-modified.
@@ -659,21 +650,21 @@
    #{} s1))
 
 (comment
- (defn map->FileMetadata [m]
-   (apply ->FileMetadata ((juxt :size :etag :path :encrypted-path :last-modified :remote? (constantly nil)) m)))
-
- (assert
-  (=
-   #{(map->FileMetadata {:size 1 :etag 2 :path 2 :encrypted-path 2 :last-modified 2})}
-   (diff-file-metadata-sets
-    (into #{}
-          (map map->FileMetadata)
-          [{:size 1 :etag 1 :path 1 :encrypted-path 1 :last-modified 1}
-           {:size 1 :etag 2 :path 2 :encrypted-path 2 :last-modified 2}])
-    (into #{}
-          (map map->FileMetadata)
-          [{:size 1 :etag 1 :path 1 :encrypted-path 1 :last-modified 1}
-           {:size 1 :etag 1 :path 2 :encrypted-path 2 :last-modified 1}])))))
+  (defn map->FileMetadata [m]
+    (apply ->FileMetadata ((juxt :size :etag :path :encrypted-path :last-modified :remote? (constantly nil)) m)))
+
+  (assert
+   (=
+    #{(map->FileMetadata {:size 1 :etag 2 :path 2 :encrypted-path 2 :last-modified 2})}
+    (diff-file-metadata-sets
+     (into #{}
+           (map map->FileMetadata)
+           [{:size 1 :etag 1 :path 1 :encrypted-path 1 :last-modified 1}
+            {:size 1 :etag 2 :path 2 :encrypted-path 2 :last-modified 2}])
+     (into #{}
+           (map map->FileMetadata)
+           [{:size 1 :etag 1 :path 1 :encrypted-path 1 :last-modified 1}
+            {:size 1 :etag 1 :path 2 :encrypted-path 2 :last-modified 1}])))))
 
 (extend-protocol IChecksum
   FileMetadata
@@ -726,7 +717,6 @@
 
   common-util/path-normalize)
 
-
 ;;; ### APIs
 ;; `RSAPI` call apis through rsapi package, supports operations on files
 
@@ -765,16 +755,13 @@
   (<get-graph-encrypt-keys [this graph-uuid])
   (<upload-graph-encrypt-keys [this graph-uuid public-key encrypted-private-key]))
 
-
 (defprotocol IRemoteControlAPI
   "api functions provided for outside the sync process"
-  (<delete-remote-files-control [this graph-uuid filepaths])
-  )
+  (<delete-remote-files-control [this graph-uuid filepaths]))
 
 (defprotocol IToken
   (<get-token [this]))
 
-
 (defn <case-different-local-file-exist?
   "e.g. filepath=\"pages/Foo.md\"
   found-filepath=\"pages/foo.md\"
@@ -921,149 +908,10 @@
   (<add-new-version [_this repo path content]
     (p->c (ipc/ipc "addVersionFile" (config/get-local-dir repo) path content))))
 
-(deftype ^:large-vars/cleanup-todo CapacitorAPI [^:mutable graph-uuid' ^:mutable private-key ^:mutable public-key']
-  IToken
-  (<get-token [_this]
-    (user/<wrap-ensure-id&access-token
-     (state/get-auth-id-token)))
-
-  IRSAPI
-  (rsapi-ready? [_ graph-uuid] (and (= graph-uuid graph-uuid') private-key public-key'))
-  (<key-gen [_]
-    (go (let [r (<! (p->c (.keygen mobile-util/file-sync #js {})))]
-          (-> r
-              (js->clj :keywordize-keys true)))))
-  (<set-env [_ graph-uuid prod? secret-key public-key]
-    (set! graph-uuid' graph-uuid)
-    (set! private-key secret-key)
-    (set! public-key' public-key)
-    (p->c (.setEnv mobile-util/file-sync (clj->js {:graphUUID graph-uuid
-                                                   :env (if prod? "prod" "dev")
-                                                   :secretKey secret-key
-                                                   :publicKey public-key}))))
-
-  (<get-local-all-files-meta [this graph-uuid base-path]
-    (go
-      (let [r (<! (p->c (.getLocalAllFilesMeta mobile-util/file-sync (clj->js {:graphUUID graph-uuid
-                                                                               :basePath base-path}))))]
-        (or (guard-ex r)
-            (<! (<build-local-file-metadatas this graph-uuid (.-result r)))))))
-
-  (<get-local-files-meta [this graph-uuid base-path filepaths]
-    (go
-      (let [r (<! (p->c (.getLocalFilesMeta mobile-util/file-sync
-                                            (clj->js {:graphUUID graph-uuid
-                                                      :basePath base-path
-                                                      :filePaths filepaths}))))]
-        (assert (not (instance? ExceptionInfo r)) "get-local-files-meta shouldn't return exception")
-        (<! (<build-local-file-metadatas this graph-uuid (.-result r))))))
-
-  (<rename-local-file [_ graph-uuid base-path from to]
-    (p->c (.renameLocalFile mobile-util/file-sync
-                            (clj->js {:graphUUID graph-uuid
-                                      :basePath base-path
-                                      :from (path-normalize from)
-                                      :to (path-normalize to)}))))
-
-  (<update-local-files [this graph-uuid base-path filepaths]
-    (go
-      (let [token-or-exp (<! (<get-token this))
-            filepaths' (map path-normalize filepaths)]
-        (or (guard-ex token-or-exp)
-            (<! (p->c (.updateLocalFiles mobile-util/file-sync (clj->js {:graphUUID graph-uuid
-                                                                         :basePath base-path
-                                                                         :filePaths filepaths'
-                                                                         :token token-or-exp}))))))))
-  (<fetch-remote-files [this graph-uuid base-path filepaths]
-    (go
-      (let [token-or-exp (<! (<get-token this))]
-        (or (guard-ex token-or-exp)
-            (js->clj
-             (.-value
-              (<! (<retry-rsapi
-                   #(p->c (.fetchRemoteFiles mobile-util/file-sync
-                                             (clj->js {:graphUUID graph-uuid
-                                                       :basePath base-path
-                                                       :filePaths filepaths
-                                                       :token token-or-exp})))))))))))
-  (<download-version-files [this graph-uuid base-path filepaths]
-    (go
-      (let [token-or-exp (<! (<get-token this))]
-        (or (guard-ex token-or-exp)
-            (<! (<retry-rsapi
-                 #(p->c (.updateLocalVersionFiles mobile-util/file-sync
-                                                  (clj->js {:graphUUID graph-uuid
-                                                            :basePath base-path
-                                                            :filePaths filepaths
-                                                            :token token-or-exp})))))))))
-
-  (<delete-local-files [_ graph-uuid base-path filepaths]
-    (let [normalized-filepaths (mapv path-normalize filepaths)]
-      (go
-        (let [r (<! (<retry-rsapi #(p->c (.deleteLocalFiles mobile-util/file-sync
-                                                            (clj->js {:graphUUID graph-uuid
-                                                                      :basePath base-path
-                                                                      :filePaths normalized-filepaths})))))]
-          r))))
-
-  (<update-remote-files [this graph-uuid base-path filepaths local-txid]
-    (let [normalized-filepaths (mapv path-normalize filepaths)]
-      (go
-        (let [token-or-exp (<! (<get-token this))
-              r (or (guard-ex token-or-exp)
-                    (<! (p->c (.updateRemoteFiles mobile-util/file-sync
-                                                  (clj->js {:graphUUID graph-uuid
-                                                            :basePath base-path
-                                                            :filePaths normalized-filepaths
-                                                            :txid local-txid
-                                                            :token token-or-exp
-                                                            :fnameEncryption true})))))]
-          (or (guard-ex r)
-              (get (js->clj r) "txid"))))))
-
-  (<delete-remote-files [this graph-uuid base-path filepaths local-txid]
-    (let [normalized-filepaths (mapv path-normalize filepaths)]
-      (go
-        (let [token-or-exp (<! (<get-token this))
-              r (or (guard-ex token-or-exp)
-                    (<! (p->c (.deleteRemoteFiles mobile-util/file-sync
-                                                  (clj->js {:graphUUID graph-uuid
-                                                            :basePath base-path
-                                                            :filePaths normalized-filepaths
-                                                            :txid local-txid
-                                                            :token token-or-exp})))))]
-          (or (guard-ex r)
-              (get (js->clj r) "txid"))))))
-
-  (<encrypt-fnames [_ graph-uuid fnames]
-    (go
-      (let [r (<! (p->c (.encryptFnames mobile-util/file-sync
-                                        (clj->js {:graphUUID graph-uuid
-                                                  :filePaths fnames}))))]
-        (or (guard-ex r)
-            (get (js->clj r) "value")))))
-  (<decrypt-fnames [_ graph-uuid fnames]
-    (go (let [r (<! (p->c (.decryptFnames mobile-util/file-sync
-                                          (clj->js {:graphUUID graph-uuid
-                                                    :filePaths fnames}))))]
-          (if (instance? ExceptionInfo r)
-            (ex-info "decrypt-failed" {:fnames fnames} (ex-cause r))
-            (get (js->clj r) "value")))))
-  (<cancel-all-requests [_]
-    (p->c (.cancelAllRequests mobile-util/file-sync)))
-  (<add-new-version [_this repo path content]
-    (p->c (capacitor-fs/backup-file repo :version-file-dir path content))))
-
 (def rsapi (cond
              (util/electron?)
              (->RSAPI nil nil nil)
 
-             (mobile-util/native-ios?)
-             (->CapacitorAPI nil nil nil)
-
-             (mobile-util/native-android?)
-             (->CapacitorAPI nil nil nil)
-
              :else
              nil))
 
@@ -1128,8 +976,8 @@
      (go-loop []
        (let [{:keys [val stop]}
              (async/alt!
-              debug-print-sync-events-loop-stop-chan {:stop true}
-              out-ch ([v] {:val v}))]
+               debug-print-sync-events-loop-stop-chan {:stop true}
+               out-ch ([v] {:val v}))]
          (cond
            stop (do (async/unmix-all out-mix)
                     (doseq [[topic ch] topic&chs]
@@ -1138,12 +986,10 @@
            val (do (pp/pprint [:debug :sync-event val])
                    (recur))))))))
 
-
 (defn stop-debug-print-sync-events-loop
   []
   (offer! debug-print-sync-events-loop-stop-chan true))
 
-
 ;;; sync events ends
 
 (defn- fire-file-sync-storage-exceed-limit-event!
@@ -1232,7 +1078,6 @@
                 (recur result-file-meta-list others))))
         (vals (persistent! result-file-meta-list))))))
 
-
 (extend-type RemoteAPI
   IRemoteAPI
   (<user-info [this]
@@ -1438,13 +1283,10 @@
 
 (comment
   (declare remoteapi)
-  (<delete-remote-files-control remoteapi (second @graphs-txid) ["pages/aa.md"])
-
-  )
+  (<delete-remote-files-control remoteapi (second @graphs-txid) ["pages/aa.md"]))
 
 (def remoteapi (->RemoteAPI nil))
 
-
 (def ^:private *get-graph-salt-memoize-cache (atom {}))
 (defn update-graph-salt-cache [graph-uuid v]
   {:pre [(map? v)
@@ -1521,9 +1363,6 @@
           val val
           timeout' (recur))))))
 
-
-
-
 (defn- assert-local-txid<=remote-txid
   []
   (when-let [local-txid (last @graphs-txid)]
@@ -1548,7 +1387,6 @@
          sync-state--remove-recent-remote->local-files
          sync-state--stopped?)
 
-
 (defn- filetxns=>recent-remote->local-files
   [filetxns]
   (let [{:keys [update-filetxns delete-filetxns rename-filetxns]}
@@ -1610,7 +1448,7 @@
                                       (fs/unlink! repo (path/path-join repo-dir base-file) {}))
                                      ;; base-content != current-content, merge, do not delete
                                      (p/let [merged-content (diff-merge/three-way-merge base-content "" current-content format)]
-                                       (fs/write-file! repo repo-dir current-change-file merged-content {:skip-compare? true})
+                                       (fs/write-plain-text-file! repo repo-dir current-change-file merged-content {:skip-compare? true})
                                        (file-handler/alter-file repo current-change-file merged-content {:re-render-root? true
                                                                                                          :from-disk? true
                                                                                                          :fs/event :fs/remote-file-change}))))
@@ -1660,7 +1498,7 @@
                                                     merged-content (diff-merge/three-way-merge base-content incoming-content current-content format)]
                                               (when (seq merged-content)
                                                 (p/do!
-                                                 (fs/write-file! repo repo-dir current-change-file merged-content {:skip-compare? true})
+                                                 (fs/write-plain-text-file! repo repo-dir current-change-file merged-content {:skip-compare? true})
                                                  (file-handler/alter-file repo current-change-file merged-content {:re-render-root? true
                                                                                                                    :from-disk? true
                                                                                                                    :fs/event :fs/remote-file-change})))))))
@@ -1687,7 +1525,7 @@
 
                                           ;; else
                                             (p/do!
-                                             (fs/write-file! repo repo-dir current-change-file merged-content {:skip-compare? true})
+                                             (fs/write-plain-text-file! repo repo-dir current-change-file merged-content {:skip-compare? true})
                                              (file-handler/alter-file repo current-change-file merged-content {:re-render-root? true
                                                                                                                :from-disk? true
                                                                                                                :fs/event :fs/remote-file-change})))))))))))))))))
@@ -1724,8 +1562,8 @@
                      [recent-remote->local-file-item])
               (<! (<delete-local-files rsapi graph-uuid base-path [relative-p*]))
               (go (<! (timeout 5000))
-                (swap! *sync-state sync-state--remove-recent-remote->local-files
-                       [recent-remote->local-file-item])))))
+                  (swap! *sync-state sync-state--remove-recent-remote->local-files
+                         [recent-remote->local-file-item])))))
 
         (let [update-local-files-ch (if (state/enable-sync-diff-merge?)
                                       (<fetch-remote-and-update-local-files graph-uuid base-path (map relative-path filetxns))
@@ -1781,8 +1619,8 @@
                                                      (not (instance? ExceptionInfo r)))]
           ;; remove these recent-remote->local-file-items 5s later
           (go (<! (timeout 5000))
-            (swap! *sync-state sync-state--remove-recent-remote->local-files
-                   recent-remote->local-file-items))
+              (swap! *sync-state sync-state--remove-recent-remote->local-files
+                     recent-remote->local-file-items))
           (cond
             (instance? ExceptionInfo r) r
             @*paused                    {:pause true}
@@ -1841,7 +1679,6 @@
   [r]
   (some->> (ex-cause r) str (re-find #"Request is not yet valid")))
 
-
 ;; type = "change" | "add" | "unlink"
 (deftype FileChangeEvent [type dir path stat checksum]
   IRelativePath
@@ -1876,7 +1713,6 @@
   (-pr-writer [_ w _opts]
     (write-all w (str {:type type :base-path dir :path path :size (:size stat) :checksum checksum}))))
 
-
 (defn- <file-change-event=>recent-remote->local-file-item
   "return nil when related local files not found"
   [graph-uuid ^FileChangeEvent e]
@@ -1949,8 +1785,8 @@
     (go-loop []
       (let [{:keys [rename-event local-change]}
             (async/alt!
-             rename-page-event-chan ([v] {:rename-event v}) ;; {:repo X :old-path X :new-path}
-             local-changes-chan' ([v] {:local-change v}))]
+              rename-page-event-chan ([v] {:rename-event v}) ;; {:repo X :old-path X :new-path}
+              local-changes-chan' ([v] {:local-change v}))]
         (cond
           rename-event
           (let [repo-dir (config/get-repo-dir (:repo rename-event))
@@ -1963,13 +1799,13 @@
             (swap! *rename-events conj k1 k2)
             ;; remove rename-events after 2s
             (go (<! (timeout 3000))
-              (swap! *rename-events disj k1 k2))
+                (swap! *rename-events disj k1 k2))
             ;; add 2 simulated file-watcher events
             (>! ch (->FileChangeEvent "unlink" repo-dir (:old-path rename-event*) nil nil))
             (>! ch (->FileChangeEvent "add" repo-dir (:new-path rename-event*)
                                       {:mtime (tc/to-long (t/now))
                                        :size 1 ; add a fake size
-                                       } "fake-checksum"))
+                                       }"fake-checksum"))
             (recur))
           local-change
           (cond
@@ -2046,7 +1882,6 @@
     (js/localStorage.removeItem k))
   (reset! pwd-map {}))
 
-
 (defn encrypt+persist-pwd!
   "- persist encrypted pwd at local-storage"
   [pwd graph-uuid]
@@ -2094,7 +1929,6 @@
       (when (and private-key (string/starts-with? private-key "AGE-SECRET-KEY"))
         (set-keys&notify graph-uuid public-key private-key)))))
 
-
 (def <restored-pwd (chan (async/sliding-buffer 1)))
 (def <restored-pwd-pub (async/pub <restored-pwd :graph-uuid))
 
@@ -2124,7 +1958,6 @@
   (swap! pwd-map dissoc graph-uuid)
   (remove-pwd! graph-uuid))
 
-
 (defn- <loop-ensure-pwd&keys
   [graph-uuid repo *stopped?]
   (let [<restored-pwd-sub-chan (chan 1)]
@@ -2186,7 +2019,6 @@
                       (clear-pwd! graph-uuid)
                       (recur))))))))))
 
-
 (defn- <set-env&keys
   [prod? graph-uuid]
   (let [{:keys [private-key public-key]} (get @pwd-map graph-uuid)]
@@ -2241,7 +2073,6 @@
            (fn [_ _ _ _]
              (offer! recent-edited-chan true)))
 
-
 ;;; ### sync state
 
 (def *resume-state
@@ -2385,7 +2216,6 @@
   (contains? #{::idle ::local->remote ::remote->local ::local->remote-full-sync ::remote->local-full-sync}
              (:state sync-state)))
 
-
 ;;; ### remote->local syncer & local->remote syncer
 
 (defprotocol IRemote->LocalSync
@@ -2641,11 +2471,11 @@
          local-files-meta-map))))
 
 (defrecord ^:large-vars/cleanup-todo
-  Local->RemoteSyncer [user-uuid graph-uuid base-path repo *sync-state remoteapi'
-                       ^:mutable rate *txid *txid-for-get-deletion-log
-                       ^:mutable remote->local-syncer stop-chan *stopped *paused
+ Local->RemoteSyncer [user-uuid graph-uuid base-path repo *sync-state remoteapi'
+                      ^:mutable rate *txid *txid-for-get-deletion-log
+                      ^:mutable remote->local-syncer stop-chan *stopped *paused
                        ;; control chans
-                       private-immediately-local->remote-chan private-recent-edited-chan]
+                      private-immediately-local->remote-chan private-recent-edited-chan]
   Object
   (filter-file-change-events-fn [_]
     (fn [^FileChangeEvent e]
@@ -2812,21 +2642,21 @@
                                             <!
                                             (sort-by (sort-file-metadata-fn :recent-days-range recent-10-days-range) >))
                 change-events
-                                       (sequence
-                                        (comp
+                (sequence
+                 (comp
                                          ;; convert to FileChangeEvent
-                                         (map #(->FileChangeEvent "change" base-path (.get-normalized-path ^FileMetadata %)
-                                                                  {:size (:size %)} (:etag %)))
-                                         (remove ignored?))
-                                        diff-local-files)
+                  (map #(->FileChangeEvent "change" base-path (.get-normalized-path ^FileMetadata %)
+                                           {:size (:size %)} (:etag %)))
+                  (remove ignored?))
+                 diff-local-files)
                 distinct-change-events (-> (distinct-file-change-events change-events)
                                            filter-upload-files-with-reserved-chars)
                 _                      (swap! *sync-state #(sync-state-reset-full-local->remote-files % distinct-change-events))
                 change-events-partitions
-                                       (sequence
+                (sequence
                                         ;; partition FileChangeEvents
-                                        (partition-file-change-events upload-batch-size)
-                                        distinct-change-events)]
+                 (partition-file-change-events upload-batch-size)
+                 distinct-change-events)]
             (println "[full-sync(local->remote)]"
                      (count (flatten change-events-partitions)) "files need to sync and"
                      (count delete-local-files) "local files need to delete")
@@ -2847,8 +2677,8 @@
                              [fake-recent-remote->local-file-item])
                       (<! (<delete-local-files rsapi graph-uuid base-path [(relative-path f)]))
                       (go (<! (timeout 5000))
-                        (swap! *sync-state sync-state--remove-recent-remote->local-files
-                               [fake-recent-remote->local-file-item])))))
+                          (swap! *sync-state sync-state--remove-recent-remote->local-files
+                                 [fake-recent-remote->local-file-item])))))
                 (recur fs)))
 
             ;; 2. upload local files
@@ -2866,13 +2696,12 @@
                               (or need-sync-remote graph-has-been-deleted unknown stop) r)))))]
               ;; update *txid-for-get-deletion-log
               (reset! *txid-for-get-deletion-log @*txid)
-              r
-              )))))))
+              r)))))))
 
 ;;; ### put all stuff together
 
 (defrecord ^:large-vars/cleanup-todo
-    SyncManager [user-uuid graph-uuid base-path *sync-state
+ SyncManager [user-uuid graph-uuid base-path *sync-state
               ^Local->RemoteSyncer local->remote-syncer ^Remote->LocalSyncer remote->local-syncer remoteapi'
               ^:mutable ratelimit-local-changes-chan
               *txid *txid-for-get-deletion-log
@@ -3256,9 +3085,7 @@
         local->remote-syncer (->Local->RemoteSyncer user-uuid graph-uuid
                                                     base-path
                                                     repo *sync-state remoteapi-with-stop
-                                                    (if (mobile-util/native-platform?)
-                                                      2000
-                                                      10000)
+                                                    10000
                                                     *txid *txid-for-get-deletion-log nil (chan) *stopped? *paused?
                                                     (chan 1) (chan 1))
         remote->local-syncer (->Remote->LocalSyncer user-uuid graph-uuid base-path
@@ -3276,7 +3103,6 @@
     (reset! current-sm-graph-uuid graph-uuid)
     (sync-manager user-uuid graph-uuid base-path repo txid *sync-state)))
 
-
 (defn <sync-stop []
   (go
     (when-let [sm ^SyncManager (state/get-file-sync-manager (state/get-current-file-sync-graph-uuid))]
@@ -3290,11 +3116,6 @@
 
     (reset! current-sm-graph-uuid nil)))
 
-(defn <sync-local->remote-now []
-  (go
-    (when-let [_sm ^SyncManager (state/get-file-sync-manager (state/get-current-file-sync-graph-uuid))]
-      (offer! immediately-local->remote-chan true))))
-
 (defn sync-need-password!
   []
   (when-let [sm ^SyncManager (state/get-file-sync-manager (state/get-current-file-sync-graph-uuid))]
@@ -3319,15 +3140,15 @@
   (go
     (let [r (<! (<list-remote-graphs remoteapi))
           result
-            (or
+          (or
              ;; if api call failed, assume this remote graph still exists
-             (instance? ExceptionInfo r)
-             (and
-              (contains? r :Graphs)
-              (->> (:Graphs r)
-                   (mapv :GraphUUID)
-                   set
-                   (#(contains? % local-graph-uuid)))))]
+           (instance? ExceptionInfo r)
+           (and
+            (contains? r :Graphs)
+            (->> (:Graphs r)
+                 (mapv :GraphUUID)
+                 set
+                 (#(contains? % local-graph-uuid)))))]
 
       (when-not result
         (notification/show! (t :file-sync/graph-deleted) :warning false))
@@ -3347,7 +3168,6 @@
   (when-let [graph-uuid (second @graphs-txid)]
     (get-pwd graph-uuid)))
 
-
 (defn- <connectivity-testing
   []
   (go
@@ -3441,52 +3261,8 @@
           (finally
             (reset! *sync-starting false)))))))
 
-(defn- restart-if-stopped!
-  [is-active?]
-  (cond
-    (and is-active? (graph-sync-off? (second @graphs-txid)))
-    (<sync-start)
-
-    :else
-    (offer! pause-resume-chan is-active?)))
-
-(def app-state-changed-cursor (rum/cursor state/state :mobile/app-state-change))
-
 (def finished-local->remote-chan (chan 1))
 
-(add-watch app-state-changed-cursor "sync"
-           (fn [_ _ _ {:keys [is-active?]}]
-             (cond
-               (mobile-util/native-android?)
-               (when-not is-active?
-                 (<sync-local->remote-now))
-
-               (mobile-util/native-ios?)
-               (let [*task-id (atom nil)]
-                 (if is-active?
-                   (restart-if-stopped! is-active?)
-                   (when (state/get-current-file-sync-graph-uuid)
-                     (p/let [task-id (.beforeExit ^js BackgroundTask
-                                                  (fn []
-                                                    (go
-                                                      ;; Wait for file watcher events
-                                                      (<! (timeout 2000))
-                                                      (util/drain-chan finished-local->remote-chan)
-                                                      (<! (<sync-local->remote-now))
-                                                      ;; wait at most 20s
-                                                      (async/alts! [finished-local->remote-chan (timeout 20000)])
-                                                      (p/let [active? (mobile-util/app-active?)]
-                                                        (when-not active?
-                                                          (offer! pause-resume-chan is-active?)))
-                                                      (<! (timeout 5000))
-                                                      (prn "finish task: " @*task-id)
-                                                      (let [opt #js {:taskId @*task-id}]
-                                                        (.finish ^js BackgroundTask opt)))))]
-                       (reset! *task-id task-id)))))
-
-               :else
-               nil)))
-
 ;;; ### some add-watches
 
 ;; TODO: replace this logic by pause/resume state
@@ -3517,7 +3293,6 @@
     (<sync-start))
   (recur))
 
-
 ;;; ### some sync events handler
 
 ;; re-exec remote->local-full-sync when it failed before
@@ -3543,6 +3318,5 @@
 
 ;;; add-tap
 (comment
- (def *x (atom nil))
- (add-tap (fn [v] (reset! *x v)))
- )
+  (def *x (atom nil))
+  (add-tap (fn [v] (reset! *x v))))

+ 0 - 5
src/main/frontend/handler.cljs

@@ -30,7 +30,6 @@
             [frontend.handler.ui :as ui-handler]
             [frontend.handler.user :as user-handler]
             [frontend.idb :as idb]
-            [frontend.mobile.core :as mobile]
             [frontend.mobile.util :as mobile-util]
             [frontend.modules.instrumentation.core :as instrument]
             [frontend.modules.shortcut.core :as shortcut]
@@ -92,8 +91,6 @@
          (fn []
            (js/console.log "db restored, setting up repo hooks")
 
-           (state/pub-event! [:modal/nfs-ask-permission])
-
            (page-handler/init-commands!)
 
            (watch-for-date!)
@@ -169,8 +166,6 @@
   (events/run!)
 
   (p/do!
-   (when (mobile-util/native-platform?)
-     (mobile/mobile-preinit))
    (-> (p/let [_ (db-browser/start-db-worker!)
                repos (repo-handler/get-repos)
                _ (state/set-repos! repos)

+ 2 - 19
src/main/frontend/handler/assets.cljs

@@ -5,7 +5,6 @@
             [frontend.common.thread-api :as thread-api :refer [def-thread-api]]
             [frontend.config :as config]
             [frontend.fs :as fs]
-            [frontend.fs.nfs :as nfs]
             [frontend.mobile.util :as mobile-util]
             [frontend.state :as state]
             [frontend.util :as util]
@@ -155,8 +154,6 @@
               css
               (map vector rel-paths blob-urls)))))
 
-(defonce *assets-url-cache (atom {}))
-
 (defn <make-asset-url
   "Make asset URL for UI element, to fill img.src"
   [path] ;; path start with "/assets"(editor) or compatible for "../assets"(whiteboards)
@@ -187,21 +184,7 @@
         (config/db-based-graph? (state/get-current-repo)) ; memory fs
         (p/let [binary (fs/read-file repo-dir path {})
                 blob (js/Blob. (array binary) (clj->js {:type "image"}))]
-          (when blob (js/URL.createObjectURL blob)))
-
-        :else ;; nfs
-        (let [handle-path (str "handle/" full-path)
-              cached-url  (get @*assets-url-cache (keyword handle-path))]
-          (if cached-url
-            (p/resolved cached-url)
-            ;; Loading File from handle cache
-            ;; Use await file handle, to ensure all handles are loaded.
-            (p/let [handle (nfs/await-get-nfs-file-handle repo handle-path)
-                    file   (and handle (.getFile handle))]
-              (when file
-                (p/let [url (js/URL.createObjectURL file)]
-                  (swap! *assets-url-cache assoc (keyword handle-path) url)
-                  url)))))))))
+          (when blob (js/URL.createObjectURL blob)))))))
 
 (defn- decode-digest
   [^js/Uint8Array digest]
@@ -266,7 +249,7 @@
         repo-dir (config/get-repo-dir repo)
         file-path (path/path-join common-config/local-assets-dir
                                   (str asset-block-id-str "." asset-type))]
-    (fs/write-file! repo repo-dir file-path data {})))
+    (fs/write-plain-text-file! repo repo-dir file-path data {})))
 
 (defn <unlink-asset
   [repo asset-block-id asset-type]

+ 2 - 1
src/main/frontend/handler/db_based/rtc_flows.cljs

@@ -142,5 +142,6 @@ conditions:
      (map vector)
      network-online&rtc-not-running-flow)]
    (apply c.m/mix)
-   (m/eduction (filter (fn [_] (some? (state/get-auth-id-token)))))
+   (m/latest vector flows/current-login-user-flow)
+   (m/eduction (keep (fn [[current-user trigger-event]] (when current-user trigger-event))))
    (c.m/debounce 200)))

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

@@ -6,7 +6,7 @@
             [frontend.handler.property :as property-handler]
             [frontend.modules.outliner.op :as outliner-op]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
-            [frontend.ref :as ref]
+            [frontend.util.ref :as ref]
             [frontend.state :as state]
             [logseq.db :as ldb]))
 

+ 12 - 10
src/main/frontend/handler/draw.cljs

@@ -25,16 +25,18 @@
   (let [path file
         repo (state/get-current-repo)]
     (when repo
-      (->
-       (p/do!
-        (create-draws-directory! repo)
-        (db/transact! repo
-                      [{:file/path path
-                        :block/name (util/page-name-sanity-lc file)
-                        :block/file {:file/path path}}]))
-       (p/catch (fn [error]
-                  (prn "Write file failed, path: " path ", data: " data)
-                  (js/console.dir error)))))))
+      (let [repo-dir (config/get-repo-dir repo)]
+        (->
+         (p/do!
+          (create-draws-directory! repo)
+          (fs/write-plain-text-file! repo repo-dir path data nil)
+          (db/transact! repo
+                        [{:file/path path
+                          :block/name (util/page-name-sanity-lc file)
+                          :block/file {:file/path path}}]))
+         (p/catch (fn [error]
+                    (prn "Write file failed, path: " path ", data: " data)
+                    (js/console.dir error))))))))
 
 (defn load-excalidraw-file
   [file ok-handler]

+ 17 - 36
src/main/frontend/handler/editor.cljs

@@ -3,6 +3,7 @@
             [clojure.string :as string]
             [clojure.walk :as w]
             [dommy.core :as dom]
+            [electron.ipc :as ipc]
             [frontend.commands :as commands]
             [frontend.config :as config]
             [frontend.date :as date]
@@ -16,7 +17,6 @@
             [frontend.format.block :as block]
             [frontend.format.mldoc :as mldoc]
             [frontend.fs :as fs]
-            [frontend.fs.capacitor-fs :as capacitor-fs]
             [frontend.handler.assets :as assets-handler]
             [frontend.handler.block :as block-handler]
             [frontend.handler.common :as common-handler]
@@ -35,7 +35,7 @@
             [frontend.modules.outliner.op :as outliner-op]
             [frontend.modules.outliner.tree :as tree]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
-            [frontend.ref :as ref]
+            [frontend.util.ref :as ref]
             [frontend.search :as search]
             [frontend.state :as state]
             [frontend.template :as template]
@@ -46,7 +46,6 @@
             [frontend.util.list :as list]
             [frontend.util.text :as text-util]
             [frontend.util.thingatpt :as thingatpt]
-            [goog.crypt.base64 :as base64]
             [goog.dom :as gdom]
             [goog.dom.classes :as gdom-classes]
             [goog.object :as gobj]
@@ -1054,6 +1053,8 @@
   (state/set-block-op-type! :cut)
   (when-let [blocks (->> (get-selected-blocks)
                          (remove #(dom/has-class? % "property-value-container"))
+                         (remove (fn [block] (or (= "true" (dom/attr block "data-query"))
+                                                 (= "true" (dom/attr block "data-transclude")))))
                          seq)]
     ;; remove queries
     (let [dom-blocks (remove (fn [block] (= "true" (dom/attr block "data-query"))) blocks)]
@@ -1382,8 +1383,6 @@
              (property-file/remove-properties-when-file-based repo format)
              string/trim)))
 
-(def insert-command! editor-common-handler/insert-command!)
-
 (defn delete-asset-of-block!
   [{:keys [repo asset-block href full-text block-id local? delete-local?] :as _opts}]
   (let [block (db-model/query-block-by-uuid block-id)
@@ -1407,36 +1406,16 @@
                                (path/resolve-relative-path block-file-rpath href)))]
             (fs/unlink! repo asset-fpath nil)))))))
 
-(defn- write-file!
-  [repo dir file file-rpath file-name]
-  (if (util/electron?)
-    (if-let [from (not-empty (.-path file))]
-      (-> (js/window.apis.copyFileToAssets dir file-rpath from)
-          (p/catch #(js/console.error "Debug: Copy Asset Error#" %)))
-      (-> (p/let [buffer (.arrayBuffer file)]
-            (fs/write-file! repo dir file-rpath buffer {:skip-compare? false}))
-          (p/catch #(js/console.error "Debug: Writing Asset #" %))))
-    (->
-     (p/do! (js/console.debug "Debug: Writing Asset #" dir file-rpath)
-            (cond
-              (mobile-util/native-platform?)
-                          ;; capacitor fs accepts Blob, File implements Blob
-              (p/let [buffer (.arrayBuffer file)
-                      content (base64/encodeByteArray (js/Uint8Array. buffer))
-                      fpath (path/path-join dir file-rpath)]
-                (capacitor-fs/<write-file-with-base64 fpath content))
-
-              (config/db-based-graph? repo) ;; memory-fs
-              (p/let [buffer (.arrayBuffer file)
-                      content (js/Uint8Array. buffer)]
-                (fs/write-file! repo dir file-rpath content nil))
-
-              :else
-              (throw (ex-info "Paste failed"
-                              {:file-name file-name}))))
-     (p/catch (fn [error]
-                (prn :paste-file-error)
-                (js/console.error error))))))
+(defn db-based-save-asset!
+  [repo dir file file-rpath]
+  (p/let [buffer (.arrayBuffer file)]
+    (if (util/electron?)
+      (ipc/ipc "writeFile" repo (path/path-join dir file-rpath) buffer)
+      ;; web
+      (p/let [buffer (.arrayBuffer file)
+              content (js/Uint8Array. buffer)]
+        ;; actually, writing binary using memory fs
+        (fs/write-plain-text-file! repo dir file-rpath content nil)))))
 
 (defn db-based-save-assets!
   "Save incoming(pasted) assets to assets directory.
@@ -1470,7 +1449,7 @@
                    insert-opts {:custom-uuid block-id
                                 :edit-block? false
                                 :properties properties}
-                   _ (write-file! repo dir file file-rpath file-name)
+                   _ (db-based-save-asset! repo dir file file-rpath)
                    edit-block (state/get-edit-block)
                    insert-to-current-block-page? (and (:block/uuid edit-block) (string/blank? (state/get-edit-content)) (not pdf-area?))
                    insert-opts' (if insert-to-current-block-page?
@@ -1486,6 +1465,8 @@
              (or new-entity
                  (throw (ex-info "Can't save asset" {:files files}))))))))))
 
+(def insert-command! editor-common-handler/insert-command!)
+
 (defn db-upload-assets!
   "Paste asset for db graph and insert link to current editing block"
   [repo id ^js files format uploading? drop-or-paste?]

+ 13 - 85
src/main/frontend/handler/events.cljs

@@ -3,8 +3,7 @@
   core.async channel to handle them. Any part of the system can dispatch
   one of these events using state/pub-event!"
   (:refer-clojure :exclude [run!])
-  (:require ["@capacitor/filesystem" :refer [Directory Filesystem]]
-            ["@sentry/react" :as Sentry]
+  (:require ["@sentry/react" :as Sentry]
             [cljs-bean.core :as bean]
             [clojure.core.async :as async]
             [clojure.core.async.interop :refer [p->c]]
@@ -14,9 +13,7 @@
             [frontend.date :as date]
             [frontend.db :as db]
             [frontend.db.async :as db-async]
-            [frontend.db.conn :as conn]
             [frontend.db.model :as db-model]
-            [frontend.db.persist :as db-persist]
             [frontend.db.transact :as db-transact]
             [frontend.extensions.fsrs :as fsrs]
             [frontend.fs :as fs]
@@ -30,7 +27,6 @@
             [frontend.handler.db-based.rtc-flows :as rtc-flows]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.export :as export]
-            [frontend.handler.file-sync :as file-sync-handler]
             [frontend.handler.graph :as graph-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.page :as page-handler]
@@ -49,7 +45,6 @@
             [frontend.modules.shortcut.core :as st]
             [frontend.persist-db :as persist-db]
             [frontend.quick-capture :as quick-capture]
-            [frontend.search :as search]
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.util.persist-var :as persist-var]
@@ -85,29 +80,22 @@
   (when (= (:url repo) current-repo)
     (file-sync-restart!)))
 
-;; FIXME(andelf): awful multi-arty function.
-;; Should use a `-impl` function instead of the awful `skip-ios-check?` param with nested callback.
 (defn- graph-switch
-  ([graph]
-   (graph-switch graph false))
-  ([graph skip-ios-check?]
-   (let [db-based? (config/db-based-graph? graph)]
-     (if (and (mobile-util/native-ios?) (not skip-ios-check?))
-       (state/pub-event! [:validate-appId graph-switch graph])
-       (do
-         (state/set-current-repo! graph)
-         (page-handler/init-commands!)
+  [graph]
+  (let [db-based? (config/db-based-graph? graph)]
+    (state/set-current-repo! graph)
+    (page-handler/init-commands!)
          ;; load config
-         (repo-config-handler/restore-repo-config! graph)
-         (when-not (= :draw (state/get-current-route))
-           (route-handler/redirect-to-home!))
-         (when-not db-based?
+    (repo-config-handler/restore-repo-config! graph)
+    (when-not (= :draw (state/get-current-route))
+      (route-handler/redirect-to-home!))
+    (when-not db-based?
            ;; graph-switch will trigger a rtc-start automatically
            ;; (rtc-handler/<rtc-start! graph)
-           (file-sync-restart!))
-         (when-let [dir-name (and (not db-based?) (config/get-repo-dir graph))]
-           (fs/watch-dir! dir-name))
-         (graph-handler/settle-metadata-to-local! {:last-seen-at (js/Date.now)}))))))
+      (file-sync-restart!))
+    (when-let [dir-name (and (not db-based?) (config/get-repo-dir graph))]
+      (fs/watch-dir! dir-name))
+    (graph-handler/settle-metadata-to-local! {:last-seen-at (js/Date.now)})))
 
 ;; Parameters for the `persist-db` function, to show the notification messages
 (defn- graph-switch-on-persisted
@@ -271,72 +259,12 @@
       (when-let [toolbar (.querySelector main-node "#mobile-editor-toolbar")]
         (set! (.. toolbar -style -bottom) 0)))))
 
-(defn- get-ios-app-id
-  [repo-url]
-  (when repo-url
-    (let [app-id (-> (first (string/split repo-url "/Documents"))
-                     (string/split "/")
-                     last)]
-      app-id)))
-
-(defmethod handle :validate-appId [[_ graph-switch-f graph]]
-  (when-let [deprecated-repo (or graph (state/get-current-repo))]
-    (if (mobile-util/in-iCloud-container-path? deprecated-repo)
-      ;; Installation is not changed for iCloud
-      (when graph-switch-f
-        (graph-switch-f graph true)
-        (state/pub-event! [:graph/ready (state/get-current-repo)]))
-      ;; Installation is changed for App Documents directory
-      (p/let [deprecated-app-id (get-ios-app-id deprecated-repo)
-              current-document-url (.getUri Filesystem #js {:path ""
-                                                            :directory (.-Documents Directory)})
-              current-app-id (-> (js->clj current-document-url :keywordize-keys true)
-                                 get-ios-app-id)]
-        (if (= deprecated-app-id current-app-id)
-          (when graph-switch-f (graph-switch-f graph true))
-          (do
-            (notification/show! [:div "Migrating from previous App installation..."]
-                                :warning
-                                true)
-            (prn ::migrate-app-id :from deprecated-app-id :to current-app-id)
-            (file-sync-stop!)
-            (.unwatch mobile-util/fs-watcher)
-            (let [current-repo (string/replace deprecated-repo deprecated-app-id current-app-id)
-                  current-repo-dir (config/get-repo-dir current-repo)]
-              (try
-                ;; replace app-id part of repo url
-                (reset! conn/conns
-                        (update-keys @conn/conns
-                                     (fn [key]
-                                       (if (string/includes? key deprecated-app-id)
-                                         (string/replace key deprecated-app-id current-app-id)
-                                         key))))
-                (db-persist/rename-graph! deprecated-repo current-repo)
-                (search/remove-db! deprecated-repo)
-                (state/add-repo! {:url current-repo :nfs? true})
-                (state/delete-repo! {:url deprecated-repo})
-                (catch :default e
-                  (js/console.error e)))
-              (state/set-current-repo! current-repo)
-              (repo-config-handler/restore-repo-config! current-repo)
-              (when graph-switch-f (graph-switch-f current-repo true))
-              (.watch mobile-util/fs-watcher #js {:path current-repo-dir})
-              (file-sync-restart!))))
-        (state/pub-event! [:graph/ready (state/get-current-repo)])))))
-
 (defmethod handle :plugin/hook-db-tx [[_ {:keys [blocks tx-data] :as payload}]]
   (when-let [payload (and (seq blocks)
                           (merge payload {:tx-data (map #(into [] %) tx-data)}))]
     (plugin-handler/hook-plugin-db :changed payload)
     (plugin-handler/hook-plugin-block-changes payload)))
 
-(defmethod handle :mobile-file-watcher/changed [[_ ^js event]]
-  (let [type (.-event event)
-        payload (js->clj event :keywordize-keys true)]
-    (fs-watcher/handle-changed! type payload)
-    (when (file-sync-handler/enable-sync?)
-      (sync/file-watch-handler type payload))))
-
 (defmethod handle :rebuild-slash-commands-list [[_]]
   (page-handler/rebuild-slash-commands-list!))
 

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

@@ -19,13 +19,11 @@
             [frontend.db :as db]
             [frontend.extensions.fsrs :as fsrs]
             [frontend.extensions.srs :as srs]
-            [frontend.fs.capacitor-fs :as capacitor-fs]
-            [frontend.fs.nfs :as nfs]
             [frontend.fs.sync :as sync]
             [frontend.handler.db-based.rtc :as rtc-handler]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.events :as events]
-            [frontend.handler.file-based.nfs :as nfs-handler]
+            [frontend.handler.file-based.native-fs :as nfs-handler]
             [frontend.handler.file-sync :as file-sync-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.page :as page-handler]
@@ -93,35 +91,6 @@
   (shui/dialog-open!
    (component-page/batch-delete-dialog selected-rows ok-handler)))
 
-(defn ask-permission
-  [repo]
-  (when
-   (and (not (util/electron?))
-        (not (mobile-util/native-platform?)))
-    (fn [{:keys [close]}]
-      [:div
-       ;; TODO: fn translation with args
-       [:p
-        "Grant native filesystem permission for directory: "
-        [:b (config/get-local-dir repo)]]
-       (ui/button
-        (t :settings-permission/start-granting)
-        :class "ui__modal-enter"
-        :on-click (fn []
-                    (nfs/check-directory-permission! repo)
-                    (close)))])))
-
-(defn get-local-repo
-  []
-  (when-let [repo (state/get-current-repo)]
-    (when (config/local-file-based-graph? repo)
-      repo)))
-
-(defmethod events/handle :modal/nfs-ask-permission []
-  (when-let [repo (get-local-repo)]
-    (some-> (ask-permission repo)
-            (shui/dialog-open! {:align :top}))))
-
 (defmethod events/handle :modal/show-cards [[_ cards-id]]
   (let [db-based? (config/db-based-graph? (state/get-current-repo))]
     (shui/dialog-open!
@@ -129,12 +98,6 @@
      {:id :srs
       :label "flashcards__cp"})))
 
-(defmethod events/handle :modal/show-instruction [_]
-  (shui/dialog-open!
-   capacitor-fs/instruction
-   {:id :instruction
-    :label "instruction__cp"}))
-
 (defmethod events/handle :modal/show-themes-modal [[_ classic?]]
   (if classic?
     (plugin/open-select-theme!)

+ 22 - 48
src/main/frontend/handler/file_based/editor.cljs

@@ -1,6 +1,7 @@
 (ns frontend.handler.file-based.editor
   "File-based graph implementation"
   (:require [clojure.string :as string]
+            [electron.ipc :as ipc]
             [frontend.commands :as commands]
             [frontend.config :as config]
             [frontend.date :as date]
@@ -9,8 +10,6 @@
             [frontend.db.query-dsl :as query-dsl]
             [frontend.format.block :as block]
             [frontend.format.mldoc :as mldoc]
-            [frontend.fs :as fs]
-            [frontend.fs.capacitor-fs :as capacitor-fs]
             [frontend.handler.assets :as assets-handler]
             [frontend.handler.block :as block-handler]
             [frontend.handler.common.editor :as editor-common-handler]
@@ -19,14 +18,12 @@
             [frontend.handler.file-based.repeated :as repeated]
             [frontend.handler.file-based.status :as status]
             [frontend.handler.property.file :as property-file]
-            [frontend.mobile.util :as mobile-util]
             [frontend.modules.outliner.op :as outliner-op]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.util.file-based.clock :as clock]
             [frontend.util.file-based.drawer :as drawer]
-            [goog.crypt.base64 :as base64]
             [logseq.common.path :as path]
             [logseq.common.util :as common-util]
             [logseq.common.util.block-ref :as block-ref]
@@ -302,51 +299,28 @@
    (p/all
     (for [[index ^js file] (map-indexed vector files)]
       ;; WARN file name maybe fully qualified path when paste file
-      (p/let [file-name (util/node-path.basename (.-name file))
-              [file-stem ext-full ext-base] (if file-name
-                                              (let [ext-base (util/node-path.extname file-name)
-                                                    ext-full (if-not (config/extname-of-supported? ext-base)
-                                                               (util/full-path-extname file-name) ext-base)]
-                                                [(subs file-name 0 (- (count file-name)
-                                                                      (count ext-full))) ext-full ext-base])
-                                              ["" "" ""])
-              filename  (str (gen-filename index file-stem) ext-full)
-              file-rpath  (str asset-dir-rpath "/" filename)
-              matched-alias (assets-handler/get-matched-alias-by-ext ext-base)
-              file-rpath (cond-> file-rpath
-                           (not (nil? matched-alias))
-                           (string/replace #"^[.\/\\]*assets[\/\\]+" ""))
-              dir (or (:dir matched-alias) repo-dir)]
-        (if (util/electron?)
-          (do (js/console.debug "Debug: Copy Asset #" dir file-rpath)
-              (-> (if-let [from (not-empty (.-path file))]
-                    (js/window.apis.copyFileToAssets dir file-rpath from)
-                    (p/let [content (.arrayBuffer file)]
-                      (fs/write-file! repo repo-dir file-rpath content {:skip-compare? true})))
-                  (p/then
-                   (fn [dest]
-                     [file-rpath
-                      (if (string? dest) (js/File. #js[] dest) file)
-                      (path/path-join dir file-rpath)
-                      matched-alias]))
-                  (p/catch #(js/console.error "Debug: Copy Asset Error#" %))))
+      (let [file-name (util/node-path.basename (.-name file))
+            [file-stem ext-full ext-base] (if file-name
+                                            (let [ext-base (util/node-path.extname file-name)
+                                                  ext-full (if-not (config/extname-of-supported? ext-base)
+                                                             (util/full-path-extname file-name) ext-base)]
+                                              [(subs file-name 0 (- (count file-name)
+                                                                    (count ext-full))) ext-full ext-base])
+                                            ["" "" ""])
+            filename  (str (gen-filename index file-stem) ext-full)
+            file-rpath  (str asset-dir-rpath "/" filename)
+            matched-alias (assets-handler/get-matched-alias-by-ext ext-base)
+            file-rpath (cond-> file-rpath
+                         (not (nil? matched-alias))
+                         (string/replace #"^[.\/\\]*assets[\/\\]+" ""))
+            dir (or (:dir matched-alias) repo-dir)]
 
-          (->
-           (p/do! (js/console.debug "Debug: Writing Asset #" dir file-rpath)
-                  (cond
-                    (mobile-util/native-platform?)
-                   ;; capacitor fs accepts Blob, File implements Blob
-                    (p/let [buffer (.arrayBuffer file)
-                            content (base64/encodeByteArray (js/Uint8Array. buffer))
-                            fpath (path/path-join dir file-rpath)]
-                      (capacitor-fs/<write-file-with-base64 fpath content))
-
-                    :else                ; nfs
-                    (fs/write-file! repo dir file-rpath (.stream file) nil))
-                  [file-rpath file (path/path-join dir file-rpath) matched-alias])
-           (p/catch (fn [error]
-                      (prn :paste-file-error)
-                      (js/console.error error))))))))))
+        (p/do! (js/console.debug "Debug: Writing Asset #" dir file-rpath)
+               (p/let [content (.arrayBuffer file)
+                       file-fpath (path/path-join dir file-rpath)]
+                 ;; file based version support electron only
+                 (ipc/ipc "writeFile" repo file-fpath content))
+               [file-rpath file (path/path-join dir file-rpath) matched-alias]))))))
 
 ;; assets/journals_2021_02_03_1612350230540_0.png
 (defn resolve-relative-path

+ 6 - 6
src/main/frontend/handler/file_based/events.cljs

@@ -17,7 +17,7 @@
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.events :as events]
             [frontend.handler.file-based.file :as file-handler]
-            [frontend.handler.file-based.nfs :as nfs-handler]
+            [frontend.handler.file-based.native-fs :as nfs-handler]
             [frontend.handler.file-sync :as file-sync-handler]
             [frontend.handler.notification :as notification]
             [frontend.handler.page :as page-handler]
@@ -289,11 +289,11 @@
                                  (let [dir (config/get-repo-dir repo)]
                                    (p/let [content (fs/read-file dir file)]
                                      (let [new-content (string/replace content (str id) (str (random-uuid)))]
-                                       (p/let [_ (fs/write-file! repo
-                                                                 dir
-                                                                 file
-                                                                 new-content
-                                                                 {})]
+                                       (p/let [_ (fs/write-plain-text-file! repo
+                                                                            dir
+                                                                            file
+                                                                            new-content
+                                                                            {})]
                                          (reset! resolved? true))))))
                      :class "inline mx-1")
           "it."]])]]))

+ 41 - 54
src/main/frontend/handler/file_based/file.cljs

@@ -1,28 +1,25 @@
 (ns frontend.handler.file-based.file
   "Provides util handler fns for file graph files"
   (:refer-clojure :exclude [load-file])
-  (:require [frontend.config :as config]
+  (:require [electron.ipc :as ipc]
+            [frontend.config :as config]
             [frontend.db :as db]
             [frontend.db.file-based.model :as file-model]
             [frontend.fs :as fs]
-            [frontend.fs.nfs :as nfs]
-            [frontend.fs.capacitor-fs :as capacitor-fs]
-            [frontend.handler.file-based.reset-file :as reset-file-handler]
             [frontend.handler.common.config-edn :as config-edn-common-handler]
-            [frontend.handler.repo-config :as repo-config-handler]
+            [frontend.handler.file-based.reset-file :as reset-file-handler]
             [frontend.handler.global-config :as global-config-handler]
+            [frontend.handler.repo-config :as repo-config-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.schema.handler.global-config :as global-config-schema]
             [frontend.schema.handler.repo-config :as repo-config-schema]
             [frontend.state :as state]
             [frontend.util :as util]
-            [logseq.common.util :as common-util]
-            [electron.ipc :as ipc]
             [lambdaisland.glogi :as log]
-            [promesa.core :as p]
-            [frontend.mobile.util :as mobile-util]
+            [logseq.common.config :as common-config]
             [logseq.common.path :as path]
-            [logseq.common.config :as common-config]))
+            [logseq.common.util :as common-util]
+            [promesa.core :as p]))
 
 ;; TODO: extract all git ops using a channel
 
@@ -32,9 +29,9 @@
    (p/let [content (fs/read-file (config/get-repo-dir repo-url) path)]
      content)
    (p/catch
-       (fn [e]
-         (println "Load file failed: " path)
-         (js/console.error e)))))
+    (fn [e]
+      (println "Load file failed: " path)
+      (js/console.error e)))))
 
 (defn- load-multiple-files
   [repo-url paths]
@@ -64,7 +61,7 @@
     (-> (p/all (load-multiple-files repo-url files))
         (p/then (fn [contents]
                   (let [file-contents (cond->
-                                        (zipmap files contents)
+                                       (zipmap files contents)
 
                                         (seq images)
                                         (merge (zipmap images (repeat (count images) ""))))
@@ -73,21 +70,14 @@
                                          :file/content content})]
                     (ok-handler file-contents))))
         (p/catch (fn [error]
-                   (log/error :nfs/load-files-error repo-url)
+                   (log/error :fs/load-files-error repo-url)
                    (log/error :exception error))))))
 
 (defn backup-file!
   "Backup db content to bak directory"
   [repo-url path db-content content]
-  (cond
-    (util/electron?)
-    (ipc/ipc "backupDbFile" repo-url path db-content content)
-
-    (mobile-util/native-platform?)
-    (capacitor-fs/backup-file-handle-changed! repo-url path db-content)
-
-    :else
-    nil))
+  (when (util/electron?)
+    (ipc/ipc "backupDbFile" repo-url path db-content content)))
 
 (defn- detect-deprecations
   [path content]
@@ -115,7 +105,7 @@
         path-dir (config/get-repo-dir repo)
         write-file-options' (merge write-file-options
                                    (when original-content {:old-content original-content}))]
-    (fs/write-file! repo path-dir path content write-file-options')))
+    (fs/write-plain-text-file! repo path-dir path content write-file-options')))
 
 (defn alter-global-file
   "Does pre-checks on a global file, writes if it's not already written
@@ -126,18 +116,18 @@
     (do
       (detect-deprecations path content)
       (when (validate-file path content)
-       (-> (p/let [_ (when-not from-disk?
-                       (fs/write-file! "" nil path content {:skip-compare? true}))]
-                  (p/do! (global-config-handler/restore-global-config!)
-                         (state/pub-event! [:shortcut/refresh])))
-           (p/catch (fn [error]
-                      (state/pub-event! [:notification/show
-                                         {:content (str "Failed to write to file " path ", error: " error)
-                                          :status :error}])
-                      (log/error :write/failed error)
-                      (state/pub-event! [:capture-error
-                                         {:error error
-                                          :payload {:type :write-file/failed-for-alter-file}}]))))))
+        (-> (p/let [_ (when-not from-disk?
+                        (fs/write-plain-text-file! "" nil path content {:skip-compare? true}))]
+              (p/do! (global-config-handler/restore-global-config!)
+                     (state/pub-event! [:shortcut/refresh])))
+            (p/catch (fn [error]
+                       (state/pub-event! [:notification/show
+                                          {:content (str "Failed to write to file " path ", error: " error)
+                                           :status :error}])
+                       (log/error :write/failed error)
+                       (state/pub-event! [:capture-error
+                                          {:error error
+                                           :payload {:type :write-file/failed-for-alter-file}}]))))))
     (log/error :msg "alter-global-file does not support this file" :file path)))
 
 (defn alter-file
@@ -209,23 +199,20 @@
                        (when path
                          (let [path (common-util/path-normalize path)
                                original-content (get file->content path)]
-                          (-> (p/let [_ (or
-                                         (util/electron?)
-                                         (nfs/check-directory-permission! repo))]
-                                (fs/write-file! repo (config/get-repo-dir repo) path content
-                                                {:old-content original-content}))
-                              (p/catch (fn [error]
-                                         (state/pub-event! [:notification/show
-                                                            {:content (str "Failed to save the file " path ". Error: "
-                                                                           (str error))
-                                                             :status :error
-                                                             :clear? false}])
-                                         (state/pub-event! [:capture-error
-                                                            {:error error
-                                                             :payload {:type :write-file/failed}}])
-                                         (log/error :write-file/failed {:path path
-                                                                        :content content
-                                                                        :error error})))))))
+                           (-> (fs/write-plain-text-file! repo (config/get-repo-dir repo) path content
+                                                          {:old-content original-content})
+                               (p/catch (fn [error]
+                                          (state/pub-event! [:notification/show
+                                                             {:content (str "Failed to save the file " path ". Error: "
+                                                                            (str error))
+                                                              :status :error
+                                                              :clear? false}])
+                                          (state/pub-event! [:capture-error
+                                                             {:error error
+                                                              :payload {:type :write-file/failed}}])
+                                          (log/error :write-file/failed {:path path
+                                                                         :content content
+                                                                         :error error})))))))
         finish-handler (fn []
                          (when finish-handler
                            (finish-handler)))]

+ 5 - 7
src/main/frontend/handler/file_based/nfs.cljs → src/main/frontend/handler/file_based/native_fs.cljs

@@ -1,12 +1,11 @@
-(ns frontend.handler.file-based.nfs
-  "The File System Access API, https://web.dev/file-system-access/."
+(ns frontend.handler.file-based.native-fs
+  "Native fs including Electron and mobile"
   (:require ["/frontend/utils" :as utils]
             [clojure.set :as set]
             [clojure.string :as string]
             [frontend.config :as config]
             [frontend.db :as db]
             [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]
@@ -142,7 +141,7 @@
                                      (state/set-loading-files! repo false)
                                      (when ok-handler (ok-handler {:url repo})))))))
                 (p/catch (fn [error]
-                           (log/error :nfs/load-files-error repo)
+                           (log/error :fs/load-files-error repo)
                            (log/error :exception error)))))))
       (p/catch (fn [error]
                  (log/error :exception error)
@@ -253,8 +252,7 @@
       (->
        (p/let [handle (when-not electron? (idb/get-item handle-path))]
          (when (or handle electron? mobile-native?)
-           (p/let [_ (when nfs? (nfs/verify-permission repo true))
-                   local-files-result (fs/get-files repo-dir)
+           (p/let [local-files-result (fs/get-files repo-dir)
                    _ (when (config/global-config-enabled?)
                        ;; reload global config into state
                        (global-config-handler/restore-global-config!))
@@ -262,7 +260,7 @@
                                  (remove-ignore-files repo-dir nfs?))]
              (handle-diffs! repo nfs? old-files new-files re-index? ok-handler))))
        (p/catch (fn [error]
-                  (log/error :nfs/load-files-error repo)
+                  (log/error :fs/load-files-error repo)
                   (log/error :exception error)))
        (p/finally (fn [_]
                     (state/set-graph-syncing? false)))))))

+ 15 - 51
src/main/frontend/handler/file_based/repo.cljs

@@ -1,25 +1,25 @@
 (ns frontend.handler.file-based.repo
   "Repo fns for creating, loading and parsing file graphs"
-  (:require [frontend.config :as config]
+  (:require [clojure.core.async :as async]
+            [clojure.core.async.interop :refer [p->c]]
+            [frontend.config :as config]
             [frontend.db :as db]
             [frontend.db.file-based.model :as file-model]
             [frontend.fs :as fs]
             [frontend.handler.file-based.file :as file-handler]
-            [frontend.handler.repo-config :as repo-config-handler]
             [frontend.handler.file-based.reset-file :as reset-file-handler]
+            [frontend.handler.repo-config :as repo-config-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.ui :as ui-handler]
             [frontend.spec :as spec]
             [frontend.state :as state]
             [frontend.util :as util]
-            [promesa.core :as p]
-            [shadow.resource :as rc]
-            [logseq.graph-parser :as graph-parser]
             [logseq.common.config :as common-config]
-            [clojure.core.async :as async]
-            [medley.core :as medley]
             [logseq.common.path :as path]
-            [clojure.core.async.interop :refer [p->c]]))
+            [logseq.graph-parser :as graph-parser]
+            [medley.core :as medley]
+            [promesa.core :as p]
+            [shadow.resource :as rc]))
 
 (defn- create-contents-file
   [repo-url]
@@ -54,49 +54,13 @@
 
 (comment
   (defn- create-dummy-notes-page
-   [repo-url content]
-   (spec/validate :repos/url repo-url)
-   (let [repo-dir (config/get-repo-dir repo-url)
-         file-rpath (str (config/get-pages-directory) "/how_to_make_dummy_notes.md")]
-     (p/let [_ (fs/mkdir-if-not-exists (path/path-join repo-dir (config/get-pages-directory)))
-             _file-exists? (fs/create-if-not-exists repo-url repo-dir file-rpath content)]
-       (reset-file-handler/reset-file! repo-url file-rpath content)))))
-
-(comment
-  (defn- create-today-journal-if-not-exists
-   [repo-url {:keys [content]}]
-   (spec/validate :repos/url repo-url)
-   (when (state/enable-journals? repo-url)
-     (let [repo-dir (config/get-repo-dir repo-url)
-           format (state/get-preferred-format repo-url)
-           title (date/today)
-           file-name (date/journal-title->default title)
-           default-content (util/default-content-with-title format)
-           template (state/get-default-journal-template)
-           template (when (and template
-                               (not (string/blank? template)))
-                      template)
-           content (cond
-                     content
-                     content
-
-                     template
-                     (str default-content template)
-
-                     :else
-                     default-content)
-           file-rpath (path/path-join (config/get-journals-directory) (str file-name "."
-                                                                           (config/get-file-extension format)))
-           page-exists? (ldb/get-page (db/get-db) title)
-           empty-blocks? (db/page-empty? repo-url (util/page-name-sanity-lc title))]
-       (when (or empty-blocks? (not page-exists?))
-         (p/let [_ (nfs/check-directory-permission! repo-url)
-                 _ (fs/mkdir-if-not-exists (path/path-join repo-dir (config/get-journals-directory)))
-                 file-exists? (fs/file-exists? repo-dir file-rpath)]
-           (when-not file-exists?
-             (p/let [_ (reset-file-handler/reset-file! repo-url file-rpath content)]
-               (fs/create-if-not-exists repo-url repo-dir file-rpath content)))))))))
-
+    [repo-url content]
+    (spec/validate :repos/url repo-url)
+    (let [repo-dir (config/get-repo-dir repo-url)
+          file-rpath (str (config/get-pages-directory) "/how_to_make_dummy_notes.md")]
+      (p/let [_ (fs/mkdir-if-not-exists (path/path-join repo-dir (config/get-pages-directory)))
+              _file-exists? (fs/create-if-not-exists repo-url repo-dir file-rpath content)]
+        (reset-file-handler/reset-file! repo-url file-rpath content)))))
 
 (defn create-config-file-if-not-exists
   "Creates a default logseq/config.edn if it doesn't exist"

+ 13 - 13
src/main/frontend/handler/global_config.cljs

@@ -2,14 +2,14 @@
   "This ns is a system component that encapsulates global config functionality.
   Unlike repo config, this also manages a directory for configuration. This
   component depends on a repo."
-  (:require [frontend.fs :as fs]
-            [frontend.state :as state]
-            [promesa.core :as p]
-            [shadow.resource :as rc]
+  (:require [borkdude.rewrite-edn :as rewrite]
             [clojure.edn :as edn]
             [electron.ipc :as ipc]
-            [borkdude.rewrite-edn :as rewrite]
-            [logseq.common.path :as path]))
+            [frontend.fs :as fs]
+            [frontend.state :as state]
+            [logseq.common.path :as path]
+            [promesa.core :as p]
+            [shadow.resource :as rc]))
 
 ;; Use defonce to avoid broken state on dev reload
 ;; Also known as home directory a.k.a. '~'
@@ -50,30 +50,30 @@
         config-path (global-config-path)]
     (p/let [_ (fs/mkdir-if-not-exists config-dir)
             file-exists? (fs/create-if-not-exists repo-url nil config-path default-content)]
-           (when-not file-exists?
-             (set-global-config-state! default-content)))))
+      (when-not file-exists?
+        (set-global-config-state! default-content)))))
 
 (defn restore-global-config!
   "Sets global config state from config file"
   []
   (let [config-path (global-config-path)]
     (p/let [config-content (fs/read-file nil config-path)]
-           (set-global-config-state! config-content))))
+      (set-global-config-state! config-content))))
 
 (defn set-global-config-kv!
   [k v]
   (let [result (rewrite/parse-string
-                 (or (state/get-global-config-str-content) "{}"))
+                (or (state/get-global-config-str-content) "{}"))
         ks (if (sequential? k) k [k])
         v (cond->> v
-                   (map? v)
-                   (reduce-kv (fn [a k v] (rewrite/assoc a k v)) (rewrite/parse-string "{}")))
+            (map? v)
+            (reduce-kv (fn [a k v] (rewrite/assoc a k v)) (rewrite/parse-string "{}")))
         new-result (if (and (= 1 (count ks))
                             (nil? v))
                      (rewrite/dissoc result (first ks))
                      (rewrite/assoc-in result ks v))
         new-str-content (str new-result)]
-    (fs/write-file! nil nil (global-config-path) new-str-content {:skip-compare? true})
+    (fs/write-plain-text-file! nil nil (global-config-path) new-str-content {:skip-compare? true})
     (state/set-global-config! (rewrite/sexpr new-result) new-str-content)))
 
 (defn start

+ 2 - 2
src/main/frontend/handler/page.cljs

@@ -17,7 +17,7 @@
             [frontend.handler.db-based.page :as db-page-handler]
             [frontend.handler.db-based.property :as db-property-handler]
             [frontend.handler.editor :as editor-handler]
-            [frontend.handler.file-based.nfs :as nfs-handler]
+            [frontend.handler.file-based.native-fs :as nfs-handler]
             [frontend.handler.file-based.page :as file-page-handler]
             [frontend.handler.file-based.page-property :as file-page-property]
             [frontend.handler.graph :as graph-handler]
@@ -28,7 +28,7 @@
             [frontend.mobile.util :as mobile-util]
             [frontend.modules.outliner.op :as outliner-op]
             [frontend.modules.outliner.ui :as ui-outliner-tx]
-            [frontend.ref :as ref]
+            [frontend.util.ref :as ref]
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.util.cursor :as cursor]

+ 23 - 16
src/main/frontend/handler/plugin.cljs

@@ -67,6 +67,13 @@
   (invoke-exported-api :unlink_installed_web_plugin key)
   (invoke-exported-api :unlink_plugin_user_settings key))
 
+(defn assets-theme-to-file
+  [theme]
+  (when theme
+    (cond-> theme
+      (util/electron?)
+      (update :url #(some-> % (string/replace-first "assets://" "file://"))))))
+
 (defn load-plugin-preferences
   []
   (-> (invoke-exported-api :load_user_preferences)
@@ -211,7 +218,6 @@
                    (let [e (or evt ctx)]
                      (when-let [{:keys [status payload only-check]} (bean/->clj e)]
                        (case (keyword status)
-
                          :completed
                          (let [{:keys [id dst name title theme web-pkg]} payload
                                name (or title name "Untitled")]
@@ -333,6 +339,7 @@
                      :action (fn []
                                (state/pub-event!
                                 [:exec-plugin-cmd {:type type :key key :pid pid :cmd cmd :action action}]))}]
+
     palette-cmd))
 
 (defn simple-cmd-keybinding->shortcut-args
@@ -483,7 +490,7 @@
 (defn select-a-plugin-theme
   [pid]
   (when-let [themes (get (group-by :pid (:plugin/installed-themes @state/state)) pid)]
-    (when-let [theme (first themes)]
+    (when-let [theme (assets-theme-to-file (first themes))]
       (js/LSPluginCore.selectTheme (bean/->js theme)))))
 
 (defn update-plugin-settings-state
@@ -642,7 +649,7 @@
             dotroot (get-ls-dotdir-root)
             filepath (util/node-path.join dotroot dirname (str key ".json"))]
         (if (util/electron?)
-          (fs/write-file! repo nil filepath (js/JSON.stringify data nil 2) {:skip-compare? true})
+          (fs/write-plain-text-file! repo nil filepath (js/JSON.stringify data nil 2) {:skip-compare? true})
           (idb/set-item! filepath data))))))
 
 (defn make-fn-to-unlink-dotdir-json
@@ -884,13 +891,13 @@
 
                   (.on "themes-changed" (fn [^js themes]
                                           (swap! state/state assoc :plugin/installed-themes
-                                                 (vec (mapcat (fn [[pid vs]] (mapv #(assoc % :pid pid) (bean/->clj vs))) (bean/->clj themes))))))
+                                            (vec (mapcat (fn [[pid vs]] (mapv #(assoc % :pid pid) (bean/->clj vs))) (bean/->clj themes))))))
 
-                  (.on "theme-selected" (fn [^js theme ^js opts]
+                  (.on "theme-selected" (fn [^js theme]
                                           (let [theme (bean/->clj theme)
-                                                _opts (bean/->clj opts)
-                                                url (:url theme)
-                                                mode (or (:mode theme) (state/sub :ui/theme))]
+                                                theme (assets-theme-to-file theme)
+                                                url   (:url theme)
+                                                mode  (or (:mode theme) (state/sub :ui/theme))]
                                             (when mode
                                               (state/set-custom-theme! mode theme)
                                               (state/set-theme-mode! mode))
@@ -939,16 +946,16 @@
         plugins-async)
 
       (p/then
-        (fn [plugins-async]
+       (fn [plugins-async]
           ;; true indicate for preboot finished
-          (state/set-state! :plugin/indicator-text true)
+         (state/set-state! :plugin/indicator-text true)
           ;; wait for the plugin register async messages
-          (js/setTimeout
-            (fn [] (callback)
-              (some-> (seq plugins-async)
-                (p/delay 16)
-                (p/then #(.register js/LSPluginCore (bean/->js plugins-async) true))))
-            (if (util/electron?) 64 0))))
+         (js/setTimeout
+          (fn [] (callback)
+            (some-> (seq plugins-async)
+                    (p/delay 16)
+                    (p/then #(.register js/LSPluginCore (bean/->js plugins-async) true))))
+          (if (util/electron?) 64 0))))
       (p/catch
        (fn [^js e]
          (log/error :setup-plugin-system-error e)

+ 2 - 2
src/main/frontend/handler/plugin_config.cljs

@@ -40,14 +40,14 @@ when a plugin is installed, updated or removed"
                               str)]
          ;; fs protocols require repo and dir when they aren't necessary. For this component,
          ;; neither is needed so these are blank and nil respectively
-    (fs/write-file! "" nil (plugin-config-path) updated-content {:skip-compare? true})))
+    (fs/write-plain-text-file! "" nil (plugin-config-path) updated-content {:skip-compare? true})))
 
 (defn remove-plugin
   "Removes a plugin from plugin.edn"
   [plugin-id]
   (p/let [content (fs/read-file "" (plugin-config-path))
           updated-content (-> content rewrite/parse-string (rewrite/dissoc (keyword plugin-id)) str)]
-    (fs/write-file! "" nil (plugin-config-path) updated-content {:skip-compare? true})))
+    (fs/write-plain-text-file! "" nil (plugin-config-path) updated-content {:skip-compare? true})))
 
 (defn- create-plugin-config-file-if-not-exists
   []

+ 6 - 3
src/main/frontend/handler/user.cljs

@@ -127,12 +127,16 @@
   ([id-token access-token]
    (state/set-auth-id-token id-token)
    (state/set-auth-access-token access-token)
-   (set-token-to-localstorage! id-token access-token))
+   (set-token-to-localstorage! id-token access-token)
+   (some->> (parse-jwt (state/get-auth-id-token))
+            (reset! flows/*current-login-user)))
   ([id-token access-token refresh-token]
    (state/set-auth-id-token id-token)
    (state/set-auth-access-token access-token)
    (state/set-auth-refresh-token refresh-token)
-   (set-token-to-localstorage! id-token access-token refresh-token)))
+   (set-token-to-localstorage! id-token access-token refresh-token)
+   (some->> (parse-jwt (state/get-auth-id-token))
+            (reset! flows/*current-login-user))))
 
 (defn- <refresh-tokens
   "return refreshed id-token, access-token"
@@ -201,7 +205,6 @@
    (:jwtToken (:idToken session))
    (:jwtToken (:accessToken session))
    (:token (:refreshToken session)))
-  (reset! flows/*current-login-user (parse-jwt (state/get-auth-id-token)))
   (state/pub-event! [:user/fetch-info-and-graphs]))
 
 (defn ^:export login-with-username-password-e2e

+ 8 - 7
src/main/frontend/idb.cljs

@@ -21,13 +21,14 @@
   (when (and key @store)
     (idb-keyval/set key value @store)))
 
-(defn rename-item!
-  [old-key new-key]
-  (when (and old-key new-key @store)
-    (p/let [value (idb-keyval/get old-key @store)]
-      (when value
-        (idb-keyval/set new-key value @store)
-        (idb-keyval/del old-key @store)))))
+(comment
+  (defn rename-item!
+    [old-key new-key]
+    (when (and old-key new-key @store)
+      (p/let [value (idb-keyval/get old-key @store)]
+        (when value
+          (idb-keyval/set new-key value @store)
+          (idb-keyval/del old-key @store))))))
 
 (comment
   (defn set-batch!

+ 1 - 1
src/main/frontend/mobile/action_bar.cljs

@@ -7,7 +7,7 @@
             [frontend.handler.editor :as editor-handler]
             [frontend.mixins :as mixins]
             [frontend.mobile.util :as mobile-util]
-            [frontend.ref :as ref]
+            [frontend.util.ref :as ref]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [frontend.util :as util]

+ 4 - 51
src/main/frontend/mobile/core.cljs

@@ -3,16 +3,12 @@
   (:require ["@capacitor/app" :refer [^js App]]
             ["@capacitor/keyboard" :refer [^js Keyboard]]
             [clojure.string :as string]
-            [promesa.core :as p]
-            [frontend.fs.capacitor-fs :as capacitor-fs]
             [frontend.handler.editor :as editor-handler]
             [frontend.mobile.deeplink :as deeplink]
             [frontend.mobile.intent :as intent]
             [frontend.mobile.util :as mobile-util]
             [frontend.state :as state]
-            [frontend.util :as util]
-            [cljs-bean.core :as bean]
-            [frontend.config :as config]))
+            [frontend.util :as util]))
 
 (def *init-url (atom nil))
 ;; FIXME: `appUrlOpen` are fired twice when receiving a same intent.
@@ -21,14 +17,6 @@
 (def *last-shared-url (atom nil))
 (def *last-shared-seconds (atom 0))
 
-(defn mobile-preinit
-  "preinit logic of mobile platforms: setup document folder permission"
-  []
-  (when (mobile-util/native-ios?)
-    ;; Caution: This must be called before any file accessing
-    (capacitor-fs/ios-ensure-documents!)))
-
-
 (defn mobile-postinit
   "postinit logic of mobile platforms: handle deeplink and intent"
   []
@@ -40,30 +28,7 @@
 (defn- ios-init
   "Initialize iOS-specified event listeners"
   []
-  (p/let [^js paths (capacitor-fs/ios-ensure-documents!)]
-    (when paths
-      (let [paths (-> paths
-                      bean/->clj
-                      (update-vals capacitor-fs/ios-force-include-private))]
-        (state/set-state! :mobile/container-urls paths)
-        (println "iOS container path: " paths))))
-
-  ;; Fix iOS App directory change across installation
-  (when (not (config/demo-graph?))
-    (state/pub-event! [:validate-appId]))
-
-  (mobile-util/check-ios-zoomed-display)
-
-  ;; keep this the same logic as src/main/electron/listener.cljs
-  (.addListener mobile-util/file-sync "debug"
-                (fn [event]
-                  (let [event (js->clj event :keywordize-keys true)
-                        payload (:data event)]
-                    (when (or (= (:event event) "download:progress")
-                              (= (:event event) "upload:progress"))
-                      (state/set-state! [:file-sync/graph-state (:graphUUID payload) :file-sync/progress (:file payload)] payload))))))
-
-
+  (mobile-util/check-ios-zoomed-display))
 
 (defn- android-init
   "Initialize Android-specified event listeners"
@@ -98,13 +63,7 @@
                        (js/window.history.back)))))
 
   (.addEventListener js/window "sendIntentReceived"
-                     #(intent/handle-received))
-
-  (.addListener mobile-util/file-sync "progress"
-                (fn [event]
-                  (js/console.log "🔄" event)
-                  (let [event (js->clj event :keywordize-keys true)]
-                    (state/set-state! [:file-sync/graph-state (:graphUUID event) :file-sync/progress (:file event)] event)))))
+                     #(intent/handle-received)))
 
 (defn- app-state-change-handler
   [^js state]
@@ -112,8 +71,7 @@
   (when (state/get-current-repo)
     (let [is-active? (.-isActive state)]
       (when-not is-active?
-        (editor-handler/save-current-block!))
-      (state/set-mobile-app-state-change is-active?))))
+        (editor-handler/save-current-block!)))))
 
 (defn- general-init
   "Initialize event listeners used by both iOS and Android"
@@ -129,10 +87,6 @@
                         (reset! *last-shared-seconds (.getSeconds (js/Date.)))
                         (deeplink/deeplink url))))))
 
-  (.addListener mobile-util/fs-watcher "watcher"
-                (fn [event]
-                  (state/pub-event! [:mobile-file-watcher/changed event])))
-
   (.addListener Keyboard "keyboardWillShow"
                 (fn [^js info]
                   (let [keyboard-height (.-keyboardHeight info)]
@@ -147,7 +101,6 @@
 
   (.addListener App "appStateChange" app-state-change-handler))
 
-
 (defn init! []
   (when (mobile-util/native-android?)
     (android-init))

+ 1 - 1
src/main/frontend/mobile/graph_picker.cljs

@@ -3,7 +3,7 @@
    [clojure.string :as string]
    [frontend.components.svg :as svg]
    [frontend.fs :as fs]
-   [frontend.handler.file-based.nfs :as nfs-handler]
+   [frontend.handler.file-based.native-fs :as nfs-handler]
    [frontend.handler.notification :as notification]
    [frontend.handler.page :as page-handler]
    [frontend.mobile.util :as mobile-util]

+ 1 - 1
src/main/frontend/mobile/intent.cljs

@@ -14,7 +14,7 @@
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.notification :as notification]
             [frontend.mobile.util :as mobile-util]
-            [frontend.ref :as ref]
+            [frontend.util.ref :as ref]
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.util.fs :as fs-util]

+ 10 - 22
src/main/frontend/mobile/util.cljs

@@ -1,10 +1,8 @@
 (ns frontend.mobile.util
-  (:require ["@capacitor/core" :refer [Capacitor registerPlugin ^js Plugins]]
+  (:require ["@capacitor/core" :refer [Capacitor registerPlugin]]
             ["@capacitor/splash-screen" :refer [SplashScreen]]
-            ["@logseq/capacitor-file-sync" :refer [FileSync]]
             [clojure.string :as string]
-            [promesa.core :as p]
-            [goog.object :as gobj]))
+            [promesa.core :as p]))
 
 (defn platform []
   (.getPlatform Capacitor))
@@ -25,13 +23,7 @@
 
 (defonce folder-picker (registerPlugin "FolderPicker"))
 (when (native-ios?)
-  (defonce ios-utils (registerPlugin "Utils"))
-  (defonce ios-file-container (registerPlugin "FileContainer")))
-
-;; NOTE: both iOS and android share the same API
-(when (native-platform?)
-  (defonce file-sync FileSync)
-  (defonce fs-watcher (registerPlugin "FsWatcher")))
+  (defonce ios-utils (registerPlugin "Utils")))
 
 (defn hide-splash []
   (.hide SplashScreen))
@@ -99,14 +91,10 @@
   [path]
   (string/includes? path "/iCloud~com~logseq~logseq/"))
 
-(defn is-iCloud-container-path?
-  "Check whether `path' is iCloud container path on iOS"
-  [path]
-  (re-matches #"/iCloud~com~logseq~logseq/Documents/?$" path))
-
-(defn app-active?
-  "Whether the app is active. This function returns a promise."
-  []
-  (let [app ^js (gobj/get Plugins "App")]
-    (p/let [state (.getState app)]
-      (gobj/get state "isActive"))))
+(comment
+  (defn app-active?
+    "Whether the app is active. This function returns a promise."
+    []
+    (let [app ^js (gobj/get Plugins "App")]
+      (p/let [state (.getState app)]
+        (gobj/get state "isActive")))))

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

@@ -122,15 +122,8 @@
 
 (defn <export-db!
   [repo data]
-  (cond
-    (util/electron?)
-    (ipc/ipc :db-export repo data)
-
-    ;; TODO: browser nfs-supported? auto backup
-
-    ;;
-    :else
-    nil))
+  (when (util/electron?)
+    (ipc/ipc :db-export repo data)))
 
 (defn- sqlite-error-handler
   [error]

+ 0 - 15
src/main/frontend/state.cljs

@@ -71,7 +71,6 @@
       :notification/show?                    false
       :notification/content                  nil
       :repo/loading-files?                   {}
-      :nfs/user-granted?                     {}
       :nfs/refreshing?                       nil
       :instrument/disabled?                  (storage/get "instrument-disabled")
       ;; TODO: how to detect the network reliably?
@@ -219,10 +218,6 @@
       :mobile/show-toolbar?                  false
       :mobile/show-recording-bar?            false
       :mobile/show-tabbar?                   false
-;;; Used to monitor mobile app status,
-;;; value spec:
-;;; {:is-active? bool, :timestamp int}
-      :mobile/app-state-change                 (atom nil)
 
       ;; plugin
       :plugin/enabled                        (and util/plugin-platform?
@@ -2229,12 +2224,6 @@ Similar to re-frame subscriptions"
                 (every? not-empty (vals agent-opts)))
       (str protocol "://" host ":" port))))
 
-(defn set-mobile-app-state-change
-  [is-active?]
-  (set-state! :mobile/app-state-change
-              {:is-active? is-active?
-               :timestamp (inst-ms (js/Date.))}))
-
 (defn get-sync-graph-by-id
   [graph-uuid]
   (when graph-uuid
@@ -2280,10 +2269,6 @@ Similar to re-frame subscriptions"
   []
   (:pdf/current @state))
 
-(defn nfs-user-granted?
-  [repo]
-  (get-in @state [:nfs/user-granted? repo]))
-
 (defn set-current-pdf!
   [inflated-file]
   (let [settle-file! #(set-state! :pdf/current inflated-file)]

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