From 74e39025458181dc74946f2c50ebc928075dc052 Mon Sep 17 00:00:00 2001 From: Oleh Polisan Date: Tue, 18 Jun 2024 21:38:48 +0300 Subject: [PATCH] feat: save icons into vesktop cache --- src/main/ipc.ts | 4 +- src/main/mainWindow.ts | 31 ++++++++++++--- src/preload/VesktopNative.ts | 15 ++++++- .../components/settings/TraySettings.tsx | 9 +++-- src/renderer/patches/tray.ts | 39 +++++++++++-------- src/shared/IpcEvents.ts | 4 ++ 6 files changed, 74 insertions(+), 28 deletions(-) diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 3a1a187..0b4a440 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -28,7 +28,7 @@ import { IpcEvents } from "../shared/IpcEvents"; import { setBadgeCount } from "./appBadge"; import { autoStart } from "./autoStart"; 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 { handle, handleSync } from "./utils/ipcWrappers"; import { PopoutWindows } from "./utils/popout"; @@ -163,3 +163,5 @@ watch( handle(IpcEvents.SET_TRAY_ICON, (_, iconURI) => setTrayIcon(iconURI)); handle(IpcEvents.GET_TRAY_ICON, (_, iconName) => getTrayIconFile(iconName)); 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()); diff --git a/src/main/mainWindow.ts b/src/main/mainWindow.ts index 04135e9..e1b9e15 100755 --- a/src/main/mainWindow.ts +++ b/src/main/mainWindow.ts @@ -11,12 +11,12 @@ import { dialog, Menu, MenuItemConstructorOptions, - nativeImage, nativeTheme, screen, session, Tray } from "electron"; +import { mkdirSync, writeFileSync } from "fs"; import { readFile, rm } from "fs/promises"; import { join } from "path"; import { IpcEvents } from "shared/IpcEvents"; @@ -503,10 +503,10 @@ export async function createWindows() { initArRPC(); } -export async function setTrayIcon(iconURI: string) { +export async function setTrayIcon(iconName: string) { if (!tray || tray.isDestroyed()) return; - if (iconURI !== "" && iconURI !== "icon") { - tray.setImage(nativeImage.createFromDataURL(iconURI)); + if (iconName !== "icon") { + tray.setImage(join(DATA_DIR, "TrayIcons", iconName + ".png")); return; } tray.setImage(join(STATIC_DIR, "icon.png")); @@ -514,10 +514,31 @@ export async function setTrayIcon(iconURI: string) { export async function getTrayIconFile(iconName: string) { const Icons = new Set(["speaking", "muted", "deafened", "idle"]); - + // add here checks for user-defined icons if (!Icons.has(iconName)) { iconName = "icon"; return readFile(join(STATIC_DIR, "icon.png")); } 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); +} diff --git a/src/preload/VesktopNative.ts b/src/preload/VesktopNative.ts index 30b1b9e..6e5510e 100644 --- a/src/preload/VesktopNative.ts +++ b/src/preload/VesktopNative.ts @@ -26,8 +26,6 @@ export const VesktopNative = { getVersion: () => sendSync(IpcEvents.GET_VERSION), setBadgeCount: (count: number) => invoke(IpcEvents.SET_BADGE_COUNT, count), supportsWindowsTransparency: () => sendSync(IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY), - setTrayIcon: (iconURI: string) => invoke(IpcEvents.SET_TRAY_ICON, iconURI), - getTrayIcon: (iconName: string) => invoke(IpcEvents.GET_TRAY_ICON, iconName), getAccentColor: () => invoke(IpcEvents.GET_SYSTEM_ACCENT_COLOR) }, autostart: { @@ -81,5 +79,18 @@ export const VesktopNative = { clipboard: { copyImage: (imageBuffer: Uint8Array, imageSrc: string) => invoke(IpcEvents.CLIPBOARD_COPY_IMAGE, imageBuffer, imageSrc) + }, + tray: { + setIcon: (iconURI: string) => invoke(IpcEvents.SET_TRAY_ICON, iconURI), + getIcon: (iconName: string) => invoke(IpcEvents.GET_TRAY_ICON, iconName), + createIconResponse: (iconName: string, iconDataURL: string) => + invoke(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(IpcEvents.GENERATE_TRAY_ICONS), + setCurrentVoiceIcon: (listener: (...args: any[]) => void) => { + ipcRenderer.on(IpcEvents.SET_CURRENT_VOICE_TRAY_ICON, listener); + } } }; diff --git a/src/renderer/components/settings/TraySettings.tsx b/src/renderer/components/settings/TraySettings.tsx index 7cce673..5d501fc 100644 --- a/src/renderer/components/settings/TraySettings.tsx +++ b/src/renderer/components/settings/TraySettings.tsx @@ -9,7 +9,7 @@ import "./traySetting.css"; import { Margins } from "@vencord/types/utils"; import { findByCodeLazy } from "@vencord/types/webpack"; 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 { SettingsComponent } from "./Settings"; @@ -40,7 +40,7 @@ export const TraySwitch: SettingsComponent = ({ settings }) => { value={settings.tray ?? true} onChange={async v => { settings.tray = v; - if (isInCall) setCurrentTrayIcon(); + setCurrentTrayIcon(); }} note="Add a tray icon for Vesktop" > @@ -63,7 +63,7 @@ export const TrayIconPicker: SettingsComponent = ({ settings }) => { onChange={newColor => { const hexColor = newColor.toString(16).padStart(6, "0"); settings.trayColor = hexColor; - if (isInCall) setCurrentTrayIcon(); + VesktopNative.tray.generateTrayIcons(); }} showEyeDropper={false} suggestedColors={presets} @@ -75,6 +75,7 @@ export const TrayIconPicker: SettingsComponent = ({ settings }) => { }; export const TrayFillColorSwitch: SettingsComponent = ({ settings }) => { + if (!settings.tray) return null; return (
@@ -93,7 +94,7 @@ export const TrayFillColorSwitch: SettingsComponent = ({ settings }) => { closeOnSelect={true} select={v => { settings.trayAutoFill = v; - if (isInCall) setCurrentTrayIcon(); + VesktopNative.tray.generateTrayIcons(); }} isSelected={v => v === settings.trayAutoFill} serialize={s => s} diff --git a/src/renderer/patches/tray.ts b/src/renderer/patches/tray.ts index ffadcd1..703676d 100644 --- a/src/renderer/patches/tray.ts +++ b/src/renderer/patches/tray.ts @@ -10,15 +10,27 @@ import { FluxDispatcher, UserStore } from "@vencord/types/webpack/common"; const voiceActions = findByPropsLazy("isSelfMute"); -export var isInCall = false; +var isInCall = false; 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 fillColor = VesktopNative.settings.get().trayAutoFill ?? "auto"; try { - var svg = await VesktopNative.app.getTrayIcon(iconName); + var svg = await VesktopNative.tray.getIcon(iconName); svg = svg.replace(/#f6bfac/gim, "#" + (pickedColor ?? "3DB77F")); if (fillColor !== "auto") { svg = svg.replace(/black/gim, fillColor); @@ -35,32 +47,27 @@ async function changeIconColor(iconName: string) { if (ctx) { ctx.drawImage(img, 0, 0); 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)}`; } catch (error) { logger.error("Error: ", error); } -} +}); -export function setCurrentTrayIcon() { - if (voiceActions.isSelfDeaf()) { - changeIconColor("deafened"); - } else if (voiceActions.isSelfMute()) { - changeIconColor("muted"); - } else { - changeIconColor("idle"); - } -} +VesktopNative.tray.setCurrentVoiceIcon(() => { + setCurrentTrayIcon(); +}); onceReady.then(() => { + VesktopNative.tray.generateTrayIcons(); const userID = UserStore.getCurrentUser().id; FluxDispatcher.subscribe("SPEAKING", params => { if (params.userId === userID) { if (params.speakingFlags) { - changeIconColor("speaking"); + VesktopNative.tray.setIcon("speaking"); } else { setCurrentTrayIcon(); } @@ -80,7 +87,7 @@ onceReady.then(() => { isInCall = true; setCurrentTrayIcon(); } else if (params.state === "RTC_DISCONNECTED") { - VesktopNative.app.setTrayIcon("icon"); + VesktopNative.tray.setIcon("icon"); isInCall = false; } }); diff --git a/src/shared/IpcEvents.ts b/src/shared/IpcEvents.ts index b7813fd..fd07c04 100644 --- a/src/shared/IpcEvents.ts +++ b/src/shared/IpcEvents.ts @@ -53,5 +53,9 @@ export const enum IpcEvents { SET_TRAY_ICON = "VCD_SET_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" }