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

Merge remote-tracking branch 'upstream/master' into whiteboards

Peng Xiao 3 лет назад
Родитель
Сommit
5e44055984

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

@@ -19,24 +19,16 @@ on:
         description: "Build from Git Ref(master)"
         required: true
         default: "master"
-      enable-file-sync:
-        description: 'Build with file sync support'
-        type: boolean
-        required: true
-        default: false
       enable-file-sync-production:
         description: 'File sync production mode'
         type: boolean
         required: true
-        default: false
+        default: true
   workflow_call:
     inputs:
       build-target:
         type: string
         required: true
-      enable-file-sync:
-        description: 'Build with file sync support'
-        type: boolean
       enable-file-sync-production:
         description: 'File sync production mode'
         type: boolean
@@ -111,7 +103,6 @@ jobs:
 
       - name: Set Build Environment Variables
         run: |
-          echo "ENABLE_FILE_SYNC=${{ inputs.enable-file-sync == 'true' || github.event.inputs.enable-file-sync == 'true' }}" >> $GITHUB_ENV
           echo "ENABLE_FILE_SYNC_PRODUCTION=${{ inputs.enable-file-sync-production == 'true' || github.event.inputs.enable-file-sync-production == 'true' }}" >> $GITHUB_ENV
 
       - name: Compile CLJS - android variant, use es6 instead of es-next

+ 1 - 8
.github/workflows/build-desktop-release.yml

@@ -28,16 +28,11 @@ on:
         type: boolean
         required: true
         default: true
-      enable-file-sync:
-        description: 'Build with file sync support'
-        type: boolean
-        required: true
-        default: false
       enable-file-sync-production:
         description: 'File sync production mode'
         type: boolean
         required: true
-        default: false
+        default: true
       enable-plugins:
         description: 'Build with plugin system support'
         type: boolean
@@ -128,7 +123,6 @@ jobs:
       - name: Set Build Environment Variables (only when workflow_dispath)
         if: ${{ github.event_name == 'workflow_dispatch' }}
         run: |
-          echo "ENABLE_FILE_SYNC=${{ github.event.inputs.enable-file-sync }}" >> $GITHUB_ENV
           echo "ENABLE_PLUGINS=${{ github.event.inputs.enable-plugins }}" >> $GITHUB_ENV
           echo "ENABLE_FILE_SYNC_PRODUCTION=${{ github.event.inputs.enable-file-sync-production }}" >> $GITHUB_ENV
 
@@ -432,7 +426,6 @@ jobs:
     if: ${{ github.event_name == 'schedule' || github.event.inputs.build-android == 'true' }}
     with:
       build-target: "${{ github.event.inputs.build-target }}"
-      enable-file-sync: "${{ github.event.inputs.enable-file-sync == 'true' }}"
       enable-file-sync-production: "${{ github.event.inputs.enable-file-sync-production == 'true' }}"
     secrets:
       ANDROID_KEYSTORE: "${{ secrets.ANDROID_KEYSTORE }}"

+ 2 - 2
android/app/build.gradle

@@ -6,8 +6,8 @@ android {
         applicationId "com.logseq.app"
         minSdkVersion rootProject.ext.minSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
-        versionCode 37
-        versionName "0.8.3"
+        versionCode 38
+        versionName "0.8.4"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         aaptOptions {
              // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.

+ 1 - 1
deps/graph-parser/src/logseq/graph_parser/extract.cljc

@@ -106,7 +106,7 @@
   [format ast properties file content {:keys [date-formatter page-name-order db] :as options}]
   (try
     (let [page (get-page-name file ast page-name-order)
-          [_original-page-name page-name _journal-day] (gp-block/convert-page-if-journal page date-formatter)
+          [page page-name _journal-day] (gp-block/convert-page-if-journal page date-formatter)
           blocks (->> (gp-block/extract-blocks ast content false format (dissoc options :page-name-order))
                       (gp-block/with-parent-and-left {:block/name page-name}))
           ref-pages (atom #{})

+ 2 - 0
ios/App/App.xcodeproj/project.pbxproj

@@ -543,6 +543,7 @@
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = K378MFWK59;
+				ENABLE_BITCODE = NO;
 				INFOPLIST_FILE = App/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -569,6 +570,7 @@
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = K378MFWK59;
+				ENABLE_BITCODE = NO;
 				INFOPLIST_FILE = App/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";

+ 4 - 0
public/index.html

@@ -2,6 +2,10 @@
 <html>
 <head>
   <meta charset="utf-8">
+  <!-- Handle iOS indexedDB preloading. Ref: https://developer.apple.com/forums/thread/681201 -->
+  <script type="text/javascript">
+    window.indexedDB;
+  </script>
   <meta content="minimum-scale=1, initial-scale=1, maximum-scale=1, width=device-width, shrink-to-fit=no" name="viewport">
   <link rel="stylesheet" href="/static/css/tabler-icons.min.css">
   <link href="/static/css/style.css" rel="stylesheet" type="text/css">

+ 2 - 2
resources/package.json

@@ -1,6 +1,6 @@
 {
   "name": "Logseq",
-  "version": "0.8.3",
+  "version": "0.8.4",
   "main": "electron.js",
   "author": "Logseq",
   "license": "AGPL-3.0",
@@ -36,7 +36,7 @@
     "https-proxy-agent": "5.0.0",
     "@sentry/electron": "2.5.1",
     "posthog-js": "1.10.2",
-    "@logseq/rsapi": "0.0.33",
+    "@logseq/rsapi": "0.0.35",
     "electron-deeplink": "1.0.10",
     "abort-controller": "3.0.0"
   },

+ 0 - 1
shadow-cljs.edn

@@ -38,7 +38,6 @@
                            :warnings           {:fn-deprecated false
                                                 :redef false}}
         :closure-defines  {goog.debug.LOGGING_ENABLED       true
-                           frontend.config/ENABLE-FILE-SYNC #shadow/env ["ENABLE_FILE_SYNC" :as :bool :default false]
                            frontend.config/ENABLE-PLUGINS   #shadow/env ["ENABLE_PLUGINS"   :as :bool :default true]
                            frontend.config/ENABLE-FILE-SYNC-PRODUCTION #shadow/env ["ENABLE_FILE_SYNC_PRODUCTION" :as :bool :default false]}
 

+ 4 - 1
src/electron/electron/core.cljs

@@ -206,7 +206,10 @@
                                             (p/let [graph-name (get-graph-name (state/get-graph-path))
                                                     _ (handler/broadcast-persist-graph! graph-name)]
                                               (handler/open-new-window!)))
-                                   :accelerator "CommandOrControl+N"}
+                                   :accelerator (if mac? 
+                                                  "CommandOrControl+N"
+                                                  ;; Avoid conflict with `Control+N` shortcut to move down in the text editor on Windows/Linux
+                                                  "Shift+CommandOrControl+N")}
                                   (if mac?
                                     {:role "close"}
                                     {:role "quit"})]}

+ 2 - 1
src/main/electron/listener.cljs

@@ -5,6 +5,7 @@
             [frontend.handler.route :as route-handler]
             [frontend.handler.editor :as editor-handler]
             [frontend.handler.ui :as ui-handler]
+            [frontend.handler.file-sync :as file-sync-handler]
             [frontend.config :as config]
             [clojure.string :as string]
             [cljs-bean.core :as bean]
@@ -45,7 +46,7 @@
                      (fn [data]
                        (let [{:keys [type payload]} (bean/->clj data)]
                          (watcher-handler/handle-changed! type payload)
-                         (when config/enable-file-sync?
+                         (when (file-sync-handler/enable-sync?)
                            (sync/file-watch-handler type payload)))))
 
   (js/window.apis.on "notification"

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

@@ -529,8 +529,8 @@
                (str " page-property-key block-property"))
       :data-ref page-name
       :on-mouse-down (fn [e] (open-page-ref e page-name redirect-page-name page-name-in-block contents-page? whiteboard-page?))
-      :on-key-up (fn [e] ((when (= (.-key e) "Enter")
-                           (open-page-ref e page-name redirect-page-name page-name-in-block contents-page? whiteboard-page?))))}
+      :on-key-up (fn [e] (when (and e (= (.-key e) "Enter"))
+                           (open-page-ref e page-name redirect-page-name page-name-in-block contents-page? whiteboard-page?)))}
 
      (if (and (coll? children) (seq children))
        (for [child children]

+ 0 - 1
src/main/frontend/components/file_sync.cljs

@@ -230,7 +230,6 @@
       [:div.cp__file-sync-indicator
        (when (and (not config/publishing?)
                   (user-handler/logged-in?))
-
          (ui/dropdown-with-links
           (fn [{:keys [toggle-fn]}]
             (if (not off?)

+ 9 - 6
src/main/frontend/components/header.cljs

@@ -34,10 +34,12 @@
 (rum/defc login < rum/reactive
   []
   (let [_ (state/sub :auth/id-token)
-        loading? (state/sub [:ui/loading? :login])]
+        loading? (state/sub [:ui/loading? :login])
+        sync-enabled? (file-sync-handler/enable-sync?)
+        logged? (user-handler/logged-in?)]
     (when-not (or config/publishing?
-                  (user-handler/logged-in?)
-                  (not (state/enable-sync?)))
+                  logged?
+                  (not sync-enabled?))
       [:a.button.text-sm.font-medium.block {:on-click #(js/window.open config/LOGIN-URL)}
        [:span (t :login)]
        (when loading?
@@ -155,7 +157,8 @@
                                                  (state/set-left-sidebar-open!
                                                   (not (:ui/left-sidebar-open? @state/state))))})
         custom-home-page? (and (state/custom-home-page?)
-                               (= (state/sub-default-home-page) (state/get-current-page)))]
+                               (= (state/sub-default-home-page) (state/get-current-page)))
+        sync-enabled? (file-sync-handler/enable-sync?)]
     [:div.cp__header#head
      {:class           (util/classnames [{:electron-mac   electron-mac?
                                           :native-ios     (mobile-util/native-ios?)
@@ -187,7 +190,7 @@
              (ui/icon "chevron-left" {:style {:fontSize 25}})])))]
 
      [:div.r.flex
-      (when (and (not file-sync-handler/hiding-login&file-sync)
+      (when (and sync-enabled?
                  current-repo
                  (not (config/demo-graph? current-repo))
                  (user-handler/alpha-user?))
@@ -197,7 +200,7 @@
                  (not custom-home-page?))
         (home-button))
 
-      (when-not file-sync-handler/hiding-login&file-sync
+      (when sync-enabled?
         (login))
 
       (when plugin-handler/lsp-enabled?

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

@@ -74,7 +74,7 @@
           file-path (when (util/electron?) (page-handler/get-page-file-path))
           _ (state/sub :auth/id-token)
           file-sync-graph-uuid (and (user-handler/logged-in?)
-                                    (not file-sync-handler/hiding-login&file-sync)
+                                    (file-sync-handler/enable-sync?)
                                     (file-sync-handler/get-current-graph-uuid))]
       (when (and page (not block?))
         (->>

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

@@ -185,14 +185,20 @@
                       :options (cond->
                                 {:on-click
                                  (fn []
-                                   (state/pub-event! [:graph/ask-for-re-index *multiple-windows?]))})}]
+                                   (state/pub-event! [:graph/ask-for-re-index *multiple-windows?]))})}
+        new-window-link (when (and (util/electron?)
+                                   ;; New Window button in menu bar of macOS is available.
+                                   (not util/mac?))
+                          {:title        (t :open-new-window)
+                           :options {:on-click #(state/pub-event! [:graph/open-new-window nil])}})]
     (->>
      (concat repo-links
              [(when (seq repo-links) {:hr true})
               {:title (t :new-graph) :options {:on-click #(page-handler/ls-dir-files! shortcut/refresh!)}}
               {:title (t :all-graphs) :options {:href (rfe/href :repos)}}
               refresh-link
-              reindex-link])
+              reindex-link
+              new-window-link])
      (remove nil?))))
 
 (rum/defcs repos-dropdown < rum/reactive

+ 18 - 6
src/main/frontend/components/sidebar.cljs

@@ -214,7 +214,7 @@
     href :href}]
   [:div
    {:class class}
-   [:a.item.group.flex.items-center.px-2.py-2.text-sm.font-medium.rounded-md
+   [:a.item.group.flex.items-center.text-sm.font-medium.rounded-md
     {:on-click on-click-handler
      :class (when active "active")
      :href href}
@@ -224,7 +224,14 @@
 (rum/defc sidebar-nav
   [route-match close-modal-fn left-sidebar-open? srs-open?]
   (let [default-home (get-default-home-if-valid)
-        route-name (get-in route-match [:data :name])]
+        route-name (get-in route-match [:data :name])
+        on-contents-scroll #(when-let [^js el (.-target %)]
+                              (let [top (.-scrollTop el)
+                                    cls (.-classList el)
+                                    cls' "is-scrolled"]
+                                (if (> top 2)
+                                  (.add cls cls')
+                                  (.remove cls cls'))))]
 
     [:div.left-sidebar-inner.flex-1.flex.flex-col.min-h-0
      {:on-click #(when-let [^js target (and (util/sm-breakpoint?) (.-target %))]
@@ -232,14 +239,15 @@
                                [".favorites .bd" ".recent .bd" ".dropdown-wrapper" ".nav-header"])
                      (close-modal-fn)))}
 
-     [:div.flex.flex-col.pb-4.wrap.gap-4.relative
+     [:div.flex.flex-col.wrap.gap-1.relative
       (when (mobile-util/native-platform?)
         [:div.fake-bar.absolute
          [:button
           {:on-click state/toggle-left-sidebar!}
           (ui/icon "menu-2" {:style {:fontSize ui/icon-size}})]])
 
-      [:nav.px-4.flex.flex-col.gap-1 {:aria-label "Navigation menu"}
+      [:nav.px-4.flex.flex-col.gap-1
+       {:aria-label "Navigation menu"}
        (repo/repos-dropdown)
 
        [:div.nav-header.flex.gap-1.flex-col
@@ -291,9 +299,13 @@
             :icon  "whiteboard"
             :icon-extention? true}))]]
 
-      (when left-sidebar-open? (favorites t))
+      [:div.nav-contents-container.flex.flex-col.gap-1.pt-1
+       {:on-scroll on-contents-scroll}
+       (when left-sidebar-open?
+         (favorites t))
 
-      (when (and left-sidebar-open? (not config/publishing?)) (recent-pages t))
+       (when (and left-sidebar-open? (not config/publishing?))
+         (recent-pages t))]
 
       [:footer.px-2 {:class "new-page"}
        (when-not config/publishing?

+ 14 - 2
src/main/frontend/components/sidebar.css

@@ -93,7 +93,6 @@
   > .wrap {
     height: calc(100vh - var(--ls-headbar-inner-top-padding) - 50px);
     margin-top: 40px;
-    padding-bottom: 60px;
     width: 100%;
 
     > .fake-bar {
@@ -128,6 +127,8 @@
   }
 
   a.item {
+    @apply px-2 py-2 sm:py-1.5;
+    
     user-select: none;
     transition: background-color .3s;
 
@@ -151,6 +152,17 @@
     }
   }
 
+  .nav-contents-container {
+    @apply h-full flex-grow-0 flex-grow-0
+    overflow-x-hidden overflow-y-auto;
+
+    padding-bottom: 60px;
+
+    &.is-scrolled {
+      border-top: 1px solid var(--ls-tertiary-border-color);
+    }
+  }
+
   .nav-content-item {
     &-inner {
       border-radius: 8px;
@@ -292,7 +304,7 @@
   top: var(--ls-headbar-inner-top-padding);
   left: 0;
   z-index: var(--ls-z-index-level-5);
-  transition: width 1.2s;
+  transition: width .5s;
 
   a {
     color: var(--ls-primary-text-color);

+ 5 - 13
src/main/frontend/config.cljs

@@ -1,12 +1,12 @@
 (ns frontend.config
   (:require [clojure.set :as set]
             [clojure.string :as string]
+            [frontend.mobile.util :as mobile-util]
             [frontend.state :as state]
             [frontend.util :as util]
-            [shadow.resource :as rc]
-            [logseq.graph-parser.util :as gp-util]
             [logseq.graph-parser.config :as gp-config]
-            [frontend.mobile.util :as mobile-util]))
+            [logseq.graph-parser.util :as gp-util]
+            [shadow.resource :as rc]))
 
 (goog-define DEV-RELEASE false)
 (defonce dev-release? DEV-RELEASE)
@@ -21,13 +21,6 @@
 
 (goog-define ENABLE-FILE-SYNC-PRODUCTION false)
 
-;; prod env
-;; (goog-define FILE-SYNC-PROD? true)
-;; (goog-define LOGIN-URL
-;;              "https://logseq-prod.auth.us-east-1.amazoncognito.com/login?client_id=3c7np6bjtb4r1k1bi9i049ops5&response_type=code&scope=email+openid+phone&redirect_uri=logseq%3A%2F%2Fauth-callback")
-;; (goog-define API-DOMAIN "api-prod.logseq.com")
-;; (goog-define WS-URL "wss://b2rp13onu2.execute-api.us-east-1.amazonaws.com/production?graphuuid=%s")
-
 (if ENABLE-FILE-SYNC-PRODUCTION
   (do (def FILE-SYNC-PROD? true)
       (def LOGIN-URL
@@ -42,8 +35,6 @@
       (def WS-URL "wss://ws-dev.logseq.com/file-sync?graphuuid=%s")))
 
 ;; feature flags
-(goog-define ENABLE-FILE-SYNC false)
-(defonce enable-file-sync? (or ENABLE-FILE-SYNC dev?)) ;; always enable file-sync when dev
 
 (goog-define ENABLE-PLUGINS true)
 (defonce enable-plugins? ENABLE-PLUGINS)
@@ -257,10 +248,11 @@
 (defonce local-repo "local")
 
 (defn demo-graph?
+  "Demo graph or nil graph?"
   ([]
    (demo-graph? (state/get-current-repo)))
   ([graph]
-   (= graph local-repo)))
+   (or (nil? graph) (= graph local-repo))))
 
 (defonce recycle-dir ".recycle")
 (def config-file "config.edn")

+ 14 - 0
src/main/frontend/dicts.cljc

@@ -210,6 +210,7 @@
         :re-index-detail "Rebuild the graph"
         :re-index-multiple-windows-warning "You need to close the other windows before re-index this graph."
         :re-index-discard-unsaved-changes-warning "Re-index will discard the current graph, and then processes all the files again as they are currently stored on disk. You will lose unsaved changes and it might take a while. Continue?"
+        :open-new-window "New window"
         :sync-from-local-files "Refresh"
         :sync-from-local-files-detail "Import changes from local files"
         :sync-from-local-changes-detected "Refresh detects and processes files modified on your disk and diverged from the actual Logseq page content. Continue?"
@@ -531,6 +532,7 @@
         :graph-view "Graph anzeigen"
         :more "Mehr"
         :no "Nein"
+        :open-new-window "Neues Fenster"
         :page-search "In der aktuellen Seite suchen"
         :plugins "Plugins"
         :re-index-detail "Graph neu aufbauen"
@@ -676,6 +678,7 @@
         :on "Aan"
         :open "Open"
         :open-a-directory "Open een lokale map"
+        :open-new-window "Nieuwe venster"
         :or "of"
         :page-search "Zoek in de huidige pagina"
         :parsing-files "Bestanden analyseren"
@@ -1312,6 +1315,7 @@
            :re-index-detail "重新建立索引"
            :re-index-multiple-windows-warning "在重建当前图谱索引前,你需要先关闭其它窗口"
            :re-index-discard-unsaved-changes-warning "重建索引将丢弃当前图谱,之后重新导入保存在磁盘上的所有文件。此操作将丢弃未保存的更改,同时可能需要一段时间。是否继续?"
+           :open-new-window "打开新窗口"
            :sync-from-local-files "刷新(读取本地最新文件)"
            :sync-from-local-files-detail "读取本地最新文件"
            :sync-from-local-changes-detected "执行刷新操作将会导入磁盘上修改过的、或是与实际Logseq页面内容不同的文件。是否继续?"
@@ -1941,6 +1945,7 @@
         :re-index-detail "Reconstruir el grafo"
         :re-index-multiple-windows-warning "Debe cerrar las otras ventanas antes de reindexar este grafo."
         :re-index-discard-unsaved-changes-warning "Al reindexar se descartará el grafo actual y se procesarán nuevamente todos los archivos según como están actualmente almacenados en disco. Perderá los cambios no guardados y puede tardar un poco. ¿Continuar?"
+        :open-new-window "Nueva ventana"
         :sync-from-local-files "Refrescar"
         :sync-from-local-files-detail "Importar cambios de los archivos locales"
         :sync-from-local-changes-detected "Refrescar detecta y procesa los archivos modificados en su disco que difieren del contenido actual de la página en Logseq. ¿Continuar?"
@@ -2245,6 +2250,7 @@
            :delete "Slett"
            :re-index "Indekser på nytt"
            :re-index-detail "Bygg grafen på nytt"
+           :open-new-window "Nytt vindu"
            :sync-from-local-files "Oppfrisk"
            :sync-from-local-files-detail "Importer endringer fra lokale filer"
            :unlink "koble fra"
@@ -2602,6 +2608,7 @@
            :re-index-detail "Re-indexar gráfico"
            :open "Abrir"
            :open-a-directory "Abrir uma pasta local"
+           :open-new-window "Nova janela"
            :user/delete-account "Excluir conta"
            :user/delete-your-account "Apague a sua conta"
            :user/delete-account-notice "Todas as suas páginas publicadas em logseq.com serão apagadas."
@@ -2876,6 +2883,7 @@
            :delete "Apagar"
            :re-index "Re-indexar"
            :re-index-detail "Reconstruir o grafo"
+           :open-new-window "Nova Janela"
            :sync-from-local-files "Atualizar"
            :sync-from-local-files-detail "Importar alterações de ficheiros locais"
            :unlink "remover ligação"
@@ -3222,6 +3230,7 @@
         :sync-from-local-files "Обновить"
         :sync-from-local-changes-detected "При обновлении будут найдены и обработаны файлы, изменённые на диске и отличающиеся от текущего содержимого страниц Logseq. Продолжить?"
         :sync-from-local-files-detail "Импортировать изменения из локальных файлов"
+        :open-new-window "Новое окно"
         :unlink "отвязать"
         :search/publishing "Искать"
         :search "Искать или создать страницу"
@@ -3543,6 +3552,7 @@
         :re-index-detail "グラフ再構築"
         :re-index-multiple-windows-warning "このグラフのインデックスを再構築する前に、Logseq で開いている他のウィンドウを閉じる必要があります。"
         :re-index-discard-unsaved-changes-warning "インデックスの再構築は現在のグラフをいったん破棄し、現在ディスク上にある全てのファイルから再構築します。未保存の内容は失われます。また、少し時間がかかります。実行してもよいですか?"
+        :open-new-window "新規ウィンドウ"
         :sync-from-local-files "再表示"
         :sync-from-local-files-detail "ローカルファイルの変更点をインポート"
         :sync-from-local-changes-detected " 再表示は、ディスク上で変更されて Logseq 上のページと内容が変わってしまったファイルを検出し、読み込みます。実行してもよいですか?"
@@ -3863,6 +3873,7 @@
         :re-index-detail "Ricostruisci il grafo"
         :re-index-multiple-windows-warning "È necessario chiudere le altre finestre prima di reindicizzare questo grafo."
         :re-index-discard-unsaved-changes-warning "La reindicizzazione elimina il grafo corrente, quindi elabora nuovamente tutti i file poiché sono attualmente archiviati su disco. Perderai le modifiche non salvate e potrebbe volerci del tempo. Continuare?"
+        :open-new-window "Nuova finestra"
         :sync-from-local-files "Ricarica"
         :sync-from-local-files-detail "Importa cambiamenti da un file locale"
         :sync-from-local-changes-detected "Il ricaricamento rileva ed elabora i file modificati sul disco e divergenti dal contenuto effettivo della pagina Logseq. Continuare?"
@@ -4194,6 +4205,7 @@
         :re-index-detail "Grafiği yeniden oluştur"
         :re-index-multiple-windows-warning "Bu grafik için yeniden dizin oluşturmadan önce diğer pencereleri kapatmanız gerekiyor."
         :re-index-discard-unsaved-changes-warning "Yeniden dizin oluşturmak mevcut grafiği siler ve ardından tüm dosyaları o anda diskte depolandıkları şekilde yeniden işler. Kaydedilmemiş değişiklikleri kaybedeceksiniz ve bu biraz zaman alabilir. Devam edilsin mi?"
+        :open-new-window "Yeni pencere"
         :sync-from-local-files "Yenile"
         :sync-from-local-files-detail "Yerel dosyalardan değişiklikleri içeri aktarın"
         :sync-from-local-changes-detected "Yenile, diskinizde değiştirilen ve gerçek Logseq sayfa içeriğinden ayrılan dosyaları algılar ve işler. Devam edilsin mi?"
@@ -4519,6 +4531,7 @@
         :re-index-detail "그래프 다시 빌드"
         :re-index-multiple-windows-warning "그래프를 다시 인덱싱 전 다른 윈도우를 모두 닫아야 합니다."
         :re-index-discard-unsaved-changes-warning "인덱싱을 다시 하게 되면 현재 나타나 있는 그래프가 사라지며, 하드 디스크에 저장된 파일대로 그래프를 재구성하게 됩니다. 저장되지 않은 변경사항들이 삭제되며 약간의 시간이 걸릴 수 있습니다. 계속하시겠습니까?"
+        :open-new-window "새 창"
         :sync-from-local-files "새로고침"
         :sync-from-local-files-detail "로컬 파일로부터 변경 사항 불러오기"
         :sync-from-local-changes-detected "그래프를 새로 고치면 로컬 디스크에서 Logseq과는 다르게 변경된 파일들을 감지하고 처리합니다. 계속하시겠습니까?"
@@ -4844,6 +4857,7 @@
         :re-index-detail "Przebuduj graf od nowa"
         :re-index-multiple-windows-warning "Musisz zamknąć inne okna zanim rozpoczniesz proces ponownej indeksacji"
         :re-index-discard-unsaved-changes-warning "Ponowna indeksacja odrzuci zmiany w obecnym grafie a następnie przejdzie po wszystkich plikach zapisanych na dysku. Utracisz wszystkie niezapisane zmiany a sam proces może trochę potrwać. Kontynuować?"
+        :open-new-window "Nowe okno"
         :sync-from-local-files "Odśwież"
         :sync-from-local-files-detail "Importuj zmiany z lokalnych plików"
         :sync-from-local-changes-detected "Funkcja Odśwież wykrywa i procesuje pliki zmianione na dysku. Wszystkie pliki różniące się od tych w Logseq zostaną ponownie wczytane. Kontynuować?"

+ 7 - 2
src/main/frontend/fs/capacitor_fs.cljs

@@ -220,6 +220,11 @@
 
 (defn get-file-path [dir path]
   (let [dir (some-> dir (string/replace #"/+$" ""))
+        dir (if (string/starts-with? dir "/")
+              (do
+                (js/console.trace "WARN: detect absolute path, use URL instead")
+                (str "file://" (js/encodeURI dir)))
+              dir)
         path (some-> path (string/replace #"^/+" ""))]
     (cond (nil? path)
           dir
@@ -274,8 +279,8 @@
       result))
   (readdir [_this dir]                  ; recursive
     (let [dir (if-not (string/starts-with? dir "file://")
-                 (str "file://" dir)
-                 dir)]
+                (str "file://" dir)
+                dir)]
       (readdir dir)))
   (unlink! [this repo path _opts]
     (p/let [path (get-file-path nil path)

+ 36 - 4
src/main/frontend/handler.cljs

@@ -1,5 +1,6 @@
 (ns frontend.handler
   (:require [cljs.reader :refer [read-string]]
+            [clojure.string :as string]
             [electron.ipc :as ipc]
             [electron.listener :as el]
             [frontend.components.block :as block]
@@ -8,9 +9,10 @@
             [frontend.components.reference :as reference]
             [frontend.components.whiteboard :as whiteboard]
             [frontend.config :as config]
-            [frontend.context.i18n :as i18n]
+            [frontend.context.i18n :as i18n :refer [t]]
             [frontend.db :as db]
             [frontend.db.conn :as conn]
+            [frontend.db.persist :as db-persist]
             [frontend.db.react :as react]
             [frontend.error :as error]
             [frontend.extensions.srs :as srs]
@@ -30,6 +32,7 @@
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.state :as state]
             [frontend.storage :as storage]
+            [frontend.ui :as ui]
             [frontend.util :as util]
             [frontend.util.persist-var :as persist-var]
             [goog.object :as gobj]
@@ -157,6 +160,31 @@
               (js/window.location.reload)))
      2000)))
 
+;; FIXME: Another get-repos implementation at src\main\frontend\handler\repo.cljs
+(defn- get-repos
+  []
+  (p/let [nfs-dbs (db-persist/get-all-graphs)]
+    ;; TODO: Better IndexDB migration handling
+    (cond
+      (and (mobile-util/native-platform?)
+           (some #(or (string/includes? % " ")
+                      (string/includes? % "logseq_local_/")) nfs-dbs))
+      (do (notification/show! ["DB version is not compatible, please clear cache then re-add your graph back."
+                               (ui/button
+                                (t :settings-page/clear-cache)
+                                :class    "ui__modal-enter"
+                                :class    "text-sm p-1"
+                                :on-click clear-cache!)] :error false)
+          {:url config/local-repo
+           :example? true})
+
+      (seq nfs-dbs)
+      (map (fn [db] {:url db :nfs? true}) nfs-dbs)
+
+      :else
+      [{:url config/local-repo
+        :example? true}])))
+
 (defn- register-components-fns!
   []
   (state/set-page-blocks-cp! page/page-blocks-cp)
@@ -188,9 +216,13 @@
 
     (events/run!)
 
-    (p/let [repos (repo-handler/get-repos)]
-      (state/set-repos! repos)
-      (restore-and-setup! repos db-schema))
+    (-> (p/let [repos (get-repos)]
+          (state/set-repos! repos)
+          (restore-and-setup! repos db-schema))
+        (p/catch (fn [e]
+                   (js/console.error "Error while restoring repos: " e)))
+        (p/finally (fn []
+                     (state/set-db-restoring! false))))
     (when (mobile-util/native-platform?)
       (p/do! (mobile-util/hide-splash)))
 

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

@@ -515,7 +515,7 @@
         payload (-> event
                     (js->clj :keywordize-keys true))]
     (fs-watcher/handle-changed! type payload)
-    (when config/enable-file-sync?
+    (when (file-sync-handler/enable-sync?)
      (sync/file-watch-handler type payload))))
 
 (defmethod handle :rebuild-slash-commands-list [[_]]

+ 6 - 2
src/main/frontend/handler/file_sync.cljs

@@ -14,9 +14,13 @@
 
 (def *beta-unavailable? (volatile! false))
 
-(def hiding-login&file-sync (not config/enable-file-sync?))
 (def refresh-file-sync-component (atom false))
 
+(defn enable-sync?
+  []
+  (or (state/enable-sync?)
+      config/dev?))
+
 (defn current-graph-sync-on?
   []
   (when-let [sync-state (state/sub [:file-sync/sync-state (state/get-current-repo)])]
@@ -196,4 +200,4 @@
 
 (defn reset-user-state! []
   (vreset! *beta-unavailable? false)
-  (state/set-state! :file-sync/onboarding-state nil))
+  (state/set-state! :file-sync/onboarding-state nil))

+ 75 - 73
src/main/frontend/handler/web/nfs.cljs

@@ -2,28 +2,28 @@
   "The File System Access API, https://web.dev/file-system-access/."
   (:require ["/frontend/utils" :as utils]
             [cljs-bean.core :as bean]
+            [clojure.core.async :as async]
             [clojure.set :as set]
             [clojure.string :as string]
             [frontend.config :as config]
             [frontend.db :as db]
+            [frontend.encrypt :as encrypt]
             [frontend.fs :as fs]
             [frontend.fs.nfs :as nfs]
             [frontend.handler.common :as common-handler]
             [frontend.handler.repo :as repo-handler]
             [frontend.handler.route :as route-handler]
             [frontend.idb :as idb]
+            [frontend.mobile.util :as mobile-util]
             [frontend.search :as search]
             [frontend.state :as state]
             [frontend.util :as util]
             [frontend.util.fs :as util-fs]
             [goog.object :as gobj]
             [lambdaisland.glogi :as log]
-            [promesa.core :as p]
-            [frontend.mobile.util :as mobile-util]
-            [logseq.graph-parser.util :as gp-util]
             [logseq.graph-parser.config :as gp-config]
-            [clojure.core.async :as async]
-            [frontend.encrypt :as encrypt]))
+            [logseq.graph-parser.util :as gp-util]
+            [promesa.core :as p]))
 
 (defn remove-ignore-files
   [files dir-name nfs?]
@@ -118,17 +118,17 @@
     (set-files-aux! handles)))
 
 ;; TODO: extract code for `ls-dir-files` and `reload-dir!`
-(defn ls-dir-files-with-handler!
+(defn ^:large-vars/cleanup-todo ls-dir-files-with-handler!
   ([ok-handler] (ls-dir-files-with-handler! ok-handler nil))
   ([ok-handler {:keys [empty-dir?-or-pred dir-result-fn]}]
-  (let [path-handles (atom {})
-        electron? (util/electron?)
-        mobile-native? (mobile-util/native-platform?)
-        nfs? (and (not electron?)
-                  (not mobile-native?))
-        *repo (atom nil)]
+   (let [path-handles (atom {})
+         electron? (util/electron?)
+         mobile-native? (mobile-util/native-platform?)
+         nfs? (and (not electron?)
+                   (not mobile-native?))
+         *repo (atom nil)]
     ;; TODO: add ext filter to avoid loading .git or other ignored file handlers
-    (->
+     (->
       (p/let [result (if (fn? dir-result-fn)
                        (dir-result-fn {:path-handles path-handles :nfs? nfs?})
                        (fs/open-dir (fn [path handle]
@@ -150,64 +150,64 @@
               _ (state/set-loading-files! repo true)
               _ (when-not (or (state/home?) (state/setups-picker?))
                   (route-handler/redirect-to-home! false))]
-             (reset! *repo repo)
-             (when-not (string/blank? dir-name)
-               (p/let [root-handle-path (str config/local-handle-prefix dir-name)
-                       _ (when nfs?
-                           (idb/set-item! root-handle-path root-handle)
-                           (nfs/add-nfs-file-handle! root-handle-path root-handle))
-                       result (nth result 1)
-                       files (-> (->db-files mobile-native? electron? dir-name result)
-                                 (remove-ignore-files dir-name nfs?))
-                       _ (when nfs?
-                           (let [file-paths (set (map :file/path files))]
-                             (swap! path-handles (fn [handles]
-                                                   (->> handles
-                                                        (filter (fn [[path _handle]]
-                                                                  (or
-                                                                    (contains? file-paths
-                                                                               (string/replace-first path (str dir-name "/") ""))
-                                                                    (let [last-part (last (string/split path "/"))]
-                                                                      (contains? #{config/app-name
-                                                                                   gp-config/default-draw-directory
-                                                                                   (config/get-whiteboards-directory)
-                                                                                   (config/get-journals-directory)
-                                                                                   (config/get-pages-directory)}
-                                                                                 last-part)))))
-                                                        (into {})))))
+        (reset! *repo repo)
+        (when-not (string/blank? dir-name)
+          (p/let [root-handle-path (str config/local-handle-prefix dir-name)
+                  _ (when nfs?
+                      (idb/set-item! root-handle-path root-handle)
+                      (nfs/add-nfs-file-handle! root-handle-path root-handle))
+                  result (nth result 1)
+                  files (-> (->db-files mobile-native? electron? dir-name result)
+                            (remove-ignore-files dir-name nfs?))
+                  _ (when nfs?
+                      (let [file-paths (set (map :file/path files))]
+                        (swap! path-handles (fn [handles]
+                                              (->> handles
+                                                   (filter (fn [[path _handle]]
+                                                             (or
+                                                              (contains? file-paths
+                                                                         (string/replace-first path (str dir-name "/") ""))
+                                                              (let [last-part (last (string/split path "/"))]
+                                                                (contains? #{config/app-name
+                                                                             gp-config/default-draw-directory
+                                                                             (config/get-journals-directory)
+                                                                             (config/get-whiteboards-directory)
+                                                                             (config/get-pages-directory)}
+                                                                           last-part)))))
+                                                   (into {})))))
 
-                           (set-files! @path-handles))
-                       markup-files (filter-markup-and-built-in-files files)]
-                      (-> (p/all (map (fn [file]
-                                        (p/let [content (if nfs?
-                                                          (.text (:file/file file))
-                                                          (:file/content file))
-                                                content (encrypt/decrypt content)]
-                                               (assoc file :file/content content))) markup-files))
-                          (p/then (fn [result]
-                                    (p/let [files (map #(dissoc % :file/file) result)
-                                            graph-txid-meta (util-fs/read-graph-txid-info dir-name)
-                                            graph-uuid (and (vector? graph-txid-meta) (second graph-txid-meta))]
-                                      (if-let [exists-graph (state/get-sync-graph-by-uuid graph-uuid)]
-                                        (state/pub-event!
-                                         [:notification/show
-                                          {:content (str "This graph already exists in \"" (:root exists-graph) "\"")
-                                           :status :warning}])
-                                        (do
-                                          (repo-handler/start-repo-db-if-not-exists! repo)
-                                          (async/go
-                                            (let [_finished? (async/<! (repo-handler/load-repo-to-db! repo
-                                                                                                      {:new-graph?   true
-                                                                                                       :empty-graph? (nil? (seq markup-files))
-                                                                                                       :nfs-files    files}))]
-                                              (state/add-repo! {:url repo :nfs? true})
-                                              (state/set-loading-files! repo false)
-                                              (when ok-handler (ok-handler {:url repo}))
-                                              (fs/watch-dir! dir-name)
-                                              (db/persist-if-idle! repo))))))))
-                          (p/catch (fn [error]
-                                     (log/error :nfs/load-files-error repo)
-                                     (log/error :exception error)))))))
+                      (set-files! @path-handles))
+                  markup-files (filter-markup-and-built-in-files files)]
+            (-> (p/all (map (fn [file]
+                              (p/let [content (if nfs?
+                                                (.text (:file/file file))
+                                                (:file/content file))
+                                      content (encrypt/decrypt content)]
+                                (assoc file :file/content content))) markup-files))
+                (p/then (fn [result]
+                          (p/let [files (map #(dissoc % :file/file) result)
+                                  graph-txid-meta (util-fs/read-graph-txid-info dir-name)
+                                  graph-uuid (and (vector? graph-txid-meta) (second graph-txid-meta))]
+                            (if-let [exists-graph (state/get-sync-graph-by-uuid graph-uuid)]
+                              (state/pub-event!
+                               [:notification/show
+                                {:content (str "This graph already exists in \"" (:root exists-graph) "\"")
+                                 :status :warning}])
+                              (do
+                                (repo-handler/start-repo-db-if-not-exists! repo)
+                                (async/go
+                                  (let [_finished? (async/<! (repo-handler/load-repo-to-db! repo
+                                                                                            {:new-graph?   true
+                                                                                             :empty-graph? (nil? (seq markup-files))
+                                                                                             :nfs-files    files}))]
+                                    (state/add-repo! {:url repo :nfs? true})
+                                    (state/set-loading-files! repo false)
+                                    (when ok-handler (ok-handler {:url repo}))
+                                    (fs/watch-dir! dir-name)
+                                    (db/persist-if-idle! repo))))))))
+                (p/catch (fn [error]
+                           (log/error :nfs/load-files-error repo)
+                           (log/error :exception error)))))))
       (p/catch (fn [error]
                  (log/error :exception error)
                  (when mobile-native?
@@ -215,8 +215,10 @@
                     [:notification/show {:content (str error) :status :error}]))
                  (when (contains? #{"AbortError" "Error"} (gobj/get error "name"))
                    (when @*repo (state/set-loading-files! @*repo false))
-                   (throw error)
-                   )))))))
+                   (throw error))))
+      (p/finally
+        (fn []
+          (state/set-loading-files! @*repo false)))))))
 
 (defn ls-dir-files-with-path!
   ([path] (ls-dir-files-with-path! path nil))
@@ -228,7 +230,7 @@
                                                 (fn [path handle]
                                                   (when nfs?
                                                     (swap! path-handles assoc path handle))))]
-                                 [path files-result])))]
+                            [path files-result])))]
      (ls-dir-files-with-handler!
       (:ok-handler opts)
       (merge {:dir-result-fn dir-result-fn} opts)))))

+ 1 - 1
src/main/frontend/idbkv.js

@@ -16,7 +16,7 @@ class Store {
         return;
       }
       this._dbp = new Promise((resolve, reject) => {
-        const openreq = indexedDB.open(this._dbName, this._version);
+        const openreq = window.indexedDB.open(this._dbName, this._version);
         openreq.onerror = () => reject(openreq.error);
         openreq.onsuccess = () => resolve(openreq.result);
         // First time setup: create an empty object store

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

@@ -402,7 +402,7 @@
 
 (defn user-groups
   []
-  (set (get-in @state [:user/info :UserGroups])))
+  (set (sub [:user/info :UserGroups])))
 
 (defn enable-sync?
   []

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

@@ -1,3 +1,3 @@
 (ns frontend.version)
 
-(defonce version "0.8.3")
+(defonce version "0.8.4")