From 183a38849579f393dd8599a423f6da24362dc32e Mon Sep 17 00:00:00 2001 From: Oleh Polisan Date: Sat, 22 Jun 2024 14:09:47 +0300 Subject: [PATCH] feat: choose icon from .svg --- src/main/ipc.ts | 4 +- src/main/tray.ts | 17 ++++- src/preload/VesktopNative.ts | 14 +++- .../components/settings/TraySettings.tsx | 73 ++++++++----------- src/renderer/patches/tray.ts | 7 +- 5 files changed, 61 insertions(+), 54 deletions(-) diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 1b0dccb..5c4fce6 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -163,8 +163,8 @@ handle(IpcEvents.SET_TRAY_ICON, (_, iconURI) => setTrayIcon(iconURI)); handle(IpcEvents.GET_TRAY_ICON, (_, iconPath) => getTrayIconFile(iconPath)); handleSync(IpcEvents.GET_TRAY_ICON_SYNC, (_, iconPath) => getTrayIconFileSync(iconPath)); handle(IpcEvents.GET_SYSTEM_ACCENT_COLOR, () => getAccentColor()); -handle(IpcEvents.CREATE_TRAY_ICON_RESPONSE, (_, iconName, dataURL, isCustomIcon) => - createTrayIcon(iconName, dataURL, isCustomIcon) +handle(IpcEvents.CREATE_TRAY_ICON_RESPONSE, (_, iconName, dataURL, isCustomIcon, isSvg) => + createTrayIcon(iconName, dataURL, isCustomIcon, isSvg) ); handle(IpcEvents.GENERATE_TRAY_ICONS, () => generateTrayIcons()); handle(IpcEvents.SELECT_TRAY_ICON, async (_, iconName) => pickTrayIcon(iconName)); diff --git a/src/main/tray.ts b/src/main/tray.ts index 6e66e0a..9c69744 100644 --- a/src/main/tray.ts +++ b/src/main/tray.ts @@ -112,14 +112,19 @@ export function getTrayIconFileSync(iconName: string) { } } -export async function createTrayIcon(iconName: string, iconDataURL: string, isCustomIcon: boolean = false) { +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 }); - // writeFileSync(join(ICONS_DIR, iconName + "_custom.png"), img.toDataURL(), "base64"); - writeFileSync(join(ICONS_DIR, iconName + "_custom.png"), img.toPNG()); + 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"); } @@ -144,11 +149,15 @@ export async function pickTrayIcon(iconName: string) { const res = await dialog.showOpenDialog(mainWin!, { properties: ["openFile"], - filters: [{ name: "Image", extensions: ["png", "jpg"] }] + 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 }); diff --git a/src/preload/VesktopNative.ts b/src/preload/VesktopNative.ts index c2138bc..d8a8c71 100644 --- a/src/preload/VesktopNative.ts +++ b/src/preload/VesktopNative.ts @@ -86,10 +86,16 @@ export const VesktopNative = { setIcon: (iconURI: string) => invoke(IpcEvents.SET_TRAY_ICON, iconURI), getIcon: (iconName: string) => invoke(IpcEvents.GET_TRAY_ICON, iconName), getIconSync: (iconName: string) => sendSync(IpcEvents.GET_TRAY_ICON_SYNC, iconName), - createIconResponse: (iconName: string, iconDataURL: string, isCustomIcon: boolean = true) => - invoke(IpcEvents.CREATE_TRAY_ICON_RESPONSE, iconName, iconDataURL, isCustomIcon), - createIconRequest: (listener: (iconName: string) => void) => { - ipcRenderer.on(IpcEvents.CREATE_TRAY_ICON_REQUEST, (_, iconPath: string) => listener(iconPath)); + createIconResponse: ( + iconName: string, + iconDataURL: string, + isCustomIcon: boolean = true, + isSvg: boolean = true + ) => invoke(IpcEvents.CREATE_TRAY_ICON_RESPONSE, iconName, iconDataURL, isCustomIcon, isSvg), + createIconRequest: (listener: (iconName: string, svg: string) => void) => { + ipcRenderer.on(IpcEvents.CREATE_TRAY_ICON_REQUEST, (_, iconPath: string, svg: string) => + listener(iconPath, svg) + ); }, generateTrayIcons: () => invoke(IpcEvents.GENERATE_TRAY_ICONS), setCurrentVoiceIcon: (listener: (...args: any[]) => void) => { diff --git a/src/renderer/components/settings/TraySettings.tsx b/src/renderer/components/settings/TraySettings.tsx index f2f852e..3650f3d 100644 --- a/src/renderer/components/settings/TraySettings.tsx +++ b/src/renderer/components/settings/TraySettings.tsx @@ -41,6 +41,36 @@ const statusToSettingsKey = { deafened: { key: "trayDeafenedOverride", label: "Deafened Icon" } }; +async function changeIcon(iconName, settings) { + const choice = await VesktopNative.fileManager.selectTrayIcon(iconName); + switch (choice) { + case "cancelled": + return; + case "invalid": + Toasts.show({ + message: "Please select a valid .png or .jpg image!", + id: Toasts.genId(), + type: Toasts.Type.FAILURE + }); + return; + } + + const updateIcon = () => { + const iconKey = statusToSettingsKey[iconName as keyof typeof statusToSettingsKey].key; + settings[iconKey] = true; + const iconDataURL = VesktopNative.tray.getIconSync(iconName); + const img = document.getElementById(iconName) as HTMLImageElement; + if (img) { + img.src = iconDataURL; + } + setCurrentTrayIcon(); + }; + + // sometimes new icon may not be generated in time and will be used old icon :c + if (choice === "svg") setTimeout(updateIcon, 50); + else updateIcon(); +} + function trayEditButton(iconName: string) { const Settings = useSettings(); return ( @@ -58,27 +88,7 @@ function trayEditButton(iconName: string) { width="40" height="40" onClick={async () => { - const choice = await VesktopNative.fileManager.selectTrayIcon(iconName); - switch (choice) { - case "cancelled": - return; - case "invalid": - Toasts.show({ - message: "Please select a valid .png or .jpg image!", - id: Toasts.genId(), - type: Toasts.Type.FAILURE - }); - return; - } - - const iconKey = statusToSettingsKey[iconName as keyof typeof statusToSettingsKey].key; - Settings[iconKey] = true; - const iconDataURL = VesktopNative.tray.getIconSync(iconName); - const img = document.getElementById(iconName) as HTMLImageElement; - if (img) { - img.src = iconDataURL; - } - setCurrentTrayIcon(); + changeIcon(iconName, Settings); }} /> @@ -102,26 +112,7 @@ function TrayModalComponent({ modalProps, close }: { modalProps: any; close: () {trayEditButton(iconName)}