Add Notification badge
|
@ -7,7 +7,7 @@
|
|||
import { app, BrowserWindow } from "electron";
|
||||
import { readFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { ICON_PATH, STATIC_DIR } from "shared/paths";
|
||||
import { ICON_PATH, VIEW_DIR } from "shared/paths";
|
||||
|
||||
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
|
||||
|
||||
|
@ -20,7 +20,7 @@ export function createAboutWindow() {
|
|||
|
||||
makeLinksOpenExternally(about);
|
||||
|
||||
const html = readFileSync(join(STATIC_DIR, "about.html"), "utf-8").replaceAll("%VERSION%", app.getVersion());
|
||||
const html = readFileSync(join(VIEW_DIR, "about.html"), "utf-8").replaceAll("%VERSION%", app.getVersion());
|
||||
|
||||
about.loadURL("data:text/html;charset=utf-8," + html);
|
||||
|
||||
|
|
50
src/main/appBadge.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { app, NativeImage, nativeImage } from "electron";
|
||||
import { join } from "path";
|
||||
import { BADGE_DIR } from "shared/paths";
|
||||
|
||||
const imgCache = new Map<number, NativeImage>();
|
||||
function loadBadge(index: number) {
|
||||
const cached = imgCache.get(index);
|
||||
if (cached) return cached;
|
||||
|
||||
const img = nativeImage.createFromPath(join(BADGE_DIR, `${index}.ico`));
|
||||
imgCache.set(index, img);
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
let lastIndex: null | number = -1;
|
||||
|
||||
export function setBadgeCount(count: number) {
|
||||
switch (process.platform) {
|
||||
case "darwin":
|
||||
case "linux":
|
||||
if (count === -1) count = 0;
|
||||
app.setBadgeCount(count);
|
||||
break;
|
||||
case "win32":
|
||||
const [index, description] = getBadgeIndexAndDescription(count);
|
||||
if (lastIndex === index) break;
|
||||
|
||||
lastIndex = index;
|
||||
|
||||
// circular import shenanigans
|
||||
const { mainWin } = require("./mainWindow") as typeof import("./mainWindow");
|
||||
mainWin.setOverlayIcon(index === null ? null : loadBadge(index), description);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function getBadgeIndexAndDescription(count: number): [number | null, string] {
|
||||
if (count === -1) return [11, "Unread Messages"];
|
||||
if (count === 0) return [null, "No Notifications"];
|
||||
|
||||
const index = Math.max(1, Math.min(count, 10));
|
||||
return [index, `${index} Notification`];
|
||||
}
|
|
@ -9,7 +9,7 @@ import { BrowserWindow } from "electron/main";
|
|||
import { copyFileSync, mkdirSync, readdirSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { SplashProps } from "shared/browserWinProperties";
|
||||
import { STATIC_DIR } from "shared/paths";
|
||||
import { VIEW_DIR } from "shared/paths";
|
||||
|
||||
import { autoStart } from "./autoStart";
|
||||
import { DATA_DIR } from "./constants";
|
||||
|
@ -33,7 +33,7 @@ export function createFirstLaunchTour() {
|
|||
width: 550
|
||||
});
|
||||
|
||||
win.loadFile(join(STATIC_DIR, "first-launch.html"));
|
||||
win.loadFile(join(VIEW_DIR, "first-launch.html"));
|
||||
win.webContents.addListener("console-message", (_e, _l, msg) => {
|
||||
if (msg === "cancel") return app.exit();
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import { join } from "path";
|
|||
import { debounce } from "shared/utils/debounce";
|
||||
|
||||
import { IpcEvents } from "../shared/IpcEvents";
|
||||
import { setBadgeCount } from "./appBadge";
|
||||
import { autoStart } from "./autoStart";
|
||||
import { VENCORD_FILES_DIR, VENCORD_QUICKCSS_FILE } from "./constants";
|
||||
import { mainWin } from "./mainWindow";
|
||||
|
@ -89,6 +90,8 @@ ipcMain.handle(IpcEvents.SELECT_VENCORD_DIR, async () => {
|
|||
return dir;
|
||||
});
|
||||
|
||||
ipcMain.handle(IpcEvents.SET_BADGE_COUNT, (_, count: number) => setBadgeCount(count));
|
||||
|
||||
function readCss() {
|
||||
return readFile(VENCORD_QUICKCSS_FILE, "utf-8").catch(() => "");
|
||||
}
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
import { BrowserWindow } from "electron";
|
||||
import { join } from "path";
|
||||
import { SplashProps } from "shared/browserWinProperties";
|
||||
import { STATIC_DIR } from "shared/paths";
|
||||
import { VIEW_DIR } from "shared/paths";
|
||||
|
||||
export function createSplashWindow() {
|
||||
const splash = new BrowserWindow(SplashProps);
|
||||
|
||||
splash.loadFile(join(STATIC_DIR, "splash.html"));
|
||||
splash.loadFile(join(VIEW_DIR, "splash.html"));
|
||||
|
||||
return splash;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@ import { invoke, sendSync } from "./typedIpcs";
|
|||
export const VencordDesktopNative = {
|
||||
app: {
|
||||
relaunch: () => invoke<void>(IpcEvents.RELAUNCH),
|
||||
getVersion: () => sendSync<void>(IpcEvents.GET_VERSION)
|
||||
getVersion: () => sendSync<void>(IpcEvents.GET_VERSION),
|
||||
setBadgeCount: (count: number) => invoke<void>(IpcEvents.SET_BADGE_COUNT, count)
|
||||
},
|
||||
autostart: {
|
||||
isEnabled: () => sendSync<boolean>(IpcEvents.AUTOSTART_ENABLED),
|
||||
|
|
43
src/renderer/appBadge.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0
|
||||
* Vencord Desktop, a desktop app aiming to give you a snappier Discord Experience
|
||||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { filters, waitFor } from "@vencord/types/webpack";
|
||||
import { RelationshipStore } from "@vencord/types/webpack/common";
|
||||
|
||||
import { Settings } from "./settings";
|
||||
|
||||
let GuildReadStateStore: any;
|
||||
let NotificationSettingsStore: any;
|
||||
|
||||
export function setBadge() {
|
||||
if (Settings.store.appBadge === false) return;
|
||||
|
||||
const mentionCount = GuildReadStateStore.getTotalMentionCount();
|
||||
const pendingRequests = RelationshipStore.getPendingCount();
|
||||
const hasUnread = GuildReadStateStore.hasAnyUnread();
|
||||
const disableUnreadBadge = NotificationSettingsStore.getDisableUnreadBadge();
|
||||
|
||||
let totalCount = mentionCount + pendingRequests;
|
||||
if (!totalCount && hasUnread && !disableUnreadBadge) totalCount = -1;
|
||||
|
||||
VencordDesktopNative.app.setBadgeCount(totalCount);
|
||||
}
|
||||
|
||||
let toFind = 3;
|
||||
|
||||
function waitForAndSubscribeToStore(name: string, cb?: (m: any) => void) {
|
||||
waitFor(filters.byStoreName(name), store => {
|
||||
cb?.(store);
|
||||
store.addChangeListener(setBadge);
|
||||
|
||||
toFind--;
|
||||
if (toFind === 0) setBadge();
|
||||
});
|
||||
}
|
||||
|
||||
waitForAndSubscribeToStore("GuildReadStateStore", store => (GuildReadStateStore = store));
|
||||
waitForAndSubscribeToStore("NotificationSettingsStore", store => (NotificationSettingsStore = store));
|
||||
waitForAndSubscribeToStore("RelationshipStore");
|
|
@ -8,6 +8,7 @@ import "./settings.css";
|
|||
|
||||
import { Margins } from "@vencord/types/utils";
|
||||
import { Button, Forms, Select, Switch, Text, useState } from "@vencord/types/webpack/common";
|
||||
import { setBadge } from "renderer/appBadge";
|
||||
import { useSettings } from "renderer/settings";
|
||||
|
||||
export default function SettingsUi() {
|
||||
|
@ -72,6 +73,18 @@ export default function SettingsUi() {
|
|||
Start With System
|
||||
</Switch>
|
||||
|
||||
<Switch
|
||||
value={Settings.appBadge ?? true}
|
||||
onChange={v => {
|
||||
Settings.appBadge = v;
|
||||
if (v) setBadge();
|
||||
else VencordDesktopNative.app.setBadgeCount(0);
|
||||
}}
|
||||
note="Show mention badge on the app icon"
|
||||
>
|
||||
Notification Badge
|
||||
</Switch>
|
||||
|
||||
{switches.map(([key, text, note, def, predicate]) => (
|
||||
<Switch
|
||||
value={(Settings[key as any] ?? def ?? false) && predicate?.() !== false}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import "./fixes";
|
||||
import "./appBadge";
|
||||
|
||||
console.log("read if cute :3");
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@ export const enum IpcEvents {
|
|||
|
||||
SPELLCHECK_SET_LANGUAGES = "VCD_SPELLCHECK_SET_LANGUAGES",
|
||||
|
||||
SET_BADGE_COUNT = "VCD_SET_BADGE_COUNT",
|
||||
|
||||
CAPTURER_GET_LARGE_THUMBNAIL = "VCD_CAPTURER_GET_LARGE_THUMBNAIL",
|
||||
|
||||
AUTOSTART_ENABLED = "VCD_AUTOSTART_ENABLED",
|
||||
|
|
|
@ -7,4 +7,6 @@
|
|||
import { join } from "path";
|
||||
|
||||
export const STATIC_DIR = /* @__PURE__ */ join(__dirname, "..", "..", "static");
|
||||
export const VIEW_DIR = /* @__PURE__ */ join(STATIC_DIR, "views");
|
||||
export const BADGE_DIR = /* @__PURE__ */ join(STATIC_DIR, "badges");
|
||||
export const ICON_PATH = /* @__PURE__ */ join(STATIC_DIR, "icon.png");
|
||||
|
|
1
src/shared/settings.d.ts
vendored
|
@ -19,6 +19,7 @@ export interface Settings {
|
|||
skippedUpdate?: string;
|
||||
staticTitle?: boolean;
|
||||
arRPC?: boolean;
|
||||
appBadge?: boolean;
|
||||
|
||||
firstLaunch?: boolean;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import { githubGet, ReleaseData } from "main/utils/vencordLoader";
|
|||
import { join } from "path";
|
||||
import { SplashProps } from "shared/browserWinProperties";
|
||||
import { IpcEvents } from "shared/IpcEvents";
|
||||
import { STATIC_DIR } from "shared/paths";
|
||||
import { VIEW_DIR } from "shared/paths";
|
||||
|
||||
export interface UpdateData {
|
||||
currentVersion: string;
|
||||
|
@ -101,5 +101,5 @@ function openNewUpdateWindow() {
|
|||
}
|
||||
});
|
||||
|
||||
win.loadFile(join(STATIC_DIR, "updater.html"));
|
||||
win.loadFile(join(VIEW_DIR, "updater.html"));
|
||||
}
|
||||
|
|
BIN
static/badges/1.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
static/badges/10.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
static/badges/11.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
static/badges/2.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
static/badges/3.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
static/badges/4.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
static/badges/5.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
static/badges/6.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
static/badges/7.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
static/badges/8.ico
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
static/badges/9.ico
Normal file
After Width: | Height: | Size: 15 KiB |