Browse Source

Merge remote-tracking branch 'origin/master' into fix/priority-style-issues

# Conflicts:
#	resources/css/common.css
#	src/main/frontend/components/block.cljs
#	src/main/frontend/components/block.css
#	src/main/frontend/components/right_sidebar.cljs
charlie 4 years ago
parent
commit
0492b28235

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

@@ -674,7 +674,7 @@
                              (page-handler/rename-when-alter-title-property! old-page-name path format content value)
                              (file/alter-file (state/get-current-repo) path (string/trim value)
                                               {:re-render-root? true}))))
-                       (when-not (contains? #{:insert :indent-outdent} (state/get-editor-op))
+                       (when-not (contains? #{:insert :indent-outdent :auto-save} (state/get-editor-op))
                          (editor-handler/save-block! (get-state state) value))))
                    state)}
   [state {:keys [on-hide dummy? node format block block-parent-id]

+ 6 - 4
src/main/frontend/components/repo.cljs

@@ -42,8 +42,8 @@
            (when (nfs-handler/supported?)
              [:div.flex.flex-col
               [:div (ui/button
-                      (t :open-a-directory)
-                      :on-click nfs-handler/ls-dir-files)]
+                     (t :open-a-directory)
+                     :on-click nfs-handler/ls-dir-files)]
               [:span.warning.mt-2.text-sm "Warning: this is an experimental feature,"
                [:br]
                "please only use it for testing purpose."]])]
@@ -62,7 +62,8 @@
                                       "Clone again and re-index the db")
                              :on-click (fn []
                                          (if local?
-                                           (nfs-handler/refresh! url)
+                                           (nfs-handler/refresh! url
+                                                                 repo-handler/create-today-journal!)
                                            (repo-handler/rebuild-index! url))
                                          (js/setTimeout
                                           (fn []
@@ -92,7 +93,8 @@
           (let [syncing? (state/sub :graph/syncing?)]
             [:div.ml-2.mr-1.opacity-70.hover:opacity-100 {:class (if syncing? "loader" "initial")}
              [:a
-              {:on-click #(nfs-handler/refresh! repo)
+              {:on-click #(nfs-handler/refresh! repo
+                                                repo-handler/create-today-journal!)
                :title (str "Sync files with the local directory: " (config/get-local-dir repo))}
               svg/refresh]])
           (let [changed-files (state/sub [:repo/changed-files repo])

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

@@ -180,7 +180,7 @@
           (build-sidebar-item repo idx db-id block-type block-data t))]
     (when item
       (let [collapse? (state/sub [:ui/sidebar-collapsed-blocks db-id])]
-        [:div.sidebar-item.content
+        [:div.sidebar-item.content.color-level
          (let [[title component] item]
            [:div.flex.flex-col
             [:div.flex.flex-row.justify-between

+ 6 - 1
src/main/frontend/components/svg.cljs

@@ -201,7 +201,7 @@
 
 (rum/defc tip
   []
-  [:svg.h-8.w-8.tip-shadow.tip
+  [:svg.h-8.w-8.tip
    {:view-box "0 0 352 512"
     :fill "currentColor"}
    [:path
@@ -420,5 +420,10 @@
      :stroke-linejoin "round"
      :stroke-linecap "round"}]])
 
+(def page
+  [:svg.h-5.w-4 {:viewbox "0 0 24 24", :fill "none", :xmlns "http://www.w3.org/2000/svg"}
+   [:path {:d "M2 0.5H6.78272L13.5 7.69708V18C13.5 18.8284 12.8284 19.5 12 19.5H2C1.17157 19.5 0.5 18.8284 0.5 18V2C0.5 1.17157 1.17157 0.5 2 0.5Z", :fill "var(--ls-active-primary-color)"}]
+   [:path {:d "M7 5.5V0L14 7.5H9C7.89543 7.5 7 6.60457 7 5.5Z", :fill "var(--ls-active-secondary-color)"}]])
+
 (def online
   (hero-icon "M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"))

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

@@ -281,7 +281,8 @@
 
 (defonce idb-db-prefix "logseq-db/")
 (defonce local-db-prefix "logseq_local_")
-(defonce local-handle-prefix (str "handle/" local-db-prefix))
+(defonce local-handle "handle")
+(defonce local-handle-prefix (str local-handle "/" local-db-prefix))
 
 (defn local-db?
   [s]

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

@@ -1981,15 +1981,10 @@
     (doall
      (for [{:keys [url]} repos]
        (let [repo url
-
              db-name (datascript-files-db repo)
              db-conn (d/create-conn db-schema/files-db-schema)]
          (swap! conns assoc db-name db-conn)
-         (p/let [stored (-> (idb/get-item db-name)
-                            (p/then (fn [result]
-                                      result))
-                            (p/catch (fn [error]
-                                       nil)))
+         (p/let [stored (idb/get-item db-name)
                  _ (when stored
                      (let [stored-db (string->db stored)
                            attached-db (d/db-with stored-db [(me-tx stored-db me)])]

+ 17 - 0
src/main/frontend/extensions/slide.css

@@ -0,0 +1,17 @@
+.reveal {
+  color: var(--ls-primary-text-color)
+}
+
+.slide .reveal section img {
+  margin: 1rem auto;
+}
+
+.reveal .progress span {
+  display: block;
+  height: 100%;
+  width: 100%;
+  background-color: currentColor;
+  transition: transform .8s cubic-bezier(.26, .86, .44, .985);
+  transform-origin: 0 0;
+  transform: scaleX(0);
+}

+ 106 - 78
src/main/frontend/fs.cljs

@@ -52,19 +52,20 @@
     (local-db? dir)
     (let [[root new-dir] (rest (string/split dir "/"))
           root-handle (str "handle/" root)]
-      (p/let [handle (idb/get-item root-handle)]
-        (when handle (utils/verifyPermission handle true))
-        (when (and handle new-dir
-                   (not (string/blank? new-dir)))
-          (-> (p/let [handle (.getDirectoryHandle ^js handle new-dir
-                                                  #js {:create true})
-                      handle-path (str root-handle "/" new-dir)
-                      _ (idb/set-item! handle-path handle)]
-                (add-nfs-file-handle! handle-path handle)
-                (println "Stored handle: " (str root-handle "/" new-dir)))
-              (p/catch (fn [error]
-                         (println "mkdir error: " error ", dir: " dir)
-                         (js/console.error error)))))))
+      (->
+       (p/let [handle (idb/get-item root-handle)
+               _ (when handle (utils/verifyPermission handle true))]
+         (when (and handle new-dir
+                    (not (string/blank? new-dir)))
+           (p/let [handle (.getDirectoryHandle ^js handle new-dir
+                                               #js {:create true})
+                   handle-path (str root-handle "/" new-dir)
+                   _ (idb/set-item! handle-path handle)]
+             (add-nfs-file-handle! handle-path handle)
+             (println "Stored handle: " (str root-handle "/" new-dir)))))
+       (p/catch (fn [error]
+                  (println "mkdir error: " error ", dir: " dir)
+                  (js/console.error error)))))
 
     (and dir js/window.pfs)
     (js/window.pfs.mkdir dir)
@@ -132,69 +133,95 @@
      :else
      (js/window.pfs.readFile (str dir "/" path) option))))
 
+(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)]
+      (db/set-file-last-modified-at! repo path last-modified))))
+
 (defn write-file
-  ([dir path content]
-   (write-file dir path content nil))
-  ([dir path content {:keys [old-content nfs-saved-handler last-modified-at]}]
-   (cond
-     (local-db? dir)
-     (let [parts (string/split path "/")
-           basename (last parts)
-           sub-dir (->> (butlast parts)
-                        (remove string/blank?)
-                        (string/join "/"))
-           sub-dir-handle-path (str "handle/"
-                                    (subs dir 1)
-                                    (if sub-dir
-                                      (str "/" sub-dir)))
-           handle-path (if (= "/" (last sub-dir-handle-path))
-                         (subs sub-dir-handle-path 0 (dec (count sub-dir-handle-path)))
-                         sub-dir-handle-path)
-           basename-handle-path (str handle-path "/" basename)]
-       (p/let [file-handle (idb/get-item basename-handle-path)
-               _ (add-nfs-file-handle! basename-handle-path file-handle)]
-         (if file-handle
-           (p/let [local-file (.getFile file-handle)
-                   local-content (.text local-file)
-                   local-last-modified-at (gobj/get local-file "lastModified")
-                   current-time (util/time-ms)
-                   new? (> current-time local-last-modified-at)
-                   not-changed? (= last-modified-at local-last-modified-at)
-                   format (-> (util/get-file-ext path)
-                              (config/get-file-format))]
-             (if (and local-content old-content new? not-changed?)
-               (do
-                 (utils/verifyPermission file-handle true)
-                 (p/let [_ (utils/writeFile file-handle content)
-                         file (.getFile file-handle)]
-                   (nfs-saved-handler file)))
-               (do
-                 (js/alert (str "The file has been modified in your local disk! File path: " path
-                                ", save your changes and click the refresh button to reload it.")))))
-           ;; create file handle
-           (->
-            (p/let [handle (idb/get-item handle-path)]
-              (if handle
+  ([repo dir path content]
+   (write-file repo dir path content nil))
+  ([repo dir path content {:keys [old-content last-modified-at]}]
+   (->
+    (cond
+      (local-db? dir)
+      (let [parts (string/split path "/")
+            basename (last parts)
+            sub-dir (->> (butlast parts)
+                         (remove string/blank?)
+                         (string/join "/"))
+            sub-dir-handle-path (str "handle/"
+                                     (subs dir 1)
+                                     (if sub-dir
+                                       (str "/" sub-dir)))
+            handle-path (if (= "/" (last sub-dir-handle-path))
+                          (subs sub-dir-handle-path 0 (dec (count sub-dir-handle-path)))
+                          sub-dir-handle-path)
+            basename-handle-path (str handle-path "/" basename)]
+        (p/let [file-handle (idb/get-item basename-handle-path)]
+          (when file-handle
+            (add-nfs-file-handle! basename-handle-path file-handle))
+          (if file-handle
+            (p/let [local-file (.getFile file-handle)
+                    local-content (.text local-file)
+                    local-last-modified-at (gobj/get local-file "lastModified")
+                    current-time (util/time-ms)
+                    new? (> current-time local-last-modified-at)
+                    new-created? (nil? last-modified-at)
+                    not-changed? (= last-modified-at local-last-modified-at)
+                    format (-> (util/get-file-ext path)
+                               (config/get-file-format))]
+              ;; (println {:last-modified-at last-modified-at
+              ;;           :local-last-modified-at local-last-modified-at
+              ;;           :not-changed? not-changed?
+              ;;           :new-created? new-created?})
+              (if (and local-content old-content new?
+                       (or not-changed? new-created?))
                 (do
-                  (utils/verifyPermission handle true)
-                  (p/let [file-handle (.getFileHandle ^js handle basename #js {:create true})
-                          _ (idb/set-item! basename-handle-path file-handle)
+                  (p/let [_ (utils/verifyPermission file-handle true)
                           _ (utils/writeFile file-handle content)
                           file (.getFile file-handle)]
-                    (nfs-saved-handler file)))
-                (println "Error: directory handle not exists: " handle-path)))
-            (p/catch (fn [error]
-                       (println "Write local file failed: " {:path path})
-                       (js/console.error error)))))))
+                    (when file
+                      (nfs-saved-handler repo path file))))
+                (do
+                  (js/alert (str "The file has been modified in your local disk! File path: " path
+                                 ", save your changes and click the refresh button to reload it.")))))
+            ;; create file handle
+            (->
+             (p/let [handle (idb/get-item handle-path)]
+               (if handle
+                 (do
+                   (p/let [_ (utils/verifyPermission handle true)
+                           file-handle (.getFileHandle ^js handle basename #js {:create true})
+                           _ (idb/set-item! basename-handle-path file-handle)
+                           _ (utils/writeFile file-handle content)
+                           file (.getFile file-handle)]
+                     (when file
+                       (nfs-saved-handler repo path file))))
+                 (println "Error: directory handle not exists: " handle-path)))
+             (p/catch (fn [error]
+                        (println "Write local file failed: " {:path path})
+                        (js/console.error error)))))))
 
-     js/window.pfs
-     (js/window.pfs.writeFile (str dir "/" path) content)
+      js/window.pfs
+      (js/window.pfs.writeFile (str dir "/" path) content)
 
-     :else
-     nil)))
+      :else
+      nil)
+    (p/catch (fn [error]
+               (log/error :file/write-failed? {:dir dir
+                                               :path path
+                                               :error error})
+               ;; Disable this temporarily
+               ;; (js/alert "Current file can't be saved! Please copy its content to your local file system and click the refresh button.")
+)))))
 
 (defn rename
-  [old-path new-path]
+  [repo old-path new-path]
   (cond
     (local-db? old-path)
     ;; create new file
@@ -204,7 +231,7 @@
             handle (idb/get-item (str "handle" old-path))
             file (.getFile handle)
             content (.text file)
-            _ (write-file dir new-basename content)]
+            _ (write-file repo dir new-basename content)]
       (unlink old-path nil))
 
     :else
@@ -246,18 +273,19 @@
            (mkdir dir)))))))
 
 (defn create-if-not-exists
-  ([dir path]
-   (create-if-not-exists dir path ""))
-  ([dir path initial-content]
+  ([repo dir path]
+   (create-if-not-exists repo dir path ""))
+  ([repo dir path initial-content]
    (let [path (if (util/starts-with? path "/")
                 path
                 (str "/" path))]
-     (util/p-handle
-      (stat dir path)
-      (fn [_stat] true)
-      (fn [error]
-        (write-file dir path initial-content)
-        false)))))
+     (->
+      (p/let [_ (stat dir path)]
+        true)
+      (p/catch
+       (fn [_error]
+         (p/let [_ (write-file repo dir path initial-content)]
+           false)))))))
 
 (defn file-exists?
   [dir path]

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

@@ -26,14 +26,8 @@
 (defn- watch-for-date!
   []
   (js/setInterval (fn []
-                    (state/set-today! (date/today))
-                    (when-let [repo (state/get-current-repo)]
-                      (when (or (db/cloned? repo)
-                                (config/local-db? repo))
-                        (let [today-page (string/lower-case (date/today))]
-                          (when (empty? (db/get-page-blocks-no-cache repo today-page))
-                            (repo-handler/create-today-journal-if-not-exists repo))))))
-                  1000))
+                    (when-not (state/nfs-refreshing?)
+                      (repo-handler/create-today-journal!))) 1000))
 
 (defn store-schema!
   []

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

@@ -61,18 +61,18 @@
       (let [repo-dir (util/get-repo-dir repo)]
         (->
          (p/do!
-           (create-draws-directory! repo)
-           (fs/write-file repo-dir path data)
-           (git-handler/git-add repo path)
-           (ok-handler file)
-           (let [modified-at (tc/to-long (t/now))]
-             (db/transact! repo
-                           [{:file/path path
-                             :file/last-modified-at modified-at}
-                            {:page/name file
-                             :page/file path
-                             :page/last-modified-at (tc/to-long (t/now))
-                             :page/journal? false}])))
+          (create-draws-directory! repo)
+          (fs/write-file repo repo-dir path data)
+          (git-handler/git-add repo path)
+          (ok-handler file)
+          (let [modified-at (tc/to-long (t/now))]
+            (db/transact! repo
+                          [{:file/path path
+                            :file/last-modified-at modified-at}
+                           {:page/name file
+                            :page/file path
+                            :page/last-modified-at (tc/to-long (t/now))
+                            :page/journal? false}])))
          (p/catch (fn [error]
                     (prn "Write file failed, path: " path ", data: " data)
                     (js/console.dir error))))))))

+ 128 - 119
src/main/frontend/handler/editor.cljs

@@ -458,7 +458,7 @@
                                       (or (:page/original-name page)
                                           (:page/name page)))
                                     (text/remove-level-spaces value (keyword format)))]
-                   (p/let [_ (fs/create-if-not-exists dir file-path content)
+                   (p/let [_ (fs/create-if-not-exists repo dir file-path content)
                            _ (git-handler/git-add repo path)]
                      (db/reset-file! repo path content)
                      (ui-handler/re-render-root!)
@@ -696,7 +696,7 @@
             (let [content (util/default-content-with-title format (or
                                                                    (:page/original-name page)
                                                                    (:page/name page)))]
-              (p/let [_ (fs/create-if-not-exists dir file-path content)
+              (p/let [_ (fs/create-if-not-exists repo dir file-path content)
                       _ (git-handler/git-add repo path)]
                 (db/reset-file! repo path
                                 (str content
@@ -955,8 +955,6 @@
             :data [block]}
            [[file-path new-content]])
 
-          (state/set-editor-op! nil)
-
           (when (or (seq ref-pages) (seq ref-blocks))
             (ui-handler/re-render-root!)))))))
 
@@ -964,7 +962,8 @@
   [state repo e]
   (let [{:keys [id block-id block-parent-id dummy? value pos format]} (get-state state)]
     (when (and block-id
-               (not= :block/delete (state/get-editor-op)))
+               (not= :block/delete (state/get-editor-op))
+               (not (state/file-in-writing!)))
       (state/set-editor-op! :block/delete)
       (let [page-id (:db/id (:block/page (db/entity [:block/uuid block-id])))
             page-blocks-count (and page-id (db/get-page-blocks-count repo page-id))
@@ -990,8 +989,8 @@
                                0)]
                       (edit-block! block pos format id
                                    {:custom-content new-value
-                                    :tail-len tail-len})))))))
-          (state/set-editor-op! nil))))))
+                                    :tail-len tail-len})))))))))
+      (state/set-editor-op! nil))))
 
 (defn delete-blocks!
   [repo block-uuids]
@@ -1349,24 +1348,32 @@
 
 (defn save-current-block-when-idle!
   []
-  (when-let [repo (state/get-current-repo)]
-    (when (state/input-idle? repo)
-      (let [input-id (state/get-edit-input-id)
-            block (state/get-edit-block)
-            elem (and input-id (gdom/getElement input-id))
-            db-block (db/entity [:block/uuid (:block/uuid block)])
-            db-content (:block/content db-block)
-            db-content-without-heading (and db-content
-                                            (util/safe-subs db-content (:block/level db-block)))
-            value (and elem (gobj/get elem "value"))]
-        (when (and block value db-content-without-heading
-                   (not= (string/trim db-content-without-heading)
-                         (string/trim value)))
-          (let [cur-pos (util/get-input-pos elem)]
-            (save-block-aux! block value (:block/format block))
-            ;; Restore the cursor after saving the block
-            (when (and elem cur-pos)
-              (util/set-caret-pos! elem cur-pos))))))))
+  (when-not (state/file-in-writing!)
+    (when-let [repo (state/get-current-repo)]
+      (when (state/input-idle? repo)
+        (state/set-editor-op! :auto-save)
+        (try
+          (let [input-id (state/get-edit-input-id)
+                block (state/get-edit-block)
+                db-block (when-let [block-id (:block/uuid block)]
+                           (db/pull [:block/uuid block-id]))
+                elem (and input-id (gdom/getElement input-id))
+                db-content (:block/content db-block)
+                db-content-without-heading (and db-content
+                                                (util/safe-subs db-content (:block/level db-block)))
+                value (and elem (gobj/get elem "value"))]
+            (when (and block value db-content-without-heading
+                       (or
+                        (not= (string/trim db-content-without-heading)
+                              (string/trim value))))
+              (let [cur-pos (util/get-input-pos elem)]
+                (save-block-aux! db-block value (:block/format db-block))
+               ;; Restore the cursor after saving the block
+                (when (and elem cur-pos)
+                  (util/set-caret-pos! elem cur-pos)))))
+          (catch js/Error error
+            (log/error :save-block-failed error)))
+        (state/set-editor-op! nil)))))
 
 (defn on-up-down
   [state e up?]
@@ -1641,39 +1648,40 @@
   ([state direction]
    (adjust-block-level! state direction 100))
   ([state direction retry-limit]
-   (if (= :insert (state/get-editor-op))
-     (if (> retry-limit 0)
-       (js/setTimeout #(adjust-block-level! state direction (dec retry-limit)) 20)
-       (log/error :editor/indent-outdent-retry-max-limit {:direction direction}))
-     (do
-       (state/set-editor-op! :indent-outdent)
-       (let [{:keys [block block-parent-id value config]} (get-state state)
-             start-level (:start-level config)
-             format (:block/format block)
-             level (:block/level block)
-             previous-level (or (get-previous-block-level block-parent-id) 1)
-             [add? remove?] (case direction
-                              :left [false true]
-                              :right [true false]
-                              [(<= level previous-level)
-                               (and (> level previous-level)
-                                    (> level 2))])
-             final-level (cond
-                           add? (inc level)
-                           remove? (if (> level 2)
-                                     (dec level)
-                                     level)
-                           :else level)
-             new-value (block/with-levels value format (assoc block :block/level final-level))]
-         (when (and
-                (not (and (= direction :left)
-                          (get config :id)
-                          (util/uuid-string? (get config :id))
-                          (<= final-level start-level)))
-                (<= (- final-level previous-level) 1))
-           (save-block-if-changed! block new-value
-                                   {:indent-left? (= direction :left)})))
-       (state/set-editor-op! nil)))))
+   (when-not (state/file-in-writing!)
+     (if (= :insert (state/get-editor-op))
+       (if (> retry-limit 0)
+         (js/setTimeout #(adjust-block-level! state direction (dec retry-limit)) 20)
+         (log/error :editor/indent-outdent-retry-max-limit {:direction direction}))
+       (do
+         (state/set-editor-op! :indent-outdent)
+         (let [{:keys [block block-parent-id value config]} (get-state state)
+               start-level (:start-level config)
+               format (:block/format block)
+               level (:block/level block)
+               previous-level (or (get-previous-block-level block-parent-id) 1)
+               [add? remove?] (case direction
+                                :left [false true]
+                                :right [true false]
+                                [(<= level previous-level)
+                                 (and (> level previous-level)
+                                      (> level 2))])
+               final-level (cond
+                             add? (inc level)
+                             remove? (if (> level 2)
+                                       (dec level)
+                                       level)
+                             :else level)
+               new-value (block/with-levels value format (assoc block :block/level final-level))]
+           (when (and
+                  (not (and (= direction :left)
+                            (get config :id)
+                            (util/uuid-string? (get config :id))
+                            (<= final-level start-level)))
+                  (<= (- final-level previous-level) 1))
+             (save-block-if-changed! block new-value
+                                     {:indent-left? (= direction :left)})))
+         (state/set-editor-op! nil))))))
 
 (defn adjust-blocks-level!
   [blocks direction])
@@ -1694,67 +1702,68 @@
 
 (defn move-up-down
   [e up?]
-  (when-let [block-id (:block/uuid (state/get-edit-block))]
-    (let [block-parent-id (state/get-editing-block-dom-id)
-          block (db/entity [:block/uuid block-id])
-          meta (:block/meta block)
-          page (:block/page block)
-          block-dom-node (gdom/getElement block-parent-id)
-          prev-block (get-prev-block-non-collapsed block-dom-node)
-          next-block (get-next-block-non-collapsed block-dom-node)
-          repo (state/get-current-repo)
-          move-upwards-to-parent? (and up? prev-block (< (d/attr prev-block "level") (:block/level block)))
-          move-down-to-higher-level? (and (not up?) next-block (< (d/attr next-block "level") (:block/level block)))]
-      (when-let [sibling-block (cond
-                                 move-upwards-to-parent?
-                                 prev-block
-                                 move-down-to-higher-level?
-                                 next-block
-                                 :else
-                                 (let [f (if up? util/get-prev-block-with-same-level util/get-next-block-with-same-level)]
-                                   (f block-dom-node)))]
-        (when-let [sibling-block-id (d/attr sibling-block "blockid")]
-          (when-let [sibling-block (db/pull-block (medley/uuid sibling-block-id))]
-            (let [sibling-meta (:block/meta sibling-block)
-                  hc1 (db/get-block-and-children-no-cache repo (:block/uuid block))
-                  hc2 (if (or move-upwards-to-parent? move-down-to-higher-level?)
-                        [sibling-block]
-                        (db/get-block-and-children-no-cache repo (:block/uuid sibling-block)))]
-              ;; Same page and next to the other
-              (when (and
-                     (= (:db/id (:block/page block))
-                        (:db/id (:block/page sibling-block)))
-                     (or
-                      (and up? (= (:end-pos (:block/meta (last hc2))) (:start-pos (:block/meta (first hc1)))))
-                      (and (not up?) (= (:end-pos (:block/meta (last hc1))) (:start-pos (:block/meta (first hc2)))))))
-                (let [hc1-content (block-and-children-content hc1)
-                      hc2-content (block-and-children-content hc2)
-                      file (db/get-block-file (:block/uuid block))
-                      file-path (:file/path file)
-                      old-file-content (db/get-file file-path)
-                      [start-pos end-pos new-content blocks] (if up?
-                                                               [(:start-pos sibling-meta)
-                                                                (get-in (last hc1) [:block/meta :end-pos])
-                                                                (str hc1-content hc2-content)
-                                                                (concat hc1 hc2)]
-                                                               [(:start-pos meta)
-                                                                (get-in (last hc2) [:block/meta :end-pos])
-                                                                (str hc2-content hc1-content)
-                                                                (concat hc2 hc1)])]
-                  (when (and start-pos end-pos)
-                    (let [new-file-content (utf8/insert! old-file-content start-pos end-pos new-content)
-                          modified-time (modified-time-tx page file)
-                          blocks-meta (rebuild-blocks-meta start-pos blocks)]
-                      (profile
-                       (str "Move block " (if up? "up: " "down: "))
-                       (repo-handler/transact-react-and-alter-file!
-                        repo
-                        (concat
-                         blocks-meta
-                         modified-time)
-                        {:key :block/change
-                         :data (map (fn [block] (assoc block :block/page page)) blocks)}
-                        [[file-path new-file-content]])))))))))))))
+  (when-not (state/file-in-writing!)
+    (when-let [block-id (:block/uuid (state/get-edit-block))]
+      (let [block-parent-id (state/get-editing-block-dom-id)
+            block (db/entity [:block/uuid block-id])
+            meta (:block/meta block)
+            page (:block/page block)
+            block-dom-node (gdom/getElement block-parent-id)
+            prev-block (get-prev-block-non-collapsed block-dom-node)
+            next-block (get-next-block-non-collapsed block-dom-node)
+            repo (state/get-current-repo)
+            move-upwards-to-parent? (and up? prev-block (< (d/attr prev-block "level") (:block/level block)))
+            move-down-to-higher-level? (and (not up?) next-block (< (d/attr next-block "level") (:block/level block)))]
+        (when-let [sibling-block (cond
+                                   move-upwards-to-parent?
+                                   prev-block
+                                   move-down-to-higher-level?
+                                   next-block
+                                   :else
+                                   (let [f (if up? util/get-prev-block-with-same-level util/get-next-block-with-same-level)]
+                                     (f block-dom-node)))]
+          (when-let [sibling-block-id (d/attr sibling-block "blockid")]
+            (when-let [sibling-block (db/pull-block (medley/uuid sibling-block-id))]
+              (let [sibling-meta (:block/meta sibling-block)
+                    hc1 (db/get-block-and-children-no-cache repo (:block/uuid block))
+                    hc2 (if (or move-upwards-to-parent? move-down-to-higher-level?)
+                          [sibling-block]
+                          (db/get-block-and-children-no-cache repo (:block/uuid sibling-block)))]
+               ;; Same page and next to the other
+                (when (and
+                       (= (:db/id (:block/page block))
+                          (:db/id (:block/page sibling-block)))
+                       (or
+                        (and up? (= (:end-pos (:block/meta (last hc2))) (:start-pos (:block/meta (first hc1)))))
+                        (and (not up?) (= (:end-pos (:block/meta (last hc1))) (:start-pos (:block/meta (first hc2)))))))
+                  (let [hc1-content (block-and-children-content hc1)
+                        hc2-content (block-and-children-content hc2)
+                        file (db/get-block-file (:block/uuid block))
+                        file-path (:file/path file)
+                        old-file-content (db/get-file file-path)
+                        [start-pos end-pos new-content blocks] (if up?
+                                                                 [(:start-pos sibling-meta)
+                                                                  (get-in (last hc1) [:block/meta :end-pos])
+                                                                  (str hc1-content hc2-content)
+                                                                  (concat hc1 hc2)]
+                                                                 [(:start-pos meta)
+                                                                  (get-in (last hc2) [:block/meta :end-pos])
+                                                                  (str hc2-content hc1-content)
+                                                                  (concat hc2 hc1)])]
+                    (when (and start-pos end-pos)
+                      (let [new-file-content (utf8/insert! old-file-content start-pos end-pos new-content)
+                            modified-time (modified-time-tx page file)
+                            blocks-meta (rebuild-blocks-meta start-pos blocks)]
+                        (profile
+                         (str "Move block " (if up? "up: " "down: "))
+                         (repo-handler/transact-react-and-alter-file!
+                          repo
+                          (concat
+                           blocks-meta
+                           modified-time)
+                          {:key :block/change
+                           :data (map (fn [block] (assoc block :block/page page)) blocks)}
+                          [[file-path new-file-content]]))))))))))))))
 
 (defn expand!
   []

+ 4 - 13
src/main/frontend/handler/file.cljs

@@ -118,11 +118,6 @@
         (p/catch (fn [error]
                    (log/error :load-files-error error))))))
 
-(defn nfs-saved-handler
-  [repo path file]
-  (when-let [last-modified (gobj/get file "lastModified")]
-    (db/set-file-last-modified-at! repo path last-modified)))
-
 (defn alter-file
   [repo path content {:keys [reset? re-render-root? add-history? update-status?]
                       :or {reset? true
@@ -139,10 +134,8 @@
         (db/reset-file! repo path content))
       (db/set-file-content! repo path content))
     (util/p-handle
-     (fs/write-file (util/get-repo-dir repo) path content {:old-content original-content
-                                                           :last-modified-at (db/get-file-last-modified-at repo path)
-                                                           :nfs-saved-handler (fn [file]
-                                                                                (nfs-saved-handler repo path file))})
+     (fs/write-file repo (util/get-repo-dir repo) path content {:old-content original-content
+                                                                :last-modified-at (db/get-file-last-modified-at repo path)})
      (fn [_]
        (git-handler/git-add repo path update-status?)
        (when (= path (str config/app-name "/" config/config-file))
@@ -194,11 +187,9 @@
     (let [write-file-f (fn [[path content]]
                          (let [original-content (get file->content path)]
                            (-> (p/let [_ (fs/check-directory-permission! repo)]
-                                 (fs/write-file (util/get-repo-dir repo) path content
+                                 (fs/write-file repo (util/get-repo-dir repo) path content
                                                 {:old-content original-content
-                                                 :last-modified-at (db/get-file-last-modified-at repo path)
-                                                 :nfs-saved-handler (fn [file]
-                                                                      (nfs-saved-handler repo path file))}))
+                                                 :last-modified-at (db/get-file-last-modified-at repo path)}))
                                (p/catch (fn [error]
                                           (log/error :write-file/failed {:path path
                                                                          :content content

+ 24 - 23
src/main/frontend/handler/page.cljs

@@ -63,7 +63,7 @@
                 :error)
                ;; create the file
                (let [content (util/default-content-with-title format title)]
-                 (p/let [_ (fs/create-if-not-exists dir file-path content)
+                 (p/let [_ (fs/create-if-not-exists repo dir file-path content)
                          _ (git-handler/git-add repo path)]
                    (db/reset-file! repo path content)
                    (when redirect?
@@ -293,7 +293,8 @@
         old-path (:file/path file)
         new-path (compute-new-file-path old-path new-name)]
     (->
-     (p/let [_ (fs/rename (str (util/get-repo-dir repo) "/" old-path)
+     (p/let [_ (fs/rename repo
+                          (str (util/get-repo-dir repo) "/" old-path)
                           (str (util/get-repo-dir repo) "/" new-path))]
        ;; update db
        (db/transact! repo [{:db/id (:db/id file)
@@ -428,32 +429,32 @@
   [project permalink]
   (let [url (util/format "%s%s/%s" config/api project permalink)]
     (js/Promise.
-      (fn [resolve reject]
-        (util/delete url
-          (fn [result]
-            (resolve result))
-          (fn [error]
-            (log/error :page/http-delete-failed error)
-            (reject error)))))))
+     (fn [resolve reject]
+       (util/delete url
+                    (fn [result]
+                      (resolve result))
+                    (fn [error]
+                      (log/error :page/http-delete-failed error)
+                      (reject error)))))))
 
 (defn get-page-list-by-project-name
   [project]
   (js/Promise.
-    (fn [resolve _]
-      (if-not (string? project)
-        (resolve :project-name-is-invalid)
-        (let [url (util/format "%sprojects/%s/pages" config/api project)]
+   (fn [resolve _]
+     (if-not (string? project)
+       (resolve :project-name-is-invalid)
+       (let [url (util/format "%sprojects/%s/pages" config/api project)]
          (util/fetch url
-           (fn [result]
-             (log/debug :page/get-page-list result)
-             (let [data (:result result)]
-               (if (sequential? data)
-                 (do (state/set-published-pages data)
-                     (resolve data))
-                 (log/error :page/http-get-list-result-malformed result))))
-           (fn [error]
-             (log/error :page/http-get-list-failed error)
-             (resolve error))))))))
+                     (fn [result]
+                       (log/debug :page/get-page-list result)
+                       (let [data (:result result)]
+                         (if (sequential? data)
+                           (do (state/set-published-pages data)
+                               (resolve data))
+                           (log/error :page/http-get-list-result-malformed result))))
+                     (fn [error]
+                       (log/error :page/http-get-list-failed error)
+                       (resolve error))))))))
 
 (defn update-state-and-notify
   [page-name]

+ 20 - 12
src/main/frontend/handler/repo.cljs

@@ -6,6 +6,7 @@
             [lambdaisland.glogi :as log]
             [frontend.state :as state]
             [frontend.db :as db]
+            [frontend.idb :as idb]
             [frontend.git :as git]
             [cljs-bean.core :as bean]
             [frontend.date :as date]
@@ -52,7 +53,7 @@
         dir (str repo-dir "/" app-dir)]
     (p/let [_ (fs/mkdir-if-not-exists dir)]
       (let [default-content config/config-default-content]
-        (p/let [file-exists? (fs/create-if-not-exists repo-dir (str app-dir "/" config/config-file) default-content)]
+        (p/let [file-exists? (fs/create-if-not-exists repo-url repo-dir (str app-dir "/" config/config-file) default-content)]
           (let [path (str app-dir "/" config/config-file)
                 old-content (when file-exists?
                               (db/get-file repo-url path))
@@ -73,7 +74,7 @@
         file-path (str "/" path)
         default-content (util/default-content-with-title format "contents")]
     (p/let [_ (fs/mkdir-if-not-exists (str repo-dir "/" (state/get-pages-directory)))
-            file-exists? (fs/create-if-not-exists repo-dir file-path default-content)]
+            file-exists? (fs/create-if-not-exists repo-url repo-dir file-path default-content)]
       (when-not file-exists?
         (db/reset-file! repo-url path default-content)
         (git-handler/git-add repo-url path)))))
@@ -86,7 +87,7 @@
         file-path (str "/" path)
         default-content ""]
     (p/let [_ (fs/mkdir-if-not-exists (str repo-dir "/" config/app-name))
-            file-exists? (fs/create-if-not-exists repo-dir file-path default-content)]
+            file-exists? (fs/create-if-not-exists repo-url repo-dir file-path default-content)]
       (when-not file-exists?
         (db/reset-file! repo-url path default-content)
         (git-handler/git-add repo-url path)))))
@@ -98,7 +99,7 @@
         path (str (config/get-pages-directory) "/how_to_make_dummy_notes.md")
         file-path (str "/" path)]
     (p/let [_ (fs/mkdir-if-not-exists (str repo-dir "/" (config/get-pages-directory)))
-            _file-exists? (fs/create-if-not-exists repo-dir file-path content)]
+            _file-exists? (fs/create-if-not-exists repo-url repo-dir file-path content)]
       (db/reset-file! repo-url path content))))
 
 (defn create-today-journal-if-not-exists
@@ -106,8 +107,6 @@
    (create-today-journal-if-not-exists repo-url nil))
   ([repo-url content]
    (spec/validate :repos/url repo-url)
-   (when (config/local-db? repo-url)
-     (fs/check-directory-permission! repo-url))
    (let [repo-dir (util/get-repo-dir repo-url)
          format (state/get-preferred-format repo-url)
          title (date/today)
@@ -133,13 +132,24 @@
          empty-blocks? (empty? (db/get-page-blocks-no-cache repo-url (string/lower-case title)))]
      (when (or empty-blocks?
                (not page-exists?))
-       (p/let [_ (fs/mkdir-if-not-exists (str repo-dir "/" config/default-journals-directory))
-               file-exists? (fs/create-if-not-exists repo-dir file-path content)]
+       (p/let [_ (fs/check-directory-permission! repo-url)
+               _ (fs/mkdir-if-not-exists (str repo-dir "/" config/default-journals-directory))
+               file-exists? (fs/create-if-not-exists repo-url repo-dir file-path content)]
          (when-not file-exists?
            (db/reset-file! repo-url path content)
            (ui-handler/re-render-root!)
            (git-handler/git-add repo-url path)))))))
 
+(defn create-today-journal!
+  []
+  (state/set-today! (date/today))
+  (when-let [repo (state/get-current-repo)]
+    (when (or (db/cloned? repo)
+              (config/local-db? repo))
+      (let [today-page (string/lower-case (date/today))]
+        (when (empty? (db/get-page-blocks-no-cache repo today-page))
+          (create-today-journal-if-not-exists repo))))))
+
 (defn create-default-files!
   [repo-url]
   (spec/validate :repos/url repo-url)
@@ -452,10 +462,8 @@
                       (fs/rmdir (util/get-repo-dir url))
                       (state/delete-repo! repo))]
     (if (config/local-db? url)
-      (do
-        (delete-db-f)
-        ;; clear handles
-)
+      (p/let [_ (idb/clear-local-db! url)] ; clear file handles
+        (delete-db-f))
       (util/delete (str config/api "repos/" id)
                    delete-db-f
                    (fn [error]

+ 68 - 59
src/main/frontend/handler/web/nfs.cljs

@@ -167,68 +167,77 @@
           handle-path (str config/local-handle-prefix dir-name)
           path-handles (atom {})]
       (state/set-graph-syncing? true)
-      (p/let [handle (idb/get-item handle-path)
-              _ (when handle (utils/verifyPermission handle true))
-              files-result (utils/getFiles handle true
-                                           (fn [path handle]
-                                             (swap! path-handles assoc path handle)))
-              new-files (-> (->db-files dir-name files-result)
-                            remove-ignore-files)
-              _ (let [file-paths (set (map :file/path new-files))]
-                  (swap! path-handles (fn [handles]
-                                        (->> handles
-                                             (filter (fn [[path _handle]]
-                                                       (contains? file-paths
-                                                                  (string/replace-first path (str dir-name "/") ""))))
-                                             (into {})))))
-              _ (set-files! @path-handles)
-              get-file-f (fn [path files] (some #(when (= (:file/path %) path) %) files))
-              {:keys [added modified deleted] :as diffs} (compute-diffs old-files new-files)
-              ;; Use the same labels as isomorphic-git
-              rename-f (fn [typ col] (mapv (fn [file] {:type typ :path file}) col))
-              _ (when (seq deleted)
-                  (p/all (map (fn [path]
-                                (let [handle-path (str handle-path path)]
-                                  (idb/remove-item! handle-path)
-                                  (fs/remove-nfs-file-handle! handle-path))) deleted)))
-              added-or-modified (set (concat added modified))
-              _ (when (seq added-or-modified)
-                  (p/all (map (fn [path]
-                                (when-let [handle (get @path-handles path)]
-                                  (idb/set-item! (str handle-path path) handle))) added-or-modified)))]
-        (-> (p/all (map (fn [path]
-                          (when-let [file (get-file-f path new-files)]
-                            (p/let [content (.text (:file/file file))]
-                              (assoc file :file/content content)))) added-or-modified))
-            (p/then (fn [result]
-                      (let [files (map #(dissoc % :file/file :file/handle) result)
-                            non-modified? (fn [file]
-                                            (let [content (:file/content file)
-                                                  old-content (:file/content (get-file-f (:file/path file) old-files))]
-                                              (= content old-content)))
-                            non-modified-files (->> (filter non-modified? files)
-                                                    (map :file/path))
-                            modified-files (remove non-modified? files)
-                            modified (set/difference (set modified) (set non-modified-files))
-                            diffs (concat
-                                   (rename-f "remove" deleted)
-                                   (rename-f "add" added)
-                                   (rename-f "modify" modified))]
-                        (when (or (and (seq diffs) (seq modified-files))
-                                  (seq diffs) ; delete
+      (p/let [handle (idb/get-item handle-path)]
+        (when handle
+          (p/let [_ (when handle (utils/verifyPermission handle true))
+                  files-result (utils/getFiles handle true
+                                               (fn [path handle]
+                                                 (swap! path-handles assoc path handle)))
+                  new-files (-> (->db-files dir-name files-result)
+                                remove-ignore-files)
+                  _ (let [file-paths (set (map :file/path new-files))]
+                      (swap! path-handles (fn [handles]
+                                            (->> handles
+                                                 (filter (fn [[path _handle]]
+                                                           (contains? file-paths
+                                                                      (string/replace-first path (str dir-name "/") ""))))
+                                                 (into {})))))
+                  _ (set-files! @path-handles)
+                  get-file-f (fn [path files] (some #(when (= (:file/path %) path) %) files))
+                  {:keys [added modified deleted] :as diffs} (compute-diffs old-files new-files)
+                  ;; Use the same labels as isomorphic-git
+                  rename-f (fn [typ col] (mapv (fn [file] {:type typ :path file}) col))
+                  _ (when (seq deleted)
+                      (let [deleted (doall
+                                     (-> (map (fn [path] (if (= "/" (first path))
+                                                           path
+                                                           (str "/" path))) deleted)
+                                         (distinct)))]
+                        (p/all (map (fn [path]
+                                      (let [handle-path (str handle-path path)]
+                                        (idb/remove-item! handle-path)
+                                        (fs/remove-nfs-file-handle! handle-path))) deleted))))
+                  added-or-modified (set (concat added modified))
+                  _ (when (seq added-or-modified)
+                      (p/all (map (fn [path]
+                                    (when-let [handle (get @path-handles path)]
+                                      (idb/set-item! (str handle-path path) handle))) added-or-modified)))]
+            (-> (p/all (map (fn [path]
+                              (when-let [file (get-file-f path new-files)]
+                                (p/let [content (.text (:file/file file))]
+                                  (assoc file :file/content content)))) added-or-modified))
+                (p/then (fn [result]
+                          (let [files (map #(dissoc % :file/file :file/handle) result)
+                                non-modified? (fn [file]
+                                                (let [content (:file/content file)
+                                                      old-content (:file/content (get-file-f (:file/path file) old-files))]
+                                                  (= content old-content)))
+                                non-modified-files (->> (filter non-modified? files)
+                                                        (map :file/path))
+                                modified-files (remove non-modified? files)
+                                modified (set/difference (set modified) (set non-modified-files))
+                                diffs (concat
+                                       (rename-f "remove" deleted)
+                                       (rename-f "add" added)
+                                       (rename-f "modify" modified))]
+                            (when (or (and (seq diffs) (seq modified-files))
+                                      (seq diffs) ; delete
 )
-                          (repo-handler/load-repo-to-db! repo
-                                                         {:diffs diffs
-                                                          :nfs-files modified-files})))))
-            (p/catch (fn [error]
-                       (log/error :nfs/load-files-error error)))
-            (p/finally (fn [_]
-                         (state/set-graph-syncing? false))))))))
+                              (repo-handler/load-repo-to-db! repo
+                                                             {:diffs diffs
+                                                              :nfs-files modified-files})))))
+                (p/catch (fn [error]
+                           (log/error :nfs/load-files-error error)))
+                (p/finally (fn [_]
+                             (state/set-graph-syncing? false))))))))))
 
-(defn- refresh!
-  [repo]
+(defn refresh!
+  [repo ok-handler]
   (when repo
-    (reload-dir! repo)))
+    (state/set-nfs-refreshing! true)
+    (p/let [_ (reload-dir! repo)
+            _ (ok-handler)]
+      (state/set-nfs-refreshing! false))))
 
 (defn supported?
   []

+ 10 - 0
src/main/frontend/idb.cljs

@@ -5,6 +5,7 @@
             [promesa.core :as p]
             [clojure.string :as string]
             [frontend.config :as config]
+            [frontend.util :as util]
             [frontend.storage :as storage]))
 
 ;; offline db
@@ -53,3 +54,12 @@
   (p/let [ks (get-keys)]
     (->> (filter (fn [k] (string/starts-with? k (str config/idb-db-prefix config/local-db-prefix))) ks)
          (map #(string/replace-first % config/idb-db-prefix "")))))
+
+(defn clear-local-db!
+  [repo]
+  (when repo
+    (p/let [ks (get-keys)
+            ks (filter (fn [k] (string/starts-with? k (str config/local-handle "/" repo))) ks)]
+      (when (seq ks)
+        (p/all (map (fn [key]
+                      (remove-item! key)) ks))))))

+ 14 - 5
src/main/frontend/state.cljs

@@ -27,6 +27,7 @@
     :repo/sync-status {}
     :repo/changed-files nil
     :nfs/loading-files? nil
+    :nfs/refreshing? nil
     ;; TODO: how to detect the network reliably?
     :network/online? true
     :indexeddb/support? true
@@ -797,16 +798,16 @@
                                  (if (= (:repo project) current-repo)
                                    (conj acc (merge project new-kvs))
                                    (conj acc project)))
-                         []
-                         projects)]
+                               []
+                               projects)]
       (set-state! [:me :projects] new-projects))))
 
 (defn remove-current-project
   []
   (when-let [current-repo (get-current-repo)]
     (update-state! [:me :projects]
-      (fn [projects]
-        (remove #(= (:repo %) current-repo) projects)))))
+                   (fn [projects]
+                     (remove #(= (:repo %) current-repo) projects)))))
 
 (defn set-indexedb-support!
   [value]
@@ -939,7 +940,7 @@
 (defn set-published-pages
   [pages]
   (when-let [repo (get-current-repo)]
-   (set-state! [:me :published-pages repo] pages)))
+    (set-state! [:me :published-pages repo] pages)))
 
 (defn reset-published-pages
   []
@@ -1002,6 +1003,14 @@
   [repo file?]
   (get-in (:db/latest-txs @state) [repo file?]))
 
+(defn set-nfs-refreshing!
+  [value]
+  (set-state! :nfs/refreshing? value))
+
+(defn nfs-refreshing?
+  []
+  (:nfs/refreshing? @state))
+
 ;; TODO: Move those to the uni `state`
 
 (defonce editor-op (atom nil))

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

@@ -93,7 +93,7 @@
                      [:div {:style {:margin-right "8px"}} title]
                      ;; [:div {:style {:position "absolute" :right "8px"}}
                      ;;  icon]
-]]
+                     ]]
           (rum/with-key
             (menu-link new-options child)
             title)))])

+ 6 - 6
src/main/frontend/utils.js

@@ -116,16 +116,16 @@ export var verifyPermission = async function (handle, readWrite) {
   if (readWrite) {
     options.mode = 'readwrite';
   }
-  // Check if permission was already granted. If so, return true.
+  // Check if permission was already granted.
   if ((await handle.queryPermission(options)) === 'granted') {
-    return true;
+    return;
   }
-  // Request permission. If the user grants permission, return true.
+  // Request permission. If the user grants permission, just return.
   if ((await handle.requestPermission(options)) === 'granted') {
-    return true;
+    return;
   }
-  // The user didn't grant permission, so return false.
-  return false;
+  // The user didn't grant permission, throw an error.
+  throw new Error("Permission is not granted");
 }
 
 export var openDirectory = async function (options = {}, cb) {

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

@@ -1,3 +1,3 @@
 (ns frontend.version)
 
-(defonce version "0.0.4.8-3")
+(defonce version "0.0.4.8-5")