Browse Source

Merge branch 'master' into refactor/remove-editor-ugly-timers

Tienson Qin 5 years ago
parent
commit
6794aada93

+ 3 - 1
readme.org

@@ -40,6 +40,8 @@ You'll also need to make a Logseq DB in PostgreSQL. Do that with ~createdb logse
          # Replace your-code-directory and your-app.private-key.pem with yours
          export GITHUB_APP_PEM="/your-code-directory/your-app.private-key.pem"
          export LOG_PATH="/tmp/logseq"
+         export PG_USERNAME="xxx"
+         export PG_PASSWORD="xxx"
        #+END_SRC
 
 *** 5. Compile to JavaScript
@@ -85,4 +87,4 @@ You'll also need to make a Logseq DB in PostgreSQL. Do that with ~createdb logse
     Run ~start-windows.bat~ which is located in the repo. This will open a second terminal that runs Logseq's backend server.
     To completely stop Logseq, you'll need to also close that second terminal that was opened.
 
-    ~start-windows.bat~ will try to start PostgreSQL for you if it's not already started.
+    ~start-windows.bat~ will try to start PostgreSQL for you if it's not already started.

+ 2 - 0
resources/css/common.css

@@ -10,6 +10,7 @@
     --ls-primary-background-color: #002b36;
     --ls-secondary-background-color: #073642;
     --ls-tertiary-background-color: #0f4552;
+    --ls-quaternary-background-color: #01313d;
 
     --ls-block-properties-background-color: #02222a;
     --ls-search-background-color: var(--ls-primary-background-color);
@@ -52,6 +53,7 @@
     --ls-primary-background-color: white;
     --ls-secondary-background-color: #D8E1E8;
     --ls-tertiary-background-color: #f0f8ff;
+    --ls-quaternary-background-color: #f7f7f7;
 
     --ls-block-properties-background-color: var(--ls-tertiary-background-color);
     --ls-search-background-color: var(--ls-primary-background-color);

+ 9 - 9
resources/css/table.css

@@ -10,25 +10,25 @@ table {
 }
 
 tr {
-    border-bottom: 1px solid #ddd;
+    border-bottom: 1px solid var(--ls-border-color);
 }
 
 th {
     font-size: 14px;
     font-weight: 400;
     color: #039;
-    border-bottom: 2px solid #6678b1;
+    border-bottom: 2px solid var(--ls-border-color);
     padding: 10px 8px;
 }
 
 td {
-    border-bottom: 1px solid #ccc;
+    border-bottom: 1px solid var(--ls-border-color);
     padding: 6px 8px;
     text-align: left;
 }
 
-tr:nth-child(even) {background: #F7F7F7}
-tr:nth-child(odd) {background: #FFF}
+tr:nth-child(even) {background: var(--ls-quaternary-background-color);}
+tr:nth-child(odd) {background: var(--ls-primary-background-color);}
 
 caption.t-above {caption-side:top}
 caption.t-bottom {caption-side:bottom}
@@ -39,14 +39,14 @@ figcaption{margin-top:.3em}
 .org-center{text-align:center}
 
 .dark-theme th {
-    color: #a4b5b6;
+    color: var(--ls-primary-text-color);
 }
 
-.dark-theme tr:nth-child(even) {background: #01313d}
-.dark-theme tr:nth-child(odd) {background: #002b36}
+.dark-theme tr:nth-child(even) {background: var(--ls-quaternary-background-color);}
+.dark-theme tr:nth-child(odd) {background: var(--ls-primary-background-color);}
 .dark-theme td, .dark-theme tr {
     border-bottom: none;
 }
 .dark-theme th {
-    border-bottom: 2px solid #546376;
+    border-bottom: 2px solid var(--ls-border-color);
 }

+ 36 - 39
src/main/frontend/components/diff.cljs

@@ -5,6 +5,7 @@
             [frontend.handler.git :as git-handler]
             [frontend.handler.file :as file]
             [frontend.handler.notification :as notification]
+            [frontend.handler.common :as common-handler]
             [frontend.state :as state]
             [clojure.string :as string]
             [frontend.db :as db]
@@ -138,38 +139,34 @@
   {:will-mount
    (fn [state]
      (when-let [repo (state/get-current-repo)]
-       (git-handler/get-latest-commit
-        repo
-        (fn [commit]
-          (let [local-oid (gobj/get commit "oid")
-                remote-oid (db/get-key-value repo
-                                             :git/remote-latest-commit)]
-            (p/let [result (git/get-local-diffs repo local-oid remote-oid)]
-              (reset! diffs result)
-              (reset! remote-hash-id remote-oid)
-              (doseq [{:keys [type path]} result]
-                (when (contains? #{"added" "modify"}
-                                 type)
-                  (github/get-content
-                   (state/get-github-token repo)
-                   repo
-                   path
-                   remote-oid
-                   (fn [{:keys [repo-url path ref content]}]
-                     (swap! state/state
-                            assoc-in [:github/contents repo-url remote-oid path] content))
-                   (fn [response]
-                     (when (= (gobj/get response "status") 401)
-                       (notification/show!
-                        [:span.text-gray-700.mr-2
-                         (util/format
-                          "Please make sure that you've installed the logseq app for the repo %s on GitHub. "
-                          repo)
-                         (ui/button
-                          "Install Logseq on GitHub"
-                          :href (str "https://github.com/apps/" config/github-app-name "/installations/new"))]
-                        :error
-                        false)))))))))))
+       (p/let [remote-latest-commit (common-handler/get-remote-ref repo)
+               local-latest-commit (common-handler/get-ref repo)
+               result (git/get-local-diffs repo local-latest-commit remote-latest-commit)]
+         (reset! diffs result)
+         (reset! remote-hash-id remote-latest-commit)
+         (doseq [{:keys [type path]} result]
+           (when (contains? #{"added" "modify"}
+                            type)
+             (github/get-content
+              (state/get-github-token repo)
+              repo
+              path
+              remote-latest-commit
+              (fn [{:keys [repo-url path ref content]}]
+                (swap! state/state
+                       assoc-in [:github/contents repo-url remote-latest-commit path] content))
+              (fn [response]
+                (when (= (gobj/get response "status") 401)
+                  (notification/show!
+                   [:span.text-gray-700.mr-2
+                    (util/format
+                     "Please make sure that you've installed the logseq app for the repo %s on GitHub. "
+                     repo)
+                    (ui/button
+                      "Install Logseq on GitHub"
+                      :href (str "https://github.com/apps/" config/github-app-name "/installations/new"))]
+                   :error
+                   false))))))))
      state)
    :will-unmount
    (fn [state]
@@ -206,13 +203,13 @@
          (if pushing?
            [:span (ui/loading "Pushing")]
            (ui/button "Commit and push"
-                      :on-click
-                      (fn []
-                        (let [commit-message (if (string/blank? @commit-message)
-                                               "Merge"
-                                               @commit-message)]
-                          (reset! *pushing? true)
-                          (git-handler/commit-and-force-push! commit-message *pushing?)))))]]
+             :on-click
+             (fn []
+               (let [commit-message (if (string/blank? @commit-message)
+                                      "Merge"
+                                      @commit-message)]
+                 (reset! *pushing? true)
+                 (git-handler/commit-and-force-push! commit-message *pushing?)))))]]
 
        :else
        [:div "No diffs"])]))

+ 23 - 18
src/main/frontend/components/right_sidebar.cljs

@@ -135,7 +135,7 @@
           (block-cp repo idx block-data)]]))
 
     :page
-    (let [page-name (get-in block-data [:page :page/name])]
+    (let [page-name (:page/name block-data)]
       [[:a {:href (rfe/href :page {:name (util/url-encode page-name)})}
         (util/capitalize-all page-name)]
        [:div.ml-2
@@ -171,24 +171,29 @@
 
 (rum/defc sidebar-item < rum/reactive
   [repo idx db-id block-type block-data t]
-  (let [collapse? (state/sub [:ui/sidebar-collapsed-blocks db-id])
-        item (build-sidebar-item repo idx db-id block-type block-data t)]
+  (let [item
+        (if (= :page block-type)
+          (let [page (db/query-entity-in-component db-id)]
+            (when (seq page)
+              (build-sidebar-item repo idx db-id block-type page t)))
+          (build-sidebar-item repo idx db-id block-type block-data t))]
     (when item
-      [:div.sidebar-item.content
-       (let [[title component] item]
-         [:div.flex.flex-col
-          [:div.flex.flex-row.justify-between
-           [:div.flex.flex-row.justify-center
-            [:a.opacity-50.hover:opacity-100.flex.items-center.pr-1
-             {:on-click #(state/sidebar-block-toggle-collapse! db-id)}
-             (if collapse?
-               (svg/caret-right)
-               (svg/caret-down))]
-            [:div.ml-1
-             title]]
-           (close #(state/sidebar-remove-block! idx))]
-          [:div {:class (if collapse? "hidden" "initial")}
-           component]])])))
+      (let [collapse? (state/sub [:ui/sidebar-collapsed-blocks db-id])]
+        [:div.sidebar-item.content
+         (let [[title component] item]
+           [:div.flex.flex-col
+            [:div.flex.flex-row.justify-between
+             [:div.flex.flex-row.justify-center
+              [:a.opacity-50.hover:opacity-100.flex.items-center.pr-1
+               {:on-click #(state/sidebar-block-toggle-collapse! db-id)}
+               (if collapse?
+                 (svg/caret-right)
+                 (svg/caret-down))]
+              [:div.ml-1
+               title]]
+             (close #(state/sidebar-remove-block! idx))]
+            [:div {:class (if collapse? "hidden" "initial")}
+             component]])]))))
 
 (defn- get-page
   [match]

+ 4 - 0
src/main/frontend/config.cljs

@@ -249,6 +249,10 @@
 (defonce default-pages-directory "pages")
 (defonce default-draw-directory "draws")
 
+(defn get-pages-directory
+  []
+  (or (state/get-pages-directory) default-pages-directory))
+
 (defn draw?
   [path]
   (util/starts-with? path default-draw-directory))

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

@@ -213,6 +213,20 @@
    (when-let [db (get-conn repo)]
      (d/entity db id-or-lookup-ref))))
 
+(defn query-entity-in-component
+  ([id-or-lookup-ref]
+   (entity (state/get-current-repo) id-or-lookup-ref))
+  ([repo id-or-lookup-ref]
+   (let [k [:entity id-or-lookup-ref]
+         result-atom (:result (get @query-state k))]
+     (when-let [component *query-component*]
+       (add-query-component! k component))
+     (when-let [db (get-conn repo)]
+       (let [result (d/entity db id-or-lookup-ref)
+             result-atom (or result-atom (atom nil))]
+         (set! (.-state result-atom) result)
+         (add-q! k nil nil result-atom identity identity identity))))))
+
 (def touch d/touch)
 
 (defn get-current-page
@@ -2499,6 +2513,5 @@
                :repo repo
                :git/cloned? (cloned? repo)
                :git/status (get-key-value repo :git/status)
-               :git/latest-commit (get-key-value repo :git/latest-commit)
                :git/error (get-key-value repo :git/error)})
             repos))))

+ 0 - 4
src/main/frontend/db_schema.cljs

@@ -24,10 +24,6 @@
    ;; Git
    :repo/url        {:db/unique :db.unique/identity}
    :repo/cloned?    {}
-   ;; local
-   :git/latest-commit {}
-   ;; remote
-   :git/remote-latest-commit {}
    :git/status {}
    :git/last-pulled-at {}
    ;; last error, better we should record all the errors

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

@@ -193,7 +193,7 @@ title: How to take dummy notes?
         :git/token-server "The server will never store it"
         :git/create-personal-access-token "How to create a Github personal access token?"
         :git/push "Push now"
-        :git/push-failed "Push failed, please check your network!"
+        :git/push-failed "Push failed!"
         :git/local-changes-synced "All local changes are synced!"
         :git/pull "Pull now"
         :git/last-pull "Last pulled at"
@@ -441,7 +441,7 @@ title: How to take dummy notes?
            :git/token-server "服务器将永远不会存储它"
            :git/create-personal-access-token "如何创建 Github 个人访问令牌?"
            :git/push "现在 push"
-           :git/push-failed "Push 失败, 请检查网络!"
+           :git/push-failed "Push 失败!"
            :git/local-changes-synced "所有本地更改已同步!"
            :git/pull "现在 pull"
            :git/last-pull "最后 pull 时间 "

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

@@ -12,7 +12,8 @@
             [frontend.handler.repo :as repo-handler]
             [frontend.handler.file :as file-handler]
             [frontend.handler.ui :as ui-handler]
-            [frontend.ui :as ui]))
+            [frontend.ui :as ui]
+            [goog.object :as gobj]))
 
 (defn- watch-for-date!
   []
@@ -103,6 +104,16 @@
                            (set! (.-returnValue (or e js/window.event)) message)
                            message)))))
 
+(defn- handle-connection-change
+  [e]
+  (let [online? (= (gobj/get e "type") "online")]
+    (state/set-online! online?)))
+
+(defn set-network-watcher!
+  []
+  (js/window.addEventListener "online" handle-connection-change)
+  (js/window.addEventListener "offline" handle-connection-change))
+
 (defn start!
   [render]
   (let [me (and js/window.user (bean/->clj js/window.user))
@@ -114,6 +125,8 @@
     (state/set-db-restoring! true)
     (render)
 
+    (set-network-watcher!)
+
     (util/indexeddb-check?
      (fn [_error]
        (notification/show! "Sorry, it seems that your browser doesn't support IndexedDB, we recommend to use latest Chrome(Chromium) or Firefox(Non-private mode)." :error false)

+ 46 - 12
src/main/frontend/handler/common.cljs

@@ -4,21 +4,55 @@
             [cljs-bean.core :as bean]
             [promesa.core :as p]
             [frontend.util :as util]
-            [frontend.text :as text]))
+            [frontend.text :as text]
+            [frontend.git :as git]
+            [frontend.db :as db]))
 
+(defn get-ref
+  [repo-url]
+  (git/resolve-ref repo-url "HEAD"))
+
+(defn get-remote-ref
+  [repo-url]
+  (let [branch (state/get-default-branch repo-url)]
+    ;; TODO: what if the remote is not named "origin", check the api from isomorphic-git
+    (git/resolve-ref repo-url (str "refs/remotes/origin/" branch))))
+
+
+;; Should include un-pushed committed files too
 (defn check-changed-files-status
-  []
-  (when-let [repo (state/get-current-repo)]
-    (when (and
-           (gobj/get js/window "workerThread")
-           (gobj/get js/window.workerThread "getChangedFiles"))
-      (->
-       (p/let [files (js/window.workerThread.getChangedFiles (util/get-repo-dir repo))]
-         (let [files (bean/->clj files)]
-           (state/set-changed-files! repo files)))
-       (p/catch (fn [error]
-                  (js/console.dir error)))))))
+  ([]
+   (check-changed-files-status (state/get-current-repo)))
+  ([repo]
+   (when (and
+          repo
+          (gobj/get js/window "workerThread")
+          (gobj/get js/window.workerThread "getChangedFiles"))
+     (->
+      (p/let [files (js/window.workerThread.getChangedFiles (util/get-repo-dir repo))]
+        (let [files (bean/->clj files)]
+          (p/let [remote-latest-commit (get-remote-ref repo)
+                  local-latest-commit (get-ref repo)
+                  descendent? (git/descendent? repo local-latest-commit remote-latest-commit)
+                  diffs (git/get-diffs repo local-latest-commit remote-latest-commit)]
+            (let [files (if descendent?
+                          (->> (concat (map :path diffs) files)
+                               distinct)
+                          files)]
+              (state/set-changed-files! repo files)))))
+      (p/catch (fn [error]
+                 (js/console.dir error)))))))
 
 (defn copy-to-clipboard-without-id-property!
   [content]
   (util/copy-to-clipboard! (text/remove-id-property content)))
+
+(comment
+  (let [repo (state/get-current-repo)]
+    (p/let [remote-oid (get-remote-ref repo)
+            local-oid (get-ref repo)
+            diffs (git/get-diffs repo local-oid remote-oid)]
+      (println {:local-oid local-oid
+                :remote-oid remote-oid
+                :diffs diffs})))
+  )

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

@@ -436,7 +436,7 @@
                  path (str
                        (if journal-page?
                          config/default-journals-directory
-                         config/default-pages-directory)
+                         (config/get-pages-directory))
                        "/"
                        (if journal-page?
                          (date/journal-title->default title)
@@ -666,7 +666,7 @@
             path (str
                   (if journal-page?
                     config/default-journals-directory
-                    config/default-pages-directory)
+                    (config/get-pages-directory))
                   "/"
                   (if journal-page?
                     (date/journal-title->default title)

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

@@ -20,7 +20,7 @@
                        (when-let [text (:text file)]
                          (let [path (str (if journal?
                                            config/default-journals-directory
-                                           config/default-pages-directory)
+                                           (config/get-pages-directory))
                                          "/"
                                          (if journal?
                                            (date/journal-title->default title)

+ 9 - 45
src/main/frontend/handler/git.cljs

@@ -12,14 +12,6 @@
             [frontend.handler.common :as common-handler]
             [cljs-time.local :as tl]))
 
-(defn- set-latest-commit!
-  [repo-url hash]
-  (db/set-key-value repo-url :git/latest-commit hash))
-
-(defn- set-remote-latest-commit!
-  [repo-url hash]
-  (db/set-key-value repo-url :git/remote-latest-commit hash))
-
 (defn- set-git-status!
   [repo-url value]
   (db/set-key-value repo-url :git/status value)
@@ -44,47 +36,19 @@
        (p/catch (fn [error]
                   (println "git add '" file "' failed: " error)
                   (js/console.error error))))))
-(defn get-latest-commit
-  ([repo-url handler]
-   (get-latest-commit repo-url handler 1))
-  ([repo-url handler length]
-   (-> (p/let [commits (git/log repo-url length)]
-         (handler (if (= length 1)
-                    (first commits)
-                    commits)))
-       (p/catch (fn [error]
-                  (println "get latest commit failed: " error)
-                  (js/console.log (.-stack error))
-                  ;; TODO: safe check
-                  (println "It might be an empty repo"))))))
-
-(defn set-latest-commit-if-exists! [repo-url]
-  (get-latest-commit
-   repo-url
-   (fn [commit]
-     (when-let [hash (gobj/get commit "oid")]
-       (set-latest-commit! repo-url hash)))))
-
-(defn set-remote-latest-commit-if-exists! [repo-url]
-  (get-latest-commit
-   repo-url
-   (fn [commit]
-     (when-let [hash (gobj/get commit "oid")]
-       (set-remote-latest-commit! repo-url hash)))))
 
 (defn commit-and-force-push!
   [commit-message pushing?]
   (when-let [repo (frontend.state/get-current-repo)]
-    (let [remote-oid (db/get-key-value repo
-                                       :git/remote-latest-commit)]
-      (p/let [commit-oid (git/commit repo commit-message (array remote-oid))
-              result (git/write-ref! repo commit-oid)
-              push-result (git/push repo
-                                    (state/get-github-token repo)
-                                    true)]
-        (reset! pushing? false)
-        (notification/clear! nil)
-        (route-handler/redirect! {:to :home})))))
+    (p/let [remote-oid (common-handler/get-remote-ref repo)
+            commit-oid (git/commit repo commit-message (array remote-oid))
+            result (git/write-ref! repo commit-oid)
+            push-result (git/push repo
+                                  (state/get-github-token repo)
+                                  true)]
+      (reset! pushing? false)
+      (notification/clear! nil)
+      (route-handler/redirect! {:to :home}))))
 
 (defn git-set-username-email!
   [repo-url {:keys [name email]}]

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

@@ -30,7 +30,7 @@
         journal-page? (date/valid-journal-title? title)
         directory (if journal-page?
                     config/default-journals-directory
-                    config/default-pages-directory)]
+                    (config/get-pages-directory))]
     (when dir
       (p/let [_ (-> (fs/mkdir (str dir "/" directory))
                     (p/catch (fn [_e])))]

+ 89 - 95
src/main/frontend/handler/repo.cljs

@@ -144,9 +144,9 @@
 (defn create-dummy-notes-page
   [repo-url content]
   (let [repo-dir (util/get-repo-dir repo-url)
-        path (str config/default-pages-directory "/how_to_make_dummy_notes.md")
+        path (str (config/get-pages-directory) "/how_to_make_dummy_notes.md")
         file-path (str "/" path)]
-    (p/let [_ (-> (fs/mkdir (str repo-dir "/" config/default-pages-directory))
+    (p/let [_ (-> (fs/mkdir (str repo-dir "/" (config/get-pages-directory)))
                   (p/catch (fn [_e])))
             _file-exists? (fs/create-if-not-exists repo-dir file-path content)]
       (db/reset-file! repo-url path content))))
@@ -303,90 +303,81 @@
          (db/get-conn repo-url true)
          (db/cloned? repo-url)
          token)
-    (let [remote-latest-commit (db/get-key-value repo-url :git/remote-latest-commit)
-          local-latest-commit (db/get-key-value repo-url :git/latest-commit)]
-      (p/let [descendent? (git/descendent? repo-url local-latest-commit remote-latest-commit)]
-        (when (or (= local-latest-commit remote-latest-commit)
-                  (nil? local-latest-commit)
-                  (not descendent?)
-                  force-pull?)
-          (p/let [files (js/window.workerThread.getChangedFiles (util/get-repo-dir repo-url))]
-            (when (empty? files)
-              (let [status (db/get-key-value repo-url :git/status)]
-                (when (or
-                       force-pull?
-                       (and
-                        ;; (not= status :push-failed)
-                        (not= status :pushing)
-                        (not (state/get-edit-input-id))
-                        (not (state/in-draw-mode?))))
-                  (git-handler/set-git-status! repo-url :pulling)
-                  (let [latest-commit (db/get-key-value repo-url :git/latest-commit)]
-                    (->
-                     (p/let [result (git/fetch repo-url token)]
-                       (let [{:keys [fetchHead]} (bean/->clj result)]
-                         (when fetchHead
-                           (git-handler/set-remote-latest-commit! repo-url fetchHead))
-                         (-> (git/merge repo-url)
-                             (p/then (fn [result]
-                                       (-> (git/checkout repo-url)
-                                           (p/then (fn [result]
-                                                     (git-handler/set-git-status! repo-url nil)
-                                                     (git-handler/set-git-last-pulled-at! repo-url)
-                                                     (when (and latest-commit fetchHead
-                                                                (not= latest-commit fetchHead))
-                                                       (p/let [diffs (git/get-diffs repo-url latest-commit fetchHead)]
-                                                         (when (seq diffs)
-                                                           (load-db-and-journals! repo-url diffs false)
-                                                           (git-handler/set-latest-commit! repo-url fetchHead))))))
-                                           (p/catch (fn [error]
-                                                      (git-handler/set-git-status! repo-url :checkout-failed)
-                                                      (git-handler/set-git-error! repo-url error))))
-                                       (state/set-changed-files! repo-url nil)))
-                             (p/catch (fn [error]
-                                        (println "Git pull error:")
-                                        (js/console.error error)
-                                        (git-handler/set-git-status! repo-url :merge-failed)
-                                        (git-handler/set-git-error! repo-url error)
-                                        (git-handler/get-latest-commit
-                                         repo-url
-                                         (fn [commit]
-                                           (let [local-oid (gobj/get commit "oid")
-                                                 remote-oid (db/get-key-value repo-url
-                                                                              :git/remote-latest-commit)]
-                                             (p/let [result (git/get-local-diffs repo-url local-oid remote-oid)]
-                                               (if (seq result)
-                                                 (do
-                                                   (notification/show!
-                                                    [:p.content
-                                                     "Failed to merge, please "
-                                                     [:span.text-gray-700.font-bold
-                                                      "resolve any diffs first."]]
-                                                    :error)
-                                                   (route-handler/redirect! {:to :diff}))
-                                                 (push repo-url {:commit-push? true
-                                                                 :force? true
-                                                                 :commit-message "Merge push without diffed files"})))))))))))
-                     (p/catch (fn [error]
-                                (println "Pull error:" (str error))
-                                (js/console.error error)
-                                ;; token might be expired, request new token
-
-                                (cond
-                                  (and (or (string/includes? (str error) "401")
-                                           (string/includes? (str error) "404"))
-                                       (not fallback?))
-                                  (request-app-tokens!
-                                   (fn []
-                                     (pull repo-url (state/get-github-token repo-url) {:fallback? true}))
-                                   nil)
-
-                                  (or (string/includes? (str error) "401")
-                                      (string/includes? (str error) "404"))
-                                  (show-install-error! repo-url (util/format "Failed to fetch %s." repo-url))
-
-                                  :else
-                                  nil))))))))))))))
+    (p/let [remote-latest-commit (common-handler/get-remote-ref repo-url)
+            local-latest-commit (common-handler/get-ref repo-url)
+            descendent? (git/descendent? repo-url local-latest-commit remote-latest-commit)]
+      (when (or (= local-latest-commit remote-latest-commit)
+                (nil? local-latest-commit)
+                (not descendent?)
+                force-pull?)
+        (p/let [files (js/window.workerThread.getChangedFiles (util/get-repo-dir repo-url))]
+          (when (empty? files)
+            (let [status (db/get-key-value repo-url :git/status)]
+              (when (or
+                     force-pull?
+                     (and
+                      (not= status :pushing)
+                      (not (state/get-edit-input-id))
+                      (not (state/in-draw-mode?))))
+                (git-handler/set-git-status! repo-url :pulling)
+                (->
+                 (p/let [result (git/fetch repo-url token)]
+                   (let [{:keys [fetchHead]} (bean/->clj result)]
+                     (-> (git/merge repo-url)
+                         (p/then (fn [result]
+                                   (-> (git/checkout repo-url)
+                                       (p/then (fn [result]
+                                                 (git-handler/set-git-status! repo-url nil)
+                                                 (git-handler/set-git-last-pulled-at! repo-url)
+                                                 (when (and local-latest-commit fetchHead
+                                                            (not= local-latest-commit fetchHead))
+                                                   (p/let [diffs (git/get-diffs repo-url local-latest-commit fetchHead)]
+                                                     (when (seq diffs)
+                                                       (load-db-and-journals! repo-url diffs false))))
+                                                 (common-handler/check-changed-files-status repo-url)))
+                                       (p/catch (fn [error]
+                                                  (git-handler/set-git-status! repo-url :checkout-failed)
+                                                  (git-handler/set-git-error! repo-url error))))))
+                         (p/catch (fn [error]
+                                    (println "Git pull error:")
+                                    (js/console.error error)
+                                    (git-handler/set-git-status! repo-url :merge-failed)
+                                    (git-handler/set-git-error! repo-url error)
+                                    (p/let [remote-latest-commit (common-handler/get-remote-ref repo-url)
+                                            local-latest-commit (common-handler/get-ref repo-url)
+                                            result (git/get-local-diffs repo-url local-latest-commit remote-latest-commit)]
+                                      (if (seq result)
+                                        (do
+                                          (notification/show!
+                                           [:p.content
+                                            "Failed to merge, please "
+                                            [:span.text-gray-700.font-bold
+                                             "resolve any diffs first."]]
+                                           :error)
+                                          (route-handler/redirect! {:to :diff}))
+                                        (push repo-url {:commit-push? true
+                                                        :force? true
+                                                        :commit-message "Merge push without diffed files"}))))))))
+                 (p/catch (fn [error]
+                            (println "Pull error:" (str error))
+                            (js/console.error error)
+                            ;; token might be expired, request new token
+
+                            (cond
+                              (and (or (string/includes? (str error) "401")
+                                       (string/includes? (str error) "404"))
+                                   (not fallback?))
+                              (request-app-tokens!
+                               (fn []
+                                 (pull repo-url (state/get-github-token repo-url) {:fallback? true}))
+                               nil)
+
+                              (or (string/includes? (str error) "401")
+                                  (string/includes? (str error) "404"))
+                              (show-install-error! repo-url (util/format "Failed to fetch %s." repo-url))
+
+                              :else
+                              nil))))))))))))
 
 (defn push
   [repo-url {:keys [commit-message fallback? diff-push? commit-push? force?]
@@ -410,7 +401,6 @@
                                      "Logseq auto save"
                                      commit-message)]
                 (p/let [commit-oid (git/commit repo-url commit-message)]
-                  (git-handler/set-latest-commit-if-exists! repo-url)
                   (git-handler/set-git-status! repo-url :pushing)
                   (when-let [token (state/get-github-token repo-url)]
                     (util/p-handle
@@ -418,12 +408,11 @@
                      (fn [result]
                        (git-handler/set-git-status! repo-url nil)
                        (git-handler/set-git-error! repo-url nil)
-                       (state/set-changed-files! repo-url nil)
-                       (git-handler/set-remote-latest-commit! repo-url commit-oid))
+                       (common-handler/check-changed-files-status repo-url))
                      (fn [error]
                        (println "Git push error: ")
                        (js/console.error error)
-                       (common-handler/check-changed-files-status)
+                       (common-handler/check-changed-files-status repo-url)
                        (let [permission? (or (string/includes? (str error) "401")
                                              (string/includes? (str error) "404"))]
                          (cond
@@ -439,8 +428,15 @@
                            (do
                              (git-handler/set-git-status! repo-url :push-failed)
                              (git-handler/set-git-error! repo-url error)
-                             (when permission?
-                               (show-install-error! repo-url (util/format "Failed to push to %s. " repo-url)))))))))))))
+                             (cond
+                               permission?
+                               (show-install-error! repo-url (util/format "Failed to push to %s. " repo-url))
+
+                               (state/online?)
+                               (pull repo-url token {:force-pull? true})
+
+                               :else    ; offline
+                               nil)))))))))))
           (p/catch (fn [error]
                      (println "Git push error: ")
                      (js/console.dir error)))))))
@@ -469,9 +465,7 @@
         (state/set-git-clone-repo! "")
         (state/set-current-repo! repo-url)
         (db/start-db-conn! (:me @state/state) repo-url)
-        (db/mark-repo-as-cloned repo-url)
-        (git-handler/set-latest-commit-if-exists! repo-url)
-        (git-handler/set-remote-latest-commit-if-exists! repo-url))
+        (db/mark-repo-as-cloned repo-url))
       (fn [e]
         (if (and (not fallback?)
                  (or (string/includes? (str e) "401")

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

@@ -22,6 +22,8 @@
     :repo/importing-to-db? nil
     :repo/sync-status {}
     :repo/changed-files nil
+    ;; TODO: how to detect the network reliably?
+    :network/online? true
     :indexeddb/support? true
     ;; TODO: save in local storage so that if :changed? is true when user
     ;; reloads the browser, the app should re-index the repo (another way
@@ -178,6 +180,11 @@
 
      (get-in @state [:me :preferred_format] "markdown")))))
 
+(defn get-pages-directory
+  []
+  (when-let [repo (get-current-repo)]
+    (:pages-directory (get-config repo))))
+
 (defn get-preferred-workflow
   []
   (keyword
@@ -833,6 +840,14 @@
   []
   (get-in @state [:repo/changed-files (get-current-repo)]))
 
+(defn set-online!
+  [value]
+  (set-state! :network/online? value))
+
+(defn online?
+  []
+  (:network/online? @state))
+
 (defonce editor-op (atom nil))
 (defn set-editor-op!
   [value]

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

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