Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: internal blocks paste #8932

Merged
merged 10 commits into from
Mar 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 8 additions & 3 deletions e2e-tests/editor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,15 +168,14 @@ test('copy & paste block ref and replace its content', async ({ page, block }) =
await createRandomPage(page)

await block.mustType('Some random text')
// FIXME: https://github.com/logseq/logseq/issues/7541
await page.waitForTimeout(1000)

await page.keyboard.press(modKey + '+c')

await page.press('textarea >> nth=0', 'Enter')
await block.waitForBlocks(2)

await page.waitForTimeout(100)
await page.keyboard.press(modKey + '+v')
await page.waitForTimeout(100)
await page.keyboard.press('Enter')

// Check if the newly created block-ref has the same referenced content
Expand All @@ -189,10 +188,16 @@ test('copy & paste block ref and replace its content', async ({ page, block }) =

await expect(page.locator('textarea >> nth=0')).not.toHaveValue('Some random text')

// FIXME: Sometimes the cursor is in the end of the editor
for (let i = 0; i < 4; i++) {
await page.press('textarea >> nth=0', 'ArrowLeft')
}

// Trigger replace-block-reference-with-content-at-point
await page.keyboard.press(modKey + '+Shift+r')

await expect(page.locator('textarea >> nth=0')).toHaveValue('Some random text')

await block.escapeEditing()

await expect(page.locator('.block-ref >> text="Some random text"')).toHaveCount(0);
Expand Down
4 changes: 4 additions & 0 deletions e2e-tests/logseq-url.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ test("Logseq URLs (same graph)", async ({ page, block }) => {
await createRandomPage(page)
await block.mustFill("") // to enter editing mode
await page.keyboard.press(paste_key)
// paste returns a promise which is async, so we need give it a little bit
// more time
await page.waitForTimeout(100)
let cursor_locator = page.locator('textarea >> nth=0')
expect(await cursor_locator.inputValue()).toContain("page=" + page_title)
await cursor_locator.press("Enter")
Expand All @@ -32,6 +35,7 @@ test("Logseq URLs (same graph)", async ({ page, block }) => {
await createRandomPage(page)
await block.mustFill("") // to enter editing mode
await page.keyboard.press(paste_key)
await page.waitForTimeout(100)
cursor_locator = page.locator('textarea >> nth=0')
expect(await cursor_locator.inputValue()).toContain("block-id=")
await cursor_locator.press("Enter")
Expand Down
3 changes: 2 additions & 1 deletion src/main/frontend/components/export.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -207,5 +207,6 @@
[:div.mt-4
(ui/button (if @*copied? "Copied to clipboard!" "Copy to clipboard")
:on-click (fn []
(util/copy-to-clipboard! @*content (when (= tp :html) @*content))
(util/copy-to-clipboard! @*content
:html (when (= tp :html) @*content))
(reset! *copied? true)))]]))
4 changes: 2 additions & 2 deletions src/main/frontend/extensions/pdf/assets.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,8 @@
[highlight ^js viewer]
(when-let [ref-block (ensure-ref-block! (state/get-current-pdf) highlight)]
(util/copy-to-clipboard!
(block-ref/->block-ref (:block/uuid ref-block)) nil
(pdf-windows/resolve-own-window viewer))))
(block-ref/->block-ref (:block/uuid ref-block))
:owner-window (pdf-windows/resolve-own-window viewer))))

(defn open-block-ref!
[block]
Expand Down
4 changes: 2 additions & 2 deletions src/main/frontend/extensions/pdf/core.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@
"copy"
(do
(util/copy-to-clipboard!
(or (:text content) (pdf-utils/fix-selection-text-breakline (.toString selection))) nil
(pdf-windows/resolve-own-window viewer))
(or (:text content) (pdf-utils/fix-selection-text-breakline (.toString selection)))
:owner-window (pdf-windows/resolve-own-window viewer))
(pdf-utils/clear-all-selection))

"link"
Expand Down
2 changes: 1 addition & 1 deletion src/main/frontend/extensions/tldraw.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
:isMobile util/mobile?
:saveAsset save-asset-handler
:makeAssetUrl editor-handler/make-asset-url
:copyToClipboard (fn [text, html] (util/copy-to-clipboard! text html))
:copyToClipboard (fn [text, html] (util/copy-to-clipboard! text :html html))
:getRedirectPageName (fn [page-name-or-uuid] (model/get-redirect-page-name page-name-or-uuid))
:insertFirstPageBlock (fn [page-name] (editor-handler/insert-first-page-block-if-not-exists! page-name {:redirect? false}))
:addNewWhiteboard (fn [page-name]
Expand Down
6 changes: 4 additions & 2 deletions src/main/frontend/handler/common.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
["ignore" :as Ignore]))

(defn copy-to-clipboard-without-id-property!
[format raw-text html]
(util/copy-to-clipboard! (property/remove-id-property format raw-text) html))
[format raw-text html blocks]
(util/copy-to-clipboard! (property/remove-id-property format raw-text)
:html html
:blocks blocks))

(defn config-with-document-mode
[config]
Expand Down
13 changes: 6 additions & 7 deletions src/main/frontend/handler/editor.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -1010,9 +1010,9 @@
[top-level-block-uuids content] (compose-copied-blocks-contents repo ids)
block (db/entity [:block/uuid (first ids)])]
(when block
(let [html (export-html/export-blocks-as-html repo top-level-block-uuids nil)]
(common-handler/copy-to-clipboard-without-id-property! (:block/format block) content (when html? html)))
(state/set-copied-blocks! content (get-all-blocks-by-ids repo top-level-block-uuids))
(let [html (export-html/export-blocks-as-html repo top-level-block-uuids nil)
copied-blocks (get-all-blocks-by-ids repo top-level-block-uuids)]
(common-handler/copy-to-clipboard-without-id-property! (:block/format block) content (when html? html) copied-blocks))
(notification/show! "Copied!" :success)))))

(defn copy-block-refs
Expand Down Expand Up @@ -1228,8 +1228,7 @@
[_top-level-block-uuids md-content] (compose-copied-blocks-contents repo [block-id])
html (export-html/export-blocks-as-html repo [block-id] nil)
sorted-blocks (tree/get-sorted-block-and-children repo (:db/id block))]
(state/set-copied-blocks! md-content sorted-blocks)
(common-handler/copy-to-clipboard-without-id-property! (:block/format block) md-content html)
(common-handler/copy-to-clipboard-without-id-property! (:block/format block) md-content html sorted-blocks)
(delete-block-aux! block true))))

(defn clear-last-selected-block!
Expand Down Expand Up @@ -1510,7 +1509,7 @@
;; assets/journals_2021_02_03_1612350230540_0.png
(defn resolve-relative-path
"Relative path to current file path.

Requires editing state"
[file-path]
(if-let [current-file-rpath (or (db-model/get-block-file-path (state/get-edit-block))
Expand Down Expand Up @@ -2052,7 +2051,7 @@
(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 keep-uuid?))
blocks)
blocks)
result (outliner-core/insert-blocks! blocks' target-block' {:sibling? sibling?
:outliner-op :paste
:replace-empty-target? replace-empty-target?
Expand Down
162 changes: 88 additions & 74 deletions src/main/frontend/handler/paste.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
[cljs.core.match :refer [match]]
[frontend.util.text :as text-util]
[frontend.format.mldoc :as mldoc]
[lambdaisland.glogi :as log]))
[lambdaisland.glogi :as log]
[promesa.core :as p]))

(defn- paste-text-parseable
[format text]
Expand Down Expand Up @@ -81,83 +82,96 @@
(clojure.string/includes? matched-text selection))))
some?))))

;; See https://developer.chrome.com/blog/web-custom-formats-for-the-async-clipboard-api/
;; for a similar example
(defn get-copied-blocks []
(p/let [clipboard-items (when (and js/window (gobj/get js/window "navigator") js/navigator.clipboard)
(js/navigator.clipboard.read))
blocks-blob ^js (when clipboard-items
(let [types (.-types ^js (first clipboard-items))]
(when (contains? (set types) "web application/logseq")
(.getType ^js (first clipboard-items)
"web application/logseq"))))
blocks-str (when blocks-blob (.text blocks-blob))]
(when blocks-str
(gp-util/safe-read-string blocks-str))))

(defn- paste-copied-blocks-or-text
;; todo: logseq/whiteboard-shapes is now text/html
[text e html]
(util/stop e)
(let [copied-blocks (state/get-copied-blocks)
input (state/get-input)
input-id (state/get-edit-input-id)
text (string/replace text "\r\n" "\n") ;; Fix for Windows platform
replace-text-f (fn [text]
(commands/delete-selection! input-id)
(commands/simple-insert! input-id text nil))
internal-paste? (and
(seq (:copy/blocks copied-blocks))
;; not copied from the external clipboard
(= (string/trimr text)
(string/trimr (:copy/content copied-blocks))))]
(if internal-paste?
(let [blocks (:copy/blocks copied-blocks)]
(when (seq blocks)
(editor-handler/paste-blocks blocks {})))
(let [shape-refs-text (when (and (not (string/blank? html))
(get-whiteboard-tldr-from-text html))
;; text should always be prepared block-ref generated in tldr
text)
{:keys [value selection] :as selection-and-format} (editor-handler/get-selection-and-format)
text-url? (gp-util/url? text)
selection-url? (gp-util/url? selection)]
(cond
(not (string/blank? shape-refs-text))
(commands/simple-insert! input-id shape-refs-text nil)

(or (and (or text-url? selection-url?)
(selection-within-link? selection-and-format))
(and text-url? selection-url?))
(replace-text-f text)

(and (or text-url?
(and value (gp-util/url? (string/trim value))))
(not (string/blank? (util/get-selected-text))))
(editor-handler/html-link-format! text)

(and (block-ref/block-ref? text)
(editor-handler/wrapped-by? input block-ref/left-parens block-ref/right-parens))
(commands/simple-insert! input-id (block-ref/get-block-ref-id text) nil)

:else
;; from external
(let [format (or (db/get-page-format (state/get-current-page)) :markdown)
html-text (let [result (when-not (string/blank? html)
(try
(html-parser/convert format html)
(catch :default e
(log/error :exception e)
nil)))]
(if (string/blank? result) nil result))
text (or html-text text)]
(match [format
(nil? (util/safe-re-find #"(?m)^\s*(?:[-+*]|#+)\s+" text))
(nil? (util/safe-re-find #"(?m)^\s*\*+\s+" text))
(nil? (util/safe-re-find #"(?:\r?\n){2,}" text))]
[:markdown false _ _]
(paste-text-parseable format text)

[:org _ false _]
(paste-text-parseable format text)

[:markdown true _ false]
(paste-segmented-text format text)

[:markdown true _ true]
(replace-text-f text)

[:org _ true false]
(paste-segmented-text format text)

[:org _ true true]
(replace-text-f text))))))))
(->
(p/let [copied-blocks (get-copied-blocks)]
(let [input (state/get-input)
input-id (state/get-edit-input-id)
text (string/replace text "\r\n" "\n") ;; Fix for Windows platform
replace-text-f (fn [text]
(let [input-id (state/get-edit-input-id)]
(commands/delete-selection! input-id)
(commands/simple-insert! input-id text nil)))
internal-paste? (seq copied-blocks)]
(if internal-paste?
(editor-handler/paste-blocks copied-blocks {})
(let [shape-refs-text (when (and (not (string/blank? html))
(get-whiteboard-tldr-from-text html))
;; text should always be prepared block-ref generated in tldr
text)
{:keys [value selection] :as selection-and-format} (editor-handler/get-selection-and-format)
text-url? (gp-util/url? text)
selection-url? (gp-util/url? selection)]
(cond
(not (string/blank? shape-refs-text))
(commands/simple-insert! input-id shape-refs-text nil)

(or (and (or text-url? selection-url?)
(selection-within-link? selection-and-format))
(and text-url? selection-url?))
(replace-text-f text)

(and (or text-url?
(and value (gp-util/url? (string/trim value))))
(not (string/blank? (util/get-selected-text))))
(editor-handler/html-link-format! text)

(and (block-ref/block-ref? text)
(editor-handler/wrapped-by? input block-ref/left-parens block-ref/right-parens))
(commands/simple-insert! input-id (block-ref/get-block-ref-id text) nil)

:else
;; from external
(let [format (or (db/get-page-format (state/get-current-page)) :markdown)
html-text (let [result (when-not (string/blank? html)
(try
(html-parser/convert format html)
(catch :default e
(log/error :exception e)
nil)))]
(if (string/blank? result) nil result))
text (or html-text text)]
(match [format
(nil? (util/safe-re-find #"(?m)^\s*(?:[-+*]|#+)\s+" text))
(nil? (util/safe-re-find #"(?m)^\s*\*+\s+" text))
(nil? (util/safe-re-find #"(?:\r?\n){2,}" text))]
[:markdown false _ _]
(paste-text-parseable format text)

[:org _ false _]
(paste-text-parseable format text)

[:markdown true _ false]
(paste-segmented-text format text)

[:markdown true _ true]
(replace-text-f text)

[:org _ true false]
(paste-segmented-text format text)

[:org _ true true]
(replace-text-f text))))))))
(p/catch (fn [error]
(prn "Paste failed: ")
(log/error :exception error)))))

(defn paste-text-in-one-block-at-point
[]
Expand Down
17 changes: 1 addition & 16 deletions src/main/frontend/state.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -217,11 +217,6 @@
;; graph -> state
:graph/parsing-state {}

;; copied blocks
:copy/blocks {:copy/content nil
:copy/graph nil
:copy/blocks nil}

:copy/export-block-text-indent-style (or (storage/get :copy/export-block-text-indent-style)
"dashes")
:copy/export-block-text-remove-options (or (storage/get :copy/export-block-text-remove-options)
Expand Down Expand Up @@ -753,7 +748,7 @@ Similar to re-frame subscriptions"
"Returns the current repo URL, or else open demo graph"
[]
(or (:git/current-repo @state)
"local"))
"local"))

(defn get-remote-graphs
[]
Expand Down Expand Up @@ -1711,16 +1706,6 @@ Similar to re-frame subscriptions"
(let [chan (get-events-chan)]
(async/put! chan payload)))

(defn get-copied-blocks
[]
(:copy/blocks @state))

(defn set-copied-blocks!
[content blocks]
(set-state! :copy/blocks {:copy/graph (get-current-repo)
:copy/content (or content (get-in @state [:copy/blocks :copy/content]))
:copy/blocks blocks}))

(defn get-export-block-text-indent-style []
(:copy/export-block-text-indent-style @state))

Expand Down
19 changes: 9 additions & 10 deletions src/main/frontend/util.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -770,16 +770,15 @@

#?(:cljs
(defn copy-to-clipboard!
([s]
(utils/writeClipboard (clj->js {:text s})))
([s html]
(utils/writeClipboard (clj->js {:text s :html html})))
([s html owner-window]
(-> (cond-> {:text s}
(not (string/blank? html))
(assoc :html html))
(bean/->js)
(utils/writeClipboard owner-window)))))
[text & {:keys [html blocks owner-window]}]
(let [data (clj->js
(gp-util/remove-nils-non-nested
{:text text
:html html
:blocks (when (seq blocks) (pr-str blocks))}))]
(if owner-window
(utils/writeClipboard data owner-window)
(utils/writeClipboard data)))))

(defn drop-nth [n coll]
(keep-indexed #(when (not= %1 n) %2) coll))
Expand Down