repo.cljs 13 KB

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