Browse Source

fix: can't copy image on mobile

Tienson Qin 1 week ago
parent
commit
f24583d82a
4 changed files with 78 additions and 54 deletions
  1. 1 1
      package.json
  2. 35 42
      src/main/frontend/components/block.cljs
  3. 38 7
      src/main/frontend/util.cljc
  4. 4 4
      yarn.lock

+ 1 - 1
package.json

@@ -114,7 +114,7 @@
         "@capacitor/android": "7.2.0",
         "@capacitor/app": "7.0.1",
         "@capacitor/camera": "7.0.1",
-        "@capacitor/clipboard": "7.0.1",
+        "@capacitor/clipboard": "^7.0.2",
         "@capacitor/core": "7.2.0",
         "@capacitor/device": "^7.0.2",
         "@capacitor/dialog": "^7.0.2",

+ 35 - 42
src/main/frontend/components/block.cljs

@@ -355,7 +355,9 @@
           (let [handle-copy!
                 (fn [_e]
                   (-> (util/copy-image-to-clipboard image-src)
-                      (p/then #(notification/show! "Copied!" :success))))
+                      (p/then #(notification/show! "Copied!" :success))
+                      (p/catch (fn [error]
+                                 (js/console.error error)))))
                 handle-delete!
                 (fn [_e]
                   (when-let [block-id (get-blockid)]
@@ -381,47 +383,38 @@
                                       :href src
                                       :title title
                                       :full-text full-text})))))))]
-            [:.asset-action-bar {:aria-hidden "true"}
-             (shui/button-group
-              (shui/button
-               {:variant :outline
-                :size :icon
-                :class "h-7 w-7"
-                :on-pointer-down util/stop
-                :on-click (fn [e]
-                            (shui/popup-show! (.closest (.-target e) ".asset-action-bar")
-                                              (fn []
-                                                [:div
-                                                 {:on-click #(shui/popup-hide!)}
-                                                 (shui/dropdown-menu-item
-                                                  {:on-click #(some-> (db/entity [:block/uuid (get-blockid)])
-                                                                      (editor-handler/edit-block! :max {:container-id :unknown-container}))}
-                                                  [:span.flex.items-center.gap-1
-                                                   (ui/icon "edit") (t :asset/edit-block)])
-                                                 (shui/dropdown-menu-item
-                                                  {:on-click handle-copy!}
-                                                  [:span.flex.items-center.gap-1
-                                                   (ui/icon "copy") (t :asset/copy)])
-                                                 (when (util/electron?)
-                                                   (shui/dropdown-menu-item
-                                                    {:on-click (fn [e]
-                                                                 (util/stop e)
-                                                                 (if local?
-                                                                   (ipc/ipc "openFileInFolder" image-src)
-                                                                   (js/window.apis.openExternal image-src)))}
-                                                    [:span.flex.items-center.gap-1
-                                                     (ui/icon "folder-pin") (t (if local? :asset/show-in-folder :asset/open-in-browser))]))
-
-                                                 (when-not config/publishing?
-                                                   [:<>
-                                                    (shui/dropdown-menu-separator)
-                                                    (shui/dropdown-menu-item
-                                                     {:on-click handle-delete!}
-                                                     [:span.flex.items-center.gap-1.text-red-700
-                                                      (ui/icon "trash") (t :asset/delete)])])])
-                                              {:align :start
-                                               :dropdown-menu? true}))}
-               (shui/tabler-icon "dots-vertical")))])])])))
+            (when asset-block
+              [:.asset-action-bar {:aria-hidden "true"}
+               (shui/dropdown-menu
+                {:on-pointer-down util/stop}
+                (shui/dropdown-menu-trigger
+                 {:as-child true}
+                 (shui/button
+                  {:variant :outline
+                   :size :icon
+                   :class "h-6 w-6"}
+                  (shui/tabler-icon "dots-vertical")))
+                (shui/dropdown-menu-content
+                 (shui/dropdown-menu-item
+                  {:on-click handle-copy!}
+                  [:span.flex.items-center.gap-1
+                   (ui/icon "copy") (t :asset/copy)])
+                 (when (util/electron?)
+                   (shui/dropdown-menu-item
+                    {:on-click (fn [e]
+                                 (util/stop e)
+                                 (if local?
+                                   (ipc/ipc "openFileInFolder" image-src)
+                                   (js/window.apis.openExternal image-src)))}
+                    [:span.flex.items-center.gap-1
+                     (ui/icon "folder-pin") (t (if local? :asset/show-in-folder :asset/open-in-browser))]))
+                 (when-not config/publishing?
+                   [:<>
+                    (shui/dropdown-menu-separator)
+                    (shui/dropdown-menu-item
+                     {:on-click handle-delete!}
+                     [:span.flex.items-center.gap-1.text-red-700
+                      (ui/icon "trash") (t :asset/delete)])])))]))])])))
 
 (rum/defcs ^:large-vars/cleanup-todo resizable-image <
   (rum/local nil ::size)

+ 38 - 7
src/main/frontend/util.cljc

@@ -1364,13 +1364,44 @@
        (set! (.-src image) data-url))))
 
 #?(:cljs
-   (defn write-blob-to-clipboard
-     [blob]
-     (->> blob
-          (js-obj (.-type blob))
-          (js/ClipboardItem.)
-          (array)
-          (js/navigator.clipboard.write))))
+   (def native-clipboard (gobj/get CapacitorClipboard "Clipboard")))
+
+#?(:cljs
+   (do
+     ;; Helper: Blob -> data URL (returns a JS Promise)
+     (defn blob->data-url [blob]
+       (js/Promise.
+        (fn [resolve reject]
+          (let [reader (js/FileReader.)]
+            (set! (.-onload reader)
+                  (fn [_e]
+                    ;; result is a "data:<mime>;base64,..." string
+                    (resolve (.-result reader))))
+            (set! (.-onerror reader)
+                  (fn [_e]
+                    (reject (.-error reader))))
+            (.readAsDataURL reader blob)))))
+
+     (defn write-blob-to-clipboard [blob]
+       (if native-clipboard
+         ;; 1) Native (Capacitor) path – expects a data URL
+         (-> (blob->data-url blob)
+             (.then (fn [data-url]
+                        ;; Your Capacitor plugin signature: { image: <data-url> }
+                      (.write native-clipboard #js {:image data-url})))
+             (.then (fn []
+                      (js/console.log "Copied via native clipboard")))
+             (.catch (fn [err]
+                       (js/console.error "Native clipboard failed" err))))
+
+         ;; 2) Web Clipboard API path (desktop browsers etc.)
+         (let [item (js/ClipboardItem.
+                     (js-obj (.-type blob) blob))]
+           (-> (.write (.-clipboard js/navigator) (array item))
+               (.then (fn []
+                        (js/console.log "Copied via web clipboard")))
+               (.catch (fn [err]
+                         (js/console.error "Web clipboard failed" err)))))))))
 
 #?(:cljs
    (defn copy-image-to-clipboard

+ 4 - 4
yarn.lock

@@ -299,10 +299,10 @@
     tslib "^2.8.1"
     xml2js "^0.6.2"
 
-"@capacitor/clipboard@7.0.1":
-  version "7.0.1"
-  resolved "https://registry.yarnpkg.com/@capacitor/clipboard/-/clipboard-7.0.1.tgz#159fefa56b7d23632e9d08c9efd9cbd75a868b21"
-  integrity sha512-n4XEHma7apLOYvyeaR9S5u3uGzDYG7WeQxmtZlwP01HneIzMnusVgw4Im6I+pMBcoUN9TfVdf6eqKph97B1bAw==
+"@capacitor/clipboard@^7.0.2":
+  version "7.0.2"
+  resolved "https://registry.yarnpkg.com/@capacitor/clipboard/-/clipboard-7.0.2.tgz#fe9ea01032581f68feda79c19304c5f82e1995f1"
+  integrity sha512-Cm37cNPvSzuao3HlcrQT18py5WUfKdaNWBHFuQcubBzBuKhqtAC9P6xZ2d0ucPFdxvsEOgO7mjs2BWIpaKqpgg==
 
 "@capacitor/[email protected]":
   version "7.2.0"