feat: save icons into vesktop cache

This commit is contained in:
Oleh Polisan 2024-06-18 21:38:48 +03:00
parent 1e5d25c943
commit 74e3902545
6 changed files with 74 additions and 28 deletions

View file

@ -28,7 +28,7 @@ import { IpcEvents } from "../shared/IpcEvents";
import { setBadgeCount } from "./appBadge"; import { setBadgeCount } from "./appBadge";
import { autoStart } from "./autoStart"; import { autoStart } from "./autoStart";
import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./constants"; import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE, VENCORD_THEMES_DIR } from "./constants";
import { getTrayIconFile, mainWin, setTrayIcon } from "./mainWindow"; import { createTrayIcon, generateTrayIcons, getTrayIconFile, mainWin, setTrayIcon } from "./mainWindow";
import { Settings } from "./settings"; import { Settings } from "./settings";
import { handle, handleSync } from "./utils/ipcWrappers"; import { handle, handleSync } from "./utils/ipcWrappers";
import { PopoutWindows } from "./utils/popout"; import { PopoutWindows } from "./utils/popout";
@ -163,3 +163,5 @@ watch(
handle(IpcEvents.SET_TRAY_ICON, (_, iconURI) => setTrayIcon(iconURI)); handle(IpcEvents.SET_TRAY_ICON, (_, iconURI) => setTrayIcon(iconURI));
handle(IpcEvents.GET_TRAY_ICON, (_, iconName) => getTrayIconFile(iconName)); handle(IpcEvents.GET_TRAY_ICON, (_, iconName) => getTrayIconFile(iconName));
handle(IpcEvents.GET_SYSTEM_ACCENT_COLOR, () => `#${systemPreferences.getAccentColor?.() || ""}`); handle(IpcEvents.GET_SYSTEM_ACCENT_COLOR, () => `#${systemPreferences.getAccentColor?.() || ""}`);
handle(IpcEvents.CREATE_TRAY_ICON_RESPONSE, (_, iconName, dataURL) => createTrayIcon(iconName, dataURL));
handle(IpcEvents.GENERATE_TRAY_ICONS, () => generateTrayIcons());

View file

@ -11,12 +11,12 @@ import {
dialog, dialog,
Menu, Menu,
MenuItemConstructorOptions, MenuItemConstructorOptions,
nativeImage,
nativeTheme, nativeTheme,
screen, screen,
session, session,
Tray Tray
} from "electron"; } from "electron";
import { mkdirSync, writeFileSync } from "fs";
import { readFile, rm } from "fs/promises"; import { readFile, rm } from "fs/promises";
import { join } from "path"; import { join } from "path";
import { IpcEvents } from "shared/IpcEvents"; import { IpcEvents } from "shared/IpcEvents";
@ -503,10 +503,10 @@ export async function createWindows() {
initArRPC(); initArRPC();
} }
export async function setTrayIcon(iconURI: string) { export async function setTrayIcon(iconName: string) {
if (!tray || tray.isDestroyed()) return; if (!tray || tray.isDestroyed()) return;
if (iconURI !== "" && iconURI !== "icon") { if (iconName !== "icon") {
tray.setImage(nativeImage.createFromDataURL(iconURI)); tray.setImage(join(DATA_DIR, "TrayIcons", iconName + ".png"));
return; return;
} }
tray.setImage(join(STATIC_DIR, "icon.png")); tray.setImage(join(STATIC_DIR, "icon.png"));
@ -514,10 +514,31 @@ export async function setTrayIcon(iconURI: string) {
export async function getTrayIconFile(iconName: string) { export async function getTrayIconFile(iconName: string) {
const Icons = new Set(["speaking", "muted", "deafened", "idle"]); const Icons = new Set(["speaking", "muted", "deafened", "idle"]);
// add here checks for user-defined icons
if (!Icons.has(iconName)) { if (!Icons.has(iconName)) {
iconName = "icon"; iconName = "icon";
return readFile(join(STATIC_DIR, "icon.png")); return readFile(join(STATIC_DIR, "icon.png"));
} }
return readFile(join(STATIC_DIR, iconName + ".svg"), "utf8"); return readFile(join(STATIC_DIR, iconName + ".svg"), "utf8");
} }
export async function createTrayIcon(iconName: string, iconDataURL: string) {
iconDataURL = iconDataURL.replace(/^data:image\/png;base64,/, "");
writeFileSync(join(DATA_DIR, "TrayIcons", 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(join(DATA_DIR, "TrayIcons"), { recursive: true });
const trayIconsColor = Settings.store.trayColor ?? "#3DB77F";
const userDefinedIcons = false;
if (userDefinedIcons) {
} else {
const Icons = ["speaking", "muted", "deafened", "idle"];
for (const icon of Icons) {
mainWin.webContents.send(IpcEvents.CREATE_TRAY_ICON_REQUEST, icon);
}
}
mainWin.webContents.send(IpcEvents.SET_CURRENT_VOICE_TRAY_ICON);
}

View file

@ -26,8 +26,6 @@ export const VesktopNative = {
getVersion: () => sendSync<void>(IpcEvents.GET_VERSION), getVersion: () => sendSync<void>(IpcEvents.GET_VERSION),
setBadgeCount: (count: number) => invoke<void>(IpcEvents.SET_BADGE_COUNT, count), setBadgeCount: (count: number) => invoke<void>(IpcEvents.SET_BADGE_COUNT, count),
supportsWindowsTransparency: () => sendSync<boolean>(IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY), supportsWindowsTransparency: () => sendSync<boolean>(IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY),
setTrayIcon: (iconURI: string) => invoke<void>(IpcEvents.SET_TRAY_ICON, iconURI),
getTrayIcon: (iconName: string) => invoke<string>(IpcEvents.GET_TRAY_ICON, iconName),
getAccentColor: () => invoke<string>(IpcEvents.GET_SYSTEM_ACCENT_COLOR) getAccentColor: () => invoke<string>(IpcEvents.GET_SYSTEM_ACCENT_COLOR)
}, },
autostart: { autostart: {
@ -81,5 +79,18 @@ export const VesktopNative = {
clipboard: { clipboard: {
copyImage: (imageBuffer: Uint8Array, imageSrc: string) => copyImage: (imageBuffer: Uint8Array, imageSrc: string) =>
invoke<void>(IpcEvents.CLIPBOARD_COPY_IMAGE, imageBuffer, imageSrc) invoke<void>(IpcEvents.CLIPBOARD_COPY_IMAGE, imageBuffer, imageSrc)
},
tray: {
setIcon: (iconURI: string) => invoke<void>(IpcEvents.SET_TRAY_ICON, iconURI),
getIcon: (iconName: string) => invoke<string>(IpcEvents.GET_TRAY_ICON, iconName),
createIconResponse: (iconName: string, iconDataURL: string) =>
invoke<void>(IpcEvents.CREATE_TRAY_ICON_RESPONSE, iconName, iconDataURL),
createIconRequest: (listener: (iconName: string) => void) => {
ipcRenderer.on(IpcEvents.CREATE_TRAY_ICON_REQUEST, (_, iconName: string) => listener(iconName));
},
generateTrayIcons: () => invoke<void>(IpcEvents.GENERATE_TRAY_ICONS),
setCurrentVoiceIcon: (listener: (...args: any[]) => void) => {
ipcRenderer.on(IpcEvents.SET_CURRENT_VOICE_TRAY_ICON, listener);
}
} }
}; };

View file

@ -9,7 +9,7 @@ import "./traySetting.css";
import { Margins } from "@vencord/types/utils"; import { Margins } from "@vencord/types/utils";
import { findByCodeLazy } from "@vencord/types/webpack"; import { findByCodeLazy } from "@vencord/types/webpack";
import { Forms, Select, Switch } from "@vencord/types/webpack/common"; import { Forms, Select, Switch } from "@vencord/types/webpack/common";
import { isInCall, setCurrentTrayIcon } from "renderer/patches/tray"; import { setCurrentTrayIcon } from "renderer/patches/tray";
import { isLinux, isMac } from "renderer/utils"; import { isLinux, isMac } from "renderer/utils";
import { SettingsComponent } from "./Settings"; import { SettingsComponent } from "./Settings";
@ -40,7 +40,7 @@ export const TraySwitch: SettingsComponent = ({ settings }) => {
value={settings.tray ?? true} value={settings.tray ?? true}
onChange={async v => { onChange={async v => {
settings.tray = v; settings.tray = v;
if (isInCall) setCurrentTrayIcon(); setCurrentTrayIcon();
}} }}
note="Add a tray icon for Vesktop" note="Add a tray icon for Vesktop"
> >
@ -63,7 +63,7 @@ export const TrayIconPicker: SettingsComponent = ({ settings }) => {
onChange={newColor => { onChange={newColor => {
const hexColor = newColor.toString(16).padStart(6, "0"); const hexColor = newColor.toString(16).padStart(6, "0");
settings.trayColor = hexColor; settings.trayColor = hexColor;
if (isInCall) setCurrentTrayIcon(); VesktopNative.tray.generateTrayIcons();
}} }}
showEyeDropper={false} showEyeDropper={false}
suggestedColors={presets} suggestedColors={presets}
@ -75,6 +75,7 @@ export const TrayIconPicker: SettingsComponent = ({ settings }) => {
}; };
export const TrayFillColorSwitch: SettingsComponent = ({ settings }) => { export const TrayFillColorSwitch: SettingsComponent = ({ settings }) => {
if (!settings.tray) return null;
return ( return (
<div className="vcd-tray-settings"> <div className="vcd-tray-settings">
<div className="vcd-tray-container"> <div className="vcd-tray-container">
@ -93,7 +94,7 @@ export const TrayFillColorSwitch: SettingsComponent = ({ settings }) => {
closeOnSelect={true} closeOnSelect={true}
select={v => { select={v => {
settings.trayAutoFill = v; settings.trayAutoFill = v;
if (isInCall) setCurrentTrayIcon(); VesktopNative.tray.generateTrayIcons();
}} }}
isSelected={v => v === settings.trayAutoFill} isSelected={v => v === settings.trayAutoFill}
serialize={s => s} serialize={s => s}

View file

@ -10,15 +10,27 @@ import { FluxDispatcher, UserStore } from "@vencord/types/webpack/common";
const voiceActions = findByPropsLazy("isSelfMute"); const voiceActions = findByPropsLazy("isSelfMute");
export var isInCall = false; var isInCall = false;
const logger = new Logger("VesktopTrayIcon"); const logger = new Logger("VesktopTrayIcon");
async function changeIconColor(iconName: string) { export function setCurrentTrayIcon() {
if (isInCall) {
if (voiceActions.isSelfDeaf()) {
VesktopNative.tray.setIcon("deafened");
} else if (voiceActions.isSelfMute()) {
VesktopNative.tray.setIcon("muted");
} else {
VesktopNative.tray.setIcon("idle");
}
}
}
VesktopNative.tray.createIconRequest(async (iconName: string) => {
const pickedColor = VesktopNative.settings.get().trayColor; const pickedColor = VesktopNative.settings.get().trayColor;
const fillColor = VesktopNative.settings.get().trayAutoFill ?? "auto"; const fillColor = VesktopNative.settings.get().trayAutoFill ?? "auto";
try { try {
var svg = await VesktopNative.app.getTrayIcon(iconName); var svg = await VesktopNative.tray.getIcon(iconName);
svg = svg.replace(/#f6bfac/gim, "#" + (pickedColor ?? "3DB77F")); svg = svg.replace(/#f6bfac/gim, "#" + (pickedColor ?? "3DB77F"));
if (fillColor !== "auto") { if (fillColor !== "auto") {
svg = svg.replace(/black/gim, fillColor); svg = svg.replace(/black/gim, fillColor);
@ -35,32 +47,27 @@ async function changeIconColor(iconName: string) {
if (ctx) { if (ctx) {
ctx.drawImage(img, 0, 0); ctx.drawImage(img, 0, 0);
const dataURL = canvas.toDataURL("image/png"); const dataURL = canvas.toDataURL("image/png");
VesktopNative.app.setTrayIcon(dataURL); VesktopNative.tray.createIconResponse(iconName, dataURL);
} }
}; };
img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`; img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
} catch (error) { } catch (error) {
logger.error("Error: ", error); logger.error("Error: ", error);
} }
} });
export function setCurrentTrayIcon() { VesktopNative.tray.setCurrentVoiceIcon(() => {
if (voiceActions.isSelfDeaf()) { setCurrentTrayIcon();
changeIconColor("deafened"); });
} else if (voiceActions.isSelfMute()) {
changeIconColor("muted");
} else {
changeIconColor("idle");
}
}
onceReady.then(() => { onceReady.then(() => {
VesktopNative.tray.generateTrayIcons();
const userID = UserStore.getCurrentUser().id; const userID = UserStore.getCurrentUser().id;
FluxDispatcher.subscribe("SPEAKING", params => { FluxDispatcher.subscribe("SPEAKING", params => {
if (params.userId === userID) { if (params.userId === userID) {
if (params.speakingFlags) { if (params.speakingFlags) {
changeIconColor("speaking"); VesktopNative.tray.setIcon("speaking");
} else { } else {
setCurrentTrayIcon(); setCurrentTrayIcon();
} }
@ -80,7 +87,7 @@ onceReady.then(() => {
isInCall = true; isInCall = true;
setCurrentTrayIcon(); setCurrentTrayIcon();
} else if (params.state === "RTC_DISCONNECTED") { } else if (params.state === "RTC_DISCONNECTED") {
VesktopNative.app.setTrayIcon("icon"); VesktopNative.tray.setIcon("icon");
isInCall = false; isInCall = false;
} }
}); });

View file

@ -53,5 +53,9 @@ export const enum IpcEvents {
SET_TRAY_ICON = "VCD_SET_TRAY_ICON", SET_TRAY_ICON = "VCD_SET_TRAY_ICON",
GET_TRAY_ICON = "VCD_GET_TRAY_ICON", GET_TRAY_ICON = "VCD_GET_TRAY_ICON",
CREATE_TRAY_ICON_REQUEST = "VCD_CREATE_TRAY_ICON_REQUEST",
CREATE_TRAY_ICON_RESPONSE = "VCD_CREATE_TRAY_ICON_RESPONSE",
GENERATE_TRAY_ICONS = "VCD_GENERATE_TRAY_ICONS",
SET_CURRENT_VOICE_TRAY_ICON = "VCD_SET_CURRENT_VOICE_ICON",
GET_SYSTEM_ACCENT_COLOR = "VCD_GET_ACCENT_COLOR" GET_SYSTEM_ACCENT_COLOR = "VCD_GET_ACCENT_COLOR"
} }