From e0320bb2f70ad6cfea425015757c62427496166b Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Thu, 5 Dec 2019 14:48:56 +0000 Subject: [PATCH] handle printing app versions --- README.md | 28 +++++++++++++++++++++++++++- appinfo.js | 1 + comms.js | 13 +++++++++++-- index.js | 41 ++++++++++++++++++++++++++++------------- 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 665a74da2..bf82a0c41 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,8 @@ easily distinguish between file types, we use the following: } ``` +See `app.json / widget.json` below for more info on the correct format. + * Create an entry in `apps.json` as follows: ``` @@ -155,8 +157,32 @@ The widget example is available in [`apps/_example_widget`](apps/_example_widget * `widget.json` - short widget name and storage names * `widget.js` - widget code +### `app.json` / `widget.json` format -#### `apps.json` format +This is the file that's loaded onto Bangle.js, which gives information +about the app. + +``` +{ + "name":"Short Name", // for Bangle.js menu + "icon":"*7chname", // for Bangle.js menu + "src":"-7chname", // source file + "type":"widget/clock/app", // optional, default "app" + // if this is 'widget' then it's not displayed in the menu + // if it's 'clock' then it'll be loaded by default at boot time + "hasWidgets" : true // optional, default false + // if true, widgets will be loaded so 'drawWidgets' can be + // used from the app. You just need to ensure you leave the + // top and bottom 24px alone + "version":"1.23", + // added by BangleApps loader on upload based on apps.json + "files:"file1,file2,file3", + // added by BangleApps loader on upload - lists all files + // that belong to the app so it can be deleted +} +``` + +### `apps.json` format ``` { "id": "appid", // 7 character app id diff --git a/appinfo.js b/appinfo.js index 6fa4aa474..7d49647fe 100644 --- a/appinfo.js +++ b/appinfo.js @@ -31,6 +31,7 @@ var AppInfo = { } catch (e) { reject(storageFile.name+" is not valid JSON"); } + if (app.version) json.version = app.version; json.files = fileContents.map(storageFile=>storageFile.name).join(","); storageFile.content = JSON.stringify(json); } diff --git a/comms.js b/comms.js index 9d103c246..3a9c37296 100644 --- a/comms.js +++ b/comms.js @@ -5,6 +5,15 @@ var Comms = { uploadApp : app => { return AppInfo.getFiles(app, httpGet).then(fileContents => { return new Promise((resolve,reject) => { + var appJSONFile = fileContents.find(f=>f.name=="+"+app.id); + var appJSON = undefined; + if (appJSONFile) + try { + appJSON=JSON.parse(appJSONFile.content); + appJSON.id = app.id; + } catch(e) { + console.log("Error decoding app JSON for",app.id,e); + } 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 @@ -13,7 +22,7 @@ uploadApp : app => { 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(); + resolve(appJSON); }); },500); }); @@ -24,7 +33,7 @@ getInstalledApps : () => { return new Promise((resolve,reject) => { Puck.write("\x03",(result) => { if (result===null) return reject(""); - Puck.eval('require("Storage").list().filter(f=>f[0]=="+").map(f=>f.substr(1))', (appList,err) => { + Puck.eval('require("Storage").list().filter(f=>f[0]=="+").map(f=>{var j=require("Storage").readJSON(f)||{};j.id=f.substr(1);return j})', (appList,err) => { if (appList===null) return reject(err || ""); console.log("getInstalledApps", appList); resolve(appList); diff --git a/index.js b/index.js index e3faa4ecb..c10f07f54 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ var appJSON = []; // List of apps and info from apps.json -var appsInstalled = []; // list of app IDs +var appsInstalled = []; // list of app JSON httpGet("apps.json").then(apps=>{ try { @@ -149,11 +149,15 @@ function refreshLibrary() { panelbody.innerHTML = visibleApps.map((app,idx) => { var icon = "icon-upload"; + var versionInfo = app.version || ""; if (app.custom) icon = "icon-menu"; - if (appsInstalled.includes(app.id)) + if (appsInstalled.find(a=>a.id==app.id)) { icon = "icon-delete"; + versionInfo+=" installed"; + } var buttons = ""; + if (versionInfo) versionInfo = " ("+versionInfo+")"; if (app.allow_emulator) buttons += ``; buttons += ``; @@ -162,7 +166,7 @@ function refreshLibrary() {
${escapeHtml(app.name)}
-

${escapeHtml(app.name)}

+

${escapeHtml(app.name)} ${versionInfo}

${escapeHtml(app.description)}

@@ -193,8 +197,8 @@ function refreshLibrary() { } else if (icon.classList.contains("icon-upload")) { icon.classList.remove("icon-upload"); icon.classList.add("loading"); - Comms.uploadApp(app).then(() => { - appsInstalled.push(app.id); + Comms.uploadApp(app).then((appJSON) => { + if (appJSON) appsInstalled.push(appJSON); showToast(app.name+" Uploaded!", "success"); icon.classList.remove("loading"); icon.classList.add("icon-delete"); @@ -208,8 +212,8 @@ function refreshLibrary() { if (app.custom) { icon.classList.remove("icon-menu"); icon.classList.add("loading"); - handleCustomApp(app).then(() => { - appsInstalled.push(app.id); + handleCustomApp(app).then((appJSON) => { + if (appJSON) appsInstalled.push(appJSON); showToast(app.name+" Uploaded!", "success"); icon.classList.remove("loading"); icon.classList.add("icon-delete"); @@ -235,7 +239,7 @@ refreshLibrary(); function removeApp(app) { return showPrompt("Delete","Really remove '"+app.name+"'?").then(() => { Comms.removeApp(app).then(()=>{ - appsInstalled = appsInstalled.filter(id=>id!=app.id); + appsInstalled = appsInstalled.filter(a=>a.id!=app.id); showToast(app.name+" removed successfully","success"); refreshMyApps(); refreshLibrary(); @@ -274,19 +278,30 @@ 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 => `
+ panelbody.innerHTML = appsInstalled.map(appJSON => { +var app = appNameToApp(appJSON.id); +var version = ""; +if (!appJSON.version) { + version = "Unknown version"; + if (app.version) version += ", latest "+app.version; +} else { + version = appJSON.version; + if (app.version == appJSON.version) version += ", up to date"; + else if (app.version) version += ", latest "+app.version; +} +return `
${escapeHtml(app.name)}
-

${escapeHtml(app.name)}

+

${escapeHtml(app.name)} (${version})

${escapeHtml(app.description)}

- `).join(""); + `}).join(""); htmlToArray(panelbody.getElementsByTagName("button")).forEach(button => { button.addEventListener("click",event => { var icon = event.target; @@ -300,8 +315,8 @@ function refreshMyApps() { function getInstalledApps() { showLoadingIndicator(); // Get apps - return Comms.getInstalledApps().then(appIDs => { - appsInstalled = appIDs; + return Comms.getInstalledApps().then(appJSON => { + appsInstalled = appJSON; handleConnectionChange(true); refreshMyApps(); refreshLibrary();