import React, { useContext, useState } from "react"; import api from "../../services/updates"; const UpdatesContext = React.createContext(); export const UpdatesProvider = ({ children }) => { const [busy, setBusy] = useState(false); const [packages, setPackages] = useState([]); const [carUpdates, setCarUpdates] = useState([]); const [totalPackages, setTotalPackages] = useState(0); const [totalCarUpdates, setTotalCarUpdates] = useState(0); const [delayCount, setDelayCount] = useState(0); let progressTimer = 0; const getPackages = async (search, token) => { let result; try { setBusy(true); result = await api.getPackages(search, token); if (result.error) throw new Error(`Get packages error. ${result.message}`); setPackages(result.data); if (search && search.offset === 0 && result.total) { setTotalPackages(result.total); } } finally { setBusy(false); } return result; }; const updatePackage = async (data, token) => { let result = null; try { setBusy(true); validateUpdatePackage(data); result = await api.updatePackage(data, token); if (result.error) throw new Error(`Update package error. ${result.message}`); } finally { setBusy(false); } return result; }; const createCarUpdates = async (data, token) => { let result; try { setBusy(true); validateCreateCarUpdates(data); result = await api.createCarUpdates(data, token); if (result.error) throw new Error(`Create 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 packages error. ${result.message}`); 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 applyProgressStatus = (item, status) => { if (status.msg === "DONE") { delete item.progress; item.status = "downloaded"; } else if (status.msg === "downloading" && status.total > 0) { let progress = Math.floor((100 * status.bytes) / status.total); if (progress > 99) progress = 0; item.progress = progress; item.status = `downloading ${progress}%`; } else if (status.error > 0) { item.status = "download error"; } else { item.status = "downloading"; } }; const applyProgressStatuses = (statuses) => { let items = JSON.parse(JSON.stringify(carUpdates)); statuses.forEach((status) => { let item = items.find((item) => status.id === item.id); if (!item || status.id === 0) return; applyProgressStatus(item, 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 !== "downloaded") 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 (carUpdates[i].status.indexOf("downloading") > -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; }; return ( {children} ); }; export const useUpdatesContext = () => useContext(UpdatesContext); const validateUpdatePackage = (data) => { if (data === null) { throw new Error("No update data"); } if (!data.package_name) { throw new Error("Package name required"); } if (!data.version) { throw new Error("Version required"); } }; const validateCreateCarUpdates = (data) => { if (data === null) { throw new Error("No car update data"); } if (!data.package_id || data.package_id === 0) { throw new Error("Package id required"); } if (!data.vins || data.vins.length === 0) { throw new Error("Cars are required"); } };