Files
ThreadNet-Web/src/tray.ts
T

117 lines
3.9 KiB
TypeScript
Raw Normal View History

/*
2024-09-06 17:56:18 +01:00
Copyright 2024 New Vector Ltd.
Copyright 2017 Karl Glatz <karl@glatz.biz>
Copyright 2017 OpenMarket Ltd
2025-01-17 11:44:49 +00:00
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
2024-09-06 17:56:18 +01:00
Please see LICENSE files in the repository root for full details.
*/
2025-07-15 15:35:08 +01:00
import { app, Tray, Menu, nativeImage, ipcMain, type IpcMainEvent } from "electron";
import { v5 as uuidv5 } from "uuid";
2022-01-10 12:57:33 +00:00
2024-10-30 18:06:03 +00:00
import { _t } from "./language-helper.js";
2022-10-13 12:42:33 +01:00
let trayIcon: Tray | null = null;
2021-06-25 14:35:58 +01:00
export function hasTray(): boolean {
2022-12-15 11:00:58 +00:00
return trayIcon !== null;
2021-06-25 14:35:58 +01:00
}
2021-06-25 14:35:58 +01:00
export function destroy(): void {
if (trayIcon) {
trayIcon.destroy();
trayIcon = null;
}
2021-06-25 14:35:58 +01:00
}
2021-06-25 14:35:58 +01:00
function toggleWin(): void {
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();
}
2021-06-25 14:35:58 +01:00
}
2021-06-25 14:35:58 +01:00
interface IConfig {
2024-10-16 17:21:49 +02:00
icon_path: string; // eslint-disable-line camelcase
2021-06-25 14:35:58 +01:00
brand: string;
}
function getUuid(): string {
// The uuid field is optional and only needed on unsigned Windows packages where the executable path changes
// The hardcoded uuid is an arbitrary v4 uuid generated on https://www.uuidgenerator.net/version4
return global.vectorConfig["uuid"] || "eba84003-e499-4563-8e9d-166e34b5cc25";
}
2021-06-25 14:35:58 +01:00
export function create(config: IConfig): void {
// no trays on darwin
2022-12-15 11:00:58 +00:00
if (process.platform === "darwin" || trayIcon) return;
2024-10-16 17:21:49 +02:00
const defaultIcon = nativeImage.createFromPath(config.icon_path);
let guid: string | undefined;
if (process.platform === "win32" && app.isPackaged) {
// Providing a GUID lets Windows be smarter about maintaining user's tray preferences
// https://github.com/electron/electron/pull/21891
// Ideally we would only specify it for signed packages but determining whether the app is signed sufficiently
// is non-trivial. So instead we have an escape hatch that unsigned packages can iterate the `uuid` in
// config.json to prevent Windows refusing GUID-reuse if their executable path changes.
guid = uuidv5(`${app.getName()}-${app.getPath("userData")}`, getUuid());
}
2024-01-30 14:02:16 +00:00
// Passing guid=undefined on Windows will cause it to throw `Error: Invalid GUID format`
// The type here is wrong, the param must be omitted, never undefined.
trayIcon = guid ? new Tray(defaultIcon, guid) : new Tray(defaultIcon);
trayIcon.setToolTip(config.brand);
initApplicationMenu();
2022-12-15 11:00:58 +00:00
trayIcon.on("click", toggleWin);
2025-07-14 15:52:41 +01:00
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);
trayIcon?.setImage(defaultIcon);
lastFavicon = null;
}
2025-07-14 15:52:41 +01:00
return;
}
2025-07-14 15:52:41 +01:00
// No need to change, shortcut
if (favicons[0] === lastFavicon) return;
lastFavicon = favicons[0];
2025-07-14 15:52:41 +01:00
const newFavicon = nativeImage.createFromDataURL(favicons[0]);
trayIcon?.setImage(newFavicon);
global.mainWindow?.setIcon(newFavicon);
});
2022-12-15 11:00:58 +00:00
global.mainWindow?.webContents.on("page-title-updated", function (ev, title) {
2022-10-13 12:42:33 +01:00
trayIcon?.setToolTip(title);
});
2021-06-25 14:35:58 +01:00
}
2021-06-25 14:35:58 +01:00
export function initApplicationMenu(): void {
if (!trayIcon) {
return;
}
const contextMenu = Menu.buildFromTemplate([
{
label: _t("action|show_hide"),
click: toggleWin,
},
2022-12-15 11:00:58 +00:00
{ type: "separator" },
{
label: _t("action|quit"),
2022-12-15 11:00:58 +00:00
click: function (): void {
app.quit();
},
},
]);
trayIcon.setContextMenu(contextMenu);
}