Move handleExternalLink to main process.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg
2022-03-31 21:10:13 -07:00
parent 27576c95e6
commit 5edffbdf21
10 changed files with 178 additions and 170 deletions

View File

@@ -1,71 +0,0 @@
import {shell} from "electron/common";
import type {HandlerDetails} from "electron/renderer";
import {app} from "@electron/remote";
import * as ConfigUtil from "../../../common/config-util";
import {ipcRenderer} from "../typed-ipc-renderer";
import * as LinkUtil from "../utils/link-util";
import type WebView from "./webview";
const dingSound = new Audio("../resources/sounds/ding.ogg");
export default function handleExternalLink(
this: WebView,
details: HandlerDetails,
): void {
const url = new URL(details.url);
const downloadPath = ConfigUtil.getConfigItem(
"downloadsPath",
`${app.getPath("downloads")}`,
);
if (LinkUtil.isUploadsUrl(this.props.url, url)) {
ipcRenderer.send("downloadFile", url.href, downloadPath);
ipcRenderer.once(
"downloadFileCompleted",
async (_event: Event, filePath: string, fileName: string) => {
const downloadNotification = new Notification("Download Complete", {
body: `Click to show ${fileName} in folder`,
silent: true, // We'll play our own sound - ding.ogg
});
downloadNotification.addEventListener("click", () => {
// Reveal file in download folder
shell.showItemInFolder(filePath);
});
ipcRenderer.removeAllListeners("downloadFileFailed");
// Play sound to indicate download complete
if (!ConfigUtil.getConfigItem("silent", false)) {
await dingSound.play();
}
},
);
ipcRenderer.once("downloadFileFailed", (_event: Event, state: string) => {
// Automatic download failed, so show save dialog prompt and download
// through webview
// Only do this if it is the automatic download, otherwise show an error (so we aren't showing two save
// prompts right after each other)
// Check that the download is not cancelled by user
if (state !== "cancelled") {
if (ConfigUtil.getConfigItem("promptDownload", false)) {
// We need to create a "new Notification" to display it, but just `Notification(...)` on its own
// doesn't work
// eslint-disable-next-line no-new
new Notification("Download Complete", {
body: "Download failed",
});
} else {
this.getWebContents().downloadURL(url.href);
}
}
ipcRenderer.removeAllListeners("downloadFileCompleted");
});
} else {
(async () => LinkUtil.openBrowser(url))();
}
}

View File

@@ -15,7 +15,6 @@ import * as SystemUtil from "../utils/system-util";
import {generateNodeFromHtml} from "./base";
import {contextMenu} from "./context-menu";
import handleExternalLink from "./handle-external-link";
const shouldSilentWebview = ConfigUtil.getConfigItem("silent", false);
@@ -127,11 +126,6 @@ export default class WebView {
registerListeners(): void {
const webContents = this.getWebContents();
webContents.setWindowOpenHandler((details) => {
handleExternalLink.call(this, details);
return {action: "deny"};
});
if (shouldSilentWebview) {
webContents.setAudioMuted(true);
}

View File

@@ -10,6 +10,7 @@ import * as ConfigUtil from "../../common/config-util";
import * as DNDUtil from "../../common/dnd-util";
import type {DndSettings} from "../../common/dnd-util";
import * as EnterpriseUtil from "../../common/enterprise-util";
import * as LinkUtil from "../../common/link-util";
import Logger from "../../common/logger-util";
import * as Messages from "../../common/messages";
import type {NavItem, ServerConf, TabData} from "../../common/types";
@@ -22,7 +23,6 @@ import {PreferenceView} from "./pages/preference/preference";
import {initializeTray} from "./tray";
import {ipcRenderer} from "./typed-ipc-renderer";
import * as DomainUtil from "./utils/domain-util";
import * as LinkUtil from "./utils/link-util";
import ReconnectUtil from "./utils/reconnect-util";
Sentry.init({});
@@ -48,6 +48,8 @@ type ServerOrFunctionalTab = ServerTab | FunctionalTab;
const rootWebContents = remote.getCurrentWebContents();
const dingSound = new Audio("../resources/sounds/ding.ogg");
export class ServerManagerView {
$addServerButton: HTMLButtonElement;
$tabsContainer: Element;
@@ -1177,6 +1179,10 @@ export class ServerManagerView {
ipcRenderer.on("open-network-settings", async () => {
await this.openSettings("Network");
});
ipcRenderer.on("play-ding-sound", async () => {
await dingSound.play();
});
}
}

View File

@@ -1,7 +1,7 @@
import {html} from "../../../../common/html";
import * as LinkUtil from "../../../../common/link-util";
import * as t from "../../../../common/translation-util";
import {generateNodeFromHtml} from "../../components/base";
import * as LinkUtil from "../../utils/link-util";
interface FindAccountsProps {
$root: Element;

View File

@@ -1,11 +1,11 @@
import {dialog} from "@electron/remote";
import {html} from "../../../../common/html";
import * as LinkUtil from "../../../../common/link-util";
import * as t from "../../../../common/translation-util";
import {generateNodeFromHtml} from "../../components/base";
import {ipcRenderer} from "../../typed-ipc-renderer";
import * as DomainUtil from "../../utils/domain-util";
import * as LinkUtil from "../../utils/link-util";
interface NewServerFormProps {
$root: Element;

View File

@@ -1,6 +1,6 @@
import {html} from "../../../../common/html";
import * as LinkUtil from "../../../../common/link-util";
import * as t from "../../../../common/translation-util";
import * as LinkUtil from "../../utils/link-util";
interface ShortcutsSectionProps {
$root: Element;

View File

@@ -1,47 +0,0 @@
import {shell} from "electron/common";
import fs from "fs";
import os from "os";
import path from "path";
import {html} from "../../../common/html";
export function isUploadsUrl(server: string, url: URL): boolean {
return url.origin === server && url.pathname.startsWith("/user_uploads/");
}
export async function openBrowser(url: URL): Promise<void> {
if (["http:", "https:", "mailto:"].includes(url.protocol)) {
await shell.openExternal(url.href);
} else {
// For security, indirect links to non-whitelisted protocols
// through a real web browser via a local HTML file.
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "zulip-redirect-"));
const file = path.join(dir, "redirect.html");
fs.writeFileSync(
file,
html`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="Refresh" content="0; url=${url.href}" />
<title>Redirecting</title>
<style>
html {
font-family: menu, "Helvetica Neue", sans-serif;
}
</style>
</head>
<body>
<p>Opening <a href="${url.href}">${url.href}</a>…</p>
</body>
</html>
`.html,
);
await shell.openPath(file);
setTimeout(() => {
fs.unlinkSync(file);
fs.rmdirSync(dir);
}, 15_000);
}
}