import { Button, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, IconButton, LinearProgress, Link, Theme, Tooltip, Typography } from "@mui/material"; // import { createStyles, makeStyles, Theme } from "@material-ui/core/styles"; import UpdateIcon from "@mui/icons-material/Update"; import { useDeviceAPI, useFirmwareAPI, usePrevious, useSnackbar } from "hooks"; import { Device, Firmware, latestFirmwareVersion, Upgrade } from "models"; import moment from "moment"; import { getContextKeys, getContextTypes } from "pbHelpers/Context"; import { useGlobalState } from "providers"; import React, { useCallback, useEffect, useState } from "react"; import { or } from "utils/types"; import { makeStyles } from "@mui/styles"; const useStyles = makeStyles((theme: Theme) => { return ({ dialogContent: { marginTop: theme.spacing(2) }, buttonProgress: { color: theme.palette.primary.main, position: "absolute", zIndex: 1 }, failed: { color: theme.palette.error.main } }) }); interface Props { device: Device; } export default function UpgradeDevice(props: Props) { const classes = useStyles(); const deviceAPI = useDeviceAPI(); const { success, error } = useSnackbar(); const firmwareAPI = useFirmwareAPI(); // const [{ firmware }] = useGlobalState(); const { device } = props; const prevDevice = usePrevious(device); const [open, setOpen] = useState(false); const prevOpen = usePrevious(open); const [upgrade, setUpgrade] = useState(Upgrade.create()); const [gettingStatus, setGettingStatus] = useState(0); const [firmware, setFirmware] = useState>(new Map()) const [firmwareLoading, setFirmwareLoading] = useState(false) const prevFirmware = usePrevious(firmware); const progress = useCallback(() => { return upgrade.status.progress; }, [upgrade.status.progress]); const currentFirmware = () => { return device.status.firmwareVersion; }; const latestFirmware = () => { return latestFirmwareVersion( firmware, device.settings.platform, device.settings.upgradeChannel ); }; useEffect(() => { if (firmwareLoading) return // if (firmware.size > 0) return setFirmwareLoading(true) firmwareAPI.getAllLatestFirmware().then(resp => { let versions: Map = new Map(); if (resp && resp.data && resp.data.firmware) { resp.data.firmware.forEach((raw: any) => { let firmware = Firmware.any(raw); let key = firmware.settings.platform + ":" + firmware.settings.channel; versions.set(key, firmware); }); } // dispatch({ key: "firmware", value: versions }); setFirmware(versions) }).finally(() => { setFirmwareLoading(false) }) }, []) const upgradeVersion = () => { return upgrade.settings.version; }; const available = () => { if (firmware.size < 1) return false return currentFirmware() !== latestFirmware(); }; const upgrading = useCallback(() => { return progress() === "upgrading"; }, [progress]); const completed = () => { return progress() === "completed" && currentFirmware() === latestFirmware(); }; const failed = () => { return progress() === "failed" && upgradeVersion() === latestFirmware(); }; const percent = () => { return Math.round((upgrade.status.offset / upgrade.status.size) * 100); }; const tooltip = () => { return upgrading() ? "Upgrade in progress: " + percent() + "%" : failed() ? "Upgrade failed" : "Upgrade Device"; }; const upgradeTimingDesc = () => { let desc = "requested " + moment(or(upgrade.status.started, 0)).fromNow(); if (completed()) { desc = "completed " + moment(or(upgrade.status.stopped, 0)).fromNow(); } else if (failed()) { desc = "failed " + moment(or(upgrade.status.changed, 0)).fromNow(); } return desc; }; const mode = () => { let mode = "prompt"; if (upgrading() || failed() || completed()) { mode = "progress"; } return mode; }; const getStatus = useCallback(() => { if (gettingStatus === device.id() && !upgrading()) { return; } setGettingStatus(device.id()); deviceAPI .getUpgradeStatus(device.id(), getContextKeys(), getContextTypes()) .then((res: any) => { if (res && res.data && res.data.upgrade) { setUpgrade(Upgrade.any(res.data.upgrade)); } }) .catch((err: any) => error(err)); }, [device, deviceAPI, error, gettingStatus, upgrading]); useEffect(() => { const deviceIdUnchanged = !prevDevice || prevDevice.id() === device.id(); if ( (open && !prevOpen) || firmware !== prevFirmware || (device !== prevDevice && deviceIdUnchanged) ) { getStatus(); } }, [open, prevOpen, getStatus, device, prevDevice, firmware, prevFirmware]); useEffect(() => { function getStatusAgain() { if (upgrading()) { getStatus(); } } const interval = setInterval(() => getStatusAgain(), 5000); return () => { clearInterval(interval); }; }, [open, getStatus, upgrading]); const close = () => { setOpen(false); }; const handleUpgrade = () => { firmwareAPI .upgradeFirmware(device.id()) .then(() => { success(device.name() + " will upgrade to " + latestFirmware() + " next time it checks in"); getStatus(); }) .catch((err: any) => { console.error(err); error(err); }); }; const restart = () => { firmwareAPI .upgradeFirmware(device.id()) .then(() => { success( "Upgrade successfully restarted, " + device.name() + " will upgrade next time it checks in" ); getStatus(); }) .catch((err: any) => { console.error(err); error(err); }); }; const cancel = () => { firmwareAPI .cancelUpgrade(device.id()) .then(() => { success("Upgrade cancelled"); }) .catch((err: any) => { console.error(err); error(err); }); }; const button = () => { return ( setOpen(true)} aria-label={tooltip()}> {upgrading() && ( )} ); }; return ( {(available() || upgrading() || failed()) && button()} {mode() === "prompt" && ( Upgrade Device {device.name()} Upgrading from {currentFirmware()} to {latestFirmware()}

It is recommended that the device be plugged in during the upgrade as it will take several minutes to complete

While the upgrade is in progress you can return to this dialog to view its status
)} {mode() === "progress" && ( Upgrading Device {device.name()} Upgrade to {latestFirmware()} {upgradeTimingDesc()}
{or(upgrade.status.offset, 0)} / {upgrade.status.size} bytes ({percent()}%) {failed() && (
Uh oh – something went wrong. You can try to{" "} restart {" "} the upgrade, if the problem persists we'll be alerted and follow up as soon as possible.
)}
{upgrading() && ( )} {failed() && ( )}
)}
); }