diff --git a/comms.js b/comms.js
index b03ac9359..6bbf3ef26 100644
--- a/comms.js
+++ b/comms.js
@@ -1,28 +1,35 @@
Puck.debug=3;
+/* 'app' is of the form:
+{ name: "T-Rex",
+ icon: "trex.png",
+ description: "T-Rex game in the style of Chrome's offline game",
+ storage: [
+ {name:"+trex",file:"trex.json"},
+ {name:"-trex",file:"trex.js"},
+ {name:"*trex",file:"trex-icon.js"}
+ ]
+}
+*/
+
+// FIXME: use UART lib so that we handle errors properly
var Comms = {
uploadApp : app => {
- /* eg
- { name: "T-Rex",
- icon: "trex.png",
- description: "T-Rex game in the style of Chrome's offline game",
- storage: [
- {name:"+trex",file:"trex.json"},
- {name:"-trex",file:"trex.js"},
- {name:"*trex",file:"trex-icon.js"}
- ]
- }
- */
return new Promise((resolve,reject) => {
// Load all files
Promise.all(app.storage.map(storageFile => httpGet("apps/"+storageFile.file)
// map each file to a command to load into storage
- .then(contents=>`require('Storage').write(${toJS(storageFile.name)},${toJS(contents)});`)))
- //
+ .then(contents=>`\x10require('Storage').write(${toJS(storageFile.name)},${storageFile.evaluate ? contents : toJS(contents)});`)))
.then(function(fileContents) {
fileContents = fileContents.join("\n");
- Puck.write(fileContents,function() {
- resolve();
+ console.log("uploadApp",fileContents);
+ // reset to ensure we have enough memory to upload what we need to
+ Puck.write("\x03reset();\n", function() {
+ setTimeout(function() { // wait for reset
+ Puck.write(fileContents,function() {
+ resolve();
+ });
+ },500);
});
});
});
@@ -31,9 +38,21 @@ getInstalledApps : () => {
return new Promise((resolve,reject) => {
Puck.write("\x03",() => {
Puck.eval('require("Storage").list().filter(f=>f[0]=="+").map(f=>f.substr(1))', appList => {
+ console.log("getInstalledApps", appList);
resolve(appList);
});
});
});
+},
+removeApp : app => { // expects an app structure
+ var cmds = app.storage.map(file=>{
+ return `\x10require("Storage").erase(${toJS(file.name)});\n`;
+ }).join("");
+ console.log("removeApp", cmds);
+ return new Promise((resolve,reject) => {
+ Puck.write("\x03"+cmds,() => {
+ resolve();
+ });
+ });
}
};
diff --git a/index.html b/index.html
index bad0efe90..f4485d452 100644
--- a/index.html
+++ b/index.html
@@ -9,10 +9,14 @@
Bangle.js loader
diff --git a/index.js b/index.js
index 770c6e5e5..25519dfc9 100644
--- a/index.js
+++ b/index.js
@@ -1,13 +1,15 @@
-var appjson = [];
+var appJSON = []; // List of apps and info from apps.json
+var appsInstalled = []; // list of app IDs
+
httpGet("apps.json").then(apps=>{
- appjson = JSON.parse(apps);
- appjson.sort(appSorter);
+ appJSON = JSON.parse(apps);
+ appJSON.sort(appSorter);
refreshLibrary();
});
// Status
// =========================================== Top Navigation
-function showToast(message) {
+function showToast(message, type) {
// toast-primary, toast-success, toast-warning or toast-error
var toastcontainer = document.getElementById("toastcontainer");
var msgDiv = htmlElement(``);
@@ -42,7 +44,7 @@ function showPrompt(title, text) {
document.body.append(modal);
htmlToArray(modal.getElementsByTagName("button")).forEach(button => {
button.addEventListener("click",event => {
- var isYes = event.target.getAttribute("isyes");
+ var isYes = event.target.getAttribute("isyes")=="1";
if (isYes) resolve();
else reject();
modal.remove();
@@ -65,7 +67,98 @@ function showTab(tabname) {
// =========================================== Library
function refreshLibrary() {
var panelbody = document.querySelector("#librarycontainer .panel-body");
- panelbody.innerHTML = appjson.map((app,idx) => `
+ panelbody.innerHTML = appJSON.map((app,idx) => `
+
+

+
+
+
${escapeHtml(app.name)}
+
${escapeHtml(app.description)}
+
+
+
+
+
+ `);
+ // set badge up top
+ var tab = document.querySelector("#tab-librarycontainer a");
+ tab.classList.add("badge");
+ tab.setAttribute("data-badge", appJSON.length);
+ htmlToArray(panelbody.getElementsByTagName("button")).forEach(button => {
+ button.addEventListener("click",event => {
+ var icon = event.target;
+ var appid = icon.getAttribute("appid");
+ var app = appJSON.find(app=>app.id==appid);
+ if (!app) return;
+ if (icon.classList.contains("icon-upload")) {
+ icon.classList.remove("icon-upload");
+ icon.classList.add("loading");
+ Comms.uploadApp(app).then(() => {
+ appsInstalled.push(app.id);
+ showToast(app.name+" Uploaded!", "success");
+ icon.classList.remove("loading");
+ icon.classList.add("icon-delete");
+ refreshMyApps();
+ }).catch(err => {
+ showToast("Upload failed, "+err, "error");
+ icon.classList.remove("loading");
+ icon.classList.add("icon-upload");
+ });
+ } else {
+ icon.classList.remove("icon-delete");
+ icon.classList.add("loading");
+ removeApp(app);
+ }
+ });
+ });
+}
+
+refreshLibrary();
+// =========================================== My Apps
+
+function removeApp(app) {
+ return showPrompt("Delete","Really remove app '"+appid+"'?").then(() => {
+ Comms.removeApp(app).then(()=>{
+ appsInstalled = appsInstalled.filter(id=>id!=app.id);
+ showToast(app.name+" removed successfully","success");
+ refreshMyApps();
+ refreshLibrary();
+ }, err=>{
+ showToast(app.name+" removal failed, "+err,"error");
+ });
+ });
+}
+
+function appNameToApp(appName) {
+ var app = appJSON.find(app=>app.id==appName);
+ if (app) return app;
+ /* If app not known, add just one file
+ which is the JSON - so we'll remove it from
+ the menu but may not get rid of all files. */
+ return { id: appName,
+ name: "Unknown app "+appName,
+ icon: "unknown.png",
+ description: "Unknown app",
+ storage: [ {name:"+"+appName}],
+ unknown: true,
+ };
+}
+
+function showLoadingIndicator() {
+ var panelbody = document.querySelector("#myappscontainer .panel-body");
+ var tab = document.querySelector("#tab-myappscontainer a");
+ // set badge up top
+ tab.classList.add("badge");
+ tab.setAttribute("data-badge", "");
+ // Loading indicator
+ panelbody.innerHTML = '
';
+}
+
+function refreshMyApps() {
+ var panelbody = document.querySelector("#myappscontainer .panel-body");
+ var tab = document.querySelector("#tab-myappscontainer a");
+ tab.setAttribute("data-badge", appsInstalled.length);
+ panelbody.innerHTML = appsInstalled.map(appNameToApp).sort(appSorter).map(app => `
@@ -74,88 +167,31 @@ function refreshLibrary() {
${escapeHtml(app.description)}
-
+
`);
- // set badge up top
- var tab = document.querySelector("#tab-librarycontainer a");
- tab.classList.add("badge");
- tab.setAttribute("data-badge", appjson.length);
htmlToArray(panelbody.getElementsByTagName("button")).forEach(button => {
button.addEventListener("click",event => {
var icon = event.target;
var appid = icon.getAttribute("appid");
- var app = appjson.find(app=>app.id==appid);
- if (!app) return;
- icon.classList.remove("icon-upload");
- icon.classList.add("loading");
- Comms.uploadApp(app).then(() => {
- showToast(app.name+" Uploaded!");
- icon.classList.remove("loading");
- icon.classList.add("icon-delete");
- }).catch(() => {
- icon.classList.remove("loading");
- icon.classList.add("icon-upload");
- });
+ var app = appNameToApp(appid);
+ removeApp(app);
});
});
}
-refreshLibrary();
-// =========================================== My Apps
-
-function appNameToApp(appName) {
- var app = appjson.find(app=>app.id==appName);
- if (app) return app;
- return { id: "appName",
- name: "Unknown app "+appName,
- icon: "unknown.png",
- description: "Unknown app",
- storage: [],
- unknown: true,
- };
-}
-
-function refreshMyApps() {
- var panelbody = document.querySelector("#myappscontainer .panel-body");
- var tab = document.querySelector("#tab-myappscontainer a");
- // set badge up top
- tab.classList.add("badge");
- tab.setAttribute("data-badge", "");
- // Loading indicator
- panelbody.innerHTML = '';
+function getInstalledApps() {
+ showLoadingIndicator();
// Get apps
Comms.getInstalledApps().then(appIDs => {
- tab.setAttribute("data-badge", appIDs.length);
- panelbody.innerHTML = appIDs.map(appNameToApp).sort(appSorter).map(app => `
-
-

-
-
-
${escapeHtml(app.name)}
-
${escapeHtml(app.description)}
-
-
-
-
-
- `);
- htmlToArray(panelbody.getElementsByTagName("button")).forEach(button => {
- button.addEventListener("click",event => {
- var icon = event.target;
- var appid = icon.getAttribute("appid");
- var app = appNameToApp(appid);
- showPrompt("Delete","Really remove app '"+appid+"'?").then(() => {
- // remove app!
- refreshMyApps();
- });
- });
- });
+ appsInstalled = appIDs;
+ refreshMyApps();
+ refreshLibrary();
});
}
document.getElementById("myappsrefresh").addEventListener("click",event=>{
- refreshMyApps();
+ getInstalledApps();
});