195 lines
5.9 KiB
TypeScript

import { mdiFolderUploadOutline } from "@mdi/js";
import Icon from "@mdi/react";
import DeleteIcon from "@mui/icons-material/Delete";
import DownloadIcon from "@mui/icons-material/Download";
import FileUploadIcon from "@mui/icons-material/FileUpload";
import RefreshIcon from "@mui/icons-material/Refresh";
import {
IconButton,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Tooltip,
} from "@mui/material";
import { filesize } from "filesize";
import React from "react";
import { OTAAPI, OTAUpdate } from "../api/OTAApi";
import { DeployOTAUpdateDialogProvider } from "../dialogs/DeployOTAUpdateDialogProvider";
import { UploadUpdateDialog } from "../dialogs/UploadUpdateDialog";
import { useAlert } from "../hooks/context_providers/AlertDialogProvider";
import { useConfirm } from "../hooks/context_providers/ConfirmDialogProvider";
import { useLoadingMessage } from "../hooks/context_providers/LoadingMessageProvider";
import { useSnackbar } from "../hooks/context_providers/SnackbarProvider";
import { AsyncWidget } from "../widgets/AsyncWidget";
import { RouterLink } from "../widgets/RouterLink";
import { SolarEnergyRouteContainer } from "../widgets/SolarEnergyRouteContainer";
export function OTARoute(): React.ReactElement {
const [list, setList] = React.useState<string[] | undefined>();
const load = async () => {
setList(await OTAAPI.SupportedPlatforms());
};
return (
<AsyncWidget
loadKey={1}
ready={!!list}
load={load}
errMsg="Failed to load OTA screen!"
build={() => <_OTARoute platforms={list!} />}
/>
);
}
function _OTARoute(p: { platforms: Array<string> }): React.ReactElement {
const key = React.useRef(1);
const [showUploadDialog, setShowUploadDialog] = React.useState(false);
const [list, setList] = React.useState<undefined | OTAUpdate[]>();
const load = async () => {
const list = await OTAAPI.ListOTAUpdates();
list.sort((a, b) =>
`${a.platform}#${a.version}`.localeCompare(`${b.platform}#${b.version}`)
);
list.reverse();
setList(list);
};
const reload = async () => {
key.current += 1;
setList(undefined);
};
return (
<SolarEnergyRouteContainer
label="OTA"
actions={
<span>
<Tooltip title="Refresh the list of updates">
<IconButton onClick={reload}>
<RefreshIcon />
</IconButton>
</Tooltip>
<Tooltip title="Upload a new update">
<IconButton onClick={() => setShowUploadDialog(true)}>
<FileUploadIcon />
</IconButton>
</Tooltip>
</span>
}
>
{showUploadDialog && (
<UploadUpdateDialog
platforms={p.platforms}
onClose={() => setShowUploadDialog(false)}
onCreated={() => {
setShowUploadDialog(false);
reload();
}}
/>
)}
<AsyncWidget
loadKey={key.current}
ready={!!list}
errMsg="Failed to load the list of OTA updates!"
load={load}
build={() => <_OTAList list={list!} onReload={reload} />}
/>
</SolarEnergyRouteContainer>
);
}
function _OTAList(p: {
list: OTAUpdate[];
onReload: () => void;
}): React.ReactElement {
const alert = useAlert();
const confirm = useConfirm();
const loadingMessage = useLoadingMessage();
const snackbar = useSnackbar();
const [deployUpdate, setDeployUpdate] = React.useState<
OTAUpdate | undefined
>();
const deleteUpdate = async (update: OTAUpdate) => {
if (
!(await confirm(
`Do you really want to delete the update for platform ${update.platform} version ${update.version}?`
))
)
return;
try {
loadingMessage.show("Deleting update...");
await OTAAPI.DeleteUpdate(update);
snackbar("The update was successfully deleted!");
p.onReload();
} catch (e) {
console.error("Failed to delete update!", e);
alert(`Failed to delete the update! ${e}`);
} finally {
loadingMessage.hide();
}
};
return (
<>
{deployUpdate && (
<DeployOTAUpdateDialogProvider
update={deployUpdate!}
onClose={() => setDeployUpdate(undefined)}
/>
)}
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell align="center">Platform</TableCell>
<TableCell align="center">Version</TableCell>
<TableCell align="center">File size</TableCell>
<TableCell align="center"></TableCell>
</TableRow>
</TableHead>
<TableBody>
{p.list.map((row, num) => (
<TableRow hover key={num}>
<TableCell align="center">{row.platform}</TableCell>
<TableCell align="center">{row.version}</TableCell>
<TableCell align="center">{filesize(row.file_size)}</TableCell>
<TableCell align="center">
<Tooltip title="Deploy the update to devices">
<IconButton onClick={() => setDeployUpdate(row)}>
<Icon path={mdiFolderUploadOutline} size={1} />
</IconButton>
</Tooltip>
<Tooltip title="Download a copy of the firmware">
<RouterLink to={OTAAPI.DownloadOTAUpdateURL(row)}>
<IconButton>
<DownloadIcon />
</IconButton>
</RouterLink>
</Tooltip>
<Tooltip title="Delete firmware update">
<IconButton onClick={() => deleteUpdate(row)}>
<DeleteIcon />
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</>
);
}