mirror of
https://github.com/element-hq/element-desktop.git
synced 2026-01-01 20:20:38 -05:00
Compare commits
11 Commits
v1.11.16
...
t3chguy/ty
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95b600e49d | ||
|
|
6d065ed16c | ||
|
|
017721ecca | ||
|
|
5d0f3921b4 | ||
|
|
ee28079c2b | ||
|
|
031ee44571 | ||
|
|
c74fdb632b | ||
|
|
d72bf8b414 | ||
|
|
34e96ea25e | ||
|
|
18f61ab734 | ||
|
|
705972d87b |
5
.github/CODEOWNERS
vendored
5
.github/CODEOWNERS
vendored
@@ -1 +1,4 @@
|
||||
* @vector-im/element-web
|
||||
* @vector-im/element-web
|
||||
/.github/workflows/** @vector-im/element-web-app-team
|
||||
/package.json @vector-im/element-web-app-team
|
||||
/yarn.lock @vector-im/element-web-app-team
|
||||
|
||||
20
package.json
20
package.json
@@ -46,8 +46,8 @@
|
||||
"electron-store": "^8.0.2",
|
||||
"electron-window-state": "^5.0.3",
|
||||
"minimist": "^1.2.6",
|
||||
"png-to-ico": "^2.1.1",
|
||||
"request": "^2.88.2"
|
||||
"node-fetch": "^2",
|
||||
"png-to-ico": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/client-s3": "^3.213.0",
|
||||
@@ -58,9 +58,10 @@
|
||||
"@types/auto-launch": "^5.0.1",
|
||||
"@types/counterpart": "^0.18.1",
|
||||
"@types/detect-libc": "^1.0.0",
|
||||
"@types/jest": "^28",
|
||||
"@types/jest": "^29.0.0",
|
||||
"@types/minimist": "^1.2.1",
|
||||
"@types/mkdirp": "^1.0.2",
|
||||
"@types/node": "^16",
|
||||
"@types/pacote": "^11.1.1",
|
||||
"@types/rimraf": "^3.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.42.0",
|
||||
@@ -68,7 +69,7 @@
|
||||
"allchange": "^1.0.6",
|
||||
"app-builder-lib": "^22.14.10",
|
||||
"asar": "^2.0.1",
|
||||
"babel-jest": "^28.1.3",
|
||||
"babel-jest": "^29.0.0",
|
||||
"chokidar": "^3.5.2",
|
||||
"detect-libc": "^1.0.3",
|
||||
"electron": "^21",
|
||||
@@ -79,12 +80,12 @@
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-matrix-org": "^0.7.0",
|
||||
"eslint-plugin-unicorn": "^44.0.2",
|
||||
"eslint-plugin-unicorn": "^45.0.0",
|
||||
"expect-playwright": "^0.8.0",
|
||||
"find-npm-prefix": "^1.0.2",
|
||||
"fs-extra": "^8.1.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"glob": "^7.1.6",
|
||||
"jest": "^28",
|
||||
"jest": "^29.0.0",
|
||||
"matrix-web-i18n": "^1.3.0",
|
||||
"mkdirp": "^1.0.3",
|
||||
"needle": "^2.5.0",
|
||||
@@ -93,7 +94,7 @@
|
||||
"playwright": "^1.25.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"tar": "^6.1.2",
|
||||
"ts-jest": "^28.0.8",
|
||||
"ts-jest": "^29.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "4.5.5"
|
||||
},
|
||||
@@ -101,9 +102,6 @@
|
||||
"matrix-seshat": "^2.3.3",
|
||||
"keytar": "^7.9.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/node": "16.11.38"
|
||||
},
|
||||
"build": {
|
||||
"appId": "im.riot.app",
|
||||
"asarUnpack": "**/*.node",
|
||||
|
||||
@@ -101,7 +101,7 @@ function humanFileSize(bytes: number, si = false, dp = 1) {
|
||||
return bytes.toFixed(dp) + ' ' + units[u];
|
||||
}
|
||||
|
||||
const dateTimeOptions = {
|
||||
const dateTimeOptions: Intl.DateTimeFormatOptions = {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "2-digit",
|
||||
|
||||
43
src/@types/global.d.ts
vendored
43
src/@types/global.d.ts
vendored
@@ -20,27 +20,26 @@ import AutoLaunch from "auto-launch";
|
||||
|
||||
import { AppLocalization } from "../language-helper";
|
||||
|
||||
// global type extensions need to use var for whatever reason
|
||||
/* eslint-disable no-var */
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
interface Global {
|
||||
mainWindow: BrowserWindow;
|
||||
appQuitting: boolean;
|
||||
appLocalization: AppLocalization;
|
||||
launcher: AutoLaunch;
|
||||
vectorConfig: Record<string, any>;
|
||||
trayConfig: {
|
||||
// eslint-disable-next-line camelcase
|
||||
icon_path: string;
|
||||
brand: string;
|
||||
};
|
||||
store: Store<{
|
||||
warnBeforeExit?: boolean;
|
||||
minimizeToTray?: boolean;
|
||||
spellCheckerEnabled?: boolean;
|
||||
autoHideMenuBar?: boolean;
|
||||
locale?: string | string[];
|
||||
disableHardwareAcceleration?: boolean;
|
||||
}>;
|
||||
}
|
||||
}
|
||||
var mainWindow: BrowserWindow | null;
|
||||
var appQuitting: boolean;
|
||||
var appLocalization: AppLocalization;
|
||||
var launcher: AutoLaunch;
|
||||
var vectorConfig: Record<string, any>;
|
||||
var trayConfig: {
|
||||
// eslint-disable-next-line camelcase
|
||||
icon_path: string;
|
||||
brand: string;
|
||||
};
|
||||
var store: Store<{
|
||||
warnBeforeExit?: boolean;
|
||||
minimizeToTray?: boolean;
|
||||
spellCheckerEnabled?: boolean;
|
||||
autoHideMenuBar?: boolean;
|
||||
locale?: string | string[];
|
||||
disableHardwareAcceleration?: boolean;
|
||||
}>;
|
||||
}
|
||||
/* eslint-enable no-var */
|
||||
|
||||
@@ -126,6 +126,8 @@ async function tryPaths(name: string, root: string, rawPaths: string[]): Promise
|
||||
throw new Error(`Failed to find ${name} files`);
|
||||
}
|
||||
|
||||
const homeserverProps = ['default_is_url', 'default_hs_url', 'default_server_name', 'default_server_config'] as const;
|
||||
|
||||
// Find the webapp resources and set up things that require them
|
||||
async function setupGlobals(): Promise<void> {
|
||||
// find the webapp asar.
|
||||
@@ -168,12 +170,14 @@ async function setupGlobals(): Promise<void> {
|
||||
// If the local config has a homeserver defined, don't use the homeserver from the build
|
||||
// config. This is to avoid a problem where Riot thinks there are multiple homeservers
|
||||
// defined, and panics as a result.
|
||||
const homeserverProps = ['default_is_url', 'default_hs_url', 'default_server_name', 'default_server_config'];
|
||||
if (Object.keys(localConfig).find(k => homeserverProps.includes(k))) {
|
||||
if (Object.keys(localConfig).find(k => homeserverProps.includes(<any>k))) {
|
||||
// Rip out all the homeserver options from the vector config
|
||||
global.vectorConfig = Object.keys(global.vectorConfig)
|
||||
.filter(k => !homeserverProps.includes(k))
|
||||
.reduce((obj, key) => {obj[key] = global.vectorConfig[key]; return obj;}, {});
|
||||
.filter(k => !homeserverProps.includes(<any>k))
|
||||
.reduce((obj, key) => {
|
||||
obj[key] = global.vectorConfig[key];
|
||||
return obj;
|
||||
}, {} as Omit<Partial<typeof global["vectorConfig"]>, keyof typeof homeserverProps>);
|
||||
}
|
||||
|
||||
global.vectorConfig = Object.assign(global.vectorConfig, localConfig);
|
||||
@@ -244,7 +248,7 @@ const warnBeforeExit = (event: Event, input: Input): void => {
|
||||
const exitShortcutPressed =
|
||||
input.type === 'keyDown' && exitShortcuts.some(shortcutFn => shortcutFn(input, process.platform));
|
||||
|
||||
if (shouldWarnBeforeExit && exitShortcutPressed) {
|
||||
if (shouldWarnBeforeExit && exitShortcutPressed && global.mainWindow) {
|
||||
const shouldCancelCloseRequest = dialog.showMessageBoxSync(global.mainWindow, {
|
||||
type: "question",
|
||||
buttons: [_t("Cancel"), _t("Close %(brand)s", {
|
||||
@@ -338,11 +342,11 @@ app.on('ready', async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { default: installExt, REACT_DEVELOPER_TOOLS, REACT_PERF } = require('electron-devtools-installer');
|
||||
installExt(REACT_DEVELOPER_TOOLS)
|
||||
.then((name) => console.log(`Added Extension: ${name}`))
|
||||
.catch((err) => console.log('An error occurred: ', err));
|
||||
.then((name: string) => console.log(`Added Extension: ${name}`))
|
||||
.catch((err: unknown) => console.log('An error occurred: ', err));
|
||||
installExt(REACT_PERF)
|
||||
.then((name) => console.log(`Added Extension: ${name}`))
|
||||
.catch((err) => console.log('An error occurred: ', err));
|
||||
.then((name: string) => console.log(`Added Extension: ${name}`))
|
||||
.catch((err: unknown) => console.log('An error occurred: ', err));
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
@@ -446,6 +450,7 @@ app.on('ready', async () => {
|
||||
if (global.store.get('minimizeToTray', true)) tray.create(global.trayConfig);
|
||||
|
||||
global.mainWindow.once('ready-to-show', () => {
|
||||
if (!global.mainWindow) return;
|
||||
mainWindowState.manage(global.mainWindow);
|
||||
|
||||
if (!argv['hidden']) {
|
||||
@@ -469,12 +474,12 @@ app.on('ready', async () => {
|
||||
// behave, eg. Mail.app)
|
||||
e.preventDefault();
|
||||
|
||||
if (global.mainWindow.isFullScreen()) {
|
||||
global.mainWindow.once('leave-full-screen', () => global.mainWindow.hide());
|
||||
if (global.mainWindow?.isFullScreen()) {
|
||||
global.mainWindow.once('leave-full-screen', () => global.mainWindow?.hide());
|
||||
|
||||
global.mainWindow.setFullScreen(false);
|
||||
} else {
|
||||
global.mainWindow.hide();
|
||||
global.mainWindow?.hide();
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -484,9 +489,9 @@ app.on('ready', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
// Handle forward/backward mouse buttons in Windows
|
||||
global.mainWindow.on('app-command', (e, cmd) => {
|
||||
if (cmd === 'browser-backward' && global.mainWindow.webContents.canGoBack()) {
|
||||
if (cmd === 'browser-backward' && global.mainWindow?.webContents.canGoBack()) {
|
||||
global.mainWindow.webContents.goBack();
|
||||
} else if (cmd === 'browser-forward' && global.mainWindow.webContents.canGoForward()) {
|
||||
} else if (cmd === 'browser-forward' && global.mainWindow?.webContents.canGoForward()) {
|
||||
global.mainWindow.webContents.goForward();
|
||||
}
|
||||
});
|
||||
@@ -508,7 +513,7 @@ app.on('window-all-closed', () => {
|
||||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
global.mainWindow.show();
|
||||
global.mainWindow?.show();
|
||||
});
|
||||
|
||||
function beforeQuit(): void {
|
||||
|
||||
@@ -39,7 +39,7 @@ ipcMain.on('loudNotification', function(): void {
|
||||
if (process.platform === 'win32' && global.mainWindow && !global.mainWindow.isFocused() && !focusHandlerAttached) {
|
||||
global.mainWindow.flashFrame(true);
|
||||
global.mainWindow.once('focus', () => {
|
||||
global.mainWindow.flashFrame(false);
|
||||
global.mainWindow?.flashFrame(false);
|
||||
focusHandlerAttached = false;
|
||||
});
|
||||
focusHandlerAttached = true;
|
||||
|
||||
@@ -63,7 +63,7 @@ export function _t(text: string, variables: IVariables = {}): string {
|
||||
|
||||
type Component = () => void;
|
||||
|
||||
type TypedStore = Store<{ locale?: string[] }>;
|
||||
type TypedStore = Store<{ locale?: string | string[] }>;
|
||||
|
||||
export class AppLocalization {
|
||||
private static readonly STORE_KEY = "locale";
|
||||
|
||||
@@ -52,7 +52,7 @@ function processUrl(url: string): void {
|
||||
global.mainWindow.loadURL(urlToLoad.href);
|
||||
}
|
||||
|
||||
function readStore(): object {
|
||||
function readStore(): Record<string, string> {
|
||||
try {
|
||||
const s = fs.readFileSync(storePath, { encoding: "utf8" });
|
||||
const o = JSON.parse(s);
|
||||
@@ -62,7 +62,7 @@ function readStore(): object {
|
||||
}
|
||||
}
|
||||
|
||||
function writeStore(data: object): void {
|
||||
function writeStore(data: Record<string, string>): void {
|
||||
fs.writeFileSync(storePath, JSON.stringify(data));
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ export function recordSSOSession(sessionID: string): void {
|
||||
export function getProfileFromDeeplink(args: string[]): string | undefined {
|
||||
// check if we are passed a profile in the SSO callback url
|
||||
const deeplinkUrl = args.find(arg => arg.startsWith(PROTOCOL + '//'));
|
||||
if (deeplinkUrl && deeplinkUrl.includes(SEARCH_PARAM)) {
|
||||
if (deeplinkUrl?.includes(SEARCH_PARAM)) {
|
||||
const parsedUrl = new URL(deeplinkUrl);
|
||||
if (parsedUrl.protocol === PROTOCOL) {
|
||||
const ssoID = parsedUrl.searchParams.get(SEARCH_PARAM)!;
|
||||
|
||||
@@ -81,15 +81,12 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
||||
// We do this here to ensure we get the path after --profile has been resolved
|
||||
const eventStorePath = path.join(app.getPath('userData'), 'EventStore');
|
||||
|
||||
const sendError = (id, e) => {
|
||||
const sendError = (id: string, e: Error) => {
|
||||
const error = {
|
||||
message: e.message,
|
||||
};
|
||||
|
||||
global.mainWindow.webContents.send('seshatReply', {
|
||||
id: id,
|
||||
error: error,
|
||||
});
|
||||
global.mainWindow?.webContents.send('seshatReply', { id, error });
|
||||
};
|
||||
|
||||
const args = payload.args || [];
|
||||
@@ -138,7 +135,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
||||
|
||||
eventIndex = new Seshat(eventStorePath, { passphrase });
|
||||
} else {
|
||||
sendError(payload.id, e);
|
||||
sendError(payload.id, <Error>e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -153,7 +150,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
||||
try {
|
||||
await index.shutdown();
|
||||
} catch (e) {
|
||||
sendError(payload.id, e);
|
||||
sendError(payload.id, <Error>e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -182,7 +179,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
||||
try {
|
||||
eventIndex?.addEvent(args[0], args[1]);
|
||||
} catch (e) {
|
||||
sendError(payload.id, e);
|
||||
sendError(payload.id, <Error>e);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -191,7 +188,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
||||
try {
|
||||
ret = await eventIndex?.deleteEvent(args[0]);
|
||||
} catch (e) {
|
||||
sendError(payload.id, e);
|
||||
sendError(payload.id, <Error>e);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -200,7 +197,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
||||
try {
|
||||
ret = await eventIndex?.commit();
|
||||
} catch (e) {
|
||||
sendError(payload.id, e);
|
||||
sendError(payload.id, <Error>e);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -209,7 +206,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
||||
try {
|
||||
ret = await eventIndex?.search(args[0]);
|
||||
} catch (e) {
|
||||
sendError(payload.id, e);
|
||||
sendError(payload.id, <Error>e);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -221,7 +218,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
||||
ret = await eventIndex.addHistoricEvents(
|
||||
args[0], args[1], args[2]);
|
||||
} catch (e) {
|
||||
sendError(payload.id, e);
|
||||
sendError(payload.id, <Error>e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -233,7 +230,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
||||
try {
|
||||
ret = await eventIndex.getStats();
|
||||
} catch (e) {
|
||||
sendError(payload.id, e);
|
||||
sendError(payload.id, <Error>e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -245,7 +242,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
||||
try {
|
||||
ret = await eventIndex.removeCrawlerCheckpoint(args[0]);
|
||||
} catch (e) {
|
||||
sendError(payload.id, e);
|
||||
sendError(payload.id, <Error>e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -257,7 +254,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
||||
try {
|
||||
ret = await eventIndex.addCrawlerCheckpoint(args[0]);
|
||||
} catch (e) {
|
||||
sendError(payload.id, e);
|
||||
sendError(payload.id, <Error>e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -269,7 +266,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
||||
try {
|
||||
ret = await eventIndex.loadFileEvents(args[0]);
|
||||
} catch (e) {
|
||||
sendError(payload.id, e);
|
||||
sendError(payload.id, <Error>e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -292,7 +289,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
||||
try {
|
||||
await eventIndex.setUserVersion(args[0]);
|
||||
} catch (e) {
|
||||
sendError(payload.id, e);
|
||||
sendError(payload.id, <Error>e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -304,7 +301,7 @@ ipcMain.on('seshat', async function(_ev: IpcMainEvent, payload): Promise<void> {
|
||||
try {
|
||||
ret = await eventIndex.getUserVersion();
|
||||
} catch (e) {
|
||||
sendError(payload.id, e);
|
||||
sendError(payload.id, <Error>e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,12 +44,12 @@ export const Settings: Record<string, Setting> = {
|
||||
},
|
||||
"Electron.alwaysShowMenuBar": { // not supported on macOS
|
||||
async read(): Promise<any> {
|
||||
return !global.mainWindow.autoHideMenuBar;
|
||||
return !global.mainWindow!.autoHideMenuBar;
|
||||
},
|
||||
async write(value: any): Promise<void> {
|
||||
global.store.set('autoHideMenuBar', !value);
|
||||
global.mainWindow.autoHideMenuBar = !value;
|
||||
global.mainWindow.setMenuBarVisibility(value);
|
||||
global.mainWindow!.autoHideMenuBar = !value;
|
||||
global.mainWindow!.setMenuBarVisibility(value);
|
||||
},
|
||||
},
|
||||
"Electron.showTrayIcon": { // not supported on macOS
|
||||
|
||||
18
src/tray.ts
18
src/tray.ts
@@ -36,12 +36,12 @@ export function destroy(): void {
|
||||
}
|
||||
|
||||
function toggleWin(): void {
|
||||
if (global.mainWindow.isVisible() && !global.mainWindow.isMinimized() && global.mainWindow.isFocused()) {
|
||||
if (global.mainWindow?.isVisible() && !global.mainWindow.isMinimized() && global.mainWindow.isFocused()) {
|
||||
global.mainWindow.hide();
|
||||
} else {
|
||||
if (global.mainWindow.isMinimized()) global.mainWindow.restore();
|
||||
if (!global.mainWindow.isVisible()) global.mainWindow.show();
|
||||
global.mainWindow.focus();
|
||||
if (global.mainWindow?.isMinimized()) global.mainWindow.restore();
|
||||
if (!global.mainWindow?.isVisible()) global.mainWindow?.show();
|
||||
global.mainWindow?.focus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,11 +60,11 @@ export function create(config: IConfig): void {
|
||||
initApplicationMenu();
|
||||
trayIcon.on('click', toggleWin);
|
||||
|
||||
let lastFavicon = null;
|
||||
global.mainWindow.webContents.on('page-favicon-updated', async function(ev, favicons) {
|
||||
let lastFavicon: string | null = null;
|
||||
global.mainWindow?.webContents.on('page-favicon-updated', async function(ev, favicons) {
|
||||
if (!favicons || favicons.length <= 0 || !favicons[0].startsWith('data:')) {
|
||||
if (lastFavicon !== null) {
|
||||
global.mainWindow.setIcon(defaultIcon);
|
||||
global.mainWindow?.setIcon(defaultIcon);
|
||||
trayIcon?.setImage(defaultIcon);
|
||||
lastFavicon = null;
|
||||
}
|
||||
@@ -89,10 +89,10 @@ export function create(config: IConfig): void {
|
||||
}
|
||||
|
||||
trayIcon?.setImage(newFavicon);
|
||||
global.mainWindow.setIcon(newFavicon);
|
||||
global.mainWindow?.setIcon(newFavicon);
|
||||
});
|
||||
|
||||
global.mainWindow.webContents.on('page-title-updated', function(ev, title) {
|
||||
global.mainWindow?.webContents.on('page-title-updated', function(ev, title) {
|
||||
trayIcon?.setToolTip(title);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ export function buildMenuTemplate(): Menu {
|
||||
// in macOS the Preferences menu item goes in the first menu
|
||||
...(!isMac ? [{
|
||||
label: _t('Preferences'),
|
||||
click() { global.mainWindow.webContents.send('preferences'); },
|
||||
click() { global.mainWindow?.webContents.send('preferences'); },
|
||||
}] : []),
|
||||
{
|
||||
role: 'togglefullscreen',
|
||||
@@ -153,7 +153,7 @@ export function buildMenuTemplate(): Menu {
|
||||
{
|
||||
label: _t('Preferences') + '…',
|
||||
accelerator: 'Command+,', // Mac-only accelerator
|
||||
click() { global.mainWindow.webContents.send('preferences'); },
|
||||
click() { global.mainWindow?.webContents.send('preferences'); },
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
|
||||
@@ -31,7 +31,8 @@ import {
|
||||
} from 'electron';
|
||||
import url from 'url';
|
||||
import fs from 'fs';
|
||||
import request from 'request';
|
||||
import fetch from 'node-fetch';
|
||||
import { pipeline } from 'stream';
|
||||
import path from 'path';
|
||||
|
||||
import { _t } from './language-helper';
|
||||
@@ -154,7 +155,10 @@ function onLinkContextMenu(ev: Event, params: ContextMenuParams, webContents: We
|
||||
if (url.startsWith("data:")) {
|
||||
await writeNativeImage(filePath, nativeImage.createFromDataURL(url));
|
||||
} else {
|
||||
request.get(url).pipe(fs.createWriteStream(filePath));
|
||||
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) {
|
||||
console.error(err);
|
||||
@@ -224,7 +228,7 @@ function cutCopyPasteSelectContextMenus(params: ContextMenuParams): MenuItemCons
|
||||
return options;
|
||||
}
|
||||
|
||||
function onSelectedContextMenu(ev, params) {
|
||||
function onSelectedContextMenu(ev: Event, params: ContextMenuParams) {
|
||||
const items = cutCopyPasteSelectContextMenus(params);
|
||||
const popupMenu = Menu.buildFromTemplate(items);
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"target": "es2016",
|
||||
"noImplicitAny": false,
|
||||
"sourceMap": false,
|
||||
"outDir": "./lib",
|
||||
"rootDir": "./src",
|
||||
|
||||
Reference in New Issue
Block a user