import.cljs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. (ns frontend.handler.db-based.import
  2. "Handles DB graph imports"
  3. (:require [clojure.edn :as edn]
  4. [datascript.core :as d]
  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. [frontend.modules.outliner.ui :as ui-outliner-tx]
  19. [frontend.modules.outliner.op :as outliner-op]))
  20. (defn import-from-sqlite-db!
  21. [buffer bare-graph-name finished-ok-handler]
  22. (let [graph (str config/db-version-prefix bare-graph-name)]
  23. (->
  24. (p/do!
  25. (persist-db/<import-db graph buffer)
  26. (state/add-repo! {:url graph})
  27. (repo-handler/restore-and-setup-repo! graph {:import-type :sqlite-db})
  28. (state/set-current-repo! graph)
  29. (persist-db/<export-db graph {})
  30. (db/transact! graph (sqlite-util/import-tx :sqlite-db) {:import-db? true})
  31. (finished-ok-handler))
  32. (p/catch
  33. (fn [e]
  34. (js/console.error e)
  35. (notification/show!
  36. (str (.-message e))
  37. :error))))))
  38. (defn import-from-debug-transit!
  39. [bare-graph-name raw finished-ok-handler]
  40. (let [graph (str config/db-version-prefix bare-graph-name)
  41. db-or-datoms (ldb/read-transit-str raw)
  42. datoms (if (d/db? db-or-datoms) (vec (d/datoms db-or-datoms :eavt)) db-or-datoms)]
  43. (p/do!
  44. (persist-db/<new graph {:import-type :debug-transit
  45. :datoms datoms})
  46. (state/add-repo! {:url graph})
  47. (repo-handler/restore-and-setup-repo! graph {:import-type :debug-transit})
  48. (db/transact! graph (sqlite-util/import-tx :debug-transit) {:import-db? true})
  49. (state/set-current-repo! graph)
  50. (finished-ok-handler))))
  51. (defn- safe-build-edn-import [export-map import-options]
  52. (try
  53. (sqlite-export/build-import export-map (db/get-db) import-options)
  54. (catch :default e
  55. (js/console.error "Import EDN error: " e)
  56. {:error "An unexpected error occurred building the import. See the javascript console for details."})))
  57. (defn- import-edn-data-from-file
  58. [export-map]
  59. (let [{:keys [init-tx block-props-tx misc-tx error] :as _txs} (safe-build-edn-import export-map {})]
  60. ;; (cljs.pprint/pprint _txs)
  61. (if error
  62. (notification/show! error :error)
  63. (let [tx-meta {::sqlite-export/imported-data? true
  64. :import-db? true}
  65. repo (state/get-current-repo)]
  66. (p/do
  67. (db/transact! repo init-tx tx-meta)
  68. (when (seq block-props-tx)
  69. (db/transact! repo block-props-tx tx-meta))
  70. (when (seq misc-tx)
  71. (db/transact! repo misc-tx tx-meta)))))))
  72. (defn import-from-edn-file!
  73. "Creates a new DB graph and imports sqlite.build EDN file"
  74. [bare-graph-name file-body finished-ok-handler]
  75. (let [graph (str config/db-version-prefix bare-graph-name)
  76. finished-error-handler
  77. #(do
  78. (state/set-state! :graph/importing nil)
  79. (shui/dialog-close-all!))
  80. edn-data (try
  81. (edn/read-string file-body)
  82. (catch :default e
  83. (js/console.error e)
  84. (notification/show! "The given EDN file is not valid EDN. Please fix and try again."
  85. :error)
  86. (finished-error-handler)
  87. nil))]
  88. (when (some? edn-data)
  89. (-> (p/do!
  90. (persist-db/<new graph {:import-type :edn})
  91. (state/add-repo! {:url graph})
  92. (repo-handler/restore-and-setup-repo! graph {:import-type :edn})
  93. (state/set-current-repo! graph)
  94. (import-edn-data-from-file edn-data)
  95. (finished-ok-handler))
  96. (p/catch
  97. (fn [e]
  98. (js/console.error e)
  99. (notification/show! (str "Unexpected error: " (.-message e))
  100. :error)
  101. (finished-error-handler)))))))
  102. (defn- import-edn-data-from-form [import-inputs _e]
  103. (let [export-map (try (edn/read-string (:import-data @import-inputs)) (catch :default _err ::invalid-import))
  104. import-block? (::sqlite-export/block export-map)
  105. block (when import-block?
  106. (if-let [eid (:block-id (first (state/get-editor-args)))]
  107. (let [ent (db/entity [:block/uuid eid])]
  108. (if-not (:block/page ent)
  109. {:error "Can't import block into a non-block entity. Please import block elsewhere."}
  110. (merge (select-keys ent [:block/uuid])
  111. {:block/page (select-keys (:block/page ent) [:block/uuid])})))
  112. (notification/show! "No block found" :warning)))]
  113. (cond (or (= ::invalid-import export-map) (not (map? export-map)))
  114. (notification/show! "The submitted EDN data is invalid! Please fix and try again." :warning)
  115. (:error block)
  116. (do
  117. (notification/show! (:error block) :error)
  118. (shui/dialog-close-all!))
  119. :else
  120. (p/let [{:keys [error]}
  121. (ui-outliner-tx/transact!
  122. {:outliner-op :batch-import-edn}
  123. (outliner-op/batch-import-edn! export-map (when block {:current-block block})))]
  124. ;; Also close cmd-k
  125. (shui/dialog-close-all!)
  126. (ui-handler/re-render-root!)
  127. (if error
  128. (notification/show! error :error)
  129. (notification/show! "Import successful!" :success))))))
  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")])))