handle printing app versions

pull/66/head
Gordon Williams 2019-12-05 14:48:56 +00:00
parent bcf0f80d29
commit e0320bb2f7
4 changed files with 67 additions and 16 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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 = " <small>("+versionInfo+")</small>";
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"><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>
</div>
<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>
</div>
<div class="tile-action">
@ -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 => `<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">
<figure class="avatar"><img src="apps/${app.icon?`${app.id}/${app.icon}`:"unknown.png"}" alt="${escapeHtml(app.name)}"></figure>
</div>
<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>
</div>
<div class="tile-action">
<button class="btn btn-link btn-action btn-lg"><i class="icon icon-delete" appid="${app.id}"></i></button>
</div>
</div>
`).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();