diff --git a/comms.js b/comms.js index f59a45962..74c150c6c 100644 --- a/comms.js +++ b/comms.js @@ -2,7 +2,15 @@ Puck.debug=3; // FIXME: use UART lib so that we handle errors properly var Comms = { -uploadApp : app => { +reset : () => { + return new Promise((resolve,reject) => { + Puck.write("\x03reset();\n", (result) => { + if (result===null) return reject(""); + setTimeout(resolve,500); + }); + }); +}, +uploadApp : (app,skipReset) => { return AppInfo.getFiles(app, httpGet).then(fileContents => { return new Promise((resolve,reject) => { var appJSONFile = fileContents.find(f=>f.name=="+"+app.id); @@ -16,16 +24,18 @@ uploadApp : app => { } fileContents = fileContents.map(storageFile=>storageFile.cmd).join("\n")+"\n"; console.log("uploadApp",fileContents); - // reset to ensure we have enough memory to upload what we need to - Puck.write("\x03reset();\n", (result) => { - if (result===null) return reject(""); - setTimeout(() => { // wait for reset - Puck.write("\x10E.showMessage('Uploading...')\n"+fileContents+"\x10E.showMessage('Hold BTN3\\nto reload')\n",(result) => { - if (result===null) return reject(""); - resolve(appJSON); - }); - },500); - }); + function doUpload() { + Puck.write(`\x10E.showMessage('Uploading\\n${app.id}...')\n${fileContents}\x10E.showMessage('Hold BTN3\\nto reload')\n`,(result) => { + if (result===null) return reject(""); + resolve(appJSON); + }); + } + if (skipReset) { + doUpload(); + } else { + // reset to ensure we have enough memory to upload what we need to + Comms.reset().then(doUpload) + } }); }); }, @@ -46,24 +56,21 @@ removeApp : app => { // expects an app structure return `\x10require("Storage").erase(${toJS(file.name)});\n`; }).join(""); console.log("removeApp", cmds); - return new Promise((resolve,reject) => { - Puck.write("\x03"+cmds+"\x10E.showMessage('Hold BTN3\\nto reload')\n",(result) => { + return Comms.reset().then(new Promise((resolve,reject) => { + Puck.write(`\x03\x10E.showMessage('Erasing\\n${app.id}...')${cmds}\x10E.showMessage('Hold BTN3\\nto reload')\n`,(result) => { if (result===null) return reject(""); resolve(); }); - }); + })); }, removeAllApps : () => { - return new Promise((resolve,reject) => { - // Use eval here so we wait for it to finish - Puck.eval('require("Storage").eraseAll()||true', (result,err) => { + return Comms.reset().then(new Promise((resolve,reject) => { + // Use write with newline here so we wait for it to finish + Puck.write('\x10E.showMessage("Erasing...");require("Storage").eraseAll();Bluetooth.println("OK")\n', (result,err) => { if (result===null) return reject(err || ""); - Puck.write('\x03\x10reset()\n',(result) => { - if (result===null) return reject(""); - resolve(); - }); - }); - }); + resolve(); + }, true /* wait for newline */); + })); }, setTime : () => { return new Promise((resolve,reject) => { diff --git a/index.js b/index.js index 12391ee6a..572009920 100644 --- a/index.js +++ b/index.js @@ -29,17 +29,31 @@ function showToast(message, type) { msgDiv.remove(); }, 5000); } -var progressToast; -Puck.writeProgress = function(charsSent, charsTotal) { - if (charsSent===undefined) { - if (progressToast) progressToast.remove(); - progressToast = undefined; - return; - } - var percent = Math.round(charsSent*100/charsTotal); +var progressToast; // the DOM element +var progressSticky; // showProgress(,,"sticky") don't remove until hideProgress("sticky") +var progressInterval; // the interval used if showProgress(..., "animate") +var progressPercent; // the current progress percentage +function showProgress(text, percent, sticky) { + if (sticky=="sticky") + progressSticky = true; if (!progressToast) { + if (progressInterval) { + clearInterval(progressInterval); + progressInterval = undefined; + } + if (percent == "animate") { + progressInterval = setInterval(function() { + progressPercent += 2; + if (progressPercent>100) progressPercent=0; + showProgress(undefined, progressPercent); + }, 100); + percent = 0; + } + progressPercent = percent; + var toastcontainer = document.getElementById("toastcontainer"); progressToast = htmlElement(`
+ ${text ? `
${text}
`:``}
@@ -51,6 +65,26 @@ Puck.writeProgress = function(charsSent, charsTotal) { pt.style.width = percent+"%"; } } +function hideProgress(sticky) { + if (progressSticky && sticky!="sticky") + return; + progressSticky = false; + if (progressInterval) { + clearInterval(progressInterval); + progressInterval = undefined; + } + if (progressToast) progressToast.remove(); + progressToast = undefined; +} + +Puck.writeProgress = function(charsSent, charsTotal) { + if (charsSent===undefined) { + hideProgress(); + return; + } + var percent = Math.round(charsSent*100/charsTotal); + showProgress(undefined, percent); +} function showPrompt(title, text, buttons) { if (!buttons) buttons={yes:1,no:1}; return new Promise((resolve,reject) => { @@ -78,7 +112,7 @@ function showPrompt(title, text, buttons) { document.body.append(modal); modal.querySelector("a[href='#close']").addEventListener("click",event => { event.preventDefault(); - reject(); + reject("User cancelled"); modal.remove(); }); htmlToArray(modal.getElementsByTagName("button")).forEach(button => { @@ -86,7 +120,7 @@ function showPrompt(title, text, buttons) { event.preventDefault(); var isYes = event.target.getAttribute("isyes")=="1"; if (isYes) resolve(); - else reject(); + else reject("User cancelled"); modal.remove(); }); }); @@ -132,7 +166,14 @@ function handleCustomApp(app) { var app = event.data; console.log("Received custom app", app); modal.remove(); - Comms.uploadApp(app).then(resolve,reject); + showProgress(`Uploading ${app.name}`,undefined,"sticky"); + Comms.uploadApp(app).then(()=>{ + hideProgress("sticky"); + resolve(); + }).catch(e => { + hideProgress("sticky"); + reject(e); + }); }, false); }); } @@ -270,7 +311,9 @@ function refreshLibrary() { // upload icon.classList.remove("icon-upload"); icon.classList.add("loading"); + showProgress(`Uploading ${app.name}`,undefined,"sticky"); Comms.uploadApp(app).then((appJSON) => { + hideProgress("sticky"); if (appJSON) appsInstalled.push(appJSON); showToast(app.name+" Uploaded!", "success"); icon.classList.remove("loading"); @@ -278,6 +321,7 @@ function refreshLibrary() { refreshMyApps(); refreshLibrary(); }).catch(err => { + hideProgress("sticky"); showToast("Upload failed, "+err, "error"); icon.classList.remove("loading"); icon.classList.add("icon-upload"); @@ -334,16 +378,19 @@ function removeApp(app) { } function updateApp(app) { + showProgress(`Upgrading ${app.name}`,undefined,"sticky"); Comms.removeApp(app).then(()=>{ showToast(app.name+" removed successfully. Updating...",); appsInstalled = appsInstalled.filter(a=>a.id!=app.id); return Comms.uploadApp(app); }).then((appJSON) => { + hideProgress("sticky"); if (appJSON) appsInstalled.push(appJSON); showToast(app.name+" Updated!", "success"); refreshMyApps(); refreshLibrary(); }, err=>{ + hideProgress("sticky"); showToast(app.name+" update failed, "+err,"error"); }); } @@ -413,14 +460,20 @@ return `
function getInstalledApps() { showLoadingIndicator("myappscontainer"); + showProgress(`Getting app list...`,undefined,"sticky"); // Get apps and files return Comms.getInstalledApps() .then(appJSON => { + hideProgress("sticky"); appsInstalled = appJSON; refreshMyApps(); refreshLibrary(); }) - .then(() => handleConnectionChange(true)); + .then(() => handleConnectionChange(true)) + .catch(err=>{ + hideProgress("sticky"); + return Promise.reject(); + }); } var connectMyDeviceBtn = document.getElementById("connectmydevice"); @@ -475,12 +528,15 @@ document.getElementById("settime").addEventListener("click",event=>{ }); document.getElementById("removeall").addEventListener("click",event=>{ showPrompt("Remove All","Really remove all apps?").then(() => { + showProgress("Removing all apps","animate", "sticky"); return Comms.removeAllApps(); }).then(()=>{ + hideProgress("sticky"); appsInstalled = []; showToast("All apps removed","success"); return getInstalledApps(); }).catch(err=>{ + hideProgress("sticky"); showToast("App removal failed, "+err,"error"); }); }); @@ -495,18 +551,25 @@ document.getElementById("installdefault").addEventListener("click",event=>{ appCount = defaultApps.length; return showPrompt("Install Defaults","Remove everything and install default apps?"); }).then(() => { + showProgress("Removing all apps","animate", "sticky"); return Comms.removeAllApps(); }).then(()=>{ + hideProgress("sticky"); appsInstalled = []; showToast(`Existing apps removed. Installing ${appCount} apps...`); - return new Promise(resolve => { + return new Promise((resolve,reject) => { function upload() { var app = defaultApps.shift(); if (app===undefined) return resolve(); + showProgress(`${app.name} (${appCount-defaultApps.length}/${appCount})`,undefined,"sticky"); Comms.uploadApp(app).then((appJSON) => { + hideProgress("sticky"); if (appJSON) appsInstalled.push(appJSON); showToast(`(${appCount-defaultApps.length}/${appCount}) ${app.name} Uploaded`); upload(); + }).catch(function() { + hideProgress("sticky"); + reject() }); } upload();