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 7dfc308..0000000 Binary files a/static/deafened.png and /dev/null differ 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 0a0e08a..0000000 Binary files a/static/idle.png and /dev/null differ diff --git a/static/idle.svg b/static/idle.svg new file mode 100644 index 0000000..43bbb4b --- /dev/null +++ b/static/idle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/static/muted.png b/static/muted.png deleted file mode 100644 index e6b3a60..0000000 Binary files a/static/muted.png and /dev/null differ diff --git a/static/muted.svg b/static/muted.svg new file mode 100644 index 0000000..fc93fb9 --- /dev/null +++ b/static/muted.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/speaking.png b/static/speaking.png deleted file mode 100644 index 6aac412..0000000 Binary files a/static/speaking.png and /dev/null differ diff --git a/static/speaking.svg b/static/speaking.svg new file mode 100644 index 0000000..dad855d --- /dev/null +++ b/static/speaking.svg @@ -0,0 +1,5 @@ + + + + +