setups.cljs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. (ns frontend.components.onboarding.setups
  2. (:require [frontend.state :as state]
  3. [rum.core :as rum]
  4. [frontend.ui :as ui]
  5. [frontend.context.i18n :refer [t]]
  6. [frontend.components.svg :as svg]
  7. [frontend.handler.page :as page-handler]
  8. [frontend.handler.route :as route-handler]
  9. [frontend.handler.ui :as ui-handler]
  10. [frontend.util :as util]
  11. [frontend.handler.web.nfs :as nfs]
  12. [frontend.mobile.util :as mobile-util]
  13. [frontend.handler.notification :as notification]
  14. [frontend.handler.external :as external-handler]
  15. [frontend.modules.shortcut.core :as shortcut]
  16. [clojure.string :as string]
  17. [goog.object :as gobj]))
  18. (defonce DEVICE (if (util/mobile?) "phone" "computer"))
  19. (rum/defc setups-container
  20. [flag content]
  21. [:div.cp__onboarding-setups.flex.flex-1
  22. (let [picker? (= flag :picker)]
  23. [:div.inner-card.flex.flex-col.items-center
  24. [:h1.text-xl
  25. (if picker?
  26. [:span [:strong (ui/icon "heart")] "Welcome to " [:strong "Logseq!"]]
  27. [:span [:strong (ui/icon "file-import")] "Import existing notes"])]
  28. [:h2
  29. (if picker?
  30. "First you need to choose a folder where Logseq will store your thoughts, ideas, notes."
  31. "You can also do this later in the app.")]
  32. content])])
  33. (rum/defc mobile-intro
  34. []
  35. [:div.mobile-intro
  36. (cond
  37. (mobile-util/native-ios?)
  38. [:div
  39. [:ul
  40. [:li "Save them in " [:span.font-bold "iCloud Drive's Logseq directory"] ", and sync them across devices using iCloud."]
  41. [:li "Save them in Logseq's directory of your device's local storage."]]]
  42. (mobile-util/native-android?)
  43. [:div
  44. "You can save them in your local storage, and use any third-party sync service to keep your notes sync with other devices. "
  45. "If you prefer to use Dropbox to sync your notes, you can use "
  46. [:a {:href "https://play.google.com/store/apps/details?id=com.ttxapps.dropsync"
  47. :target "_blank"}
  48. "Dropsync"]
  49. ". Or you can use "
  50. [:a {:href "https://play.google.com/store/apps/details?id=dk.tacit.android.foldersync.lite"
  51. :target "_blank"}
  52. "FolderSync"]
  53. "."]
  54. :else
  55. nil)])
  56. (rum/defcs picker < rum/reactive
  57. [_state]
  58. (let [parsing? (state/sub :repo/parsing-files?)]
  59. (setups-container
  60. :picker
  61. [:article.flex
  62. [:section.a
  63. [:strong "Let’s get you set up."]
  64. [:small (str "Where on your " DEVICE " do you want to save your work?")
  65. (when (mobile-util/native-platform?)
  66. (mobile-intro))]
  67. (if (or (nfs/supported?) (mobile-util/native-platform?))
  68. [:div.choose.flex.flex-col.items-center
  69. {:on-click #(page-handler/ls-dir-files!
  70. (fn []
  71. (shortcut/refresh!)))}
  72. [:i]
  73. [:div.control
  74. [:label.action-input.flex.items-center.justify-center.flex-col
  75. {:disabled parsing?}
  76. (if parsing?
  77. (ui/loading "")
  78. [[:strong "Choose a folder"]
  79. [:small "Open existing directory or Create a new one"]])]]]
  80. [:div.px-5
  81. (ui/admonition :warning
  82. [:p "It seems that your browser doesn't support the "
  83. [:a {:href "https://web.dev/file-system-access/"
  84. :target "_blank"}
  85. "new native filesystem API"]
  86. [:span ", please use any Chromium 86+ based browser like Chrome, Vivaldi, Edge, etc. Notice that the API doesn't support mobile browsers at the moment."]])])]
  87. [:section.b.flex.items-center.flex-col
  88. [:p.flex
  89. [:i.as-flex-center (ui/icon "zoom-question" {:style {:fontSize "22px"}})]
  90. [:span.flex-1.flex.flex-col
  91. [:strong "How Logseq saves your work"]
  92. [:small.opacity-60 "Inside the directory you choose, Logseq will create 4 folders."]]]
  93. [:p.text-sm.pt-5.tracking-wide
  94. [:span (str "Each page is a file stored only on your " DEVICE ".")]
  95. [:br]
  96. [:span "You may choose to sync it later."]]
  97. [:ul
  98. (for [[title label icon]
  99. [["Graphics & Documents" "/assets" "whiteboard"]
  100. ["Daily notes" "/journals" "calendar-plus"]
  101. ["PAGES" "/pages" "page"]
  102. []
  103. ["APP Internal" "/logseq" "tool"]
  104. ["Config File" "/logseq/config.edn"]]]
  105. (if-not title
  106. [:li.hr]
  107. [:li
  108. {:key title}
  109. [:i.as-flex-center
  110. {:class (when (string/ends-with? label ".edn") "is-file")}
  111. (when icon (ui/icon icon))]
  112. [:span
  113. [:strong.uppercase title]
  114. [:small.opacity-50 label]]]))]]])))
  115. (defonce *opml-imported-pages (atom nil))
  116. (defn- finished-cb
  117. []
  118. (route-handler/redirect-to-home!)
  119. (notification/show! "Import finished!" :success)
  120. (ui-handler/re-render-root!))
  121. (defn- roam-import-handler
  122. [e]
  123. (let [file (first (array-seq (.-files (.-target e))))
  124. file-name (gobj/get file "name")]
  125. (if (string/ends-with? file-name ".json")
  126. (do
  127. (state/set-state! :graph/importing :roam-json)
  128. (let [reader (js/FileReader.)]
  129. (set! (.-onload reader)
  130. (fn [e]
  131. (let [text (.. e -target -result)]
  132. (external-handler/import-from-roam-json!
  133. text
  134. #(do
  135. (state/set-state! :graph/importing nil)
  136. (finished-cb))))))
  137. (.readAsText reader file)))
  138. (notification/show! "Please choose a JSON file."
  139. :error))))
  140. (defn- lsq-import-handler
  141. [e]
  142. (let [file (first (array-seq (.-files (.-target e))))
  143. file-name (some-> (gobj/get file "name")
  144. (string/lower-case))
  145. edn? (string/ends-with? file-name ".edn")
  146. json? (string/ends-with? file-name ".json")]
  147. (if (or edn? json?)
  148. (do
  149. (state/set-state! :graph/importing :logseq)
  150. (let [reader (js/FileReader.)
  151. import-f (if edn?
  152. external-handler/import-from-edn!
  153. external-handler/import-from-json!)]
  154. (set! (.-onload reader)
  155. (fn [e]
  156. (let [text (.. e -target -result)]
  157. (import-f
  158. text
  159. #(do
  160. (state/set-state! :graph/importing nil)
  161. (finished-cb))))))
  162. (.readAsText reader file)))
  163. (notification/show! "Please choose an EDN or a JSON file."
  164. :error))))
  165. (defn- opml-import-handler
  166. [e]
  167. (let [file (first (array-seq (.-files (.-target e))))
  168. file-name (gobj/get file "name")]
  169. (if (string/ends-with? file-name ".opml")
  170. (do
  171. (state/set-state! :graph/importing :opml)
  172. (let [reader (js/FileReader.)]
  173. (set! (.-onload reader)
  174. (fn [e]
  175. (let [text (.. e -target -result)]
  176. (external-handler/import-from-opml! text
  177. (fn [pages]
  178. (reset! *opml-imported-pages pages)
  179. (state/set-state! :graph/importing nil)
  180. (finished-cb))))))
  181. (.readAsText reader file)))
  182. (notification/show! "Please choose a OPML file."
  183. :error))))
  184. (rum/defc importer < rum/reactive
  185. [{:keys [query-params]}]
  186. (if (state/sub :graph/importing)
  187. (let [{:keys [total current-idx current-page]} (state/sub :graph/importing-state)
  188. left-label [:div.flex.flex-row.font-bold
  189. (t :importing)
  190. [:div.hidden.md:flex.flex-row
  191. [:span.mr-1 ": "]
  192. [:div.text-ellipsis-wrapper {:style {:max-width 300}}
  193. current-page]]]
  194. width (js/Math.round (* (.toFixed (/ current-idx total) 2) 100))
  195. process (when (and total current-idx)
  196. (str current-idx "/" total))]
  197. (ui/progress-bar-with-label width left-label process))
  198. (setups-container
  199. :importer
  200. [:article.flex.flex-col.items-center.importer.py-16.px-8
  201. [:section.c.text-center
  202. [:h1 "Do you already have notes that you want to import?"]
  203. [:h2 "If they are in a JSON, EDN or Markdown format Logseq can work with them."]]
  204. [:section.d.md:flex
  205. [:label.action-input.flex.items-center.mx-2.my-2
  206. [:span.as-flex-center [:i (svg/roam-research 28)]]
  207. [:div.flex.flex-col
  208. [[:strong "RoamResearch"]
  209. [:small "Import a JSON Export of your Roam graph"]]]
  210. [:input.absolute.hidden
  211. {:id "import-roam"
  212. :type "file"
  213. :on-change roam-import-handler}]]
  214. [:label.action-input.flex.items-center.mx-2.my-2
  215. [:span.as-flex-center [:i (svg/logo 28)]]
  216. [:span.flex.flex-col
  217. [[:strong "EDN / JSON"]
  218. [:small "Import an EDN or a JSON Export of your Logseq graph"]]]
  219. [:input.absolute.hidden
  220. {:id "import-lsq"
  221. :type "file"
  222. :on-change lsq-import-handler}]]
  223. [:label.action-input.flex.items-center.mx-2.my-2
  224. [:span.as-flex-center (ui/icon "sitemap" {:style {:fontSize "26px"}})]
  225. [:span.flex.flex-col
  226. [[:strong "OPML"]
  227. [:small " Import OPML files"]]]
  228. [:input.absolute.hidden
  229. {:id "import-opml"
  230. :type "file"
  231. :on-change opml-import-handler}]]]
  232. (when (= "picker" (:from query-params))
  233. [:section.e
  234. [:a.button {:on-click #(route-handler/redirect-to-home!)} "Skip"]])])))