forked from FOSS/BangleApps
Added proper remove app behaviour
parent
d769d27fbd
commit
1eab865e45
29
comms.js
29
comms.js
|
@ -1,8 +1,6 @@
|
||||||
Puck.debug=3;
|
Puck.debug=3;
|
||||||
|
|
||||||
var Comms = {
|
/* 'app' is of the form:
|
||||||
uploadApp : app => {
|
|
||||||
/* eg
|
|
||||||
{ name: "T-Rex",
|
{ name: "T-Rex",
|
||||||
icon: "trex.png",
|
icon: "trex.png",
|
||||||
description: "T-Rex game in the style of Chrome's offline game",
|
description: "T-Rex game in the style of Chrome's offline game",
|
||||||
|
@ -13,17 +11,26 @@ uploadApp : app => {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// FIXME: use UART lib so that we handle errors properly
|
||||||
|
var Comms = {
|
||||||
|
uploadApp : app => {
|
||||||
return new Promise((resolve,reject) => {
|
return new Promise((resolve,reject) => {
|
||||||
// Load all files
|
// Load all files
|
||||||
Promise.all(app.storage.map(storageFile => httpGet("apps/"+storageFile.file)
|
Promise.all(app.storage.map(storageFile => httpGet("apps/"+storageFile.file)
|
||||||
// map each file to a command to load into storage
|
// 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) {
|
.then(function(fileContents) {
|
||||||
fileContents = fileContents.join("\n");
|
fileContents = fileContents.join("\n");
|
||||||
|
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() {
|
Puck.write(fileContents,function() {
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
|
},500);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -31,9 +38,21 @@ getInstalledApps : () => {
|
||||||
return new Promise((resolve,reject) => {
|
return new Promise((resolve,reject) => {
|
||||||
Puck.write("\x03",() => {
|
Puck.write("\x03",() => {
|
||||||
Puck.eval('require("Storage").list().filter(f=>f[0]=="+").map(f=>f.substr(1))', appList => {
|
Puck.eval('require("Storage").list().filter(f=>f[0]=="+").map(f=>f.substr(1))', appList => {
|
||||||
|
console.log("getInstalledApps", appList);
|
||||||
resolve(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();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,10 @@
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
.avatar img {
|
||||||
|
border-radius: 5px 5px 5px 5px;
|
||||||
|
background: #ddd;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
90
index.js
90
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=>{
|
httpGet("apps.json").then(apps=>{
|
||||||
appjson = JSON.parse(apps);
|
appJSON = JSON.parse(apps);
|
||||||
appjson.sort(appSorter);
|
appJSON.sort(appSorter);
|
||||||
refreshLibrary();
|
refreshLibrary();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Status
|
// Status
|
||||||
// =========================================== Top Navigation
|
// =========================================== Top Navigation
|
||||||
function showToast(message) {
|
function showToast(message, type) {
|
||||||
// toast-primary, toast-success, toast-warning or toast-error
|
// toast-primary, toast-success, toast-warning or toast-error
|
||||||
var toastcontainer = document.getElementById("toastcontainer");
|
var toastcontainer = document.getElementById("toastcontainer");
|
||||||
var msgDiv = htmlElement(`<div class="toast toast-primary"></div>`);
|
var msgDiv = htmlElement(`<div class="toast toast-primary"></div>`);
|
||||||
|
@ -42,7 +44,7 @@ function showPrompt(title, text) {
|
||||||
document.body.append(modal);
|
document.body.append(modal);
|
||||||
htmlToArray(modal.getElementsByTagName("button")).forEach(button => {
|
htmlToArray(modal.getElementsByTagName("button")).forEach(button => {
|
||||||
button.addEventListener("click",event => {
|
button.addEventListener("click",event => {
|
||||||
var isYes = event.target.getAttribute("isyes");
|
var isYes = event.target.getAttribute("isyes")=="1";
|
||||||
if (isYes) resolve();
|
if (isYes) resolve();
|
||||||
else reject();
|
else reject();
|
||||||
modal.remove();
|
modal.remove();
|
||||||
|
@ -65,39 +67,48 @@ function showTab(tabname) {
|
||||||
// =========================================== Library
|
// =========================================== Library
|
||||||
function refreshLibrary() {
|
function refreshLibrary() {
|
||||||
var panelbody = document.querySelector("#librarycontainer .panel-body");
|
var panelbody = document.querySelector("#librarycontainer .panel-body");
|
||||||
panelbody.innerHTML = appjson.map((app,idx) => `<div class="tile">
|
panelbody.innerHTML = appJSON.map((app,idx) => `<div class="tile">
|
||||||
<div class="tile-icon">
|
<div class="tile-icon">
|
||||||
<figure class="avatar"><img src="apps/${app.icon}" alt="${escapeHtml(app.name)}"></figure>
|
<figure class="avatar"><img src="apps/${app.icon?app.icon:"apps/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)}</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-upload" appid="${app.id}"></i></button>
|
<button class="btn btn-link btn-action btn-lg"><i class="icon ${appsInstalled.includes(app.id)?"icon-delete":"icon-upload"}" appid="${app.id}"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
// set badge up top
|
// set badge up top
|
||||||
var tab = document.querySelector("#tab-librarycontainer a");
|
var tab = document.querySelector("#tab-librarycontainer a");
|
||||||
tab.classList.add("badge");
|
tab.classList.add("badge");
|
||||||
tab.setAttribute("data-badge", appjson.length);
|
tab.setAttribute("data-badge", appJSON.length);
|
||||||
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;
|
||||||
var appid = icon.getAttribute("appid");
|
var appid = icon.getAttribute("appid");
|
||||||
var app = appjson.find(app=>app.id==appid);
|
var app = appJSON.find(app=>app.id==appid);
|
||||||
if (!app) return;
|
if (!app) return;
|
||||||
|
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(() => {
|
||||||
showToast(app.name+" Uploaded!");
|
appsInstalled.push(app.id);
|
||||||
|
showToast(app.name+" Uploaded!", "success");
|
||||||
icon.classList.remove("loading");
|
icon.classList.remove("loading");
|
||||||
icon.classList.add("icon-delete");
|
icon.classList.add("icon-delete");
|
||||||
}).catch(() => {
|
refreshMyApps();
|
||||||
|
}).catch(err => {
|
||||||
|
showToast("Upload failed, "+err, "error");
|
||||||
icon.classList.remove("loading");
|
icon.classList.remove("loading");
|
||||||
icon.classList.add("icon-upload");
|
icon.classList.add("icon-upload");
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
icon.classList.remove("icon-delete");
|
||||||
|
icon.classList.add("loading");
|
||||||
|
removeApp(app);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -105,30 +116,49 @@ function refreshLibrary() {
|
||||||
refreshLibrary();
|
refreshLibrary();
|
||||||
// =========================================== My Apps
|
// =========================================== 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) {
|
function appNameToApp(appName) {
|
||||||
var app = appjson.find(app=>app.id==appName);
|
var app = appJSON.find(app=>app.id==appName);
|
||||||
if (app) return app;
|
if (app) return app;
|
||||||
return { id: "appName",
|
/* 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,
|
name: "Unknown app "+appName,
|
||||||
icon: "unknown.png",
|
icon: "unknown.png",
|
||||||
description: "Unknown app",
|
description: "Unknown app",
|
||||||
storage: [],
|
storage: [ {name:"+"+appName}],
|
||||||
unknown: true,
|
unknown: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshMyApps() {
|
function showLoadingIndicator() {
|
||||||
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");
|
||||||
// set badge up top
|
// set badge up top
|
||||||
tab.classList.add("badge");
|
tab.classList.add("badge");
|
||||||
tab.setAttribute("data-badge", "");
|
tab.setAttribute("data-badge", "");
|
||||||
// Loading indicator
|
// Loading indicator
|
||||||
panelbody.innerHTML = '<div class="loading loading-lg"></div>';
|
panelbody.innerHTML = '<div class="tile"><div class="tile-content" style="min-height:48px;"><div class="loading loading-lg"></div></div></div>';
|
||||||
// Get apps
|
}
|
||||||
Comms.getInstalledApps().then(appIDs => {
|
|
||||||
tab.setAttribute("data-badge", appIDs.length);
|
function refreshMyApps() {
|
||||||
panelbody.innerHTML = appIDs.map(appNameToApp).sort(appSorter).map(app => `<div class="tile">
|
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">
|
||||||
<div class="tile-icon">
|
<div class="tile-icon">
|
||||||
<figure class="avatar"><img src="apps/${app.icon}" alt="${escapeHtml(app.name)}"></figure>
|
<figure class="avatar"><img src="apps/${app.icon}" alt="${escapeHtml(app.name)}"></figure>
|
||||||
</div>
|
</div>
|
||||||
|
@ -146,16 +176,22 @@ function refreshMyApps() {
|
||||||
var icon = event.target;
|
var icon = event.target;
|
||||||
var appid = icon.getAttribute("appid");
|
var appid = icon.getAttribute("appid");
|
||||||
var app = appNameToApp(appid);
|
var app = appNameToApp(appid);
|
||||||
showPrompt("Delete","Really remove app '"+appid+"'?").then(() => {
|
removeApp(app);
|
||||||
// remove app!
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInstalledApps() {
|
||||||
|
showLoadingIndicator();
|
||||||
|
// Get apps
|
||||||
|
Comms.getInstalledApps().then(appIDs => {
|
||||||
|
appsInstalled = appIDs;
|
||||||
refreshMyApps();
|
refreshMyApps();
|
||||||
});
|
refreshLibrary();
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
document.getElementById("myappsrefresh").addEventListener("click",event=>{
|
document.getElementById("myappsrefresh").addEventListener("click",event=>{
|
||||||
refreshMyApps();
|
getInstalledApps();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue