handle printing app versions
parent
bcf0f80d29
commit
e0320bb2f7
28
README.md
28
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:
|
* 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.json` - short widget name and storage names
|
||||||
* `widget.js` - widget code
|
* `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
|
{ "id": "appid", // 7 character app id
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ var AppInfo = {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
reject(storageFile.name+" is not valid JSON");
|
reject(storageFile.name+" is not valid JSON");
|
||||||
}
|
}
|
||||||
|
if (app.version) json.version = app.version;
|
||||||
json.files = fileContents.map(storageFile=>storageFile.name).join(",");
|
json.files = fileContents.map(storageFile=>storageFile.name).join(",");
|
||||||
storageFile.content = JSON.stringify(json);
|
storageFile.content = JSON.stringify(json);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
comms.js
13
comms.js
|
|
@ -5,6 +5,15 @@ var Comms = {
|
||||||
uploadApp : app => {
|
uploadApp : app => {
|
||||||
return AppInfo.getFiles(app, httpGet).then(fileContents => {
|
return AppInfo.getFiles(app, httpGet).then(fileContents => {
|
||||||
return new Promise((resolve,reject) => {
|
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";
|
fileContents = fileContents.map(storageFile=>storageFile.cmd).join("\n")+"\n";
|
||||||
console.log("uploadApp",fileContents);
|
console.log("uploadApp",fileContents);
|
||||||
// reset to ensure we have enough memory to upload what we need to
|
// reset to ensure we have enough memory to upload what we need to
|
||||||
|
|
@ -13,7 +22,7 @@ uploadApp : app => {
|
||||||
setTimeout(() => { // wait for reset
|
setTimeout(() => { // wait for reset
|
||||||
Puck.write("\x10E.showMessage('Uploading...')\n"+fileContents+"\x10E.showMessage('Hold BTN3\\nto reload')\n",(result) => {
|
Puck.write("\x10E.showMessage('Uploading...')\n"+fileContents+"\x10E.showMessage('Hold BTN3\\nto reload')\n",(result) => {
|
||||||
if (result===null) return reject("");
|
if (result===null) return reject("");
|
||||||
resolve();
|
resolve(appJSON);
|
||||||
});
|
});
|
||||||
},500);
|
},500);
|
||||||
});
|
});
|
||||||
|
|
@ -24,7 +33,7 @@ getInstalledApps : () => {
|
||||||
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) 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 || "");
|
if (appList===null) return reject(err || "");
|
||||||
console.log("getInstalledApps", appList);
|
console.log("getInstalledApps", appList);
|
||||||
resolve(appList);
|
resolve(appList);
|
||||||
|
|
|
||||||
41
index.js
41
index.js
|
|
@ -1,5 +1,5 @@
|
||||||
var appJSON = []; // List of apps and info from apps.json
|
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=>{
|
httpGet("apps.json").then(apps=>{
|
||||||
try {
|
try {
|
||||||
|
|
@ -149,11 +149,15 @@ function refreshLibrary() {
|
||||||
|
|
||||||
panelbody.innerHTML = visibleApps.map((app,idx) => {
|
panelbody.innerHTML = visibleApps.map((app,idx) => {
|
||||||
var icon = "icon-upload";
|
var icon = "icon-upload";
|
||||||
|
var versionInfo = app.version || "";
|
||||||
if (app.custom)
|
if (app.custom)
|
||||||
icon = "icon-menu";
|
icon = "icon-menu";
|
||||||
if (appsInstalled.includes(app.id))
|
if (appsInstalled.find(a=>a.id==app.id)) {
|
||||||
icon = "icon-delete";
|
icon = "icon-delete";
|
||||||
|
versionInfo+=" installed";
|
||||||
|
}
|
||||||
var buttons = "";
|
var buttons = "";
|
||||||
|
if (versionInfo) versionInfo = " <small>("+versionInfo+")</small>";
|
||||||
if (app.allow_emulator)
|
if (app.allow_emulator)
|
||||||
buttons += `<button class="btn btn-link btn-action btn-lg" title="Try in Emulator"><i class="icon icon-share" appid="${app.id}"></i></button>`;
|
buttons += `<button class="btn btn-link btn-action btn-lg" title="Try in Emulator"><i class="icon icon-share" appid="${app.id}"></i></button>`;
|
||||||
buttons += `<button class="btn btn-link btn-action btn-lg"><i class="icon ${icon}" appid="${app.id}"></i></button>`;
|
buttons += `<button class="btn btn-link btn-action btn-lg"><i class="icon ${icon}" appid="${app.id}"></i></button>`;
|
||||||
|
|
@ -162,7 +166,7 @@ function refreshLibrary() {
|
||||||
<figure class="avatar"><img src="apps/${app.icon?`${app.id}/${app.icon}`:"unknown.png"}" alt="${escapeHtml(app.name)}"></figure>
|
<figure class="avatar"><img src="apps/${app.icon?`${app.id}/${app.icon}`:"unknown.png"}" alt="${escapeHtml(app.name)}"></figure>
|
||||||
</div>
|
</div>
|
||||||
<div class="tile-content">
|
<div class="tile-content">
|
||||||
<p class="tile-title text-bold">${escapeHtml(app.name)}</p>
|
<p class="tile-title text-bold">${escapeHtml(app.name)} ${versionInfo}</p>
|
||||||
<p class="tile-subtitle">${escapeHtml(app.description)}</p>
|
<p class="tile-subtitle">${escapeHtml(app.description)}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="tile-action">
|
<div class="tile-action">
|
||||||
|
|
@ -193,8 +197,8 @@ function refreshLibrary() {
|
||||||
} else if (icon.classList.contains("icon-upload")) {
|
} else if (icon.classList.contains("icon-upload")) {
|
||||||
icon.classList.remove("icon-upload");
|
icon.classList.remove("icon-upload");
|
||||||
icon.classList.add("loading");
|
icon.classList.add("loading");
|
||||||
Comms.uploadApp(app).then(() => {
|
Comms.uploadApp(app).then((appJSON) => {
|
||||||
appsInstalled.push(app.id);
|
if (appJSON) appsInstalled.push(appJSON);
|
||||||
showToast(app.name+" Uploaded!", "success");
|
showToast(app.name+" Uploaded!", "success");
|
||||||
icon.classList.remove("loading");
|
icon.classList.remove("loading");
|
||||||
icon.classList.add("icon-delete");
|
icon.classList.add("icon-delete");
|
||||||
|
|
@ -208,8 +212,8 @@ function refreshLibrary() {
|
||||||
if (app.custom) {
|
if (app.custom) {
|
||||||
icon.classList.remove("icon-menu");
|
icon.classList.remove("icon-menu");
|
||||||
icon.classList.add("loading");
|
icon.classList.add("loading");
|
||||||
handleCustomApp(app).then(() => {
|
handleCustomApp(app).then((appJSON) => {
|
||||||
appsInstalled.push(app.id);
|
if (appJSON) appsInstalled.push(appJSON);
|
||||||
showToast(app.name+" Uploaded!", "success");
|
showToast(app.name+" Uploaded!", "success");
|
||||||
icon.classList.remove("loading");
|
icon.classList.remove("loading");
|
||||||
icon.classList.add("icon-delete");
|
icon.classList.add("icon-delete");
|
||||||
|
|
@ -235,7 +239,7 @@ refreshLibrary();
|
||||||
function removeApp(app) {
|
function removeApp(app) {
|
||||||
return showPrompt("Delete","Really remove '"+app.name+"'?").then(() => {
|
return showPrompt("Delete","Really remove '"+app.name+"'?").then(() => {
|
||||||
Comms.removeApp(app).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");
|
showToast(app.name+" removed successfully","success");
|
||||||
refreshMyApps();
|
refreshMyApps();
|
||||||
refreshLibrary();
|
refreshLibrary();
|
||||||
|
|
@ -274,19 +278,30 @@ function refreshMyApps() {
|
||||||
var panelbody = document.querySelector("#myappscontainer .panel-body");
|
var panelbody = document.querySelector("#myappscontainer .panel-body");
|
||||||
var tab = document.querySelector("#tab-myappscontainer a");
|
var tab = document.querySelector("#tab-myappscontainer a");
|
||||||
tab.setAttribute("data-badge", appsInstalled.length);
|
tab.setAttribute("data-badge", appsInstalled.length);
|
||||||
panelbody.innerHTML = appsInstalled.map(appNameToApp).sort(appSorter).map(app => `<div class="tile column col-6 col-sm-12 col-xs-12">
|
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 `<div class="tile column col-6 col-sm-12 col-xs-12">
|
||||||
<div class="tile-icon">
|
<div class="tile-icon">
|
||||||
<figure class="avatar"><img src="apps/${app.icon?`${app.id}/${app.icon}`:"unknown.png"}" alt="${escapeHtml(app.name)}"></figure>
|
<figure class="avatar"><img src="apps/${app.icon?`${app.id}/${app.icon}`:"unknown.png"}" alt="${escapeHtml(app.name)}"></figure>
|
||||||
</div>
|
</div>
|
||||||
<div class="tile-content">
|
<div class="tile-content">
|
||||||
<p class="tile-title text-bold">${escapeHtml(app.name)}</p>
|
<p class="tile-title text-bold">${escapeHtml(app.name)} <small>(${version})</small></p>
|
||||||
<p class="tile-subtitle">${escapeHtml(app.description)}</p>
|
<p class="tile-subtitle">${escapeHtml(app.description)}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="tile-action">
|
<div class="tile-action">
|
||||||
<button class="btn btn-link btn-action btn-lg"><i class="icon icon-delete" appid="${app.id}"></i></button>
|
<button class="btn btn-link btn-action btn-lg"><i class="icon icon-delete" appid="${app.id}"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).join("");
|
`}).join("");
|
||||||
htmlToArray(panelbody.getElementsByTagName("button")).forEach(button => {
|
htmlToArray(panelbody.getElementsByTagName("button")).forEach(button => {
|
||||||
button.addEventListener("click",event => {
|
button.addEventListener("click",event => {
|
||||||
var icon = event.target;
|
var icon = event.target;
|
||||||
|
|
@ -300,8 +315,8 @@ function refreshMyApps() {
|
||||||
function getInstalledApps() {
|
function getInstalledApps() {
|
||||||
showLoadingIndicator();
|
showLoadingIndicator();
|
||||||
// Get apps
|
// Get apps
|
||||||
return Comms.getInstalledApps().then(appIDs => {
|
return Comms.getInstalledApps().then(appJSON => {
|
||||||
appsInstalled = appIDs;
|
appsInstalled = appJSON;
|
||||||
handleConnectionChange(true);
|
handleConnectionChange(true);
|
||||||
refreshMyApps();
|
refreshMyApps();
|
||||||
refreshLibrary();
|
refreshLibrary();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue