Browse Source

improve(electron): adapt paste/drop asset for electron clipboard api

charlie 4 năm trước cách đây
mục cha
commit
b7619958ec

+ 41 - 3
resources/js/preload.js

@@ -1,6 +1,9 @@
 const fs = require('fs')
 const path = require('path')
-const { ipcRenderer, contextBridge, shell } = require('electron')
+const { ipcRenderer, contextBridge, shell, clipboard } = require('electron')
+
+const IS_MAC = process.platform === 'darwin'
+const IS_WIN32 = process.platform === 'win32'
 
 contextBridge.exposeInMainWorld('apis', {
   doAction: async (arg) => {
@@ -32,8 +35,16 @@ contextBridge.exposeInMainWorld('apis', {
     await shell.openExternal(url, options)
   },
 
+  /**
+   * When from is empty. The resource maybe from
+   * client paste or screenshoot.
+   * @param repoPathRoot
+   * @param to
+   * @param from?
+   * @returns {Promise<void>}
+   */
   async copyFileToAssets (repoPathRoot, to, from) {
-    if (fs.statSync(from).isDirectory()) {
+    if (from && fs.statSync(from).isDirectory()) {
       throw new Error('not support copy directory')
     }
 
@@ -45,6 +56,33 @@ contextBridge.exposeInMainWorld('apis', {
     }
 
     await fs.promises.mkdir(assetsRoot, { recursive: true })
-    await fs.promises.copyFile(from, dest)
+
+    from = !from && getFilePathFromClipboard()
+
+    if (from) {
+      // console.debug('copy file: ', from, dest)
+      return await fs.promises.copyFile(from, dest)
+    }
+
+    // support image
+    const nImg = clipboard.readImage()
+
+    if (nImg && !nImg.isEmpty()) {
+      const rawExt = path.extname(dest)
+      return await fs.promises.writeFile(
+        dest.replace(rawExt, '.png'),
+        nImg.toPNG()
+      )
+    }
+
+    // fns
+    function getFilePathFromClipboard () {
+      if (IS_WIN32) {
+        const rawFilePath = clipboard.read('FileNameW')
+        return rawFilePath.replace(new RegExp(String.fromCharCode(0), 'g'), '')
+      }
+
+      return clipboard.read('public.file-url').replace('file://', '')
+    }
   }
 })

+ 15 - 2
src/electron/electron/core.cljs

@@ -2,9 +2,10 @@
   (:require [electron.handler :as handler]
             [electron.updater :refer [init-updater]]
             [electron.utils :refer [mac? win32? prod? dev? log]]
+            [clojure.string :as string]
             ["fs" :as fs]
             ["path" :as path]
-            ["electron" :refer [BrowserWindow app] :as electron]))
+            ["electron" :refer [BrowserWindow app protocol] :as electron]))
 
 (def ROOT_PATH (path/join js/__dirname ".."))
 (def MAIN_WINDOW_ENTRY (str "file://" (path/join js/__dirname (if dev? "dev.html" "index.html"))))
@@ -12,6 +13,7 @@
 (def ^:dynamic *setup-fn* nil)
 (def ^:dynamic *teardown-fn* nil)
 (def ^:dynamic *teardown-updater* nil)
+(def ^:dynamic *teardown-interceptor* nil)
 
 ;; Handle creating/removing shortcuts on Windows when installing/uninstalling.
 (when (js/require "electron-squirrel-startup") (.quit app))
@@ -41,6 +43,15 @@
                        :logger log
                        :win    win})))
 
+(defn setup-interceptor! []
+  (.registerFileProtocol
+   protocol "assets"
+   (fn [^js request callback]
+     (let [url (.-url request)
+           path (string/replace url "assets://" "")]
+       (callback #js {:path path}))))
+  (set! *teardown-interceptor* #(.unregisterProtocol protocol "assets")))
+
 (defn main
   []
   (.on app "window-all-closed" #(when-not mac? (.quit app)))
@@ -54,13 +65,15 @@
                  (fn []
                    ;; updater
                    (setup-updater! win)
+                   (setup-interceptor!)
 
                    ;; handler
                    (handler/set-ipc-handler! win)
 
                    ;; teardown
                    #(do
-                      (when *teardown-updater* (*teardown-updater*)))))
+                      (when *teardown-updater* (*teardown-updater*))
+                      (when *teardown-interceptor* (*teardown-interceptor*)))))
 
            ;; setup effects
            (*setup-fn*)

+ 2 - 2
src/electron/electron/handler.cljs

@@ -71,7 +71,7 @@
   (last (string/split file #"\.")))
 
 (defonce file-watcher-chan "file-watcher")
-(defn send-file-watcher! [win type payload]
+(defn send-file-watcher! [^js win type payload]
   (.. win -webContents
       (send file-watcher-chan
             (bean/->js {:type type :payload payload}))))
@@ -80,7 +80,7 @@
   [win dir]
   (let [watcher (.watch watcher dir
                         (clj->js
-                         {:ignored #"^\." ; FIXME read .gitignore and other ignore paths
+                         {:ignored #"(^\.|/assets/)" ; FIXME read .gitignore and other ignore paths
                           ;; :ignoreInitial true
                           :persistent true
                           :awaitWriteFinish true}))]

+ 20 - 17
src/main/frontend/handler/editor.cljs

@@ -1570,10 +1570,8 @@
         (js/console.debug "Write asset #" dir filename file)
         (if (util/electron?)
           (let [from (.-path file)]
-            (if (string/blank? from)
-              (throw (js/Error. "TODO: can not resolved From file path"))
-              (p/then (js/window.apis.copyFileToAssets dir filename from)
-                      #(p/resolved [filename file]))))
+            (p/then (js/window.apis.copyFileToAssets dir filename from)
+                    #(p/resolved [filename file])))
           (p/then (fs/write-file! repo dir filename (.stream file) nil)
                   #(p/resolved [filename file]))))))))
 
@@ -1582,17 +1580,19 @@
 (defn make-asset-url
   [path]                                                    ;; path start with "/assets" or compatible for "../assets"
   (let [repo-dir (config/get-repo-dir (state/get-current-repo))
-        path (string/replace path "../" "/")
-        handle-path (str "handle" repo-dir path)
-        cached-url (get @*assets-url-cache (keyword handle-path))]
-    (if cached-url
-      (p/resolved cached-url)
-      (p/let [handle (frontend.idb/get-item handle-path)
-              file (and handle (.getFile handle))]
-        (when file
-          (p/let [url (js/URL.createObjectURL file)]
-            (swap! *assets-url-cache assoc (keyword handle-path) url)
-            url))))))
+        path (string/replace path "../" "/")]
+    (if (util/electron?)
+      (str "assets://" repo-dir path)
+      (let [handle-path (str "handle" repo-dir path)
+            cached-url (get @*assets-url-cache (keyword handle-path))]
+        (if cached-url
+          (p/resolved cached-url)
+          (p/let [handle (frontend.idb/get-item handle-path)
+                  file (and handle (.getFile handle))]
+            (when file
+              (p/let [url (js/URL.createObjectURL file)]
+                (swap! *assets-url-cache assoc (keyword handle-path) url)
+                url))))))))
 
 (defn delete-asset-of-block!
   [{:keys [repo href title full-text block-id local?] :as opts}]
@@ -1604,7 +1604,10 @@
     (save-block! repo block content)
     (when local?
       ;; FIXME: should be relative to current block page path
-      (fs/unlink! (config/get-repo-path repo (string/replace href #"^../" "/")) nil))))
+      (fs/unlink! (config/get-repo-path
+                   repo (-> href
+                            (string/replace #"^../" "/")
+                            (string/replace #"^assets://" ""))) nil))))
 
 (defn upload-image
   [id files format uploading? drop-or-paste?]
@@ -1617,7 +1620,7 @@
              (when-let [[url file] (and (seq res) (first res))]
                (insert-command!
                 id
-                (get-image-link format (get-asset-link url) (.-name file))
+                (get-image-link format (get-asset-link url) (if file (.-name file) "image"))
                 format
                 {:last-pattern (if drop-or-paste? "" commands/slash)
                  :restore?     true}))))

+ 38 - 36
src/main/frontend/handler/image.cljs

@@ -11,42 +11,44 @@
 
 (defn render-local-images!
   []
-  (try
-    (let [images (array-seq (gdom/getElementsByTagName "img"))
-          get-src (fn [image] (.getAttribute image "src"))
-          local-images (filter
-                        (fn [image]
-                          (let [src (get-src image)]
-                            (and src
-                                 (not (or (util/starts-with? src "http://")
-                                          (util/starts-with? src "https://")
-                                          (util/starts-with? src "blob:"))))))
-                        images)]
-      (doseq [img local-images]
-        (gobj/set img
-                  "onerror"
-                  (fn []
-                    (gobj/set (gobj/get img "style")
-                              "display" "none")))
-        (let [path (get-src img)
-              path (string/replace-first path "file:" "")
-              path (if (= (first path) \.)
-                     (subs path 1)
-                     path)]
-          (util/p-handle
-           (fs/read-file (config/get-repo-dir (state/get-current-repo))
-                         path)
-           (fn [blob]
-             (let [blob (js/Blob. (array blob) (clj->js {:type "image"}))
-                   img-url (image/create-object-url blob)]
-               (gobj/set img "src" img-url)
-               (gobj/set (gobj/get img "style")
-                         "display" "initial")))
-           (fn [error]
-             (println "Can't read local image file: ")
-             (js/console.dir error))))))
-    (catch js/Error e
-      nil)))
+  (when-not (and (util/electron?)
+                 (config/local-db? (state/get-current-repo)))
+    (try
+      (let [images (array-seq (gdom/getElementsByTagName "img"))
+            get-src (fn [image] (.getAttribute image "src"))
+            local-images (filter
+                          (fn [image]
+                            (let [src (get-src image)]
+                              (and src
+                                   (not (or (util/starts-with? src "http://")
+                                            (util/starts-with? src "https://")
+                                            (util/starts-with? src "blob:"))))))
+                          images)]
+        (doseq [img local-images]
+          (gobj/set img
+                    "onerror"
+                    (fn []
+                      (gobj/set (gobj/get img "style")
+                                "display" "none")))
+          (let [path (get-src img)
+                path (string/replace-first path "file:" "")
+                path (if (= (first path) \.)
+                       (subs path 1)
+                       path)]
+            (util/p-handle
+             (fs/read-file (config/get-repo-dir (state/get-current-repo))
+                           path)
+             (fn [blob]
+               (let [blob (js/Blob. (array blob) (clj->js {:type "image"}))
+                     img-url (image/create-object-url blob)]
+                 (gobj/set img "src" img-url)
+                 (gobj/set (gobj/get img "style")
+                           "display" "initial")))
+             (fn [error]
+               (println "Can't read local image file: ")
+               (js/console.dir error))))))
+      (catch js/Error e
+        nil))))
 
 (defn request-presigned-url
   [file filename mime-type uploading? url-handler on-processing]