170 lines
6.7 KiB
TypeScript
170 lines
6.7 KiB
TypeScript
/*
|
|
* SPDX-License-Identifier: GPL-3.0
|
|
* Vesktop, a desktop app aiming to give you a snappier Discord Experience
|
|
* Copyright (c) 2023 Vendicated and Vencord contributors
|
|
*/
|
|
|
|
import { dialog, NativeImage, nativeImage } from "electron";
|
|
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
import { readFile } from "fs/promises";
|
|
import { join } from "path";
|
|
import { IpcEvents } from "shared/IpcEvents";
|
|
import { BADGE_DIR, ICONS_DIR, STATIC_DIR } from "shared/paths";
|
|
|
|
import { lastBadgeCount } from "./appBadge";
|
|
import { mainWin, tray } from "./mainWindow";
|
|
import { Settings } from "./settings";
|
|
|
|
export const statusToSettingsKey = {
|
|
speaking: "traySpeakingOverride",
|
|
muted: "trayMutedOverride",
|
|
deafened: "trayDeafenedOverride",
|
|
idle: "trayIdleOverride",
|
|
icon: "trayMainOverride"
|
|
};
|
|
|
|
export const isCustomIcon = (status: string) => {
|
|
const settingKey = statusToSettingsKey[status as keyof typeof statusToSettingsKey];
|
|
return Settings.store[settingKey];
|
|
};
|
|
|
|
export async function setTrayIcon(iconName: string) {
|
|
if (!tray || tray.isDestroyed()) return;
|
|
const Icons = new Set(["speaking", "muted", "deafened", "idle", "icon"]);
|
|
if (!Icons.has(iconName)) return;
|
|
|
|
// if need to set main icon then check whether there is need of notif badge
|
|
if (iconName === "icon" && lastBadgeCount === -1) {
|
|
var trayImage: NativeImage;
|
|
if (isCustomIcon("icon")) {
|
|
trayImage = nativeImage.createFromPath(join(ICONS_DIR, "icon_custom.png"));
|
|
} else {
|
|
trayImage = nativeImage.createFromPath(join(ICONS_DIR, "icon.png"));
|
|
}
|
|
|
|
const badgeSvg = readFileSync(join(BADGE_DIR, `badge.svg`), "utf8");
|
|
// and send IPC call to renderer to add badge to icon
|
|
mainWin.webContents.send(IpcEvents.ADD_BADGE_TO_ICON, trayImage.toDataURL(), badgeSvg);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
var trayImage: NativeImage;
|
|
if (isCustomIcon(iconName)) {
|
|
trayImage = nativeImage.createFromPath(join(ICONS_DIR, iconName + "_custom.png"));
|
|
if (trayImage.isEmpty()) {
|
|
const iconKey = statusToSettingsKey[iconName as keyof typeof statusToSettingsKey];
|
|
Settings.store[iconKey] = false;
|
|
generateTrayIcons();
|
|
return;
|
|
}
|
|
} else trayImage = nativeImage.createFromPath(join(ICONS_DIR, iconName + ".png"));
|
|
if (trayImage.isEmpty()) {
|
|
generateTrayIcons();
|
|
return;
|
|
}
|
|
if (process.platform === "darwin") {
|
|
trayImage = trayImage.resize({ width: 16, height: 16 });
|
|
}
|
|
tray.setImage(trayImage);
|
|
} catch (error) {
|
|
console.log("Error: ", error, "Regenerating tray icons.");
|
|
generateTrayIcons(); // TODO: pass here only one icon request
|
|
}
|
|
return;
|
|
}
|
|
|
|
export async function setTrayIconWithBadge(iconDataURL: string) {
|
|
var trayImage = nativeImage.createFromDataURL(iconDataURL);
|
|
if (process.platform === "darwin") {
|
|
trayImage = trayImage.resize({ width: 16, height: 16 });
|
|
}
|
|
tray.setImage(trayImage);
|
|
}
|
|
|
|
export async function getTrayIconFile(iconName: string) {
|
|
const Icons = new Set(["speaking", "muted", "deafened", "idle"]);
|
|
if (!Icons.has(iconName)) {
|
|
iconName = "icon";
|
|
return readFile(join(STATIC_DIR, "icon.png"));
|
|
}
|
|
return readFile(join(STATIC_DIR, iconName + ".svg"), "utf8");
|
|
}
|
|
|
|
export function getTrayIconFileSync(iconName: string) {
|
|
// returns dataURL of image from TrayIcons folder
|
|
const Icons = new Set(["speaking", "muted", "deafened", "idle", "icon"]);
|
|
|
|
if (Icons.has(iconName)) {
|
|
var img: NativeImage;
|
|
if (isCustomIcon(iconName)) {
|
|
img = nativeImage.createFromPath(join(ICONS_DIR, iconName + "_custom.png"));
|
|
} else img = nativeImage.createFromPath(join(ICONS_DIR, iconName + ".png"));
|
|
img = img.resize({ width: 128, height: 128 });
|
|
if (img.isEmpty()) {
|
|
console.log("Can't open icon file. Regenerating.");
|
|
generateTrayIcons();
|
|
img = nativeImage.createFromPath(join(ICONS_DIR, iconName + ".png"));
|
|
const iconKey = statusToSettingsKey[iconName as keyof typeof statusToSettingsKey];
|
|
Settings.store[iconKey] = false;
|
|
}
|
|
return img.toDataURL();
|
|
}
|
|
}
|
|
|
|
export async function createTrayIcon(
|
|
iconName: string,
|
|
iconDataURL: string,
|
|
isCustomIcon: boolean = false,
|
|
isSvg: boolean = false
|
|
) {
|
|
// creates .png at config/TrayIcons/iconName.png from given iconDataURL
|
|
// primarily called from renderer using CREATE_TRAY_ICON_RESPONSE IPC call
|
|
iconDataURL = iconDataURL.replace(/^data:image\/png;base64,/, "");
|
|
if (isCustomIcon) {
|
|
const img = nativeImage.createFromDataURL(iconDataURL).resize({ width: 128, height: 128 });
|
|
if (isSvg) writeFileSync(join(ICONS_DIR, iconName + "_custom.png"), iconDataURL, "base64");
|
|
else writeFileSync(join(ICONS_DIR, iconName + "_custom.png"), img.toPNG());
|
|
} else {
|
|
writeFileSync(join(ICONS_DIR, iconName + ".png"), iconDataURL, "base64");
|
|
}
|
|
mainWin.webContents.send(IpcEvents.SET_CURRENT_VOICE_TRAY_ICON);
|
|
}
|
|
|
|
export async function generateTrayIcons() {
|
|
// this function generates tray icons as .png's in Vesktop cache for future use
|
|
mkdirSync(ICONS_DIR, { recursive: true });
|
|
const Icons = ["speaking", "muted", "deafened", "idle"];
|
|
for (const icon of Icons) {
|
|
mainWin.webContents.send(IpcEvents.CREATE_TRAY_ICON_REQUEST, icon);
|
|
}
|
|
const img = nativeImage.createFromPath(join(STATIC_DIR, "icon.png")).resize({ width: 128, height: 128 });
|
|
writeFileSync(join(ICONS_DIR, "icon.png"), img.toPNG());
|
|
mainWin.webContents.send(IpcEvents.SET_CURRENT_VOICE_TRAY_ICON);
|
|
}
|
|
|
|
export async function pickTrayIcon(iconName: string) {
|
|
const Icons = new Set(["speaking", "muted", "deafened", "idle", "icon"]);
|
|
if (!Icons.has(iconName)) return;
|
|
|
|
const res = await dialog.showOpenDialog(mainWin!, {
|
|
properties: ["openFile"],
|
|
filters: [{ name: "Image", extensions: ["png", "jpg", "svg"] }]
|
|
});
|
|
if (!res.filePaths.length) return "cancelled";
|
|
const dir = res.filePaths[0];
|
|
// add .svg !!
|
|
if (dir.split(".").pop() === "svg") {
|
|
mainWin.webContents.send(IpcEvents.CREATE_TRAY_ICON_REQUEST, iconName, readFileSync(dir, "utf-8"));
|
|
return "svg";
|
|
}
|
|
const image = nativeImage.createFromPath(dir);
|
|
if (image.isEmpty()) return "invalid";
|
|
const img = nativeImage.createFromPath(dir).resize({ width: 128, height: 128 });
|
|
writeFileSync(join(ICONS_DIR, iconName + "_custom.png"), img.toPNG());
|
|
return dir;
|
|
}
|
|
|
|
export async function getIconWithBadge(dataURL: string) {
|
|
tray.setImage(nativeImage.createFromDataURL(dataURL));
|
|
}
|