Added better auto-update handling. RPS-17
This commit is contained in:
3
Xdev-app-update.yml
Normal file
3
Xdev-app-update.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
provider: s3
|
||||
bucket: rps-updater
|
||||
region: ca-central-1
|
||||
116
electron/main.js
116
electron/main.js
@@ -19,6 +19,8 @@ const Nucleus = require("nucleus-nodejs");
|
||||
require("./ipc-main-handler");
|
||||
require("./analytics");
|
||||
|
||||
autoUpdater.autoDownload = false;
|
||||
|
||||
autoUpdater.logger = log;
|
||||
autoUpdater.logger.transports.file.level = "info";
|
||||
log.info("App starting...", app.getVersion());
|
||||
@@ -70,10 +72,7 @@ var menu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: `Check for Updates (currently ${app.getVersion()})`,
|
||||
click() {
|
||||
autoUpdater.checkForUpdatesAndNotify({
|
||||
title: "ImEX RPS Update Downloaded",
|
||||
body: "Restart ImEX RPS to install.",
|
||||
});
|
||||
checkForUpdates();
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -168,7 +167,6 @@ function createWindow() {
|
||||
}
|
||||
|
||||
mainWindow.maximize();
|
||||
autoUpdater.checkForUpdatesAndNotify();
|
||||
|
||||
globalShortcut.register("CommandOrControl+Shift+I", () => {
|
||||
mainWindow.webContents.toggleDevTools();
|
||||
@@ -242,46 +240,22 @@ function createTray() {
|
||||
return appIcon;
|
||||
}
|
||||
|
||||
autoUpdater.on("checking-for-update", () => {
|
||||
log.log("Checking for update...");
|
||||
});
|
||||
autoUpdater.on("update-available", (ev, info) => {
|
||||
log.log("Update available.");
|
||||
});
|
||||
autoUpdater.on("update-not-available", (ev, info) => {
|
||||
log.log("Update not available.");
|
||||
});
|
||||
autoUpdater.on("error", (ev, err) => {
|
||||
log.log("Error in auto-updater.");
|
||||
});
|
||||
autoUpdater.on("download-progress", (ev, progressObj) => {
|
||||
log.log("Download progress...");
|
||||
});
|
||||
// autoUpdater.on("update-downloaded", (ev, info) => {
|
||||
// console.log("Update downloaded; will install in 5 seconds");
|
||||
// autoUpdater.on("checking-for-update", () => {
|
||||
// log.log("Checking for update...");
|
||||
// });
|
||||
autoUpdater.on("update-downloaded", (ev, info) => {
|
||||
Nucleus.track("UPDATE_DOWNLOADED", info);
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
dialog.showMessageBox(
|
||||
{
|
||||
type: "info",
|
||||
title: "Found Updates",
|
||||
message: "Found updates, do you want update now?",
|
||||
buttons: ["Sure", "No"],
|
||||
},
|
||||
(buttonIndex) => {
|
||||
if (buttonIndex === 0) {
|
||||
const isSilent = true;
|
||||
const isForceRunAfter = true;
|
||||
autoUpdater.quitAndInstall(isSilent, isForceRunAfter);
|
||||
} else {
|
||||
logger.warn("Error");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
// autoUpdater.on("update-available", (ev, info) => {
|
||||
// log.log("Update available.");
|
||||
// });
|
||||
// autoUpdater.on("update-not-available", (ev, info) => {
|
||||
// log.log("Update not available.");
|
||||
// });
|
||||
// autoUpdater.on("error", (ev, err) => {
|
||||
// log.log("Error in auto-updater.");
|
||||
// });
|
||||
|
||||
// // autoUpdater.on("update-downloaded", (ev, info) => {
|
||||
// // console.log("Update downloaded; will install in 5 seconds");
|
||||
// // });
|
||||
|
||||
function openNoticeWindow() {
|
||||
if (noticeWindow) {
|
||||
@@ -301,3 +275,57 @@ function openNoticeWindow() {
|
||||
noticeWindow = null;
|
||||
});
|
||||
}
|
||||
|
||||
ipcMain.on(ipcTypes.app.toMain.checkForUpdates, (event, args) => {
|
||||
checkForUpdates();
|
||||
});
|
||||
|
||||
ipcMain.on(ipcTypes.app.toMain.downloadUpdates, (event, args) => {
|
||||
autoUpdater.downloadUpdate();
|
||||
});
|
||||
|
||||
ipcMain.on(ipcTypes.app.toMain.installUpdates, (event, args) => {
|
||||
const isSilent = true;
|
||||
const isForceRunAfter = true;
|
||||
autoUpdater.quitAndInstall(isSilent, isForceRunAfter);
|
||||
});
|
||||
|
||||
autoUpdater.on("download-progress", (ev) => {
|
||||
mainWindow.webContents.send(ipcTypes.app.toRenderer.downloadProgress, ev);
|
||||
});
|
||||
|
||||
autoUpdater.on("update-downloaded", (ev, info) => {
|
||||
Nucleus.track("UPDATE_DOWNLOADED", info);
|
||||
// if (process.env.NODE_ENV === "production") {
|
||||
|
||||
dialog.showMessageBox(
|
||||
{
|
||||
type: "info",
|
||||
title: "ImeX RPS Update Manager",
|
||||
message: `ImEX RPS is ready to update to Version ${ev.version}. It is highly recommended that you update immediately. Would you like to update now? RPS will automatically restart.`,
|
||||
buttons: ["Yes", "No"],
|
||||
},
|
||||
(buttonIndex) => {
|
||||
if (buttonIndex === 0) {
|
||||
const isSilent = true;
|
||||
const isForceRunAfter = true;
|
||||
autoUpdater.quitAndInstall(isSilent, isForceRunAfter);
|
||||
} else {
|
||||
logger.error("Error");
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
async function checkForUpdates() {
|
||||
try {
|
||||
log.info("Checking for updates.");
|
||||
const result = await autoUpdater.checkForUpdates();
|
||||
const { updateInfo } = result;
|
||||
mainWindow.webContents.send(
|
||||
ipcTypes.app.toRenderer.updateAvailable,
|
||||
updateInfo
|
||||
);
|
||||
} catch (error) {
|
||||
log.error("Error while checking for update", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"productName": "ImEX RPS",
|
||||
"author": "ImEX Systems Inc. <support@thinkimex.com>",
|
||||
"description": "ImEX RPS",
|
||||
"version": "1.0.5",
|
||||
"version": "1.0.3",
|
||||
"main": "electron/main.js",
|
||||
"homepage": "./",
|
||||
"dependencies": {
|
||||
|
||||
@@ -78,3 +78,13 @@ body {
|
||||
.ant-tabs-content {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.blink_me {
|
||||
animation: blinker 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes blinker {
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
.blink_me {
|
||||
animation: blinker 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes blinker {
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
import { AlertFilled } from "@ant-design/icons";
|
||||
import { Button, Layout, Progress } from "antd";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import ipcTypes from "../../../ipc.types";
|
||||
import {
|
||||
selectUpdateAvailable,
|
||||
selectUpdateProgress,
|
||||
} from "../../../redux/application/application.selectors";
|
||||
const { ipcRenderer } = window;
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
//currentUser: selectCurrentUser
|
||||
updateAvailable: selectUpdateAvailable,
|
||||
updateProgress: selectUpdateProgress,
|
||||
});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
//setUserLanguage: language => dispatch(setUserLanguage(language))
|
||||
});
|
||||
|
||||
export function UpdateManagerOrganism({ updateAvailable, updateProgress }) {
|
||||
if (!updateAvailable) return null;
|
||||
return (
|
||||
<Layout.Footer>
|
||||
{updateAvailable && !updateProgress && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<AlertFilled
|
||||
style={{ marginRight: ".5rem", color: "tomato" }}
|
||||
className="blink_me"
|
||||
/>
|
||||
<span>{`An update to ImEX RPS is available. (Version ${updateAvailable.version})`}</span>
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
onClick={() =>
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.downloadUpdates)
|
||||
}
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{updateAvailable && updateProgress && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Progress
|
||||
style={{ flex: 1, margin: "0rem .5rem" }}
|
||||
status={updateProgress.percent === 100 ? "success" : "active"}
|
||||
percent={updateProgress.percent.toFixed(1)}
|
||||
/>
|
||||
{updateProgress.percent === 100 ? (
|
||||
<div tyle={{ margin: "0rem .5rem" }}>
|
||||
<span
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
>{`Updated downloaded.`}</span>
|
||||
<Button
|
||||
type="primary"
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
onClick={() =>
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.installUpdates)
|
||||
}
|
||||
>
|
||||
Install
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<span
|
||||
style={{ margin: "0rem .5rem" }}
|
||||
>{`Downloading update at ${formatBytes(
|
||||
updateProgress.bytesPerSecond
|
||||
)})`}</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Layout.Footer>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(UpdateManagerOrganism);
|
||||
|
||||
// download Info
|
||||
// {
|
||||
// // bytesPerSecond: 6633258;
|
||||
// // delta: 2479242;
|
||||
// // percent: 100;
|
||||
// // total: 95651575;
|
||||
// // transferred: 95651575;
|
||||
// }
|
||||
|
||||
function formatBytes(bytes) {
|
||||
var marker = 1024; // Change to 1000 if required
|
||||
var decimal = 1; // Change as required
|
||||
var kiloBytes = marker; // One Kilobyte is 1024 bytes
|
||||
var megaBytes = marker * marker; // One MB is 1024 KB
|
||||
var gigaBytes = marker * marker * marker; // One GB is 1024 MB
|
||||
//var teraBytes = marker * marker * marker * marker; // One TB is 1024 GB
|
||||
|
||||
// return bytes if less than a KB
|
||||
if (bytes < kiloBytes) return bytes + " Bytes/sec";
|
||||
// return KB if less than a MB
|
||||
else if (bytes < megaBytes)
|
||||
return (bytes / kiloBytes).toFixed(decimal) + " KB/sec";
|
||||
// return MB if less than a GB
|
||||
else if (bytes < gigaBytes)
|
||||
return (bytes / megaBytes).toFixed(decimal) + " MB/sec";
|
||||
// return GB if less than a TB
|
||||
else return (bytes / gigaBytes).toFixed(decimal) + " GB/sec";
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { createStructuredSelector } from "reselect";
|
||||
import { selectBodyshop } from "../../../redux/user/user.selectors";
|
||||
import ErrorResultAtom from "../../atoms/error-result/error-result.atom";
|
||||
import SiderMenuOrganism from "../../organisms/sider-menu/sider-menu.organism";
|
||||
import UpdateManagerOrganism from "../../organisms/update-manager/update-manager.organism";
|
||||
import JobsPage from "../jobs/jobs.page";
|
||||
import ReportingPage from "../reporting/reporting.page";
|
||||
import ScanPage from "../scan/scan.page";
|
||||
@@ -39,6 +40,8 @@ export function RoutesPage({ bodyshop }) {
|
||||
<Route exact path="/scan" component={ScanPage} />
|
||||
<Route exact path="/" component={JobsPage} />
|
||||
</Layout.Content>
|
||||
|
||||
<UpdateManagerOrganism />
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
@@ -9,6 +9,13 @@ exports.default = {
|
||||
setAcceptableInsCoNm: "setAcceptableInsCoNm",
|
||||
setUserName: "setUserName",
|
||||
track: "analytics_track",
|
||||
checkForUpdates: "app_checkForUpdates",
|
||||
downloadUpdates: "app_downloadUpdates",
|
||||
installUpdates: "app_installupdates",
|
||||
},
|
||||
toRenderer: {
|
||||
updateAvailable: "app_updateAvailable",
|
||||
downloadProgress: "app_downloadProgress",
|
||||
},
|
||||
},
|
||||
store: {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import ipcTypes from "../ipc.types";
|
||||
import {
|
||||
setSettings,
|
||||
setUpdateAvailable,
|
||||
setUpdateProgress,
|
||||
setWatchedPaths,
|
||||
setWatcherStatus,
|
||||
} from "../redux/application/application.actions";
|
||||
@@ -65,3 +67,16 @@ ipcRenderer.on(
|
||||
store.dispatch(setScanEstimateList(listOfEstimates));
|
||||
}
|
||||
);
|
||||
|
||||
ipcRenderer.on(
|
||||
ipcTypes.default.app.toRenderer.updateAvailable,
|
||||
async (event, updateInfo) => {
|
||||
store.dispatch(setUpdateAvailable(updateInfo));
|
||||
}
|
||||
);
|
||||
ipcRenderer.on(
|
||||
ipcTypes.default.app.toRenderer.downloadProgress,
|
||||
async (event, progress) => {
|
||||
store.dispatch(setUpdateProgress(progress));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -48,3 +48,7 @@ export const setUpdateAvailable = (available) => ({
|
||||
type: ApplicationActionTypes.SET_UPDATE_AVAILABLE,
|
||||
payload: available,
|
||||
});
|
||||
export const setUpdateProgress = (progress) => ({
|
||||
type: ApplicationActionTypes.SET_UPDATE_PROGRESS,
|
||||
payload: progress,
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ const INITIAL_STATE = {
|
||||
selectedJobTargetPc: 0,
|
||||
settings: {},
|
||||
updateAvailable: false,
|
||||
updateProgress: null,
|
||||
};
|
||||
|
||||
const { ipcRenderer } = window;
|
||||
@@ -63,6 +64,8 @@ const applicationReducer = (state = INITIAL_STATE, action) => {
|
||||
return { ...state, settings: { ...state.settings, ...action.payload } };
|
||||
case ApplicationActionTypes.SET_UPDATE_AVAILABLE:
|
||||
return { ...state, updateAvailable: action.payload };
|
||||
case ApplicationActionTypes.SET_UPDATE_PROGRESS:
|
||||
return { ...state, updateProgress: action.payload };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -36,3 +36,7 @@ export const selectUpdateAvailable = createSelector(
|
||||
[selectApplication],
|
||||
(application) => application.updateAvailable
|
||||
);
|
||||
export const selectUpdateProgress = createSelector(
|
||||
[selectApplication],
|
||||
(application) => application.updateProgress
|
||||
);
|
||||
|
||||
@@ -9,5 +9,7 @@ const ApplicationActionTypes = {
|
||||
SET_SELECTED_JOB_TARGET_PC_SUCCESS: "SET_SELECTED_JOB_TARGET_PC_SUCCESS",
|
||||
SET_SETTINGS: "SET_SETTINGS",
|
||||
SET_UPDATE_AVAILABLE: "SET_UPDATE_AVAILABLE",
|
||||
SET_UPDATE_PROGRESS: "SET_UPDATE_PROGRESS",
|
||||
|
||||
};
|
||||
export default ApplicationActionTypes;
|
||||
|
||||
@@ -128,6 +128,7 @@ export function* signInSuccessSaga({ payload }) {
|
||||
LogRocket.identify(payload.email, {
|
||||
email: payload.email,
|
||||
});
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.checkForUpdates);
|
||||
|
||||
ipcRenderer.send(ipcTypes.default.app.toMain.track, {
|
||||
event: "SIGN_IN_SUCCESS",
|
||||
|
||||
Reference in New Issue
Block a user