From 560eea9869b87e79a4f2be3697e2116f61431633 Mon Sep 17 00:00:00 2001 From: Roldan Montilla Jr Date: Mon, 29 Jun 2020 23:22:36 +0800 Subject: [PATCH] feat: added Auto-Start Work Time, Minimize To Tray and Close To Tray options to the app settings --- app/electron/electron.ts | 97 ++++++++++++++-------- app/electron/helpers/constants.ts | 4 +- app/electron/helpers/getFromStorage.ts | 17 ++++ app/electron/helpers/index.ts | 1 + app/electron/store/index.ts | 1 - app/package.json | 2 +- app/src/contexts/CounterContext.tsx | 17 ++++ app/src/contexts/ElectronContext.tsx | 4 +- app/src/routes/Settings/FeatureSection.tsx | 27 ++++++ app/src/routes/Timer/Control/Control.tsx | 10 +-- app/src/store/settings/actions.ts | 30 +++++++ app/src/store/settings/reducer.ts | 38 ++++++++- app/src/store/settings/types.ts | 7 ++ app/src/store/timer/actions.ts | 3 +- app/src/store/timer/reducer.ts | 2 +- 15 files changed, 211 insertions(+), 49 deletions(-) create mode 100644 app/electron/helpers/getFromStorage.ts diff --git a/app/electron/electron.ts b/app/electron/electron.ts index 20eb28e3..98b65edd 100644 --- a/app/electron/electron.ts +++ b/app/electron/electron.ts @@ -16,11 +16,12 @@ import { SET_ALWAYS_ON_TOP, SET_FULLSCREEN_BREAK, SET_MINIMIZE, - SET_HIDE, + SET_CLOSE, SET_UI_THEME, SET_NATIVE_TITLEBAR, getTrayIcon, isWindow, + getFromStorage, } from "./helpers"; import store from "./store"; @@ -82,10 +83,38 @@ function createMainWindow() { win?.show(); }); - win.on("close", (e) => { + win.on("minimize", async () => { + try { + if (win) { + const data = await getFromStorage(win, "settings"); + if (data.minimizeToTray) { + if (!isFullScreen) { + win?.hide(); + tray = createSystemTray(); + } + } + } + } catch (error) { + console.log(error); + } + }); + + win.on("close", async (e) => { e.preventDefault(); - if (!isFullScreen) { - win?.hide(); + try { + if (win) { + const data = await getFromStorage(win, "settings"); + if (!data.closeToTray) { + app.exit(); + } else { + if (!isFullScreen) { + win?.hide(); + tray = createSystemTray(); + } + } + } + } catch (error) { + console.log(error); } }); @@ -96,37 +125,39 @@ function createMainWindow() { win.on("show", () => { tray?.destroy(); }); +} - win.on("hide", () => { - tray = new Tray(getTrayIcon()); - tray.setToolTip("PRODUCTIVITY TIMER"); - tray.setContextMenu( - Menu.buildFromTemplate([ - { - label: "Show the app", - click: () => { - win?.show(); - }, +function createSystemTray() { + const tray = new Tray(getTrayIcon()); + tray.setToolTip("PRODUCTIVITY TIMER"); + tray.setContextMenu( + Menu.buildFromTemplate([ + { + label: "Show the app", + click: () => { + win?.show(); }, - { - label: "Quit", - click: () => { - app.exit(); - }, + }, + { + label: "Quit", + click: () => { + app.exit(); }, - ]) - ); + }, + ]) + ); - tray?.on("click", () => { - if (!win?.isVisible()) { - win?.show(); - } else { - if (!win?.isFullScreen()) { - win?.hide(); - } + tray?.on("click", () => { + if (!win?.isVisible()) { + win?.show(); + } else { + if (!win?.isFullScreen()) { + win?.hide(); } - }); + } }); + + return tray; } if (!onlySingleIntance) { @@ -251,13 +282,9 @@ ipcMain.on(SET_UI_THEME, (e, { isDarkMode }) => { store.set("isDarkMode", isDarkMode); }); -ipcMain.on(SET_MINIMIZE, () => { - win?.minimize(); -}); +ipcMain.on(SET_MINIMIZE, () => win?.minimize()); -ipcMain.on(SET_HIDE, () => { - win?.hide(); -}); +ipcMain.on(SET_CLOSE, () => app.quit()); ipcMain.on(SET_NATIVE_TITLEBAR, (e, { useNativeTitlebar }) => { if (store.get("useNativeTitlebar") !== useNativeTitlebar) { diff --git a/app/electron/helpers/constants.ts b/app/electron/helpers/constants.ts index 961d5444..ea80aa10 100644 --- a/app/electron/helpers/constants.ts +++ b/app/electron/helpers/constants.ts @@ -3,7 +3,7 @@ export const SET_FULLSCREEN_BREAK = "SET_FULLSCREEN_BREAK"; export const SET_NATIVE_TITLEBAR = "SET_NATIVE_TITLEBAR"; export const SET_UI_THEME = "SET_UI_THEME"; export const SET_MINIMIZE = "SET_MINIMIZE"; -export const SET_HIDE = "SET_HIDE"; +export const SET_CLOSE = "SET_CLOSE"; export const TO_MAIN: string[] = [ SET_ALWAYS_ON_TOP, @@ -11,7 +11,7 @@ export const TO_MAIN: string[] = [ SET_NATIVE_TITLEBAR, SET_UI_THEME, SET_MINIMIZE, - SET_HIDE, + SET_CLOSE, ]; export const FROM_MAIN: string[] = []; diff --git a/app/electron/helpers/getFromStorage.ts b/app/electron/helpers/getFromStorage.ts new file mode 100644 index 00000000..d4d2328f --- /dev/null +++ b/app/electron/helpers/getFromStorage.ts @@ -0,0 +1,17 @@ +import { BrowserWindow } from "electron"; + +const getFromStorage = async (win: BrowserWindow, key: string) => { + try { + const data = await win.webContents.executeJavaScript( + `localStorage.getItem("${key}")` + ); + if (data === null) { + return undefined; + } + return JSON.parse(data); + } catch (error) { + return error; + } +}; + +export { getFromStorage }; diff --git a/app/electron/helpers/index.ts b/app/electron/helpers/index.ts index e8d0475d..d371dbed 100644 --- a/app/electron/helpers/index.ts +++ b/app/electron/helpers/index.ts @@ -5,3 +5,4 @@ export * from "./getTrayIcon"; export * from "./constants"; export * from "./isWindow"; export * from "./getIcon"; +export * from "./getFromStorage"; diff --git a/app/electron/store/index.ts b/app/electron/store/index.ts index 08fc7111..eb788808 100644 --- a/app/electron/store/index.ts +++ b/app/electron/store/index.ts @@ -1,6 +1,5 @@ import Store from "electron-store"; import { systemPreferences } from "electron"; -import { isWindow } from "../helpers"; type StoreProps = { isDarkMode: boolean; diff --git a/app/package.json b/app/package.json index 900c12ca..1b3fbec1 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "productivity-timer", - "version": "2.10.4-beta", + "version": "2.13.4-beta", "private": true, "license": "MIT", "main": "public/electron.js", diff --git a/app/src/contexts/CounterContext.tsx b/app/src/contexts/CounterContext.tsx index b335392c..13bc1083 100644 --- a/app/src/contexts/CounterContext.tsx +++ b/app/src/contexts/CounterContext.tsx @@ -10,6 +10,7 @@ import { TimerTypes, SPECIAL_BREAK, SettingTypes, + setPlay, } from "store"; import { useNotification, useSleepMode } from "hooks"; @@ -264,6 +265,11 @@ const CounterProvider: React.FC = ({ children }) => { case SHORT_BREAK: dispatch(setTimerType("STAY_FOCUS")); dispatch(setRound(timer.round + 1)); + + if (!settings.autoStartWorkTime) { + dispatch(setPlay(false)); + } + notification( "Short Break Finished", { body: "It is time to focus and work again." }, @@ -274,6 +280,11 @@ const CounterProvider: React.FC = ({ children }) => { case LONG_BREAK: dispatch(setTimerType("STAY_FOCUS")); dispatch(setRound(1)); + + if (!settings.autoStartWorkTime) { + dispatch(setPlay(false)); + } + notification( "Long Break Finished", { body: "It is time to focus and work again." }, @@ -283,6 +294,11 @@ const CounterProvider: React.FC = ({ children }) => { case SPECIAL_BREAK: dispatch(setTimerType("STAY_FOCUS")); + + if (!settings.autoStartWorkTime) { + dispatch(setPlay(false)); + } + notification( "Special Break Finished", { body: "It is time to focus and work again." }, @@ -304,6 +320,7 @@ const CounterProvider: React.FC = ({ children }) => { notification, config.sessionRounds, settings.notificationProperty, + settings.autoStartWorkTime, ]); useEffect(() => { diff --git a/app/src/contexts/ElectronContext.tsx b/app/src/contexts/ElectronContext.tsx index 49b45845..271836d4 100644 --- a/app/src/contexts/ElectronContext.tsx +++ b/app/src/contexts/ElectronContext.tsx @@ -10,7 +10,7 @@ export const SET_FULLSCREEN_BREAK = "SET_FULLSCREEN_BREAK"; export const SET_NATIVE_TITLEBAR = "SET_NATIVE_TITLEBAR"; export const SET_UI_THEME = "SET_UI_THEME"; export const SET_MINIMIZE = "SET_MINIMIZE"; -export const SET_HIDE = "SET_HIDE"; +export const SET_CLOSE = "SET_CLOSE"; type ElectronProps = { onMinimizeCallback?: () => void; @@ -37,7 +37,7 @@ const ElectronProvider: React.FC = ({ children }) => { const onExitCallback = useCallback(() => { if (isElectron()) { - electron.send(SET_HIDE); + electron.send(SET_CLOSE); } }, [electron]); diff --git a/app/src/routes/Settings/FeatureSection.tsx b/app/src/routes/Settings/FeatureSection.tsx index ae112b60..63b69964 100644 --- a/app/src/routes/Settings/FeatureSection.tsx +++ b/app/src/routes/Settings/FeatureSection.tsx @@ -9,6 +9,9 @@ import { setNotificationProperty, setEnableFullscreenBreak, setUseNativeTitlebar, + setAutoStartWorkTime, + setMinimizeToTray, + setCloseToTray, } from "store"; import { Toggler, TogglerProps, Collapse, Radio } from "components"; @@ -75,6 +78,30 @@ const FeatureSection: React.FC = () => { dispatch(setEnableTimerAnimation(!settings.enableTimerAnimation)); }, [dispatch, settings.enableTimerAnimation]), }, + { + id: "auto-start-work-time", + label: "Auto-start Work Time", + checked: settings.autoStartWorkTime, + onChange: useCallback(() => { + dispatch(setAutoStartWorkTime(!settings.autoStartWorkTime)); + }, [dispatch, settings.autoStartWorkTime]), + }, + { + id: "minimize-to-tray", + label: "Minimize To Tray", + checked: settings.minimizeToTray, + onChange: useCallback(() => { + dispatch(setMinimizeToTray(!settings.minimizeToTray)); + }, [dispatch, settings.minimizeToTray]), + }, + { + id: "close-to-tray", + label: "Close To Tray", + checked: settings.closeToTray, + onChange: useCallback(() => { + dispatch(setCloseToTray(!settings.closeToTray)); + }, [dispatch, settings.closeToTray]), + }, ]; const onChangeNotificationProps = useCallback( diff --git a/app/src/routes/Timer/Control/Control.tsx b/app/src/routes/Timer/Control/Control.tsx index 45af1ca9..16af5a4c 100644 --- a/app/src/routes/Timer/Control/Control.tsx +++ b/app/src/routes/Timer/Control/Control.tsx @@ -72,7 +72,7 @@ const Control: React.FC = ({ resetTimerAction }) => { activateWarning(); return; } - dispatch(setPlay()); + dispatch(setPlay(!timer.playing)); }, [dispatch, activateWarning, timer.playing, settings.enableStrictMode]); const onNotifacationSoundCallback = useCallback(() => { @@ -92,24 +92,24 @@ const Control: React.FC = ({ resetTimerAction }) => { } else { dispatch(skipTimer("LONG_BREAK")); } - if (!timer.playing) dispatch(setPlay()); + if (!timer.playing) dispatch(setPlay(!timer.playing)); break; case SHORT_BREAK: dispatch(skipTimer("STAY_FOCUS")); dispatch(setRound(timer.round + 1)); - if (!timer.playing) dispatch(setPlay()); + if (!timer.playing) dispatch(setPlay(!timer.playing)); break; case LONG_BREAK: dispatch(skipTimer("STAY_FOCUS")); dispatch(setRound(1)); - if (!timer.playing) dispatch(setPlay()); + if (!timer.playing) dispatch(setPlay(!timer.playing)); break; case SPECIAL_BREAK: dispatch(skipTimer("STAY_FOCUS")); - if (!timer.playing) dispatch(setPlay()); + if (!timer.playing) dispatch(setPlay(!timer.playing)); break; } }, [ diff --git a/app/src/store/settings/actions.ts b/app/src/store/settings/actions.ts index c47fed9f..c342a4bb 100644 --- a/app/src/store/settings/actions.ts +++ b/app/src/store/settings/actions.ts @@ -10,6 +10,9 @@ import { ENABLE_FULLSCREEN_BREAK, USE_NATIVE_TITLE_BAR, ENABLE_STRICT_MODE, + CLOSE_TO_TRAY, + MINIMIZE_TO_TRAY, + AUTO_START_WORK_TIME, } from "./types"; export const setAlwaysOnTop = ( @@ -81,6 +84,33 @@ export const setNotificationProperty = ( }; }; +export const setCloseToTray = ( + closeToTray: SettingTypes["closeToTray"] +): SettingActionTypes => { + return { + type: CLOSE_TO_TRAY, + payload: closeToTray, + }; +}; + +export const setMinimizeToTray = ( + minimizeToTray: SettingTypes["minimizeToTray"] +): SettingActionTypes => { + return { + type: MINIMIZE_TO_TRAY, + payload: minimizeToTray, + }; +}; + +export const setAutoStartWorkTime = ( + autoStartWorkTime: SettingTypes["autoStartWorkTime"] +): SettingActionTypes => { + return { + type: AUTO_START_WORK_TIME, + payload: autoStartWorkTime, + }; +}; + export const restoreDefaultSettings = () => ({ type: RESTORE_DEFAULT_SETTINGS, }); diff --git a/app/src/store/settings/reducer.ts b/app/src/store/settings/reducer.ts index 2ebf545d..24b5d985 100644 --- a/app/src/store/settings/reducer.ts +++ b/app/src/store/settings/reducer.ts @@ -10,6 +10,9 @@ import { ENABLE_FULLSCREEN_BREAK, ENABLE_TIMER_ANIMATION, USE_NATIVE_TITLE_BAR, + CLOSE_TO_TRAY, + MINIMIZE_TO_TRAY, + AUTO_START_WORK_TIME, } from "./types"; import { saveToStorage, getFromStorage, isPreferredDark } from "utils"; @@ -20,8 +23,11 @@ const defaultSettings: SettingTypes = { enableDarkTheme: isPreferredDark(), enableTimerAnimation: true, notificationSoundOn: true, - notificationProperty: "extra", + notificationProperty: "normal", useNativeTitlebar: false, + closeToTray: true, + minimizeToTray: false, + autoStartWorkTime: true, }; const settings = getFromStorage("settings") @@ -115,6 +121,36 @@ export const settingReducer = ( return newState; } + case CLOSE_TO_TRAY: { + const newState = { + ...state, + closeToTray: action.payload, + }; + + saveToStorage("settings", newState); + + return newState; + } + case MINIMIZE_TO_TRAY: { + const newState = { + ...state, + minimizeToTray: action.payload, + }; + + saveToStorage("settings", newState); + + return newState; + } + case AUTO_START_WORK_TIME: { + const newState = { + ...state, + autoStartWorkTime: action.payload, + }; + + saveToStorage("settings", newState); + + return newState; + } case RESTORE_DEFAULT_SETTINGS: saveToStorage("settings", defaultSettings); diff --git a/app/src/store/settings/types.ts b/app/src/store/settings/types.ts index 9748f68e..b4cc7221 100644 --- a/app/src/store/settings/types.ts +++ b/app/src/store/settings/types.ts @@ -8,6 +8,9 @@ export type SettingTypes = { enableTimerAnimation: boolean; useNativeTitlebar: boolean; notificationSoundOn: boolean; + closeToTray: boolean; + minimizeToTray: boolean; + autoStartWorkTime: boolean; notificationProperty: "none" | "normal" | "extra"; }; @@ -25,6 +28,10 @@ export const ENABLE_AUTO_UPDATES = `${settings} ENABLE_AUTO_UPDATES`; export const TOGGLE_NOTIFICATION_SOUND = `${settings} TOGGLE_NOTIFICATION_SOUND`; export const SET_NOTIFICATION_PROPERTY = `${settings} SET_NOTIFICATION_PROPERTY`; +export const CLOSE_TO_TRAY = `${settings} CLOSE_TO_TRAY`; +export const MINIMIZE_TO_TRAY = `${settings} MINIMIZE_TO_TRAY`; +export const AUTO_START_WORK_TIME = `${settings} AUTO_START_WORK_TIME`; + export const RESTORE_DEFAULT_SETTINGS = `${settings} RESTORE_DEFAULT_SETTINGS`; export type SettingActionTypes = { diff --git a/app/src/store/timer/actions.ts b/app/src/store/timer/actions.ts index 7af78369..ed285afc 100644 --- a/app/src/store/timer/actions.ts +++ b/app/src/store/timer/actions.ts @@ -7,9 +7,10 @@ import { SKIP_TIMER, } from "./types"; -export const setPlay = (): TimerActionTypes => { +export const setPlay = (playing: TimerTypes["playing"]): TimerActionTypes => { return { type: SET_PLAY, + payload: playing, }; }; diff --git a/app/src/store/timer/reducer.ts b/app/src/store/timer/reducer.ts index 19034b18..dbb9d0fb 100644 --- a/app/src/store/timer/reducer.ts +++ b/app/src/store/timer/reducer.ts @@ -21,7 +21,7 @@ export const timerReducer = ( case SET_PLAY: return { ...state, - playing: !state.playing, + playing: action.payload, }; case SET_TIMER_TYPE: return {