Fix progress bar - now goes smoothly up over the course of the app upload.
Also tidy it up significantly and reduce duplicationmaster
parent
86ab3706ea
commit
9b918055da
|
|
@ -129,6 +129,7 @@
|
||||||
|
|
||||||
<script src="https://www.puck-js.com/puck.js"></script>
|
<script src="https://www.puck-js.com/puck.js"></script>
|
||||||
<script src="js/utils.js"></script>
|
<script src="js/utils.js"></script>
|
||||||
|
<script src="js/ui.js"></script>
|
||||||
<script src="js/comms.js"></script>
|
<script src="js/comms.js"></script>
|
||||||
<script src="js/appinfo.js"></script>
|
<script src="js/appinfo.js"></script>
|
||||||
<script src="js/index.js"></script>
|
<script src="js/index.js"></script>
|
||||||
|
|
|
||||||
41
js/comms.js
41
js/comms.js
|
|
@ -9,14 +9,19 @@ reset : (opt) => new Promise((resolve,reject) => {
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
uploadApp : (app,skipReset) => {
|
uploadApp : (app,skipReset) => {
|
||||||
|
Progress.show({title:`Uploading ${app.name}`,sticky:true});
|
||||||
return AppInfo.getFiles(app, httpGet).then(fileContents => {
|
return AppInfo.getFiles(app, httpGet).then(fileContents => {
|
||||||
return new Promise((resolve,reject) => {
|
return new Promise((resolve,reject) => {
|
||||||
console.log("uploadApp",fileContents.map(f=>f.name).join(", "));
|
console.log("uploadApp",fileContents.map(f=>f.name).join(", "));
|
||||||
|
var maxBytes = fileContents.reduce((b,f)=>b+f.content.length, 0)||1;
|
||||||
|
var currentBytes = 0;
|
||||||
|
|
||||||
// Upload each file one at a time
|
// Upload each file one at a time
|
||||||
function doUploadFiles() {
|
function doUploadFiles() {
|
||||||
// No files left - print 'reboot' message
|
// No files left - print 'reboot' message
|
||||||
if (fileContents.length==0) {
|
if (fileContents.length==0) {
|
||||||
Puck.write(`\x10E.showMessage('Hold BTN3\\nto reload')\n`,(result) => {
|
Puck.write(`\x10E.showMessage('Hold BTN3\\nto reload')\n`,(result) => {
|
||||||
|
Progress.hide({sticky:true});
|
||||||
if (result===null) return reject("");
|
if (result===null) return reject("");
|
||||||
resolve(app);
|
resolve(app);
|
||||||
});
|
});
|
||||||
|
|
@ -24,17 +29,27 @@ uploadApp : (app,skipReset) => {
|
||||||
}
|
}
|
||||||
var f = fileContents.shift();
|
var f = fileContents.shift();
|
||||||
console.log(`Upload ${f.name} => ${JSON.stringify(f.content)}`);
|
console.log(`Upload ${f.name} => ${JSON.stringify(f.content)}`);
|
||||||
|
Progress.show({
|
||||||
|
min:currentBytes / maxBytes,
|
||||||
|
max:(currentBytes+f.content.length) / maxBytes});
|
||||||
|
currentBytes += f.content.length;
|
||||||
// Chould check CRC here if needed instead of returning 'OK'...
|
// Chould check CRC here if needed instead of returning 'OK'...
|
||||||
// E.CRC32(require("Storage").read(${JSON.stringify(app.name)}))
|
// E.CRC32(require("Storage").read(${JSON.stringify(app.name)}))
|
||||||
Puck.write(`\x10${f.cmd};Bluetooth.println("OK")\n`,(result) => {
|
Puck.write(`\x10${f.cmd};Bluetooth.println("OK")\n`,(result) => {
|
||||||
if (!result || result.trim()!="OK") return reject("Unexpected response "+(result||""));
|
if (!result || result.trim()!="OK") {
|
||||||
|
Progress.hide({sticky:true});
|
||||||
|
return reject("Unexpected response "+(result||""));
|
||||||
|
}
|
||||||
doUploadFiles();
|
doUploadFiles();
|
||||||
}, true); // wait for a newline
|
}, true); // wait for a newline
|
||||||
}
|
}
|
||||||
// Start the upload
|
// Start the upload
|
||||||
function doUpload() {
|
function doUpload() {
|
||||||
Puck.write(`\x10E.showMessage('Uploading\\n${app.id}...')\n`,(result) => {
|
Puck.write(`\x10E.showMessage('Uploading\\n${app.id}...')\n`,(result) => {
|
||||||
if (result===null) return reject("");
|
if (result===null) {
|
||||||
|
Progress.hide({sticky:true});
|
||||||
|
return reject("");
|
||||||
|
}
|
||||||
doUploadFiles();
|
doUploadFiles();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -48,10 +63,15 @@ uploadApp : (app,skipReset) => {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getInstalledApps : () => {
|
getInstalledApps : () => {
|
||||||
|
Progress.show({title:`Getting app list...`,sticky:true});
|
||||||
return new Promise((resolve,reject) => {
|
return new Promise((resolve,reject) => {
|
||||||
Puck.write("\x03",(result) => {
|
Puck.write("\x03",(result) => {
|
||||||
if (result===null) return reject("");
|
if (result===null) {
|
||||||
|
Progress.hide({sticky:true});
|
||||||
|
return reject("");
|
||||||
|
}
|
||||||
Puck.eval('require("Storage").list(/\.info$/).map(f=>{var j=require("Storage").readJSON(f,1)||{};j.id=f.slice(0,-5);return j})', (appList,err) => {
|
Puck.eval('require("Storage").list(/\.info$/).map(f=>{var j=require("Storage").readJSON(f,1)||{};j.id=f.slice(0,-5);return j})', (appList,err) => {
|
||||||
|
Progress.hide({sticky:true});
|
||||||
if (appList===null) return reject(err || "");
|
if (appList===null) return reject(err || "");
|
||||||
console.log("getInstalledApps", appList);
|
console.log("getInstalledApps", appList);
|
||||||
resolve(appList);
|
resolve(appList);
|
||||||
|
|
@ -60,6 +80,7 @@ getInstalledApps : () => {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
removeApp : app => { // expects an app structure
|
removeApp : app => { // expects an app structure
|
||||||
|
Progress.show({title:`Removing ${app.name}`,sticky:true});
|
||||||
var storage = [{name:app.id+".info"}].concat(app.storage);
|
var storage = [{name:app.id+".info"}].concat(app.storage);
|
||||||
var cmds = storage.map(file=>{
|
var cmds = storage.map(file=>{
|
||||||
return `\x10require("Storage").erase(${toJS(file.name)});\n`;
|
return `\x10require("Storage").erase(${toJS(file.name)});\n`;
|
||||||
|
|
@ -67,15 +88,21 @@ removeApp : app => { // expects an app structure
|
||||||
console.log("removeApp", cmds);
|
console.log("removeApp", cmds);
|
||||||
return Comms.reset().then(new Promise((resolve,reject) => {
|
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) => {
|
Puck.write(`\x03\x10E.showMessage('Erasing\\n${app.id}...')${cmds}\x10E.showMessage('Hold BTN3\\nto reload')\n`,(result) => {
|
||||||
|
Progress.hide({sticky:true});
|
||||||
if (result===null) return reject("");
|
if (result===null) return reject("");
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
}));
|
})).catch(function(reason) {
|
||||||
|
Progress.hide({sticky:true});
|
||||||
|
return Promise.reject(reason);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
removeAllApps : () => {
|
removeAllApps : () => {
|
||||||
|
Progress.show({title:"Removing all apps",progess:"animate",sticky:true});
|
||||||
return new Promise((resolve,reject) => {
|
return new Promise((resolve,reject) => {
|
||||||
// Use write with newline here so we wait for it to finish
|
// Use write with newline here so we wait for it to finish
|
||||||
Puck.write('\x10E.showMessage("Erasing...");require("Storage").eraseAll();Bluetooth.println("OK");reset()\n', (result,err) => {
|
Puck.write('\x10E.showMessage("Erasing...");require("Storage").eraseAll();Bluetooth.println("OK");reset()\n', (result,err) => {
|
||||||
|
Progress.hide({sticky:true});
|
||||||
if (!result || result.trim()!="OK") return reject(err || "");
|
if (!result || result.trim()!="OK") return reject(err || "");
|
||||||
resolve();
|
resolve();
|
||||||
}, true /* wait for newline */);
|
}, true /* wait for newline */);
|
||||||
|
|
@ -171,10 +198,10 @@ readStorageFile : (filename) => { // StorageFiles are different to normal storag
|
||||||
fileContent = fileContent.substr(newLineIdx+1);
|
fileContent = fileContent.substr(newLineIdx+1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
showProgress(undefined,100*fileContent.length / (fileSize||1000000));
|
Progress.show({percent:100*fileContent.length / (fileSize||1000000)});
|
||||||
}
|
}
|
||||||
if (finished) {
|
if (finished) {
|
||||||
hideProgress();
|
Progress.hide();
|
||||||
connection.received = "";
|
connection.received = "";
|
||||||
connection.cb = undefined;
|
connection.cb = undefined;
|
||||||
resolve(fileContent);
|
resolve(fileContent);
|
||||||
|
|
@ -188,7 +215,7 @@ readStorageFile : (filename) => { // StorageFiles are different to normal storag
|
||||||
while (l!==undefined) { Bluetooth.print(l); l = f.readLine(); }
|
while (l!==undefined) { Bluetooth.print(l); l = f.readLine(); }
|
||||||
Bluetooth.print("\xFF");
|
Bluetooth.print("\xFF");
|
||||||
})()\n`,() => {
|
})()\n`,() => {
|
||||||
showProgress(`Reading ${JSON.stringify(filename)}`,0);
|
Progress.show({title:`Reading ${JSON.stringify(filename)}`,percent:0});
|
||||||
console.log(`StorageFile read started...`);
|
console.log(`StorageFile read started...`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
144
js/index.js
144
js/index.js
|
|
@ -14,119 +14,7 @@ httpGet("apps.json").then(apps=>{
|
||||||
refreshFilter();
|
refreshFilter();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Status
|
|
||||||
// =========================================== Top Navigation
|
// =========================================== Top Navigation
|
||||||
function showToast(message, type) {
|
|
||||||
// toast-primary, toast-success, toast-warning or toast-error
|
|
||||||
var style = "toast-primary";
|
|
||||||
if (type=="success") style = "toast-success";
|
|
||||||
else if (type=="error") style = "toast-error";
|
|
||||||
else if (type!==undefined) console.log("showToast: unknown toast "+type);
|
|
||||||
var toastcontainer = document.getElementById("toastcontainer");
|
|
||||||
var msgDiv = htmlElement(`<div class="toast ${style}"></div>`);
|
|
||||||
msgDiv.innerHTML = message;
|
|
||||||
toastcontainer.append(msgDiv);
|
|
||||||
setTimeout(function() {
|
|
||||||
msgDiv.remove();
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
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(`<div class="toast">
|
|
||||||
${text ? `<div>${text}</div>`:``}
|
|
||||||
<div class="bar bar-sm">
|
|
||||||
<div class="bar-item" id="progressToast" role="progressbar" style="width:${percent}%;" aria-valuenow="${percent}" aria-valuemin="0" aria-valuemax="100"></div>
|
|
||||||
</div>
|
|
||||||
</div>`);
|
|
||||||
toastcontainer.append(progressToast);
|
|
||||||
} else {
|
|
||||||
var pt=document.getElementById("progressToast");
|
|
||||||
pt.setAttribute("aria-valuenow",percent);
|
|
||||||
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) => {
|
|
||||||
var modal = htmlElement(`<div class="modal active">
|
|
||||||
<!--<a href="#close" class="modal-overlay" aria-label="Close"></a>-->
|
|
||||||
<div class="modal-container">
|
|
||||||
<div class="modal-header">
|
|
||||||
<a href="#close" class="btn btn-clear float-right" aria-label="Close"></a>
|
|
||||||
<div class="modal-title h5">${escapeHtml(title)}</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="content">
|
|
||||||
${escapeHtml(text).replace(/\n/g,'<br/>')}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<div class="modal-footer">
|
|
||||||
${buttons.yes?'<button class="btn btn-primary" isyes="1">Yes</button>':''}
|
|
||||||
${buttons.no?'<button class="btn" isyes="0">No</button>':''}
|
|
||||||
${buttons.ok?'<button class="btn" isyes="1">Ok</button>':''}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>`);
|
|
||||||
document.body.append(modal);
|
|
||||||
modal.querySelector("a[href='#close']").addEventListener("click",event => {
|
|
||||||
event.preventDefault();
|
|
||||||
reject("User cancelled");
|
|
||||||
modal.remove();
|
|
||||||
});
|
|
||||||
htmlToArray(modal.getElementsByTagName("button")).forEach(button => {
|
|
||||||
button.addEventListener("click",event => {
|
|
||||||
event.preventDefault();
|
|
||||||
var isYes = event.target.getAttribute("isyes")=="1";
|
|
||||||
if (isYes) resolve();
|
|
||||||
else reject("User cancelled");
|
|
||||||
modal.remove();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function showChangeLog(appid) {
|
function showChangeLog(appid) {
|
||||||
var app = appNameToApp(appid);
|
var app = appNameToApp(appid);
|
||||||
function show(contents) {
|
function show(contents) {
|
||||||
|
|
@ -170,12 +58,11 @@ function handleCustomApp(appTemplate) {
|
||||||
Object.keys(appFiles).forEach(k => app[k] = appFiles[k]);
|
Object.keys(appFiles).forEach(k => app[k] = appFiles[k]);
|
||||||
console.log("Received custom app", app);
|
console.log("Received custom app", app);
|
||||||
modal.remove();
|
modal.remove();
|
||||||
showProgress(`Uploading ${app.name}`,undefined,"sticky");
|
|
||||||
Comms.uploadApp(app).then(()=>{
|
Comms.uploadApp(app).then(()=>{
|
||||||
hideProgress("sticky");
|
Progress.hide({sticky:true});
|
||||||
resolve();
|
resolve();
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
hideProgress("sticky");
|
Progress.hide({sticky:true});
|
||||||
reject(e);
|
reject(e);
|
||||||
});
|
});
|
||||||
}, false);
|
}, false);
|
||||||
|
|
@ -334,9 +221,8 @@ function refreshLibrary() {
|
||||||
// upload
|
// upload
|
||||||
icon.classList.remove("icon-upload");
|
icon.classList.remove("icon-upload");
|
||||||
icon.classList.add("loading");
|
icon.classList.add("loading");
|
||||||
showProgress(`Uploading ${app.name}`,undefined,"sticky");
|
|
||||||
Comms.uploadApp(app).then((appJSON) => {
|
Comms.uploadApp(app).then((appJSON) => {
|
||||||
hideProgress("sticky");
|
Progress.hide({sticky:true});
|
||||||
if (appJSON) appsInstalled.push(appJSON);
|
if (appJSON) appsInstalled.push(appJSON);
|
||||||
showToast(app.name+" Uploaded!", "success");
|
showToast(app.name+" Uploaded!", "success");
|
||||||
icon.classList.remove("loading");
|
icon.classList.remove("loading");
|
||||||
|
|
@ -344,7 +230,7 @@ function refreshLibrary() {
|
||||||
refreshMyApps();
|
refreshMyApps();
|
||||||
refreshLibrary();
|
refreshLibrary();
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
hideProgress("sticky");
|
Progress.hide({sticky:true});
|
||||||
showToast("Upload failed, "+err, "error");
|
showToast("Upload failed, "+err, "error");
|
||||||
icon.classList.remove("loading");
|
icon.classList.remove("loading");
|
||||||
icon.classList.add("icon-upload");
|
icon.classList.add("icon-upload");
|
||||||
|
|
@ -403,19 +289,16 @@ function customApp(app) {
|
||||||
|
|
||||||
function updateApp(app) {
|
function updateApp(app) {
|
||||||
if (app.custom) return customApp(app);
|
if (app.custom) return customApp(app);
|
||||||
showProgress(`Upgrading ${app.name}`,undefined,"sticky");
|
|
||||||
return Comms.removeApp(app).then(()=>{
|
return Comms.removeApp(app).then(()=>{
|
||||||
showToast(app.name+" removed successfully. Updating...",);
|
showToast(app.name+" removed successfully. Updating...",);
|
||||||
appsInstalled = appsInstalled.filter(a=>a.id!=app.id);
|
appsInstalled = appsInstalled.filter(a=>a.id!=app.id);
|
||||||
return Comms.uploadApp(app);
|
return Comms.uploadApp(app);
|
||||||
}).then((appJSON) => {
|
}).then((appJSON) => {
|
||||||
hideProgress("sticky");
|
|
||||||
if (appJSON) appsInstalled.push(appJSON);
|
if (appJSON) appsInstalled.push(appJSON);
|
||||||
showToast(app.name+" Updated!", "success");
|
showToast(app.name+" Updated!", "success");
|
||||||
refreshMyApps();
|
refreshMyApps();
|
||||||
refreshLibrary();
|
refreshLibrary();
|
||||||
}, err=>{
|
}, err=>{
|
||||||
hideProgress("sticky");
|
|
||||||
showToast(app.name+" update failed, "+err,"error");
|
showToast(app.name+" update failed, "+err,"error");
|
||||||
refreshMyApps();
|
refreshMyApps();
|
||||||
refreshLibrary();
|
refreshLibrary();
|
||||||
|
|
@ -488,18 +371,15 @@ return `<div class="tile column col-6 col-sm-12 col-xs-12">
|
||||||
|
|
||||||
function getInstalledApps() {
|
function getInstalledApps() {
|
||||||
showLoadingIndicator("myappscontainer");
|
showLoadingIndicator("myappscontainer");
|
||||||
showProgress(`Getting app list...`,undefined,"sticky");
|
|
||||||
// Get apps and files
|
// Get apps and files
|
||||||
return Comms.getInstalledApps()
|
return Comms.getInstalledApps()
|
||||||
.then(appJSON => {
|
.then(appJSON => {
|
||||||
hideProgress("sticky");
|
|
||||||
appsInstalled = appJSON;
|
appsInstalled = appJSON;
|
||||||
refreshMyApps();
|
refreshMyApps();
|
||||||
refreshLibrary();
|
refreshLibrary();
|
||||||
})
|
})
|
||||||
.then(() => handleConnectionChange(true))
|
.then(() => handleConnectionChange(true))
|
||||||
.catch(err=>{
|
.catch(err=>{
|
||||||
hideProgress("sticky");
|
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -555,15 +435,14 @@ document.getElementById("settime").addEventListener("click",event=>{
|
||||||
});
|
});
|
||||||
document.getElementById("removeall").addEventListener("click",event=>{
|
document.getElementById("removeall").addEventListener("click",event=>{
|
||||||
showPrompt("Remove All","Really remove all apps?").then(() => {
|
showPrompt("Remove All","Really remove all apps?").then(() => {
|
||||||
showProgress("Removing all apps","animate", "sticky");
|
|
||||||
return Comms.removeAllApps();
|
return Comms.removeAllApps();
|
||||||
}).then(()=>{
|
}).then(()=>{
|
||||||
hideProgress("sticky");
|
Progress.hide({sticky:true});
|
||||||
appsInstalled = [];
|
appsInstalled = [];
|
||||||
showToast("All apps removed","success");
|
showToast("All apps removed","success");
|
||||||
return getInstalledApps();
|
return getInstalledApps();
|
||||||
}).catch(err=>{
|
}).catch(err=>{
|
||||||
hideProgress("sticky");
|
Progress.hide({sticky:true});
|
||||||
showToast("App removal failed, "+err,"error");
|
showToast("App removal failed, "+err,"error");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -578,24 +457,23 @@ document.getElementById("installdefault").addEventListener("click",event=>{
|
||||||
appCount = defaultApps.length;
|
appCount = defaultApps.length;
|
||||||
return showPrompt("Install Defaults","Remove everything and install default apps?");
|
return showPrompt("Install Defaults","Remove everything and install default apps?");
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
showProgress("Removing all apps","animate", "sticky");
|
|
||||||
return Comms.removeAllApps();
|
return Comms.removeAllApps();
|
||||||
}).then(()=>{
|
}).then(()=>{
|
||||||
hideProgress("sticky");
|
Progress.hide({sticky:true});
|
||||||
appsInstalled = [];
|
appsInstalled = [];
|
||||||
showToast(`Existing apps removed. Installing ${appCount} apps...`);
|
showToast(`Existing apps removed. Installing ${appCount} apps...`);
|
||||||
return new Promise((resolve,reject) => {
|
return new Promise((resolve,reject) => {
|
||||||
function upload() {
|
function upload() {
|
||||||
var app = defaultApps.shift();
|
var app = defaultApps.shift();
|
||||||
if (app===undefined) return resolve();
|
if (app===undefined) return resolve();
|
||||||
showProgress(`${app.name} (${appCount-defaultApps.length}/${appCount})`,undefined,"sticky");
|
Progress.show({title:`${app.name} (${appCount-defaultApps.length}/${appCount})`,sticky:true});
|
||||||
Comms.uploadApp(app,"skip_reset").then((appJSON) => {
|
Comms.uploadApp(app,"skip_reset").then((appJSON) => {
|
||||||
hideProgress("sticky");
|
Progress.hide({sticky:true});
|
||||||
if (appJSON) appsInstalled.push(appJSON);
|
if (appJSON) appsInstalled.push(appJSON);
|
||||||
showToast(`(${appCount-defaultApps.length}/${appCount}) ${app.name} Uploaded`);
|
showToast(`(${appCount-defaultApps.length}/${appCount}) ${app.name} Uploaded`);
|
||||||
upload();
|
upload();
|
||||||
}).catch(function() {
|
}).catch(function() {
|
||||||
hideProgress("sticky");
|
Progress.hide({sticky:true});
|
||||||
reject()
|
reject()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -607,7 +485,7 @@ document.getElementById("installdefault").addEventListener("click",event=>{
|
||||||
showToast("Default apps successfully installed!","success");
|
showToast("Default apps successfully installed!","success");
|
||||||
return getInstalledApps();
|
return getInstalledApps();
|
||||||
}).catch(err=>{
|
}).catch(err=>{
|
||||||
hideProgress("sticky");
|
Progress.hide({sticky:true});
|
||||||
showToast("App Install failed, "+err,"error");
|
showToast("App Install failed, "+err,"error");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,140 @@
|
||||||
|
// General UI tools (progress bar, toast, prompt)
|
||||||
|
|
||||||
|
/// Handle progress bars
|
||||||
|
var Progress = {
|
||||||
|
domElement : null, // the DOM element
|
||||||
|
sticky : false, // Progress.show({..., sticky:true}) don't remove until Progress.hide({sticky:true})
|
||||||
|
interval : undefined, // the interval used if Progress.show({progress:"animate"})
|
||||||
|
percent : undefined, // the current progress percentage
|
||||||
|
min : 0, // scaling for percentage
|
||||||
|
max : 1, // scaling for percentage
|
||||||
|
|
||||||
|
/* Show a Progress message
|
||||||
|
Progress.show({
|
||||||
|
sticky : bool // keep showing text even when Progress.hide is called (unless Progress.hide({sticky:true}))
|
||||||
|
percent : number | "animate"
|
||||||
|
min : // minimum scale for percentage (default 0)
|
||||||
|
max : // maximum scale for percentage (default 1)
|
||||||
|
}) */
|
||||||
|
show : function(options) {
|
||||||
|
options = options||{};
|
||||||
|
var text = options.title;
|
||||||
|
if (options.sticky) Progress.sticky = true;
|
||||||
|
if (options.min!==undefined) Progress.min = options.min;
|
||||||
|
if (options.max!==undefined) Progress.max = options.max;
|
||||||
|
var percent = options.percent;
|
||||||
|
if (percent!==undefined)
|
||||||
|
percent = Progress.min*100 + (Progress.max-Progress.min)*percent;
|
||||||
|
if (!Progress.domElement) {
|
||||||
|
if (Progress.interval) {
|
||||||
|
clearInterval(Progress.interval);
|
||||||
|
Progress.interval = undefined;
|
||||||
|
}
|
||||||
|
if (percent == "animate") {
|
||||||
|
Progress.interval = setInterval(function() {
|
||||||
|
Progress.percent += 2;
|
||||||
|
if (Progress.percent>100) Progress.percent=0;
|
||||||
|
Progress.show({percent:Progress.percent});
|
||||||
|
}, 100);
|
||||||
|
percent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var toastcontainer = document.getElementById("toastcontainer");
|
||||||
|
Progress.domElement = htmlElement(`<div class="toast">
|
||||||
|
${text ? `<div>${text}</div>`:``}
|
||||||
|
<div class="bar bar-sm">
|
||||||
|
<div class="bar-item" id="Progress.domElement" role="progressbar" style="width:${percent}%;" aria-valuenow="${percent}" aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
|
</div>
|
||||||
|
</div>`);
|
||||||
|
toastcontainer.append(Progress.domElement);
|
||||||
|
} else {
|
||||||
|
var pt=document.getElementById("Progress.domElement");
|
||||||
|
pt.setAttribute("aria-valuenow",percent);
|
||||||
|
pt.style.width = percent+"%";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Progress.hide({sticky:true}) undoes Progress.show({title:"title", sticky:true})
|
||||||
|
hide : function(options) {
|
||||||
|
options = options||{};
|
||||||
|
if (Progress.sticky && !options.sticky)
|
||||||
|
return;
|
||||||
|
Progress.sticky = false;
|
||||||
|
Progress.min = 0;
|
||||||
|
Progress.max = 1;
|
||||||
|
if (Progress.interval) {
|
||||||
|
clearInterval(Progress.interval);
|
||||||
|
Progress.interval = undefined;
|
||||||
|
}
|
||||||
|
if (Progress.domElement) Progress.domElement.remove();
|
||||||
|
Progress.domElement = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Add progress handler so we get nice uploads
|
||||||
|
Puck.writeProgress = function(charsSent, charsTotal) {
|
||||||
|
if (charsSent===undefined) {
|
||||||
|
Progress.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var percent = Math.round(charsSent*100/charsTotal);
|
||||||
|
Progress.show({percent: percent});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show a 'toast' message for status
|
||||||
|
function showToast(message, type) {
|
||||||
|
// toast-primary, toast-success, toast-warning or toast-error
|
||||||
|
var style = "toast-primary";
|
||||||
|
if (type=="success") style = "toast-success";
|
||||||
|
else if (type=="error") style = "toast-error";
|
||||||
|
else if (type!==undefined) console.log("showToast: unknown toast "+type);
|
||||||
|
var toastcontainer = document.getElementById("toastcontainer");
|
||||||
|
var msgDiv = htmlElement(`<div class="toast ${style}"></div>`);
|
||||||
|
msgDiv.innerHTML = message;
|
||||||
|
toastcontainer.append(msgDiv);
|
||||||
|
setTimeout(function() {
|
||||||
|
msgDiv.remove();
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show a yes/no prompt
|
||||||
|
function showPrompt(title, text, buttons) {
|
||||||
|
if (!buttons) buttons={yes:1,no:1};
|
||||||
|
return new Promise((resolve,reject) => {
|
||||||
|
var modal = htmlElement(`<div class="modal active">
|
||||||
|
<!--<a href="#close" class="modal-overlay" aria-label="Close"></a>-->
|
||||||
|
<div class="modal-container">
|
||||||
|
<div class="modal-header">
|
||||||
|
<a href="#close" class="btn btn-clear float-right" aria-label="Close"></a>
|
||||||
|
<div class="modal-title h5">${escapeHtml(title)}</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="content">
|
||||||
|
${escapeHtml(text).replace(/\n/g,'<br/>')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<div class="modal-footer">
|
||||||
|
${buttons.yes?'<button class="btn btn-primary" isyes="1">Yes</button>':''}
|
||||||
|
${buttons.no?'<button class="btn" isyes="0">No</button>':''}
|
||||||
|
${buttons.ok?'<button class="btn" isyes="1">Ok</button>':''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`);
|
||||||
|
document.body.append(modal);
|
||||||
|
modal.querySelector("a[href='#close']").addEventListener("click",event => {
|
||||||
|
event.preventDefault();
|
||||||
|
reject("User cancelled");
|
||||||
|
modal.remove();
|
||||||
|
});
|
||||||
|
htmlToArray(modal.getElementsByTagName("button")).forEach(button => {
|
||||||
|
button.addEventListener("click",event => {
|
||||||
|
event.preventDefault();
|
||||||
|
var isYes = event.target.getAttribute("isyes")=="1";
|
||||||
|
if (isYes) resolve();
|
||||||
|
else reject("User cancelled");
|
||||||
|
modal.remove();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue