perf: 优化桌面端邮件图片菜单

This commit is contained in:
kuaifan 2023-08-06 17:58:54 +08:00
parent 4a75844c98
commit 2b88764c7e
4 changed files with 194 additions and 3 deletions

154
electron/electron-menu.js vendored Normal file
View File

@ -0,0 +1,154 @@
const {
clipboard,
nativeImage,
Menu,
MenuItem,
dialog,
shell,
} = require('electron')
const fs = require('fs')
const url = require('url')
const {pipeline} = require('stream')
const MAILTO_PREFIX = "mailto:";
const PERMITTED_URL_SCHEMES = ["http:", "https:", MAILTO_PREFIX];
const electronMenu = {
language: {
openInBrowser: "在浏览器中打开",
saveImageAs: "图片存储为...",
copyImage: "复制图片",
copyEmailAddress: "复制电子邮件地址",
copyLinkAddress: "复制链接地址",
copyImageAddress: "复制图片地址",
failedToSaveImage: "图片保存失败",
theImageFailedToSave: "图片无法保存",
},
setLanguage(language) {
this.language = Object.assign(this.language, language);
},
safeOpenURL(target) {
const parsedUrl = url.parse(target);
if (PERMITTED_URL_SCHEMES.includes(parsedUrl.protocol)) {
const newTarget = url.format(parsedUrl);
shell.openExternal(newTarget).then(r => {
});
}
},
async saveImageAs(url, params) {
const targetFileName = params.suggestedFilename || params.altText || "image.png";
const {filePath} = await dialog.showSaveDialog({
defaultPath: targetFileName,
});
if (!filePath) return; // user cancelled dialog
try {
if (url.startsWith("data:")) {
await electronMenu.writeNativeImage(filePath, nativeImage.createFromDataURL(url));
} else {
const resp = await fetch(url);
if (!resp.ok) throw new Error(`unexpected response ${resp.statusText}`);
if (!resp.body) throw new Error(`unexpected response has no body ${resp.statusText}`);
pipeline(resp.body, fs.createWriteStream(filePath));
}
} catch (err) {
await dialog.showMessageBox({
type: "error",
title: electronMenu.language.failedToSaveImage,
message: electronMenu.language.theImageFailedToSave,
});
}
},
writeNativeImage(filePath, img) {
switch (filePath.split(".").pop()?.toLowerCase()) {
case "jpg":
case "jpeg":
return fs.promises.writeFile(filePath, img.toJPEG(100));
case "bmp":
return fs.promises.writeFile(filePath, img.toBitmap());
case "png":
default:
return fs.promises.writeFile(filePath, img.toPNG());
}
},
webContentsMenu(webContents) {
webContents.on("context-menu", function (e, params) {
if (params.linkURL || params.srcURL) {
const url = params.linkURL || params.srcURL;
const popupMenu = new Menu();
if (!url.startsWith("blob:")) {
popupMenu.append(
new MenuItem({
label: electronMenu.language.openInBrowser,
accelerator: "o",
click() {
electronMenu.safeOpenURL(url);
},
}),
);
if (params.hasImageContents) {
popupMenu.append(
new MenuItem({
label: electronMenu.language.saveImageAs,
accelerator: "s",
click: async function () {
await electronMenu.saveImageAs(url, params);
},
}),
);
}
}
if (params.hasImageContents) {
popupMenu.append(
new MenuItem({
label: electronMenu.language.copyImage,
accelerator: "c",
click() {
webContents.copyImageAt(params.x, params.y);
},
}),
);
}
if (!url.startsWith("blob:")) {
if (url.startsWith(MAILTO_PREFIX)) {
popupMenu.append(
new MenuItem({
label: electronMenu.language.copyEmailAddress,
accelerator: "a",
click() {
clipboard.writeText(url.substring(MAILTO_PREFIX.length));
},
}),
);
} else {
popupMenu.append(
new MenuItem({
label: params.hasImageContents ? electronMenu.language.copyImageAddress : electronMenu.language.copyLinkAddress,
accelerator: "a",
click() {
clipboard.writeText(url);
},
}),
);
}
}
if (popupMenu.items.length > 0) {
popupMenu.popup({});
e.preventDefault();
}
}
})
}
}
module.exports = electronMenu;

36
electron/electron.js vendored
View File

@ -11,6 +11,7 @@ const crc = require('crc');
const zlib = require('zlib');
const utils = require('./utils');
const config = require('./package.json');
const electronMenu = require("./electron-menu");
const spawn = require("child_process").spawn;
const isMac = process.platform === 'darwin'
@ -58,6 +59,7 @@ function createMainWindow() {
openExternal(url)
return {action: 'deny'}
})
electronMenu.webContentsMenu(mainWindow.webContents)
if (devloadUrl) {
mainWindow.loadURL(devloadUrl).then(_ => {
@ -158,6 +160,7 @@ function createSubWindow(args) {
openExternal(url)
return {action: 'deny'}
})
electronMenu.webContentsMenu(browser.webContents)
if (devloadUrl) {
browser.loadURL(devloadUrl + '#' + (args.hash || args.path)).then(_ => {
@ -257,6 +260,17 @@ app.on('browser-window-focus', () => {
}
})
/**
* 设置菜单语言包
* @param args {path}
*/
ipcMain.on('setMenuLanguage', (event, args) => {
if (utils.isJson(args)) {
electronMenu.setLanguage(args)
}
event.returnValue = "ok"
})
/**
* 打开文件
* @param args {path}
@ -475,6 +489,28 @@ ipcMain.on('copyBase64Image', (event, args) => {
event.returnValue = "ok"
})
/**
* 复制图片根据坐标
* @param args
*/
ipcMain.on('copyImageAt', (event, args) => {
try {
event.sender.copyImageAt(args.x, args.y);
} catch (e) {
// log.error(e)
}
event.returnValue = "ok"
})
/**
* 保存图片
* @param args
*/
ipcMain.on('saveImageAt', async (event, args) => {
await electronMenu.saveImageAs(args.url, args.params)
event.returnValue = "ok"
})
/**
* 绑定截图快捷键
* @param args

View File

@ -138,6 +138,7 @@
height: 100%;
justify-content: center;
width: 100%;
user-select: none;
}
}
}

View File

@ -610,6 +610,7 @@ export default {
navStyle: {},
operateClient: {x: 0, y: 0},
operateVisible: false,
operatePreventScroll: 0,
operateCopys: [],
@ -2335,6 +2336,7 @@ export default {
top: `${projectRect.top + this.windowScrollY}px`,
height: projectRect.height + 'px',
}
this.operateClient = {x: event.clientX, y: event.clientY};
this.operateVisible = true;
})
},
@ -2440,9 +2442,7 @@ export default {
switch (type) {
case 'image':
if (this.$Electron) {
this.getBase64Image(value).then(base64 => {
this.$Electron.sendMessage('copyBase64Image', {base64});
})
this.$Electron.sendMessage('copyImageAt', this.operateClient);
}
break;