From b95521ea994494afd20440e24038d17d4a43e6e0 Mon Sep 17 00:00:00 2001 From: Oleh Polisan Date: Fri, 26 Apr 2024 16:51:05 +0300 Subject: [PATCH] Added custom color support --- src/main/ipc.ts | 5 +- src/main/mainWindow.ts | 18 +++++-- src/preload/VesktopNative.ts | 3 +- src/renderer/components/settings/Settings.tsx | 2 + .../components/settings/TrayColorPicker.tsx | 46 ++++++++++++++++++ .../components/settings/traySetting.css | 16 ++++++ src/renderer/patches/tray.ts | 46 +++++++++++++++--- src/shared/IpcEvents.ts | 3 +- src/shared/settings.d.ts | 1 + static/deafened.png | Bin 2853 -> 0 bytes static/deafened.svg | 1 + static/idle.png | Bin 6000 -> 0 bytes static/idle.svg | 5 ++ static/muted.png | Bin 2788 -> 0 bytes static/muted.svg | 1 + static/speaking.png | Bin 3810 -> 0 bytes static/speaking.svg | 5 ++ 17 files changed, 136 insertions(+), 16 deletions(-) create mode 100644 src/renderer/components/settings/TrayColorPicker.tsx create mode 100644 src/renderer/components/settings/traySetting.css delete mode 100644 static/deafened.png create mode 100644 static/deafened.svg delete mode 100644 static/idle.png create mode 100644 static/idle.svg delete mode 100644 static/muted.png create mode 100644 static/muted.svg delete mode 100644 static/speaking.png create mode 100644 static/speaking.svg diff --git a/src/main/ipc.ts b/src/main/ipc.ts index e010f2b..cc9d069 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -18,7 +18,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 { mainWin, setTrayIcon } from "./mainWindow"; +import { getTrayIconFile, mainWin, setTrayIcon } from "./mainWindow"; import { Settings } from "./settings"; import { handle, handleSync } from "./utils/ipcWrappers"; import { PopoutWindows } from "./utils/popout"; @@ -154,4 +154,5 @@ watch( }) ); -handle(IpcEvents.SET_TRAY_ICON, (_, iconName: string) => setTrayIcon(iconName)); +handle(IpcEvents.SET_TRAY_ICON, (_, iconURI) => setTrayIcon(iconURI)); +handle(IpcEvents.GET_TRAY_ICON, (_, iconName) => getTrayIconFile(iconName)); diff --git a/src/main/mainWindow.ts b/src/main/mainWindow.ts index e935a11..0df9ae5 100644 --- a/src/main/mainWindow.ts +++ b/src/main/mainWindow.ts @@ -11,10 +11,11 @@ import { dialog, Menu, MenuItemConstructorOptions, + nativeImage, nativeTheme, Tray } from "electron"; -import { rm } from "fs/promises"; +import { readFile, rm } from "fs/promises"; import { join } from "path"; import { IpcEvents } from "shared/IpcEvents"; import { isTruthy } from "shared/utils/guards"; @@ -479,14 +480,21 @@ export async function createWindows() { initArRPC(); } -export async function setTrayIcon(iconName: string) { +export async function setTrayIcon(iconURI: string) { if (!tray) return; + if (iconURI !== "" && iconURI !== "icon") { + tray.setImage(nativeImage.createFromDataURL(iconURI)); + return; + } + tray.setImage(join(STATIC_DIR, "icon.png")); +} - const Icons = new Set(["speaking", "muted", "deafened", "idle", "icon"]); +export async function getTrayIconFile(iconName: string) { + const Icons = new Set(["speaking", "muted", "deafened", "idle"]); if (!Icons.has(iconName)) { - console.warn("setTrayIcon: Invalid icon name", iconName); iconName = "icon"; + return readFile(join(STATIC_DIR, "icon.png")); } - tray.setImage(join(STATIC_DIR, iconName + ".png")); + return readFile(join(STATIC_DIR, iconName + ".svg"), "utf8"); } diff --git a/src/preload/VesktopNative.ts b/src/preload/VesktopNative.ts index cd8027c..fcf9bc4 100644 --- a/src/preload/VesktopNative.ts +++ b/src/preload/VesktopNative.ts @@ -25,7 +25,8 @@ export const VesktopNative = { getVersion: () => sendSync(IpcEvents.GET_VERSION), setBadgeCount: (count: number) => invoke(IpcEvents.SET_BADGE_COUNT, count), supportsWindowsTransparency: () => sendSync(IpcEvents.SUPPORTS_WINDOWS_TRANSPARENCY), - setTrayIcon: (iconName: string) => invoke(IpcEvents.SET_TRAY_ICON, iconName) + setTrayIcon: (iconURI: string) => invoke(IpcEvents.SET_TRAY_ICON, iconURI), + getTrayIcon: (iconName: string) => invoke(IpcEvents.GET_TRAY_ICON, iconName) }, autostart: { isEnabled: () => sendSync(IpcEvents.AUTOSTART_ENABLED), diff --git a/src/renderer/components/settings/Settings.tsx b/src/renderer/components/settings/Settings.tsx index d6de13c..944ad70 100644 --- a/src/renderer/components/settings/Settings.tsx +++ b/src/renderer/components/settings/Settings.tsx @@ -14,6 +14,7 @@ import { isMac, isWindows } from "renderer/utils"; import { AutoStartToggle } from "./AutoStartToggle"; import { DiscordBranchPicker } from "./DiscordBranchPicker"; import { NotificationBadgeToggle } from "./NotificationBadgeToggle"; +import { trayIconPicker } from "./TrayColorPicker"; import { VencordLocationPicker } from "./VencordLocationPicker"; import { WindowsTransparencyControls } from "./WindowsTransparencyControls"; @@ -75,6 +76,7 @@ const SettingsOptions: Record> defaultValue: true, invisible: () => isMac }, + trayIconPicker, { key: "minimizeToTray", title: "Minimize to tray", diff --git a/src/renderer/components/settings/TrayColorPicker.tsx b/src/renderer/components/settings/TrayColorPicker.tsx new file mode 100644 index 0000000..4e38dc8 --- /dev/null +++ b/src/renderer/components/settings/TrayColorPicker.tsx @@ -0,0 +1,46 @@ +/* + * 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 "./traySetting.css"; + +import { Margins } from "@vencord/types/utils"; +import { findByCodeLazy } from "@vencord/types/webpack"; +import { Forms } from "@vencord/types/webpack/common"; +import { setCurrentState } from "renderer/patches/tray"; + +import { SettingsComponent } from "./Settings"; + +const ColorPicker = findByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); + +const presets = [ + "#3DB77F", // discord default ~ + "#F6BFAC" // Vesktop inpired +]; + +export const trayIconPicker: SettingsComponent = ({ settings }) => { + if (!settings.tray) return null; // how to disable instead of hiding? + return ( +
+
+
+ Tray Icon Color + Choose a color for your tray icon! +
+ { + const hexColor = newColor.toString(16).padStart(6, "0"); + settings.trayColor = hexColor; + setCurrentState(); + }} + showEyeDropper={false} + suggestedColors={presets} + /> +
+ +
+ ); +}; diff --git a/src/renderer/components/settings/traySetting.css b/src/renderer/components/settings/traySetting.css new file mode 100644 index 0000000..b4941e3 --- /dev/null +++ b/src/renderer/components/settings/traySetting.css @@ -0,0 +1,16 @@ +.tray-settings { + display: flex; + flex-direction: column; +} + +.tray-container { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.tray-settings-labels { + display: flex; + flex-direction: column; + justify-content: flex-start; +} \ No newline at end of file diff --git a/src/renderer/patches/tray.ts b/src/renderer/patches/tray.ts index 4d94044..c0be6f0 100644 --- a/src/renderer/patches/tray.ts +++ b/src/renderer/patches/tray.ts @@ -4,19 +4,49 @@ * Copyright (c) 2023 Vendicated and Vencord contributors */ +import { Logger } from "@vencord/types/utils"; import { findByPropsLazy, onceReady } from "@vencord/types/webpack"; import { FluxDispatcher, UserStore } from "@vencord/types/webpack/common"; const muteActions = findByPropsLazy("isSelfMute"); const deafActions = findByPropsLazy("isSelfDeaf"); -function setCurrentState() { +var inCall = false; +const logger = new Logger("VesktopTrayIcon"); + +async function changeIconColor(iconName: string) { + const pickedColor = VesktopNative.settings.get().trayColor; + + try { + var svg = await VesktopNative.app.getTrayIcon(iconName); + svg = svg.replace(/#f6bfac/gim, "#" + (pickedColor ?? "3DB77F")); + const canvas = document.createElement("canvas"); + canvas.width = 128; + canvas.height = 128; + const img = new Image(); + img.width = 128; + img.height = 128; + img.onload = () => { + const ctx = canvas.getContext("2d"); + if (ctx) { + ctx.drawImage(img, 0, 0); + const dataURL = canvas.toDataURL("image/png"); + VesktopNative.app.setTrayIcon(dataURL); + } + }; + img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`; + } catch (error) { + logger.error("Error: ", error); + } +} + +export function setCurrentState() { if (deafActions.isSelfDeaf()) { - VesktopNative.app.setTrayIcon("deafened"); + changeIconColor("deafened"); } else if (muteActions.isSelfMute()) { - VesktopNative.app.setTrayIcon("muted"); + changeIconColor("muted"); } else { - VesktopNative.app.setTrayIcon("idle"); + changeIconColor("idle"); } } @@ -26,7 +56,7 @@ onceReady.then(() => { FluxDispatcher.subscribe("SPEAKING", params => { if (params.userId === userID) { if (params.speakingFlags) { - VesktopNative.app.setTrayIcon("speaking"); + changeIconColor("speaking"); } else { setCurrentState(); } @@ -34,18 +64,20 @@ onceReady.then(() => { }); FluxDispatcher.subscribe("AUDIO_TOGGLE_SELF_DEAF", () => { - setCurrentState(); + if (inCall) setCurrentState(); }); FluxDispatcher.subscribe("AUDIO_TOGGLE_SELF_MUTE", () => { - setCurrentState(); + if (inCall) setCurrentState(); }); FluxDispatcher.subscribe("RTC_CONNECTION_STATE", params => { if (params.state === "RTC_CONNECTED") { + inCall = true; setCurrentState(); } else if (params.state === "RTC_DISCONNECTED") { VesktopNative.app.setTrayIcon("icon"); + inCall = false; } }); }); diff --git a/src/shared/IpcEvents.ts b/src/shared/IpcEvents.ts index 63df0d9..f68826a 100644 --- a/src/shared/IpcEvents.ts +++ b/src/shared/IpcEvents.ts @@ -51,5 +51,6 @@ export const enum IpcEvents { CLIPBOARD_COPY_IMAGE = "VCD_CLIPBOARD_COPY_IMAGE", - SET_TRAY_ICON = "VCD_SET_TRAY_ICON" + SET_TRAY_ICON = "VCD_SET_TRAY_ICON", + GET_TRAY_ICON = "VCD_GET_TRAY_ICON" } diff --git a/src/shared/settings.d.ts b/src/shared/settings.d.ts index 7f6e74a..a03a766 100644 --- a/src/shared/settings.d.ts +++ b/src/shared/settings.d.ts @@ -11,6 +11,7 @@ export interface Settings { vencordDir?: string; transparencyOption?: "none" | "mica" | "tabbed" | "acrylic"; tray?: boolean; + trayColor?: string; minimizeToTray?: boolean; openLinksWithElectron?: boolean; staticTitle?: boolean; diff --git a/static/deafened.png b/static/deafened.png deleted file mode 100644 index 7dfc308b5d9996f45fbc5e023689f5d7d583b730..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2853 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 D=tvK_ diff --git a/static/deafened.svg b/static/deafened.svg new file mode 100644 index 0000000..c4ef926 --- /dev/null +++ b/static/deafened.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/idle.png b/static/idle.png deleted file mode 100644 index 0a0e08aa4ca3d495259bcecbf0d9d1d9eb6057c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6000 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

KN0+;;!zFuZj;4T1b!i88BIWQPkz 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 eIC=z_;rMSJ7m>N8_M?&j0000 + + + + diff --git a/static/muted.png b/static/muted.png deleted file mode 100644 index e6b3a6062eb9fef862ec36b1509614ea1e544156..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2788 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 \ No newline at end of file diff --git a/static/speaking.png b/static/speaking.png deleted file mode 100644 index 6aac412cc3dfc820bcf8d4a50e4cf77fe02f181b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3810 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 + + + +