electron-taskbar-badge
Version:
An easy way for electron apps to add app badges to the taskbar to indicate notifications and other countable things, with maximum compatibility and customizability.
128 lines (119 loc) • 4.43 kB
JavaScript
const { nativeImage, ipcMain, systemPreferences } = require('electron');
const fs = require('fs');
const BadgeGenerator = require('./badge_generator.js');
let badgeDescription = 'New notification';
let UPDATE_BADGE_EVENT;
let invokeType = 'send';
let additionalFunc = () => {
// Empty for now...
};
let currentOverlayIcon = { image: null, badgeDescription };
let currentNumber = null;
let powershell = 'C:\\Program Files\\PowerShell\\7\\pwsh.exe';
if (!fs.existsSync('C:\\Program Files\\PowerShell\\7\\pwsh.exe')) {
powershell = 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe';
}
/**
* @example const badgeOptions = {
fontColor: '#000000',
font: '62px Microsoft Yahei',
color: '#000000',
radius: 48,
updateBadgeEvent: 'notificationCount',
badgeDescription: 'Unread Notifications',
invokeType: 'handle',
max: 9,
fit: false,
useSystemAccentTheme: true,
additionalFunc: (count) => {
console.log(`Received ${count} new notifications!`);
},
};
new Badge(win, badgeOptions);
* @since 1.0.0
* @param {Electron.BrowserWindow} win
* @param {object} badgeOptions
* @returns {void}
*/
module.exports = class Badge {
constructor(win, opts = {}) {
if (process.platform !== 'win32') console.warn('Only win32 environments are supported!');
this.win = win;
this.opts = opts;
const accentColor = getLightAccentColor(powershell);
this.generator = new BadgeGenerator(win, opts, accentColor);
systemPreferences.on('accent-color-changed', () => {
this.generator = new BadgeGenerator(win, opts, getLightAccentColor(powershell));
this.generator.generate(currentNumber, true);
this.update(currentNumber);
});
if (typeof opts?.updateBadgeEvent !== 'string') {
throw new TypeError(`Invalid IPC event handler name specified.\nExpected: string\nGot: ${typeof opts?.updateBadgeEvent}`);
}
UPDATE_BADGE_EVENT = opts?.updateBadgeEvent ?? 'update-badge';
badgeDescription = opts?.badgeDescription ?? UPDATE_BADGE_EVENT;
invokeType = opts?.invokeType ?? 'send';
additionalFunc = opts?.additionalFunc ?? additionalFunc;
this.initListeners();
this.win.on('closed', () => { this.win = null; });
this.win.on('show', () => { this.win.setOverlayIcon(currentOverlayIcon.image, currentOverlayIcon.badgeDescription); });
}
update(badgeNumber) {
if (typeof badgeNumber !== 'number' && badgeNumber != null) {
throw new TypeError(`Invalid badgeNumber specified.\nExpected: number\nGot: ${typeof badgeNumber}`);
}
if (badgeNumber) {
this.generator.generate(badgeNumber).then((base64) => {
const image = nativeImage.createFromDataURL(base64);
currentOverlayIcon = {
image,
badgeDescription,
};
this.win.setOverlayIcon(currentOverlayIcon.image, currentOverlayIcon.badgeDescription);
currentNumber = badgeNumber;
});
} else {
currentOverlayIcon = {
image: null,
badgeDescription,
};
this.win.setOverlayIcon(currentOverlayIcon.image, currentOverlayIcon.badgeDescription);
}
}
initListeners() {
if (invokeType.includes('send')) {
ipcMain.on(UPDATE_BADGE_EVENT, (event, badgeNumber) => {
if (this.win) {
this.update(badgeNumber);
additionalFunc(badgeNumber);
}
event.returnValue = 'success';
});
} else {
ipcMain.handle(UPDATE_BADGE_EVENT, (event, badgeNumber) => {
if (this.win) {
this.update(badgeNumber);
additionalFunc(badgeNumber);
}
event.returnValue = 'success';
});
}
}
};
function getLightAccentColor(pwsh) {
let accentColorData;
try {
accentColorData = JSON.parse(require('child_process').execSync(`"${pwsh}" -NoProfile -Command "Get-ItemProperty -Path 'HKCU:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Accent' | Select-Object AccentPalette | ConvertTo-Json"`).toString()).AccentPalette;
} catch (error) {
return '#4cc2ff';
}
const accentColorLight = Array.from({ length: Math.ceil(accentColorData.length / 4) }, (_, i) => accentColorData.slice(i * 4, i * 4 + 4)).map(arr => arr.slice(0, arr.length - 1))[1];
return rgbToHex.apply(null, [...accentColorLight]);
}
function rgbToHex(r, g, b) {
const red = parseInt(r);
const green = parseInt(g);
const blue = parseInt(b);
const rgb = blue | (green << 8) | (red << 16);
return '#' + rgb.toString(16);
}