Skip to content

Commit

Permalink
feat: import from logseq edn
Browse files Browse the repository at this point in the history
feat: import edn with provided uuid

feat: overwrite page uuid; use properties in content

feat: error handling for importing

feat: support json import

chore: fix lint by splitting setup ui
  • Loading branch information
cnrpman committed Jun 24, 2022
1 parent 459262c commit 0cdacc3
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 75 deletions.
11 changes: 8 additions & 3 deletions deps/graph-parser/src/logseq/graph_parser/block.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,10 @@
(defn page-name->map
"Create a page's map structure given a original page name (string).
map as input is supported for legacy compatibility.
with-id?: if true, assign uuid to the map structure.
if the page entity already exists, no-op.
else, if with-id? is a uuid, the uuid is used.
otherwise, generate a uuid.
with-timestamp?: assign timestampes to the map structure.
Useful when creating new pages from references or namespaces,
as there's no chance to introduce timestamps via editing in page"
Expand All @@ -253,9 +257,10 @@
{:block/name page-name
:block/original-name original-page-name}
(when with-id?
(if page-entity
{:block/uuid (:block/uuid page-entity)}
{:block/uuid (d/squuid)}))
(let [new-uuid (cond page-entity (:block/uuid page-entity)
(uuid? with-id?) with-id?
:else (d/squuid))]
{:block/uuid new-uuid}))
(when namespace?
(let [namespace (first (gp-util/split-last "/" original-page-name))]
(when-not (string/blank? namespace)
Expand Down
135 changes: 94 additions & 41 deletions src/main/frontend/components/onboarding/setups.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -127,54 +127,124 @@
[:small.opacity-50 label]]]))]]])))

(defonce *roam-importing? (atom nil))
(defonce *lsq-importing? (atom nil))
(defonce *opml-importing? (atom nil))
(defonce *opml-imported-pages (atom nil))

(defn- finished-cb
[]
(notification/show! "Import finished!" :success)
(route-handler/redirect-to-home!))

(defn- roam-import-handler
[e]
(let [file (first (array-seq (.-files (.-target e))))
file-name (gobj/get file "name")]
(if (string/ends-with? file-name ".json")
(do
(reset! *roam-importing? true)
(let [reader (js/FileReader.)]
(set! (.-onload reader)
(fn [e]
(let [text (.. e -target -result)]
(external-handler/import-from-roam-json! text
#(do (reset! *roam-importing? false) (finished-cb))))))
(.readAsText reader file)))
(notification/show! "Please choose a JSON file."
:error))))

(defn- lsq-import-handler
[e]
(let [file (first (array-seq (.-files (.-target e))))
file-name (gobj/get file "name")]
(cond (string/ends-with? file-name ".edn")
(do
(reset! *lsq-importing? true)
(let [reader (js/FileReader.)]
(set! (.-onload reader)
(fn [e]
(let [text (.. e -target -result)]
(external-handler/import-from-edn! text
#(do (reset! *lsq-importing? false) (finished-cb))))))
(.readAsText reader file)))

(string/ends-with? file-name ".json")
(do
(reset! *lsq-importing? true)
(let [reader (js/FileReader.)]
(set! (.-onload reader)
(fn [e]
(let [text (.. e -target -result)]
(external-handler/import-from-json! text
#(do (reset! *lsq-importing? false) (finished-cb))))))
(.readAsText reader file)))

:else
(notification/show! "Please choose an EDN or a JSON file."
:error))))

(defn- opml-import-handler
[e]
(let [file (first (array-seq (.-files (.-target e))))
file-name (gobj/get file "name")]
(if (string/ends-with? file-name ".opml")
(do
(reset! *opml-importing? true)
(let [reader (js/FileReader.)]
(set! (.-onload reader)
(fn [e]
(let [text (.. e -target -result)]
(external-handler/import-from-opml! text
(fn [pages]
(reset! *opml-imported-pages pages)
(reset! *opml-importing? false)
(finished-cb))))))
(.readAsText reader file)))
(notification/show! "Please choose a OPML file."
:error))))

(rum/defc importer < rum/reactive
[{:keys [query-params]}]
(let [roam-importing? (rum/react *roam-importing?)
lsq-importing? (rum/react *lsq-importing?)
opml-importing? (rum/react *opml-importing?)
finished-cb (fn []
(notification/show! "Finished!" :success)
(route-handler/redirect-to-home!))]
importing? (or roam-importing? lsq-importing? opml-importing?)]

(setups-container
:importer
[:article.flex.flex-col.items-center.importer.py-16.px-8
[:section.c.text-center
[:h1 "Do you already have notes that you want to import?"]
[:h2 "If they are in a JSON or Markdown format Logseq can work with them."]]
[:h2 "If they are in a JSON, EDN or Markdown format Logseq can work with them."]]
[:section.d.md:flex
[:label.action-input.flex.items-center.mx-2.my-2
{:disabled (or roam-importing? opml-importing?)}
{:disabled importing?}
[:span.as-flex-center [:i (svg/roam-research 28)]]
[:div.flex.flex-col
(if roam-importing?
(ui/loading "Importing ...")
[
[:strong "RoamResearch"]
[[:strong "RoamResearch"]
[:small "Import a JSON Export of your Roam graph"]])]
[:input.absolute.hidden
{:id "import-roam"
:type "file"
:on-change (fn [e]
(let [file (first (array-seq (.-files (.-target e))))
file-name (gobj/get file "name")]
(if (string/ends-with? file-name ".json")
(do
(reset! *roam-importing? true)
(let [reader (js/FileReader.)]
(set! (.-onload reader)
(fn [e]
(let [text (.. e -target -result)]
(external-handler/import-from-roam-json! text
#(do (reset! *roam-importing? false) (finished-cb))))))
(.readAsText reader file)))
(notification/show! "Please choose a JSON file."
:error))))}]]
:on-change roam-import-handler}]]

[:label.action-input.flex.items-center.mx-2.my-2
{:disabled importing?}
[:span.as-flex-center [:i (svg/logo 28)]]
[:span.flex.flex-col
(if lsq-importing?
(ui/loading "Importing ...")
[[:strong "EDN / JSON"]
[:small "Import an EDN or a JSON Export of your Logseq graph"]])]
[:input.absolute.hidden
{:id "import-lsq"
:type "file"
:on-change lsq-import-handler}]]

[:label.action-input.flex.items-center.mx-2.my-2
{:disabled (or roam-importing? opml-importing?)}
{:disabled importing?}
[:span.as-flex-center (ui/icon "sitemap" {:style {:fontSize "26px"}})]
[:span.flex.flex-col
(if opml-importing?
Expand All @@ -185,24 +255,7 @@
[:input.absolute.hidden
{:id "import-opml"
:type "file"
:on-change (fn [e]
(let [file (first (array-seq (.-files (.-target e))))
file-name (gobj/get file "name")]
(if (string/ends-with? file-name ".opml")
(do
(reset! *opml-importing? true)
(let [reader (js/FileReader.)]
(set! (.-onload reader)
(fn [e]
(let [text (.. e -target -result)]
(external-handler/import-from-opml! text
(fn [pages]
(reset! *opml-imported-pages pages)
(reset! *opml-importing? false)
(finished-cb))))))
(.readAsText reader file)))
(notification/show! "Please choose a OPML file."
:error))))}]]]
:on-change opml-import-handler}]]]

(when (= "picker" (:from query-params))
[:section.e
Expand Down
17 changes: 12 additions & 5 deletions src/main/frontend/db.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
delete-file-blocks! delete-page-blocks delete-files delete-pages-by-files
filter-only-public-pages-and-blocks get-all-block-contents get-all-tagged-pages
get-all-templates get-block-and-children get-block-by-uuid get-block-children sort-by-left
get-block-parent get-block-parents parents-collapsed? get-block-referenced-blocks
get-block-parent get-block-parents parents-collapsed? get-block-referenced-blocks get-all-referenced-blocks-uuid
get-block-children-ids get-block-immediate-children get-block-page
get-custom-css get-date-scheduled-or-deadlines
get-file-blocks get-file-last-modified-at get-file get-file-page get-file-page-id file-exists?
Expand Down Expand Up @@ -158,13 +158,13 @@
(assoc option
:listen-handler listen-and-persist!))))

(defn restore-graph!
"Restore db from serialized db cache, and swap into the current db status"
[repo]
(defn restore-graph-from-text!
"Swap db string into the current db status
stored: the text to restore from"
[repo stored]
(p/let [db-name (datascript-db repo)
db-conn (d/create-conn db-schema/schema)
_ (swap! conns assoc db-name db-conn)
stored (db-persist/get-serialized-graph db-name)
_ (when stored
(let [stored-db (try (string->db stored)
(catch js/Error _e
Expand All @@ -178,6 +178,13 @@
(conn/reset-conn! db-conn db)))]
(d/transact! db-conn [{:schema/version db-schema/version}])))

(defn restore-graph!
"Restore db from serialized db cache"
[repo]
(p/let [db-name (datascript-db repo)
stored (db-persist/get-serialized-graph db-name)]
(restore-graph-from-text! repo stored)))

(defn restore!
[{:keys [repos]} _old-db-schema restore-config-handler]
(let [repo (or (state/get-current-repo) (:url (first repos)))]
Expand Down
17 changes: 17 additions & 0 deletions src/main/frontend/db/model.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@
react
first))))

(defn get-original-name
[page-entity]
(or (:block/original-name page-entity)
(:block/name page-entity)))

(defn get-tag-pages
[repo tag-name]
(when tag-name
Expand Down Expand Up @@ -1364,6 +1369,18 @@
(reset! blocks-count-cache n)
n)))))

(defn get-all-referenced-blocks-uuid
"Get all uuids of blocks with any back link exists."
[]
(when-let [db (conn/get-db)]
(->> (d/datoms db :avet :block/uuid)
(map :v)
(map (fn [id]
(let [e (db-utils/entity [:block/uuid id])]
(when (pos-int? (count (:block/_refs e)))
id))))
(remove nil?))))

;; block/uuid and block/content
(defn get-all-block-contents
[]
Expand Down
36 changes: 24 additions & 12 deletions src/main/frontend/handler/editor.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -942,14 +942,16 @@
(state/set-edit-content! input-id new-content)
(save-block-if-changed! block new-content))))))))

(defn- set-blocks-id!
(defn set-blocks-id!
"Persist block uuid to file if not exists"
[block-ids]
(let [block-ids (remove nil? block-ids)
col (map (fn [block-id]
(let [block (db/entity [:block/uuid block-id])]
(when-let [block (db/entity [:block/uuid block-id])]
(when-not (:block/pre-block? block)
[block-id :id (str block-id)])))
block-ids)]
block-ids)
col (remove nil? col)]
(batch-set-block-property! col)))

(defn copy-block-ref!
Expand Down Expand Up @@ -1915,10 +1917,13 @@
0))

(defn paste-blocks
"Given a vec of blocks, insert them into the target page.
keep-uuid?: if true, keep the uuid provided in the block structure."
[blocks {:keys [content-update-fn
exclude-properties
target-block
sibling?]
sibling?
keep-uuid?]
:or {exclude-properties []}}]
(let [editing-block (when-let [editing-block (state/get-edit-block)]
(some-> (db/pull (:db/id editing-block))
Expand All @@ -1945,10 +1950,11 @@
(let [format (or (:block/format target-block) (state/get-preferred-format))
blocks' (map (fn [block]
(paste-block-cleanup block page exclude-properties format content-update-fn))
blocks)
blocks)
result (outliner-core/insert-blocks! blocks' target-block {:sibling? sibling?
:outliner-op :paste
:replace-empty-target? true})]
:replace-empty-target? true
:keep-uuid? keep-uuid?})]
(edit-last-block-after-inserted! result))))))

(defn- block-tree->blocks
Expand All @@ -1965,18 +1971,24 @@
(assert fst-block "fst-block shouldn't be nil")
(assoc fst-block :block/level (:block/level block)))))))

(defn insert-block-tree-after-target
(defn insert-block-tree
"`tree-vec`: a vector of blocks.
Block element: {:content :properties :children [block-1, block-2, ...]}"
[target-block-id sibling? tree-vec format]
A block element: {:content :properties :children [block-1, block-2, ...]}"
[tree-vec format {:keys [target-block] :as opts}]
(let [blocks (block-tree->blocks tree-vec format)
target-block (db/pull target-block-id)
page-id (:db/id (:block/page target-block))
blocks (gp-block/with-parent-and-left page-id blocks)]
(paste-blocks
blocks
{:target-block target-block
:sibling? sibling?})))
opts)))

(defn insert-block-tree-after-target
"`tree-vec`: a vector of blocks.
A block element: {:content :properties :children [block-1, block-2, ...]}"
[target-block-id sibling? tree-vec format]
(insert-block-tree tree-vec format
{:target-block (db/pull target-block-id)
:sibling? sibling?}))

(defn insert-template!
([element-id db-id]
Expand Down
Loading

0 comments on commit 0cdacc3

Please sign in to comment.