Added custom color support
|
@ -18,7 +18,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 { mainWin, setTrayIcon } from "./mainWindow";
|
import { 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";
|
||||||
|
@ -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));
|
||||||
|
|
|
@ -11,10 +11,11 @@ import {
|
||||||
dialog,
|
dialog,
|
||||||
Menu,
|
Menu,
|
||||||
MenuItemConstructorOptions,
|
MenuItemConstructorOptions,
|
||||||
|
nativeImage,
|
||||||
nativeTheme,
|
nativeTheme,
|
||||||
Tray
|
Tray
|
||||||
} from "electron";
|
} from "electron";
|
||||||
import { 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";
|
||||||
import { isTruthy } from "shared/utils/guards";
|
import { isTruthy } from "shared/utils/guards";
|
||||||
|
@ -479,14 +480,21 @@ export async function createWindows() {
|
||||||
initArRPC();
|
initArRPC();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setTrayIcon(iconName: string) {
|
export async function setTrayIcon(iconURI: string) {
|
||||||
if (!tray) return;
|
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)) {
|
if (!Icons.has(iconName)) {
|
||||||
console.warn("setTrayIcon: Invalid icon name", iconName);
|
|
||||||
iconName = "icon";
|
iconName = "icon";
|
||||||
|
return readFile(join(STATIC_DIR, "icon.png"));
|
||||||
}
|
}
|
||||||
tray.setImage(join(STATIC_DIR, iconName + ".png"));
|
return readFile(join(STATIC_DIR, iconName + ".svg"), "utf8");
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,8 @@ 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: (iconName: string) => invoke<void>(IpcEvents.SET_TRAY_ICON, iconName)
|
setTrayIcon: (iconURI: string) => invoke<void>(IpcEvents.SET_TRAY_ICON, iconURI),
|
||||||
|
getTrayIcon: (iconName: string) => invoke<string>(IpcEvents.GET_TRAY_ICON, iconName)
|
||||||
},
|
},
|
||||||
autostart: {
|
autostart: {
|
||||||
isEnabled: () => sendSync<boolean>(IpcEvents.AUTOSTART_ENABLED),
|
isEnabled: () => sendSync<boolean>(IpcEvents.AUTOSTART_ENABLED),
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { isMac, isWindows } from "renderer/utils";
|
||||||
import { AutoStartToggle } from "./AutoStartToggle";
|
import { AutoStartToggle } from "./AutoStartToggle";
|
||||||
import { DiscordBranchPicker } from "./DiscordBranchPicker";
|
import { DiscordBranchPicker } from "./DiscordBranchPicker";
|
||||||
import { NotificationBadgeToggle } from "./NotificationBadgeToggle";
|
import { NotificationBadgeToggle } from "./NotificationBadgeToggle";
|
||||||
|
import { trayIconPicker } from "./TrayColorPicker";
|
||||||
import { VencordLocationPicker } from "./VencordLocationPicker";
|
import { VencordLocationPicker } from "./VencordLocationPicker";
|
||||||
import { WindowsTransparencyControls } from "./WindowsTransparencyControls";
|
import { WindowsTransparencyControls } from "./WindowsTransparencyControls";
|
||||||
|
|
||||||
|
@ -75,6 +76,7 @@ const SettingsOptions: Record<string, Array<BooleanSetting | SettingsComponent>>
|
||||||
defaultValue: true,
|
defaultValue: true,
|
||||||
invisible: () => isMac
|
invisible: () => isMac
|
||||||
},
|
},
|
||||||
|
trayIconPicker,
|
||||||
{
|
{
|
||||||
key: "minimizeToTray",
|
key: "minimizeToTray",
|
||||||
title: "Minimize to tray",
|
title: "Minimize to tray",
|
||||||
|
|
46
src/renderer/components/settings/TrayColorPicker.tsx
Normal file
|
@ -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 (
|
||||||
|
<div className="tray-settings">
|
||||||
|
<div className="tray-container">
|
||||||
|
<div className="tray-settings-labels">
|
||||||
|
<Forms.FormTitle tag="h3">Tray Icon Color</Forms.FormTitle>
|
||||||
|
<Forms.FormText>Choose a color for your tray icon!</Forms.FormText>
|
||||||
|
</div>
|
||||||
|
<ColorPicker
|
||||||
|
color={parseInt(settings.trayColor ?? "#3DB77F", 16)}
|
||||||
|
onChange={newColor => {
|
||||||
|
const hexColor = newColor.toString(16).padStart(6, "0");
|
||||||
|
settings.trayColor = hexColor;
|
||||||
|
setCurrentState();
|
||||||
|
}}
|
||||||
|
showEyeDropper={false}
|
||||||
|
suggestedColors={presets}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Forms.FormDivider className={Margins.top20 + " " + Margins.bottom20} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
16
src/renderer/components/settings/traySetting.css
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -4,19 +4,49 @@
|
||||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Logger } from "@vencord/types/utils";
|
||||||
import { findByPropsLazy, onceReady } from "@vencord/types/webpack";
|
import { findByPropsLazy, onceReady } from "@vencord/types/webpack";
|
||||||
import { FluxDispatcher, UserStore } from "@vencord/types/webpack/common";
|
import { FluxDispatcher, UserStore } from "@vencord/types/webpack/common";
|
||||||
|
|
||||||
const muteActions = findByPropsLazy("isSelfMute");
|
const muteActions = findByPropsLazy("isSelfMute");
|
||||||
const deafActions = findByPropsLazy("isSelfDeaf");
|
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()) {
|
if (deafActions.isSelfDeaf()) {
|
||||||
VesktopNative.app.setTrayIcon("deafened");
|
changeIconColor("deafened");
|
||||||
} else if (muteActions.isSelfMute()) {
|
} else if (muteActions.isSelfMute()) {
|
||||||
VesktopNative.app.setTrayIcon("muted");
|
changeIconColor("muted");
|
||||||
} else {
|
} else {
|
||||||
VesktopNative.app.setTrayIcon("idle");
|
changeIconColor("idle");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +56,7 @@ onceReady.then(() => {
|
||||||
FluxDispatcher.subscribe("SPEAKING", params => {
|
FluxDispatcher.subscribe("SPEAKING", params => {
|
||||||
if (params.userId === userID) {
|
if (params.userId === userID) {
|
||||||
if (params.speakingFlags) {
|
if (params.speakingFlags) {
|
||||||
VesktopNative.app.setTrayIcon("speaking");
|
changeIconColor("speaking");
|
||||||
} else {
|
} else {
|
||||||
setCurrentState();
|
setCurrentState();
|
||||||
}
|
}
|
||||||
|
@ -34,18 +64,20 @@ onceReady.then(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
FluxDispatcher.subscribe("AUDIO_TOGGLE_SELF_DEAF", () => {
|
FluxDispatcher.subscribe("AUDIO_TOGGLE_SELF_DEAF", () => {
|
||||||
setCurrentState();
|
if (inCall) setCurrentState();
|
||||||
});
|
});
|
||||||
|
|
||||||
FluxDispatcher.subscribe("AUDIO_TOGGLE_SELF_MUTE", () => {
|
FluxDispatcher.subscribe("AUDIO_TOGGLE_SELF_MUTE", () => {
|
||||||
setCurrentState();
|
if (inCall) setCurrentState();
|
||||||
});
|
});
|
||||||
|
|
||||||
FluxDispatcher.subscribe("RTC_CONNECTION_STATE", params => {
|
FluxDispatcher.subscribe("RTC_CONNECTION_STATE", params => {
|
||||||
if (params.state === "RTC_CONNECTED") {
|
if (params.state === "RTC_CONNECTED") {
|
||||||
|
inCall = true;
|
||||||
setCurrentState();
|
setCurrentState();
|
||||||
} else if (params.state === "RTC_DISCONNECTED") {
|
} else if (params.state === "RTC_DISCONNECTED") {
|
||||||
VesktopNative.app.setTrayIcon("icon");
|
VesktopNative.app.setTrayIcon("icon");
|
||||||
|
inCall = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -51,5 +51,6 @@ export const enum IpcEvents {
|
||||||
|
|
||||||
CLIPBOARD_COPY_IMAGE = "VCD_CLIPBOARD_COPY_IMAGE",
|
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"
|
||||||
}
|
}
|
||||||
|
|
1
src/shared/settings.d.ts
vendored
|
@ -11,6 +11,7 @@ export interface Settings {
|
||||||
vencordDir?: string;
|
vencordDir?: string;
|
||||||
transparencyOption?: "none" | "mica" | "tabbed" | "acrylic";
|
transparencyOption?: "none" | "mica" | "tabbed" | "acrylic";
|
||||||
tray?: boolean;
|
tray?: boolean;
|
||||||
|
trayColor?: string;
|
||||||
minimizeToTray?: boolean;
|
minimizeToTray?: boolean;
|
||||||
openLinksWithElectron?: boolean;
|
openLinksWithElectron?: boolean;
|
||||||
staticTitle?: boolean;
|
staticTitle?: boolean;
|
||||||
|
|
Before Width: | Height: | Size: 2.8 KiB |
1
static/deafened.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="#FFF" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M12 4c3.87 0 7 3.13 7 7v2h-2.92L21 17.92V11a9 9 0 0 0-9-9c-1.95 0-3.76.62-5.23 1.68l1.44 1.44A6.914 6.914 0 0 1 12 4zM2.27 1.72 1 3l3.33 3.32A8.899 8.899 0 0 0 3 11v7c0 1.66 1.34 3 3 3h3v-8H5v-2c0-1.17.29-2.26.79-3.22L15 17v4h3c.3 0 .59-.06.86-.14L21 23l1.27-1.27-20-20.01z"/><path fill="#f6bfac" d="M2.27 1.72 1 3l20 20 1.27-1.27-20-20.01z"/></svg>
|
After Width: | Height: | Size: 469 B |
BIN
static/idle.png
Before Width: | Height: | Size: 5.9 KiB |
5
static/idle.svg
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
|
||||||
|
<circle cx="500" cy="500" r="400" stroke="#f6bfac" stroke-width="50" fill="#f6bfac" fill-opacity="0.2"/>
|
||||||
|
<path d="M 250 500 Q 250 250 500 250" fill="none" stroke="#f6bfac" stroke-width="50"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 294 B |
BIN
static/muted.png
Before Width: | Height: | Size: 2.7 KiB |
1
static/muted.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="#FFF" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0zm0 0h24v24H0z"/><path d="M19 11h-1.7c0 .74-.16 1.43-.43 2.05l1.23 1.23c.56-.98.9-2.09.9-3.28zm-4.02.17c0-.06.02-.11.02-.17V5c0-1.66-1.34-3-3-3S9 3.34 9 5v.18l5.98 5.99zM9.02 10.28V11c0 1.66 1.33 3 2.99 3 .22 0 .44-.03.65-.08l1.66 1.66c-.71.33-1.5.52-2.31.52-2.76 0-5.3-2.1-5.3-5.1H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c.91-.13 1.77-.45 2.54-.9L19.73 21 21 19.73 4.27 3z"/><path fill="#f6bfac" d="M4.27 3 3 4.27l6.02 6.01L19.73 21 21 19.73 4.27 3"/></svg>
|
After Width: | Height: | Size: 559 B |
Before Width: | Height: | Size: 3.7 KiB |
5
static/speaking.svg
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
|
||||||
|
<circle cx="500" cy="500" r="400" stroke="#f6bfac" stroke-width="50" fill="#f6bfac"/>
|
||||||
|
<path d="M 250 500 Q 250 250 500 250" fill="none" stroke="white" stroke-width="50"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 273 B |