Skip to content

Commit

Permalink
improve(electron): adapt paste/drop asset for electron clipboard api
Browse files Browse the repository at this point in the history
  • Loading branch information
xyhp915 committed Jan 25, 2021
1 parent 6b61a3c commit b761995
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 60 deletions.
44 changes: 41 additions & 3 deletions 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) => {
Expand Down Expand Up @@ -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')
}

Expand All @@ -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://', '')
}
}
})
17 changes: 15 additions & 2 deletions src/electron/electron/core.cljs
Expand Up @@ -2,16 +2,18 @@
(: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"))))

(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))
Expand Down Expand Up @@ -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)))
Expand All @@ -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*)
Expand Down
4 changes: 2 additions & 2 deletions src/electron/electron/handler.cljs
Expand Up @@ -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}))))
Expand All @@ -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}))]
Expand Down
37 changes: 20 additions & 17 deletions src/main/frontend/handler/editor.cljs
Expand Up @@ -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]))))))))

Expand All @@ -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}]
Expand All @@ -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?]
Expand All @@ -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}))))
Expand Down
74 changes: 38 additions & 36 deletions src/main/frontend/handler/image.cljs
Expand Up @@ -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]
Expand Down

0 comments on commit b761995

Please sign in to comment.