import.cljs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. (ns frontend.handler.db-based.import
  2. "Handles DB graph imports"
  3. (:require [cljs.pprint :as pprint]
  4. [clojure.edn :as edn]
  5. [frontend.config :as config]
  6. [frontend.db :as db]
  7. [frontend.handler.notification :as notification]
  8. [frontend.handler.repo :as repo-handler]
  9. [frontend.handler.ui :as ui-handler]
  10. [frontend.persist-db :as persist-db]
  11. [frontend.state :as state]
  12. [frontend.util :as util]
  13. [logseq.db :as ldb]
  14. [logseq.db.sqlite.export :as sqlite-export]
  15. [logseq.db.sqlite.util :as sqlite-util]
  16. [logseq.shui.ui :as shui]
  17. [promesa.core :as p]))
  18. (defn import-from-sqlite-db!
  19. [buffer bare-graph-name finished-ok-handler]
  20. (let [graph (str config/db-version-prefix bare-graph-name)]
  21. (->
  22. (p/do!
  23. (persist-db/<import-db graph buffer)
  24. (state/add-repo! {:url graph})
  25. (repo-handler/restore-and-setup-repo! graph {:import-type :sqlite-db})
  26. (state/set-current-repo! graph)
  27. (persist-db/<export-db graph {})
  28. (db/transact! graph (sqlite-util/import-tx :sqlite-db) {:import-db? true})
  29. (finished-ok-handler))
  30. (p/catch
  31. (fn [e]
  32. (js/console.error e)
  33. (notification/show!
  34. (str (.-message e))
  35. :error))))))
  36. (defn import-from-debug-transit!
  37. [bare-graph-name raw finished-ok-handler]
  38. (let [graph (str config/db-version-prefix bare-graph-name)
  39. datoms (ldb/read-transit-str raw)]
  40. (p/do!
  41. (persist-db/<new graph {:import-type :debug-transit
  42. :datoms datoms})
  43. (state/add-repo! {:url graph})
  44. (repo-handler/restore-and-setup-repo! graph {:import-type :debug-transit})
  45. (db/transact! graph (sqlite-util/import-tx :debug-transit) {:import-db? true})
  46. (state/set-current-repo! graph)
  47. (finished-ok-handler))))
  48. (defn- safe-build-edn-import [export-map import-options]
  49. (try
  50. (sqlite-export/build-import export-map (db/get-db) import-options)
  51. (catch :default e
  52. (js/console.error "Import EDN error: " e)
  53. {:error "An unexpected error occurred building the import. See the javascript console for details."})))
  54. (defn- import-edn-data-from-file
  55. [export-map]
  56. (let [{:keys [init-tx block-props-tx misc-tx error] :as _txs} (safe-build-edn-import export-map {})]
  57. ;; (cljs.pprint/pprint _txs)
  58. (if error
  59. (notification/show! error :error)
  60. (let [tx-meta {::sqlite-export/imported-data? true
  61. :import-db? true}
  62. repo (state/get-current-repo)]
  63. (p/do
  64. (db/transact! repo init-tx tx-meta)
  65. (when (seq block-props-tx)
  66. (db/transact! repo block-props-tx tx-meta))
  67. (when (seq misc-tx)
  68. (db/transact! repo misc-tx tx-meta)))))))
  69. (defn import-from-edn-file!
  70. "Creates a new DB graph and imports sqlite.build EDN file"
  71. [bare-graph-name file-body finished-ok-handler]
  72. (let [graph (str config/db-version-prefix bare-graph-name)
  73. finished-error-handler
  74. #(do
  75. (state/set-state! :graph/importing nil)
  76. (shui/dialog-close-all!))
  77. edn-data (try
  78. (edn/read-string file-body)
  79. (catch :default e
  80. (js/console.error e)
  81. (notification/show! "The given EDN file is not valid EDN. Please fix and try again."
  82. :error)
  83. (finished-error-handler)
  84. nil))]
  85. (when (some? edn-data)
  86. (-> (p/do!
  87. (persist-db/<new graph {:import-type :edn})
  88. (state/add-repo! {:url graph})
  89. (repo-handler/restore-and-setup-repo! graph {:import-type :edn})
  90. (state/set-current-repo! graph)
  91. (import-edn-data-from-file edn-data)
  92. (finished-ok-handler))
  93. (p/catch
  94. (fn [e]
  95. (js/console.error e)
  96. (notification/show! (str "Unexpected error: " (.-message e))
  97. :error)
  98. (finished-error-handler)))))))
  99. (defn- import-edn-data-from-form [import-inputs _e]
  100. (let [export-map (try (edn/read-string (:import-data @import-inputs)) (catch :default _err ::invalid-import))
  101. import-block? (::sqlite-export/block export-map)
  102. block (when import-block?
  103. (if-let [eid (:block-id (first (state/get-editor-args)))]
  104. (db/entity [:block/uuid eid])
  105. (notification/show! "No block found" :warning)))]
  106. (if (= ::invalid-import export-map)
  107. (notification/show! "The submitted EDN data is invalid! Please fix and try again." :warning)
  108. (let [{:keys [init-tx block-props-tx misc-tx error] :as txs}
  109. (safe-build-edn-import export-map (when block {:current-block block}))]
  110. (pprint/pprint txs)
  111. (if error
  112. (notification/show! error :error)
  113. ;; TODO: When not import-block, use metadata that supports undo
  114. (let [tx-meta (if import-block? {:outliner-op :save-block} {::sqlite-export/imported-data? true})
  115. repo (state/get-current-repo)]
  116. (-> (p/do
  117. (db/transact! repo init-tx tx-meta)
  118. (when (seq block-props-tx)
  119. (db/transact! repo block-props-tx tx-meta))
  120. (when (seq misc-tx)
  121. (db/transact! repo misc-tx tx-meta))
  122. (when-not import-block?
  123. (ui-handler/re-render-root!)
  124. (notification/show! "Import successful!" :success)))
  125. (p/catch (fn [e]
  126. (js/console.error "Import EDN error: " e)
  127. (notification/show! "An unexpected error occurred during import. See the javascript console for details." :error))))))
  128. ;; Also close cmd-k
  129. (shui/dialog-close-all!)))))
  130. (defn ^:export import-edn-data-dialog
  131. "Displays dialog which allows users to paste and import sqlite.build EDN Data"
  132. []
  133. (let [import-inputs (atom {:import-data "" :import-block? false})]
  134. (shui/dialog-open!
  135. [:div
  136. [:label.flex.my-2.text-lg "Import EDN Data"]
  137. #_[:label.block.flex.items-center.py-3
  138. (shui/checkbox {:on-checked-change #(swap! import-inputs update :import-block? not)})
  139. [:small.pl-2 (str "Import into current block")]]
  140. (shui/textarea {:placeholder "{}"
  141. :class "overflow-y-auto"
  142. :rows 10
  143. :auto-focus true
  144. :on-change (fn [^js e] (swap! import-inputs assoc :import-data (util/evalue e)))})
  145. (shui/button {:class "mt-3"
  146. :on-click (partial import-edn-data-from-form import-inputs)}
  147. "Import")])))