Ensure we run transfers one after the other rather that potentially having them overlap if the user clicks around https://github.com/espruino/EspruinoAppLoaderCore/issues/67

master
Gordon Williams 2025-06-06 15:13:59 +01:00
parent 9d84683505
commit 700a9f9f4a
3 changed files with 68 additions and 63 deletions

116
backup.js
View File

@ -7,7 +7,7 @@ function bangleDownload() {
Progress.show({title:"Scanning...",sticky:true}); Progress.show({title:"Scanning...",sticky:true});
var normalFiles, storageFiles; var normalFiles, storageFiles;
console.log("Listing normal files..."); console.log("Listing normal files...");
Comms.reset() return Comms.reset()
.then(() => Comms.showMessage("Backing up...")) .then(() => Comms.showMessage("Backing up..."))
.then(() => Comms.listFiles({sf:false})) .then(() => Comms.listFiles({sf:false}))
.then(f => { .then(f => {
@ -66,70 +66,74 @@ function bangleDownload() {
} }
function bangleUpload() { function bangleUpload() {
Espruino.Core.Utils.fileOpenDialog({ return new Promise(resolve =>
id:"backup", Espruino.Core.Utils.fileOpenDialog({
type:"arraybuffer", id:"backup",
mimeType:".zip,application/zip"}, function(data) { type:"arraybuffer",
if (data===undefined) return; mimeType:".zip,application/zip"}, function(data) {
var promise = Promise.resolve(); if (data===undefined) return;
var zip = new JSZip(); var promise = Promise.resolve();
var cmds = ""; var zip = new JSZip();
zip.loadAsync(data).then(function(zip) { var cmds = "";
return showPrompt("Restore from ZIP","Are you sure? This will overwrite existing apps"); zip.loadAsync(data).then(function(zip) {
}).then(()=>{ return showPrompt("Restore from ZIP","Are you sure? This will overwrite existing apps");
Progress.show({title:`Reading ZIP`}); }).then(()=>{
zip.forEach(function (path, file){ Progress.show({title:`Reading ZIP`});
console.log("path"); zip.forEach(function (path, file){
promise = promise console.log("path");
.then(() => file.async("string")) promise = promise
.then(data => { .then(() => file.async("string"))
console.log("decoded", path); .then(data => {
if (data.length==0) { // https://github.com/espruino/BangleApps/issues/1593 console.log("decoded", path);
console.log("Can't restore files of length 0, ignoring "+path); if (data.length==0) { // https://github.com/espruino/BangleApps/issues/1593
} else if (path.startsWith(BACKUP_STORAGEFILE_DIR)) { console.log("Can't restore files of length 0, ignoring "+path);
path = path.substr(BACKUP_STORAGEFILE_DIR.length+1); } else if (path.startsWith(BACKUP_STORAGEFILE_DIR)) {
cmds += AppInfo.getStorageFileUploadCommands(path, data)+"\n"; path = path.substr(BACKUP_STORAGEFILE_DIR.length+1);
} else if (!path.includes("/")) { cmds += AppInfo.getStorageFileUploadCommands(path, data)+"\n";
cmds += AppInfo.getFileUploadCommands(path, data)+"\n"; } else if (!path.includes("/")) {
} else console.log("Ignoring "+path); cmds += AppInfo.getFileUploadCommands(path, data)+"\n";
} else console.log("Ignoring "+path);
});
}); });
return promise;
})
.then(()=>new Promise(resolve => {
showPrompt("Erase Storage","Erase Storage? If restoring a complete backup you should erase storage, but in some cases you may want to upload files from a ZIP while keeping your Bangle's existing data.").then(()=>resolve(true), ()=>resolve(false));
}))
.then(eraseStorage => {
if (eraseStorage) {
Progress.hide({sticky:true});
Progress.show({title:`Erasing...`});
return Comms.removeAllApps();
}})
.then(() => {
Progress.hide({sticky:true});
Progress.show({title:`Restoring...`, sticky:true});
return Comms.showMessage(`Restoring...`); })
.then(() => Comms.write("\x10"+Comms.getProgressCmd()+"\n"))
.then(() => Comms.uploadCommandList(cmds, 0, cmds.length))
.then(() => getInstalledApps(true))
.then(() => Comms.showMessage(Const.MESSAGE_RELOAD))
.then(() => {
Progress.hide({sticky:true});
showToast('Restore complete!', 'success');
resolve();
})
.catch(err => {
Progress.hide({sticky:true});
showToast('Restore failed, ' + err, 'error');
resolve();
}); });
return promise; return promise;
})
.then(()=>new Promise(resolve => {
showPrompt("Erase Storage","Erase Storage? If restoring a complete backup you should erase storage, but in some cases you may want to upload files from a ZIP while keeping your Bangle's existing data.").then(()=>resolve(true), ()=>resolve(false));
}))
.then(eraseStorage => {
if (eraseStorage) {
Progress.hide({sticky:true});
Progress.show({title:`Erasing...`});
return Comms.removeAllApps();
}})
.then(() => {
Progress.hide({sticky:true});
Progress.show({title:`Restoring...`, sticky:true});
return Comms.showMessage(`Restoring...`); })
.then(() => Comms.write("\x10"+Comms.getProgressCmd()+"\n"))
.then(() => Comms.uploadCommandList(cmds, 0, cmds.length))
.then(() => getInstalledApps(true))
.then(() => Comms.showMessage(Const.MESSAGE_RELOAD))
.then(() => {
Progress.hide({sticky:true});
showToast('Restore complete!', 'success');
})
.catch(err => {
Progress.hide({sticky:true});
showToast('Restore failed, ' + err, 'error');
}); });
return promise; }
});
} }
window.addEventListener('load', (event) => { window.addEventListener('load', (event) => {
document.getElementById("downloadallapps").addEventListener("click",event=>{ document.getElementById("downloadallapps").addEventListener("click",event=>{
bangleDownload(); startOperation({name:"Backup Apps"}, () => bangleDownload);
}); });
document.getElementById("uploadallapps").addEventListener("click",event=>{ document.getElementById("uploadallapps").addEventListener("click",event=>{
bangleUpload(); startOperation({name:"Restore Apps"}, () => bangleUpload);
}); });
}); });

2
core

@ -1 +1 @@
Subproject commit ef99424a9fbd01be504841a6c759ba4292a542f7 Subproject commit 7e7475ba3ab253099481a81e487aaacb9384f974

View File

@ -212,7 +212,7 @@ window.addEventListener('load', (event) => {
el = document.getElementById("reinstallall"); el = document.getElementById("reinstallall");
if (el) el.addEventListener("click",event=>{ if (el) el.addEventListener("click",event=>{
var promise = showPrompt("Reinstall","Really re-install all apps?").then(() => { var promise = showPrompt("Reinstall","Really re-install all apps?").then(() => {
Comms.reset().then(_ => startOperation({name:"Reinstall All Apps"}, () => Comms.reset().then(_ =>
getInstalledApps() getInstalledApps()
).then(installedapps => { ).then(installedapps => {
console.log(installedapps); console.log(installedapps);
@ -232,14 +232,14 @@ window.addEventListener('load', (event) => {
).catch(err=>{ ).catch(err=>{
Progress.hide({sticky:true}); Progress.hide({sticky:true});
showToast("App re-install failed, "+err,"error"); showToast("App re-install failed, "+err,"error");
}); }));
}); });
}); });
// Button to install all default apps in one go // Button to install all default apps in one go
el = document.getElementById("installdefault"); el = document.getElementById("installdefault");
if (el) el.addEventListener("click", event=>{ if (el) el.addEventListener("click", event=>{
getInstalledApps().then(() => { startOperation({name:"Install Default Apps"}, () => getInstalledApps().then(() => {
if (device.id == "BANGLEJS") if (device.id == "BANGLEJS")
return httpGet("defaultapps_banglejs1.json"); return httpGet("defaultapps_banglejs1.json");
if (device.id == "BANGLEJS2") if (device.id == "BANGLEJS2")
@ -250,15 +250,16 @@ window.addEventListener('load', (event) => {
}).catch(err=>{ }).catch(err=>{
Progress.hide({sticky:true}); Progress.hide({sticky:true});
showToast("App Install failed, "+err,"error"); showToast("App Install failed, "+err,"error");
}); }));
}); });
// Button to reset the Bangle's settings // Button to reset the Bangle's settings
el = document.getElementById("defaultbanglesettings"); el = document.getElementById("defaultbanglesettings");
if (el) el.addEventListener("click", event=>{ if (el) el.addEventListener("click", event=>{
showPrompt("Reset Settings","Really reset Bangle.js settings?").then(() => { showPrompt("Reset Settings","Really reset Bangle.js settings?").then(() => {
Comms.write("\x10require('Storage').erase('setting.json');load()\n"); startOperation({name:"Reset Settings"}, () =>
showToast("Settings reset!", "success"); Comms.write("\x10require('Storage').erase('setting.json');load()\n").then(() =>
showToast("Settings reset!", "success")));
}, function() { /* cancelled */ }); }, function() { /* cancelled */ });
}); });