import React, { useContext, useState } from "react"; import api from "../../services/updatesAPI"; import { validateStatusMessage } from "../../utils/statusMessage"; const FINAL_UPDATE_STATES = ["package_install_complete"]; const CarUpdatesContext = React.createContext(); const validateDeployCarUpdates = (data) => { if (data === null) { throw new Error("No car update data"); } if (!data.manifest_id || data.manifest_id === 0) { throw new Error("Manifest id required"); } if (!data.vins || data.vins.length === 0) { throw new Error("Cars are required"); } }; export const CarUpdatesProvider = ({ children }) => { const [busy, setBusy] = useState(false); const [carUpdates, setCarUpdates] = useState([]); const [totalCarUpdates, setTotalCarUpdates] = useState(0); const [delayCount, setDelayCount] = useState(0); let progressTimer = 0; const deployCarUpdates = async (data, token) => { let result; try { setBusy(true); validateDeployCarUpdates(data); result = await api.createCarUpdates(data, token); if (result.error) throw new Error(`Deploy car updates error. ${result.message}`); } finally { setBusy(false); } return result; }; const getCarUpdates = async (search, token) => { let result; try { setBusy(true); result = await api.getCarUpdates(search, token); if (result.error) throw new Error(`Get car updates error. ${result.message}`); result.data.forEach((item) => { item.progress = 0; item.msg = item.status; applyProgressStatus(item, item); }); setCarUpdates(result.data); if (search && search.offset === 0 && result.total) { setTotalCarUpdates(result.total); } } finally { setBusy(false); } return result; }; const getVINUpdates = async (vin, token) => { let result; try { setBusy(true); result = await api.getVINUpdates(vin, token); if (result.error) throw new Error(`Get VIN updates error. ${result.message}`); } finally { setBusy(false); } return result; }; const getDownloadProgress = (status) => { if (status.package_total > 0) return Math.floor((100 * status.package_current) / status.package_total); return 0; }; const getInstallProgress = (status) => { if (status.total_files > 0) return Math.floor((100 * status.installed) / status.total_files); return 0; }; const applyProgressStatus = (item, status) => { if (validateStatusMessage(status)) { if (status.msg === "downloading") { item.progress = getDownloadProgress(status); item.status = `${item.ecu || ""} downloading ${item.progress}%`.trim(); return; } else if (status.msg === "package_download_complete") { item.progress = 100; item.status = "download complete"; return; } else if (status.msg === "installing") { item.progress = getInstallProgress(status); item.status = `${item.ecu || ""} installing ${item.progress}%`.trim(); return; } else if (status.msg === "package_install_complete") { item.progress = 100; item.status = "install complete"; return; } } delete item.progress; item.status = status.msg; }; const applyProgressStatuses = (statuses) => { let items = JSON.parse(JSON.stringify(carUpdates)); statuses.forEach((status) => { let x = items.find((item) => status.car_update_id === item.id); if (!x || status.car_update_id === 0) return; applyProgressStatus(x, status); }); setCarUpdates(items); }; const updateStatusProgress = async (token) => { stopMonitor(); if (!token || carUpdates.length === 0) return; try { setBusy(true); const carupdateids = carUpdates.reduce((accum, update) => { if (update.status !== "installed") accum.push(update.id); return accum; }, []); if (carupdateids.length === 0) return; const result = await api.getCarUpdateProgress( carupdateids.join(","), token ); if (result.error) throw new Error(`Get update progress error. ${result.message}`); applyProgressStatuses(result.statuses); } catch (e) { } finally { setBusy(false); } }; const getDelay = () => { if (delayCount < 3) { setDelayCount(delayCount + 1); return 1000; } for (let i = 0, len = carUpdates.length; i < len; i++) { if (FINAL_UPDATE_STATES.indexOf(carUpdates[i].status) === -1) return 1000; } return 10000; }; const startMonitor = async (token) => { const delay = getDelay(); stopMonitor(); progressTimer = setTimeout(() => { updateStatusProgress(token); }, delay); }; const stopMonitor = async () => { if (progressTimer === 0) return; clearTimeout(progressTimer); progressTimer = 0; }; const getLog = async (query, token) => { let result; try { setBusy(true); result = await api.getCarUpdateLog(query, token); if (result.error) throw new Error(`Get car update log error. ${result.message}`); } finally { setBusy(false); } return result; }; return ( {children} ); }; export const useCarUpdatesContext = () => useContext(CarUpdatesContext);