diff --git a/config/defaults.js b/config/defaults.js index 863e8b25..30e0ed9f 100644 --- a/config/defaults.js +++ b/config/defaults.js @@ -58,6 +58,8 @@ const defaultConfig = { // the following has effect only on Windows interactive: true, toastStyle: 1, // see plugins/notifications/utils for more info + refreshOnPlayPause: false, + trayControls: true, hideButtonText: false }, "precise-volume": { diff --git a/plugins/notifications/interactive.js b/plugins/notifications/interactive.js index da6128b1..f777e024 100644 --- a/plugins/notifications/interactive.js +++ b/plugins/notifications/interactive.js @@ -2,6 +2,7 @@ const { notificationImage, icons, save_temp_icons, secondsToMinutes, ToastStyles const getSongControls = require('../../providers/song-controls'); const registerCallback = require("../../providers/song-info"); const { changeProtocolHandler } = require("../../providers/protocol-handler"); +const { setTrayOnClick, setTrayOnDoubleClick } = require("../../tray"); const { Notification, app, ipcMain } = require("electron"); const path = require('path'); @@ -22,31 +23,59 @@ module.exports = (win) => { if (app.isPackaged) save_temp_icons(); - let lastSongInfo = { url: undefined }; + let savedSongInfo; + let lastUrl; // Register songInfoCallback registerCallback(songInfo => { + savedSongInfo = { ...songInfo }; if (!songInfo.isPaused && - (songInfo.url !== lastSongInfo.url || config.get("unpauseNotification")) + (songInfo.url !== lastUrl || config.get("unpauseNotification")) ) { - lastSongInfo = { ...songInfo }; + lastUrl = songInfo.url sendNotification(songInfo); } }); + // TODO EXPERIMENTAL + if (config.get("trayControls")) { + setTrayOnClick(() => { + if (savedNotification) { + savedNotification.close(); + savedNotification = undefined; + } else if (savedSongInfo) { + sendNotification({ + ...savedSongInfo, + elapsedSeconds: currentSeconds + }) + } + }); + + setTrayOnDoubleClick(() => { + if (win.isVisible()) { + win.hide(); + } else win.show(); + }) + } + app.once("before-quit", () => { savedNotification?.close(); }); + changeProtocolHandler( (cmd) => { if (Object.keys(songControls).includes(cmd)) { songControls[cmd](); - if (cmd === 'pause' || (cmd === 'play' && !config.get("unpauseNotification"))) { + if (config.get("refreshOnPlayPause") && ( + cmd === 'pause' || + (cmd === 'play' && !config.get("unpauseNotification")) + ) + ) { setImmediate(() => sendNotification({ - ...lastSongInfo, + ...savedSongInfo, isPaused: cmd === 'pause', elapsedSeconds: currentSeconds }) @@ -82,7 +111,7 @@ function sendNotification(songInfo) { } const get_xml = (songInfo, iconSrc) => { - switch (config.get("style")) { + switch (config.get("toastStyle")) { default: case ToastStyles.logo: case ToastStyles.legacy: @@ -105,11 +134,11 @@ const iconLocation = app.isPackaged ? path.resolve(__dirname, '..', '..', 'assets/media-icons-black'); const display = (kind) => { - if (config.get("style") === ToastStyles.legacy ) { + if (config.get("toastStyle") === ToastStyles.legacy ) { return `content="${icons[kind]}"`; } else { return `\ - content="${kind.charAt(0).toUpperCase() + kind.slice(1)}"\ + content="${config.get("hideButtonText") ? "" : kind.charAt(0).toUpperCase() + kind.slice(1)}"\ imageUri="file:///${path.resolve(__dirname, iconLocation, `${kind}.png`)}" `; } diff --git a/plugins/notifications/menu.js b/plugins/notifications/menu.js index 23d33c8d..d972434e 100644 --- a/plugins/notifications/menu.js +++ b/plugins/notifications/menu.js @@ -25,6 +25,30 @@ module.exports = (_win, options) => [ // doesn't update until restart click: (item) => config.setAndMaybeRestart("interactive", item.checked), }, + { + // submenu with settings for interactive notifications (name shouldn't be too long) + label: "Interactive Settings", + submenu: [ + { + label: "Open/Close on tray click", + type: "checkbox", + checked: options.trayControls, + click: (item) => config.set("trayControls", item.checked), + }, + { + label: "Hide Button Text", + type: "checkbox", + checked: options.hideButtonText, + click: (item) => config.set("hideButtonText", item.checked), + }, + { + label: "Refresh on Play/Pause", + type: "checkbox", + checked: options.refreshOnPlayPause, + click: (item) => config.set("refreshOnPlayPause", item.checked), + } + ] + }, { label: "Toast Style", submenu: getToastStyleMenuItems(options) @@ -40,20 +64,11 @@ module.exports = (_win, options) => [ ]; function getToastStyleMenuItems(options) { - const arr = new Array(Object.keys(ToastStyles).length + 2); - - arr[0] = { - label: "Hide Button Text", - type: "checkbox", - checked: options.hideButtonText, - click: (item) => config.set("hideButtonText", item.checked), - } - - arr[1] = { type: "separator" }; + const arr = new Array(Object.keys(ToastStyles).length); // ToastStyles index starts from 1 for (const [name, index] of Object.entries(ToastStyles)) { - arr[index + 1] = { + arr[index - 1] = { label: snakeToCamel(name), type: "radio", checked: options.style === index, diff --git a/plugins/notifications/utils.js b/plugins/notifications/utils.js index 5e531ecc..3b408e07 100644 --- a/plugins/notifications/utils.js +++ b/plugins/notifications/utils.js @@ -35,7 +35,7 @@ module.exports.notificationImage = (songInfo) => { if (!songInfo.image) return icon; if (!config.get("interactive")) return nativeImageToLogo(songInfo.image); - switch(config.get("style")) { + switch(config.get("toastStyle")) { case module.exports.ToastStyles.logo: case module.exports.ToastStyles.legacy: return this.saveImage(nativeImageToLogo(songInfo.image), tempIcon); diff --git a/tray.js b/tray.js index cf407aa2..d8712d7e 100644 --- a/tray.js +++ b/tray.js @@ -1,14 +1,29 @@ const path = require("path"); -const { app, Menu, nativeImage, Tray } = require("electron"); +const { Menu, nativeImage, Tray } = require("electron"); const { restart } = require("./providers/app-controls"); const config = require("./config"); const getSongControls = require("./providers/song-controls"); // Prevent tray being garbage collected + +/** @type {Electron.Tray} */ let tray; +module.exports.setTrayOnClick = (fn) => { + if (!tray) return; + tray.removeAllListeners('click'); + tray.on("click", fn); +}; + +// wont do anything on macos since its disabled +module.exports.setTrayOnDoubleClick = (fn) => { + if (!tray) return; + tray.removeAllListeners('double-click'); + tray.on("double-click", fn); +}; + module.exports.setUpTray = (app, win) => { if (!config.get("options.tray")) { tray = undefined; @@ -17,13 +32,19 @@ module.exports.setUpTray = (app, win) => { const { playPause, next, previous } = getSongControls(win); const iconPath = path.join(__dirname, "assets", "youtube-music-tray.png"); + let trayIcon = nativeImage.createFromPath(iconPath).resize({ width: 16, height: 16, }); + tray = new Tray(trayIcon); + tray.setToolTip("Youtube Music"); + + // macOS only tray.setIgnoreDoubleClickEvents(true); + tray.on("click", () => { if (config.get("options.trayClickPlayPause")) { playPause();