mirror of
https://github.com/zulip/zulip-desktop.git
synced 2025-11-03 05:23:17 +00:00
Move handleExternalLink to main process.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
@@ -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))();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user