diff --git a/package-lock.json b/package-lock.json index af2975e..34a9b01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bodyshop-desktop", - "version": "0.0.1-alpha.3", + "version": "0.0.1-alpha.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bodyshop-desktop", - "version": "0.0.1-alpha.3", + "version": "0.0.1-alpha.0", "hasInstallScript": true, "dependencies": { "@apollo/client": "^3.13.6", @@ -14,6 +14,7 @@ "@electron-toolkit/utils": "^4.0.0", "@sentry/electron": "^6.5.0", "@sentry/vite-plugin": "^3.3.1", + "dayjs": "^1.11.13", "electron-log": "^5.3.3", "electron-store": "^8.2.0", "electron-updater": "^6.6.2", @@ -6774,7 +6775,6 @@ "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", - "dev": true, "license": "MIT" }, "node_modules/dbffile": { diff --git a/package.json b/package.json index 25ad6a7..95e84a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bodyshop-desktop", - "version": "0.0.1-alpha.1", + "version": "0.0.1-alpha.0", "description": "Shop Management System Partner", "main": "./out/main/index.js", "author": "Convenient Brands, LLC", @@ -29,6 +29,7 @@ "@electron-toolkit/utils": "^4.0.0", "@sentry/electron": "^6.5.0", "@sentry/vite-plugin": "^3.3.1", + "dayjs": "^1.11.13", "electron-log": "^5.3.3", "electron-store": "^8.2.0", "electron-updater": "^6.6.2", diff --git a/src/main/index.ts b/src/main/index.ts index 0727488..d6a5c48 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -22,6 +22,7 @@ import ipcTypes from "../util/ipcTypes.json"; import ImportJob from "./decoder/decoder"; import LocalServer from "./http-server/http-server"; import store from "./store/store"; +import { getMainWindow } from "./util/toRenderer"; import { GetAllEnvFiles } from "./watcher/watcher"; Sentry.init({ @@ -373,17 +374,17 @@ app.whenReady().then(async () => { //Check for app updates. autoUpdater.logger = log; - // if (import.meta.env.DEV) { - // // Useful for some dev/debugging tasks, but download can - // // not be validated becuase dev app is not signed - // autoUpdater.channel = "alpha"; - // autoUpdater.updateConfigPath = path.join( - // __dirname, - // "../../dev-app-update.yml", - // ); - // autoUpdater.forceDevUpdateConfig = true; - // //autoUpdater.autoDownload = false; - // } + if (import.meta.env.DEV) { + // Useful for some dev/debugging tasks, but download can + // not be validated becuase dev app is not signed + autoUpdater.channel = "alpha"; + autoUpdater.updateConfigPath = path.join( + __dirname, + "../../dev-app-update.yml", + ); + autoUpdater.forceDevUpdateConfig = true; + //autoUpdater.autoDownload = false; + } autoUpdater.on("checking-for-update", () => { log.info("Checking for update..."); @@ -439,7 +440,8 @@ app.on("window-all-closed", () => { } }); -app.on("before-quit", () => { +app.on("before-quit", (props) => { + console.log(props); preQuitMethods(); }); @@ -447,14 +449,17 @@ app.on("before-quit", () => { ipcMain.on(ipcTypes.toMain.updates.apply, () => { log.info("Applying update from renderer."); preQuitMethods(); - autoUpdater.quitAndInstall(); + setImmediate(() => { + app.removeAllListeners("window-all-closed"); + const mainWindow = getMainWindow(); + if (mainWindow) mainWindow.close(); + autoUpdater.quitAndInstall(false); + }); }); function preQuitMethods(): void { localServer.stop(); const currentSetting = store.get("app.openOnStartup") as boolean; - store.set("app.openOnStartup", !currentSetting); - log.info("Open on startup set to", !currentSetting); if (!import.meta.env.DEV) { app.setLoginItemSettings({ enabled: true, //This is a windows only command. Updates the task manager and registry. diff --git a/src/renderer/src/components/UpdateAvailable/UpdateAvailable.tsx b/src/renderer/src/components/UpdateAvailable/UpdateAvailable.tsx index a20a549..7c8011b 100644 --- a/src/renderer/src/components/UpdateAvailable/UpdateAvailable.tsx +++ b/src/renderer/src/components/UpdateAvailable/UpdateAvailable.tsx @@ -5,12 +5,13 @@ import { selectUpdateAvailable, } from "@renderer/redux/app.slice"; import { useAppSelector } from "@renderer/redux/reduxHooks"; -import { Affix, Button, Card, Progress } from "antd"; +import { Affix, Button, Card, Progress, Space, Statistic } from "antd"; import { useTranslation } from "react-i18next"; import ipcTypes from "../../../../util/ipcTypes.json"; const UpdateAvailable: React.FC = () => { const { t } = useTranslation(); + const isUpdateAvailable = useAppSelector(selectUpdateAvailable); const updateSpeed = useAppSelector(selectAppUpdateSpeed); const updateProgress = useAppSelector(selectAppUpdateProgress); @@ -39,7 +40,17 @@ const UpdateAvailable: React.FC = () => { /> {!isUpdateComplete && formatSpeed(updateSpeed)} {isUpdateComplete && ( - + + + + window.electron.ipcRenderer.send(ipcTypes.toMain.updates.apply) + } + /> + )} diff --git a/src/renderer/src/util/countdownHook.ts b/src/renderer/src/util/countdownHook.ts new file mode 100644 index 0000000..5b2acd9 --- /dev/null +++ b/src/renderer/src/util/countdownHook.ts @@ -0,0 +1,98 @@ +import React from "react"; + +const useCountDown = ( + timeToCount = 60 * 1000, + interval = 1000, +): [ + number, + { + start: (ttc?: number) => void; + pause: () => void; + resume: () => void; + reset: () => void; + }, +] => { + const [timeLeft, setTimeLeft] = React.useState(0); + const timer = React.useRef({}); + + const run = (ts) => { + if (!timer.current.started) { + timer.current.started = ts; + timer.current.lastInterval = ts; + } + + const localInterval = Math.min( + interval, + timer.current.timeLeft || Infinity, + ); + if (ts - timer.current.lastInterval >= localInterval) { + timer.current.lastInterval += localInterval; + setTimeLeft((timeLeft) => { + timer.current.timeLeft = timeLeft - localInterval; + return timer.current.timeLeft; + }); + } + + if (ts - timer.current.started < timer.current.timeToCount) { + timer.current.requestId = window.requestAnimationFrame(run); + } else { + timer.current = {}; + setTimeLeft(0); + } + }; + + const start = React.useCallback( + (ttc) => { + window.cancelAnimationFrame(timer.current.requestId); + + const newTimeToCount = ttc !== undefined ? ttc : timeToCount; + timer.current.started = null; + timer.current.lastInterval = null; + timer.current.timeToCount = newTimeToCount; + timer.current.requestId = window.requestAnimationFrame(run); + + setTimeLeft(newTimeToCount); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [], + ); + + const pause = React.useCallback(() => { + window.cancelAnimationFrame(timer.current.requestId); + timer.current.started = null; + timer.current.lastInterval = null; + timer.current.timeToCount = timer.current.timeLeft; + }, []); + + const resume = React.useCallback( + () => { + if (!timer.current.started && timer.current.timeLeft > 0) { + window.cancelAnimationFrame(timer.current.requestId); + timer.current.requestId = window.requestAnimationFrame(run); + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [], + ); + + const reset = React.useCallback(() => { + if (timer.current.timeLeft) { + window.cancelAnimationFrame(timer.current.requestId); + timer.current = {}; + setTimeLeft(0); + } + }, []); + + const actions = React.useMemo( + () => ({ start, pause, resume, reset }), // eslint-disable-next-line react-hooks/exhaustive-deps + [], + ); + + React.useEffect(() => { + return () => window.cancelAnimationFrame(timer.current.requestId); + }, []); + + return [timeLeft, actions]; +}; + +export default useCountDown;