1
0
Fork 0

Added proper remove app behaviour

master
Gordon Williams 2019-11-03 11:13:21 +00:00
parent d769d27fbd
commit 1eab865e45
3 changed files with 148 additions and 89 deletions

View File

@ -1,28 +1,35 @@
Puck.debug=3; 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 = { var Comms = {
uploadApp : app => { 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) => { 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");
Puck.write(fileContents,function() { console.log("uploadApp",fileContents);
resolve(); // 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) => { 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();
});
});
} }
}; };

View File

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

180
index.js
View File

@ -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,7 +67,98 @@ 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">
<figure class="avatar"><img src="apps/${app.icon?app.icon:"apps/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-subtitle">${escapeHtml(app.description)}</p>
</div>
<div class="tile-action">
<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>
`);
// 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 = '<div class="tile"><div class="tile-content" style="min-height:48px;"><div class="loading loading-lg"></div></div></div>';
}
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">
<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>
@ -74,88 +167,31 @@ function refreshLibrary() {
<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 icon-delete" appid="${app.id}"></i></button>
</div> </div>
</div> </div>
`); `);
// 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 => { 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 = appNameToApp(appid);
if (!app) return; removeApp(app);
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");
});
}); });
}); });
} }
refreshLibrary(); function getInstalledApps() {
// =========================================== My Apps showLoadingIndicator();
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 = '<div class="loading loading-lg"></div>';
// Get apps // Get apps
Comms.getInstalledApps().then(appIDs => { Comms.getInstalledApps().then(appIDs => {
tab.setAttribute("data-badge", appIDs.length); appsInstalled = appIDs;
panelbody.innerHTML = appIDs.map(appNameToApp).sort(appSorter).map(app => `<div class="tile"> refreshMyApps();
<div class="tile-icon"> refreshLibrary();
<figure class="avatar"><img src="apps/${app.icon}" alt="${escapeHtml(app.name)}"></figure>
</div>
<div class="tile-content">
<p class="tile-title text-bold">${escapeHtml(app.name)}</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>
`);
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();
});
});
});
}); });
} }
document.getElementById("myappsrefresh").addEventListener("click",event=>{ document.getElementById("myappsrefresh").addEventListener("click",event=>{
refreshMyApps(); getInstalledApps();
}); });