Browse Source

fix(whiteboards): some pasting issues

Peng Xiao 3 years ago
parent
commit
757ce2a528

+ 22 - 7
src/main/frontend/handler/paste.cljs

@@ -64,16 +64,29 @@
   (try (js/JSON.parse text)
        (catch :default _ #js{})))
 
+(defn- get-whiteboard-tldr-from-text
+  [text]
+  (when-let [matched-text (util/safe-re-find #"<whiteboard-tldr>(.*)</whiteboard-tldr>" text)]
+    (try-parse-as-json (second matched-text))))
+
+(defn- get-whiteboard-shape-refs-text
+  [text]
+  (when-let [tldr (get-whiteboard-tldr-from-text text)]
+    (->> (gobj/get tldr "shapes")
+         (mapv (fn [shape]
+                 (let [shape-id (gobj/get shape "id")]
+                   (block-ref/->block-ref shape-id))))
+         (string/join "\n"))))
+
 (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
-        whiteboard-shape? (= "logseq/whiteboard-shapes" (gobj/get (try-parse-as-json text) "type"))
-        text (if whiteboard-shape?
-               (block-ref/->block-ref (gobj/getValueByKeys (try-parse-as-json text) "shapes" 0 "id"))
-               text)
+        shape-refs-text (when-not (string/blank? html) (get-whiteboard-shape-refs-text html))
         internal-paste? (and
                          (seq (:copy/blocks copied-blocks))
                          ;; not copied from the external clipboard
@@ -85,6 +98,9 @@
           (editor-handler/paste-blocks blocks {})))
       (let [{:keys [value]} (editor-handler/get-selection-and-format)]
         (cond
+          (not (string/blank? shape-refs-text))
+          (commands/simple-insert! input-id shape-refs-text nil)
+
           (and (or (gp-util/url? text)
                    (and value (gp-util/url? (string/trim value))))
                (not (string/blank? (util/get-selected-text))))
@@ -92,7 +108,7 @@
 
           (and (block-ref/block-ref? text)
                (editor-handler/wrapped-by? input block-ref/left-parens block-ref/right-parens))
-          (commands/simple-insert! (state/get-edit-input-id) (block-ref/get-block-ref-id text) nil)
+          (commands/simple-insert! input-id (block-ref/get-block-ref-id text) nil)
 
           :else
           ;; from external
@@ -105,7 +121,6 @@
                                              nil)))]
                             (if (string/blank? result) nil result))
                 text (or html-text text)
-                input-id (state/get-edit-input-id)
                 replace-text-f (fn []
                                  (commands/delete-selection! input-id)
                                  (commands/simple-insert! input-id text nil))]
@@ -172,7 +187,7 @@
         (let [clipboard-data (gobj/get e "clipboardData")
               html (when-not raw-paste? (.getData clipboard-data "text/html"))
               text (.getData clipboard-data "text")]
-          (if-not (string/blank? text)
+          (if-not (and (string/blank? text) (string/blank? html))
             (paste-text-or-blocks-aux input e text html)
             (when id
               (let [_handled

+ 20 - 13
tldraw/apps/tldraw-logseq/src/hooks/usePaste.ts

@@ -41,6 +41,13 @@ const safeParseJson = (json: string) => {
   }
 }
 
+const getWhiteboardsTldrFromText = (text: string) => {
+  const innerText = text.match(/<whiteboard-tldr>(.*)<\/whiteboard-tldr>/)?.[1]
+  if (innerText) {
+    return safeParseJson(innerText)
+  }
+}
+
 interface VideoImageAsset extends TLAsset {
   size?: number[]
 }
@@ -139,11 +146,13 @@ export function usePaste() {
       }
 
       function createHTMLShape(text: string) {
-        return {
-          ...HTMLShape.defaultProps,
-          html: text,
-          point: [point[0], point[1]],
-        }
+        return [
+          {
+            ...HTMLShape.defaultProps,
+            html: text,
+            point: [point[0], point[1]],
+          },
+        ]
       }
 
       async function tryCreateShapesFromDataTransfer(dataTransfer: DataTransfer) {
@@ -208,7 +217,7 @@ export function usePaste() {
         }
         const rawText = await getDataFromType(item, 'text/html')
         if (rawText) {
-          return [createHTMLShape(rawText)]
+          return tryCreateShapeHelper(tryCreateClonedShapesFromJSON, createHTMLShape)(rawText)
         }
         return null
       }
@@ -246,7 +255,6 @@ export function usePaste() {
           return tryCreateShapeHelper(
             tryCreateShapeFromURL,
             tryCreateShapeFromIframeString,
-            tryCreateClonedShapesFromJSON,
             tryCreateLogseqPortalShapesFromString
           )(text)
         }
@@ -255,9 +263,9 @@ export function usePaste() {
       }
 
       function tryCreateClonedShapesFromJSON(rawText: string) {
-        const data = safeParseJson(rawText)
+        const data = getWhiteboardsTldrFromText(rawText)
         try {
-          if (data?.type === 'logseq/whiteboard-shapes') {
+          if (data) {
             const shapes = data.shapes as TLShapeModel[]
             assetsToClone = data.assets as TLAsset[]
             const commonBounds = BoundsUtils.getCommonBounds(
@@ -270,6 +278,7 @@ export function usePaste() {
                 maxY: (shape.point?.[1] ?? point[1]) + (shape.size?.[1] ?? 4),
               }))
             )
+            const bindings = data.bindings as Record<string, TLBinding>
             const shapesToCreate = shapes.map(shape => {
               return {
                 ...shape,
@@ -289,9 +298,7 @@ export function usePaste() {
                   return
                 }
                 // try to bind the new shape
-                const binding = app.currentPage.bindings[h.bindingId]
-                // FIXME: if copy from a different whiteboard, the binding info
-                // will not be available
+                const binding = bindings[h.bindingId]
                 if (binding) {
                   // if the copied binding from/to is in the source
                   const oldFromIdx = shapes.findIndex(s => s.id === binding.fromId)
@@ -322,7 +329,7 @@ export function usePaste() {
       }
 
       async function tryCreateShapeFromURL(rawText: string) {
-        if (isValidURL(rawText)) {
+        if (isValidURL(rawText) && !(shiftKey || fromDrop)) {
           const isYoutubeUrl = (url: string) => {
             const youtubeRegex =
               /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/

+ 9 - 5
tldraw/packages/core/src/lib/TLApp/TLApp.ts

@@ -3,7 +3,7 @@
 /* eslint-disable @typescript-eslint/no-explicit-any */
 import type { TLBounds } from '@tldraw/intersect'
 import { Vec } from '@tldraw/vec'
-import { action, computed, makeObservable, observable, transaction } from 'mobx'
+import { action, computed, makeObservable, observable, toJS, transaction } from 'mobx'
 import { GRID_SIZE } from '../../constants'
 import type {
   TLAsset,
@@ -474,17 +474,21 @@ export class TLApp<
 
   copy = () => {
     if (this.selectedShapesArray.length > 0 && !this.editingShape) {
-      const tldrawString = JSON.stringify({
-        type: 'logseq/whiteboard-shapes',
+      const jsonString = JSON.stringify({
         shapes: this.selectedShapesArray.map(shape => shape.serialized),
-        // pasting into other whiteboard may require this if any shape uses asset
+        // pasting into other whiteboard may require this if any shape uses the assets
         assets: this.getCleanUpAssets().filter(asset => {
           return this.selectedShapesArray.some(shape => shape.props.assetId === asset.id)
         }),
+        // convey the bindings to maintain the new links after pasting
+        bindings: toJS(this.currentPage.bindings),
       })
+      const tldrawString = `<whiteboard-tldr>${jsonString}</whiteboard-tldr>`
+      // FIXME: use `writeClipboard` in frontend.utils
       navigator.clipboard.write([
         new ClipboardItem({
-          'text/plain': new Blob([tldrawString], { type: 'text/plain' }),
+          'text/html': new Blob([tldrawString], { type: 'text/html' }),
+          // ??? what plain text should be used here?
         }),
       ])
     }