repo.cljs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. (ns frontend.components.repo
  2. (:require [clojure.string :as string]
  3. [frontend.components.commit :as commit]
  4. [frontend.components.encryption :as encryption]
  5. [frontend.components.svg :as svg]
  6. [frontend.components.widgets :as widgets]
  7. [frontend.config :as config]
  8. [frontend.context.i18n :as i18n]
  9. [frontend.db :as db]
  10. [frontend.encrypt :as e]
  11. [frontend.handler.common :as common-handler]
  12. [frontend.handler.export :as export-handler]
  13. [frontend.handler.page :as page-handler]
  14. [frontend.handler.repo :as repo-handler]
  15. [frontend.handler.route :as route-handler]
  16. [frontend.handler.web.nfs :as nfs-handler]
  17. [frontend.modules.shortcut.core :as shortcut]
  18. [frontend.state :as state]
  19. [frontend.ui :as ui]
  20. [frontend.util :as util]
  21. [frontend.version :as version]
  22. [reitit.frontend.easy :as rfe]
  23. [rum.core :as rum]))
  24. (rum/defc add-repo
  25. [args]
  26. (if-let [graph-types (get-in args [:query-params :graph-types])]
  27. (let [graph-types-s (->> (string/split graph-types #",")
  28. (mapv keyword))]
  29. (when (seq graph-types-s)
  30. (widgets/add-graph :graph-types graph-types-s)))
  31. (widgets/add-graph)))
  32. (rum/defc repos < rum/reactive
  33. []
  34. (let [repos (->> (state/sub [:me :repos])
  35. (remove #(= (:url %) config/local-repo)))
  36. repos (util/distinct-by :url repos)]
  37. (rum/with-context [[t] i18n/*tongue-context*]
  38. (if (seq repos)
  39. [:div#graphs
  40. [:h1.title "All Graphs"]
  41. [:p.ml-2.opacity-70
  42. (if (state/github-authed?)
  43. "A \"graph\" in Logseq could be either a local directory or a git repo."
  44. "A \"graph\" in Logseq means a local directory.")]
  45. [:div.pl-1.content.mt-3
  46. [:div.flex.flex-row.my-4
  47. (when (nfs-handler/supported?)
  48. [:div.mr-8
  49. (ui/button
  50. (t :open-a-directory)
  51. :on-click #(page-handler/ls-dir-files! shortcut/refresh!))])
  52. (when (and (state/logged?) (not (util/electron?)))
  53. (ui/button
  54. "Add another git repo"
  55. :href (rfe/href :repo-add nil {:graph-types "github"})
  56. :intent "logseq"))]
  57. (for [{:keys [id url] :as repo} repos]
  58. (let [local? (config/local-db? url)]
  59. [:div.flex.justify-between.mb-1 {:key id}
  60. (if local?
  61. [:a
  62. (config/get-local-dir url)]
  63. [:a {:target "_blank"
  64. :href url}
  65. (db/get-repo-path url)])
  66. [:div.controls
  67. (when (e/encrypted-db? url)
  68. [:a.control {:title "Show encryption information about this graph"
  69. :on-click (fn []
  70. (state/set-modal! (encryption/encryption-dialog url)))}
  71. "🔐"])
  72. [:a.text-gray-400.ml-4 {:title "No worries, unlink this graph will clear its cache only, it does not remove your files on the disk."
  73. :on-click (fn []
  74. (repo-handler/remove-repo! repo))}
  75. "Unlink"]]]))]]
  76. (widgets/add-graph)))))
  77. (defn refresh-cb []
  78. (page-handler/create-today-journal!)
  79. (shortcut/refresh!))
  80. (rum/defc sync-status < rum/reactive
  81. {:did-mount (fn [state]
  82. (js/setTimeout common-handler/check-changed-files-status 1000)
  83. state)}
  84. [repo]
  85. (when (and repo
  86. (string/starts-with? repo "https://"))
  87. (let [changed-files (state/sub [:repo/changed-files repo])
  88. should-push? (seq changed-files)
  89. git-status (state/sub [:git/status repo])
  90. pushing? (= :pushing git-status)
  91. pulling? (= :pulling git-status)
  92. git-failed? (contains?
  93. #{:push-failed
  94. :clone-failed
  95. :checkout-failed
  96. :fetch-failed
  97. :merge-failed}
  98. git-status)
  99. push-failed? (= :push-failed git-status)
  100. last-pulled-at (db/sub-key-value repo :git/last-pulled-at)
  101. ;; db-persisted? (state/sub [:db/persisted? repo])
  102. editing? (seq (state/sub :editor/editing?))]
  103. [:div.flex-row.flex.items-center.cp__repo-indicator
  104. (when pushing? svg/loading)
  105. (ui/dropdown
  106. (fn [{:keys [toggle-fn]}]
  107. [:div.cursor.w-2.h-2.sync-status.mr-2
  108. {:class (cond
  109. git-failed?
  110. "bg-red-500"
  111. (or
  112. ;; (not db-persisted?)
  113. editing?
  114. should-push? pushing?)
  115. "bg-orange-400"
  116. :else
  117. "bg-green-600")
  118. :style {:border-radius "50%"
  119. :margin-top 2}
  120. :on-mouse-over
  121. (fn [e]
  122. (toggle-fn)
  123. (js/setTimeout common-handler/check-changed-files-status 0))}])
  124. (fn [{:keys [toggle-fn]}]
  125. (rum/with-context [[t] i18n/*tongue-context*]
  126. [:div.p-2.rounded-md.shadow-xs.bg-base-3.flex.flex-col.sync-content
  127. {:on-mouse-leave toggle-fn}
  128. [:div
  129. [:div
  130. (cond
  131. push-failed?
  132. [:p (t :git/push-failed)]
  133. (and should-push? (seq changed-files))
  134. [:div.changes
  135. [:ul.overflow-y-auto {:style {:max-height 250}}
  136. (for [file changed-files]
  137. [:li {:key (str "sync-" file)}
  138. [:div.flex.flex-row.justify-between.align-items
  139. [:a {:href (rfe/href :file {:path file})}
  140. file]
  141. [:a.ml-4.text-sm.mt-1
  142. {:on-click (fn [e]
  143. (export-handler/download-file! file))}
  144. [:span (t :download)]]]])]]
  145. :else
  146. [:p (t :git/local-changes-synced)])]
  147. ;; [:a.text-sm.font-bold {:href "/diff"} "Check diff"]
  148. [:div.flex.flex-row.justify-between.align-items.mt-2
  149. (ui/button (t :git/push)
  150. :on-click (fn [] (state/set-modal! commit/add-commit-message)))
  151. (when pushing? svg/loading)]]
  152. [:hr]
  153. [:div
  154. (when-not (string/blank? last-pulled-at)
  155. [:p {:style {:font-size 12}} (t :git/last-pull)
  156. (str ": " last-pulled-at)])
  157. [:div.flex.flex-row.justify-between.align-items
  158. (ui/button (t :git/pull)
  159. :on-click (fn [] (repo-handler/pull-current-repo)))
  160. (when pulling? svg/loading)]
  161. [:a.mt-5.text-sm.opacity-50.block
  162. {:on-click (fn []
  163. (export-handler/export-repo-as-zip! repo))}
  164. (t :repo/download-zip)]
  165. [:p.pt-2.text-sm.opacity-50
  166. (t :git/version) (str " " version/version)]]])))])))
  167. (rum/defc repos-dropdown < rum/reactive
  168. [on-click]
  169. (when-let [current-repo (state/sub :git/current-repo)]
  170. (rum/with-context [[t] i18n/*tongue-context*]
  171. (let [get-repo-name (fn [repo]
  172. (if (config/local-db? repo)
  173. (config/get-local-dir repo)
  174. (db/get-repo-path repo)))
  175. repos (state/sub [:me :repos])
  176. repos (remove (fn [r] (= config/local-repo (:url r))) repos)
  177. switch-repos (remove (fn [repo]
  178. (= current-repo (:url repo)))
  179. repos)]
  180. (when (seq repos)
  181. (ui/dropdown-with-links
  182. (fn [{:keys [toggle-fn]}]
  183. [:a#repo-switch.fade-link.block.pr-2.whitespace-nowrap {:on-click toggle-fn}
  184. [:span
  185. [:span.repo-plus svg/plus]
  186. (let [repo-name (get-repo-name current-repo)
  187. repo-name (if (util/electron?)
  188. (last
  189. (string/split repo-name #"/"))
  190. repo-name)]
  191. [:span#repo-name {:title repo-name} repo-name])
  192. [:span.dropdown-caret.ml-1 {:style {:border-top-color "#6b7280"}}]]])
  193. (mapv
  194. (fn [{:keys [id url]}]
  195. {:title (get-repo-name url)
  196. :options {:class "ml-1"
  197. :on-click (fn []
  198. (repo-handler/push-if-auto-enabled! (state/get-current-repo))
  199. (state/set-current-repo! url)
  200. ;; load config
  201. (common-handler/reset-config! url nil)
  202. (shortcut/refresh!)
  203. (when-not (= :draw (state/get-current-route))
  204. (route-handler/redirect-to-home!))
  205. (when on-click
  206. (on-click url)))}})
  207. switch-repos)
  208. (cond->
  209. {:modal-class (util/hiccup->class
  210. "origin-top-right.absolute.left-0.mt-2.w-48.rounded-md.shadow-lg")
  211. :links-footer [:div
  212. (when (seq switch-repos) [:hr.my-4])
  213. [:a {:class "block px-4 py-2 text-sm transition ease-in-out duration-150 cursor menu-link"
  214. :href (rfe/href :repo-add)}
  215. (t :new-graph)]
  216. [:a {:class "block px-4 py-2 text-sm transition ease-in-out duration-150 cursor menu-link"
  217. :href (rfe/href :repos)}
  218. (t :all-graphs)]
  219. (let [nfs-repo? (config/local-db? current-repo)]
  220. (when (and nfs-repo?
  221. (not= current-repo config/local-repo)
  222. (nfs-handler/supported?))
  223. [:a {:class "block px-4 py-2 text-sm transition ease-in-out duration-150 cursor menu-link"
  224. :on-click (fn []
  225. (state/pub-event!
  226. [:modal/show
  227. [:div {:style {:max-width 700}}
  228. [:p "Refresh detects and processes files modified on your disk and diverged from the actual Logseq page content. Continue?"]
  229. (ui/button
  230. "Yes"
  231. :on-click (fn []
  232. (state/close-modal!)
  233. (nfs-handler/refresh! (state/get-current-repo) refresh-cb)))]]))}
  234. (t :sync-from-local-files)]))
  235. [:a {:class "block px-4 py-2 text-sm transition ease-in-out duration-150 cursor menu-link"
  236. :on-click (fn []
  237. (state/pub-event!
  238. [:modal/show
  239. [:div {:style {:max-width 700}}
  240. [:p "Re-index will discard the current graph, and then processes all the files again as they are currently stored on disk. You will lose unsaved changes and it might take a while. Continue?"]
  241. (ui/button
  242. "Yes"
  243. :on-click (fn []
  244. (state/close-modal!)
  245. (repo-handler/re-index!
  246. nfs-handler/rebuild-index!
  247. page-handler/create-today-journal!)))]]))}
  248. (t :re-index)]]}
  249. (seq switch-repos)
  250. (assoc :links-header [:div.font-medium.text-sm.opacity-70.px-4.py-2
  251. "Switch to:"]))))))))