Added custom color support
|
@ -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));
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -25,7 +25,8 @@ export const VesktopNative = {
|
|||
getVersion: () => sendSync<void>(IpcEvents.GET_VERSION),
|
||||
setBadgeCount: (count: number) => invoke<void>(IpcEvents.SET_BADGE_COUNT, count),
|
||||
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: {
|
||||
isEnabled: () => sendSync<boolean>(IpcEvents.AUTOSTART_ENABLED),
|
||||
|
|
|
@ -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<string, Array<BooleanSetting | SettingsComponent>>
|
|||
defaultValue: true,
|
||||
invisible: () => isMac
|
||||
},
|
||||
trayIconPicker,
|
||||
{
|
||||
key: "minimizeToTray",
|
||||
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
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
1
src/shared/settings.d.ts
vendored
|
@ -11,6 +11,7 @@ export interface Settings {
|
|||
vencordDir?: string;
|
||||
transparencyOption?: "none" | "mica" | "tabbed" | "acrylic";
|
||||
tray?: boolean;
|
||||
trayColor?: string;
|
||||
minimizeToTray?: boolean;
|
||||
openLinksWithElectron?: 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 |