diff --git a/app/main/lib/store.ts b/app/main/lib/store.ts index e9afd99..f3fd437 100644 --- a/app/main/lib/store.ts +++ b/app/main/lib/store.ts @@ -35,6 +35,8 @@ const defaultSettings: Settings = { endBreakEnabled: true, skipBreakEnabled: false, postponeBreakEnabled: true, + halfFullTrayMinutes: 20, + almostEmptyTrayMinutes: 3, }; const store = new Store<{ diff --git a/app/main/lib/tray.ts b/app/main/lib/tray.ts index 90b02d4..fa24e45 100644 --- a/app/main/lib/tray.ts +++ b/app/main/lib/tray.ts @@ -16,24 +16,50 @@ import { let tray: Tray; let lastMinsLeft = 0; + +/** + * Returns the path to the tray icon based on the status and the remaining minutes. + * + * @param {('disabled' | 'not-in-working-hours' | 'active')} status - The status of the tray icon. + * @param {number} [minsLeft] - The remaining minutes. Only used when the status is 'active'. + * @return {string} The path to the tray icon. + */ +function getTrayIconPath(status: 'disabled' | 'not-in-working-hours' | 'active', minsLeft?: number): string { + const settings = getSettings(); + + let trayIconFileName = "icon.png"; + + if (process.platform === "darwin") { + trayIconFileName = "tray-IconTemplate.png"; + } else { + switch (status) { + case 'disabled': + trayIconFileName = "icon-disabled.png"; + break; + case 'not-in-working-hours': + trayIconFileName = "icon-off-work.png"; + break; + case 'active': + if (minsLeft !== undefined) { + if (minsLeft < settings.almostEmptyTrayMinutes) { + trayIconFileName = "icon-almost-empty.png"; + } else if (minsLeft < settings.halfFullTrayMinutes) { + trayIconFileName = "icon-half-full.png"; + } + } + break; + } + } + + return process.env.NODE_ENV === "development" + ? path.join("resources", "tray", trayIconFileName) + : path.join(process.resourcesPath, "app", "resources", "tray", trayIconFileName); +} + export function buildTray(): void { if (!tray) { - let imgPath; - if (process.platform === "darwin") { - imgPath = - process.env.NODE_ENV === "development" - ? "resources/tray/tray-IconTemplate.png" - : path.join( - process.resourcesPath, - "app/resources/tray/tray-IconTemplate.png" - ); - } else { - imgPath = - process.env.NODE_ENV === "development" - ? "resources/tray/icon.png" - : path.join(process.resourcesPath, "app/resources/tray/icon.png"); - } - tray = new Tray(imgPath); + const trayIconPath = getTrayIconPath('disabled'); + tray = new Tray(trayIconPath); // On windows, context menu will not show on left click by default if (process.platform === "win32") { @@ -72,27 +98,33 @@ export function buildTray(): void { const idle = checkIdle(); const minsLeft = breakTime?.diff(moment(), "minutes"); - let nextBreak = ""; + let toolTip = ""; - if (minsLeft !== undefined) { - if (minsLeft > 1) { - nextBreak = `Next break in ${minsLeft} minutes`; - } else if (minsLeft === 1) { - nextBreak = `Next break in 1 minute`; + if (breaksEnabled) { + if (inWorkingHours) { + if (minsLeft !== undefined) { + if (minsLeft > 1) { + toolTip = `Next break in ${minsLeft} minutes`; + } else if (minsLeft === 1) { + toolTip = `Next break in 1 minute`; + } else { + toolTip = `Next break in less than a minute`; + } + tray.setImage(getTrayIconPath('active', minsLeft)); + } } else { - nextBreak = `Next break in less than a minute`; + toolTip = "Outside of working hours"; + tray.setImage(getTrayIconPath("not-in-working-hours")) } + } else { + toolTip = "Disabled"; + tray.setImage(getTrayIconPath("disabled")); } const contextMenu = Menu.buildFromTemplate([ { - label: nextBreak, - visible: breakTime !== null && inWorkingHours, - enabled: false, - }, - { - label: `Outside of working hours`, - visible: !inWorkingHours, + label: toolTip, + visible: breakTime !== null || !inWorkingHours, enabled: false, }, { @@ -121,8 +153,8 @@ export function buildTray(): void { { label: "Quit", click: quit }, ]); - // Call this again for Linux because we modified the context menu tray.setContextMenu(contextMenu); + tray.setToolTip(toolTip); } export function initTray(): void { diff --git a/app/main/lib/windows.ts b/app/main/lib/windows.ts index 6191cf1..f18890e 100644 --- a/app/main/lib/windows.ts +++ b/app/main/lib/windows.ts @@ -35,8 +35,8 @@ export function createSettingsWindow(): void { settingsWindow = new BrowserWindow({ show: false, - width: 507, - minWidth: 507, + width: 540, + minWidth: 540, height: process.platform === "win32" ? 740 : 700, minHeight: process.platform === "win32" ? 740 : 700, autoHideMenuBar: true, diff --git a/app/renderer/components/Settings.tsx b/app/renderer/components/Settings.tsx index 15624cc..72b9068 100644 --- a/app/renderer/components/Settings.tsx +++ b/app/renderer/components/Settings.tsx @@ -77,9 +77,10 @@ export default function SettingsEl() { field: string, e: React.ChangeEvent ): void => { + const value = e.target.type === 'number' ? Number(e.target.value) : e.target.value; setSettingsDraft({ ...settingsDraft, - [field]: e.target.value, + [field]: value, }); }; @@ -318,6 +319,34 @@ export default function SettingsEl() { } /> + + + minutes remaining} + disabled={!settingsDraft.breaksEnabled} + /> + + + minutes remaining} + disabled={!settingsDraft.breaksEnabled} + /> + + + } + />