| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- (ns frontend.components.diff
- (:require [rum.core :as rum]
- [frontend.util :as util]
- [frontend.config :as config]
- [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]
- [frontend.components.svg :as svg]
- [frontend.ui :as ui]
- [frontend.db :as db]
- [frontend.git :as git]
- [frontend.helper :as helper]
- [goog.object :as gobj]
- [promesa.core :as p]
- [frontend.github :as github]
- [frontend.diff :as diff]
- [medley.core :as medley]))
- (defonce remote-hash-id (atom nil))
- (defonce diff-state (atom {}))
- (defonce commit-message (atom ""))
- ;; TODO: use db :git/status
- (defonce *pushing? (atom nil))
- (defonce *edit? (atom false))
- (defonce *edit-content (atom ""))
- (defn- toggle-collapse?
- [path]
- (swap! diff-state update-in [path :collapse?] not))
- (defn- mark-as-resolved
- [path]
- (swap! diff-state assoc-in [path :resolved?] true)
- (swap! diff-state assoc-in [path :collapse?] true))
- (rum/defc diff-cp
- [diff]
- [:div
- (for [[idx {:keys [added removed value]}] diff]
- (let [bg-color (cond
- added "#057a55"
- removed "#d61f69"
- :else
- "initial")]
- [:span.diff {:key idx
- :style {:background-color bg-color}}
- value]))])
- (rum/defc file < rum/reactive
- [repo type path contents remote-oid component]
- (let [{:keys [collapse? resolved?]} (util/react (rum/cursor diff-state path))
- edit? (util/react *edit?)]
- [:div.cp__diff-file
- [:div.cp__diff-file-header
- [:a.mr-2 {:on-click (fn [] (toggle-collapse? path))}
- (if collapse?
- (svg/arrow-right)
- (svg/arrow-down))]
- [:span.cp__diff-file-header-content path]
- (when resolved?
- [:span.text-green-600
- {:dangerouslySetInnerHTML
- {:__html "✓"}}])]
- (let [content (get contents path)
- local-content (db/get-file path)]
- (if (not= content local-content)
- (let [local-content (or local-content "")
- content (or content "")
- diff (medley/indexed (diff/diff local-content content))
- diff? (some (fn [[_idx {:keys [added removed]}]]
- (or added removed))
- diff)]
- [:div.pre-line-white-space.p-2 {:class (if collapse? "hidden")
- :style {:overflow "auto"}}
- (if edit?
- [:div.grid.grid-cols-2.gap-1
- (diff-cp diff)
- (ui/textarea
- {:default-value local-content
- :on-change (fn [e]
- (reset! *edit-content (util/evalue e)))})]
- (diff-cp diff))
- (cond
- edit?
- [:div.mt-2
- (ui/button "Save"
- :on-click
- (fn []
- (reset! *edit? false)
- (let [new-content @*edit-content]
- (file/alter-file repo path new-content
- {:commit? false
- :re-render-root? true})
- (swap! state/state
- assoc-in [:github/contents repo remote-oid path] new-content)
- (mark-as-resolved path))))]
- diff?
- [:div.mt-2
- (ui/button "Use remote"
- :on-click
- (fn []
- ;; overwrite the file
- (file/alter-file repo path content
- {:commit? false
- :re-render-root? true})
- (mark-as-resolved path))
- :background "green")
- [:span.pl-2.pr-2 "or"]
- (ui/button "Keep local"
- :on-click
- (fn []
- ;; overwrite the file
- (swap! state/state
- assoc-in [:github/contents repo remote-oid path] local-content)
- (mark-as-resolved path))
- :background "pink")
- [:span.pl-2.pr-2 "or"]
- (ui/button "Edit"
- :on-click
- (fn []
- (reset! *edit? true)))]
- :else
- nil)])))]))
- ;; TODO: `n` shortcut for next diff, `p` for previous diff
- (rum/defcc diff <
- rum/reactive
- {:will-mount
- (fn [state]
- (when-let [repo (state/get-current-repo)]
- (p/let [remote-latest-commit (common-handler/get-remote-ref repo)
- local-latest-commit (common-handler/get-ref repo)
- result (git/get-diffs repo local-latest-commit remote-latest-commit)
- token (helper/get-github-token repo)]
- (reset! state/diffs result)
- (reset! remote-hash-id remote-latest-commit)
- (doseq [{:keys [type path]} result]
- (when (contains? #{"add" "modify"}
- type)
- (github/get-content
- token
- 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] (or content "")))
- (fn [response]
- (when (= (gobj/get response "status") 401)
- (notification/show!
- [:span.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]
- (reset! state/diffs nil)
- (reset! remote-hash-id nil)
- (reset! diff-state {})
- (reset! commit-message "")
- (reset! *pushing? nil)
- (reset! *edit? false)
- (reset! *edit-content "")
- state)}
- [component]
- (let [diffs (util/react state/diffs)
- remote-oid (util/react remote-hash-id)
- repo (state/get-current-repo)
- contents (if remote-oid (state/sub [:github/contents repo remote-oid]))
- pushing? (util/react *pushing?)]
- [:div#diffs {:style {:margin-bottom 200}}
- [:h1.title "Diff"]
- (cond
- (false? pushing?)
- [:div "No diffs"]
- (seq diffs)
- [:div#diffs-body
- (for [{:keys [type path]} diffs]
- (rum/with-key (file repo type path contents remote-oid component)
- path))
- [:div
- (ui/textarea
- {:placeholder "Commit message (optional)"
- :on-change (fn [e]
- (reset! commit-message (util/evalue e)))})
- (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?)))))]]
- :else
- [:div "No diffs"])]))
|