1
0
Fork 0

feat: "my files" tab to list and download files

master
feugy 2019-12-24 14:47:02 +01:00
parent d306209da7
commit 5278ec9929
3 changed files with 110 additions and 14 deletions

View File

@ -95,5 +95,35 @@ watchConnectionChange : cb => {
return () => {
clearInterval(interval);
};
},
listFiles : () => {
return new Promise((resolve,reject) => {
Puck.write("\x03",(result) => {
if (result===null) return reject("");
//use encodeURIComponent to serialize octal sequence of append files
Puck.eval('require("Storage").list().map(encodeURIComponent)', (files,err) => {
if (files===null) return reject(err || "");
files = files.map(decodeURIComponent);
console.log("listFiles", files);
resolve(files);
});
});
});
},
readFile : (file) => {
return new Promise((resolve,reject) => {
//encode name to avoid serialization issue due to octal sequence
const name = encodeURIComponent(file);
Puck.write("\x03",(result) => {
if (result===null) return reject("");
//TODO: big files will not fit in RAM.
//we should loop and read chunks one by one.
//Use btoa for binary content
Puck.eval(`btoa(require("Storage").read(decodeURIComponent("${name}"))))`, (content,err) => {
if (content===null) return reject(err || "");
resolve(atob(content));
});
});
});
}
};

View File

@ -56,6 +56,9 @@
<li class="tab-item" id="tab-myappscontainer">
<a href="javascript:showTab('myappscontainer')">My Apps</a>
</li>
<li class="tab-item" id="tab-myfscontainer">
<a href="javascript:showTab('myfscontainer')">My files</a>
</li>
<li class="tab-item" id="tab-aboutcontainer">
<a href="javascript:showTab('aboutcontainer')">About</a>
</li>
@ -87,12 +90,27 @@
<div class="container bangle-tab" id="myappscontainer" style="display:none">
<div class="panel">
<div class="panel-header" style="text-align:right">
<button class="btn" id="myappsrefresh">Refresh...</button>
<button class="btn refresh">Refresh...</button>
</div>
<div class="panel-body columns"><!-- apps go here --></div>
</div>
</div>
<div class="container bangle-tab" id="myfscontainer" style="display:none">
<div class="panel">
<div class="panel-header column col-12 container">
<div class="columns col-gapless">
<p class="column col-9">Files currently stored on the watch. Click row to download.</p>
<div class="column col-3" style="text-align:right">
<button class="btn refresh">Refresh...</button>
</div>
</div>
</div>
<table class="table table-striped table-hover panel-body">
</table>
</div>
</div>
<div class="container bangle-tab" id="aboutcontainer" style="display:none">
<div class="hero bg-gray">
<div class="hero-body">
@ -117,5 +135,6 @@
<script src="comms.js"></script>
<script src="appinfo.js"></script>
<script src="index.js"></script>
<script src="https://cdn.jsdelivr.net/npm/file-saver@2.0.2/dist/FileSaver.min.js" type="application/javascript"></script>
</body>
</html>

View File

@ -1,5 +1,6 @@
var appJSON = []; // List of apps and info from apps.json
var appsInstalled = []; // list of app JSON
var files = []; // list of files on Bangle
httpGet("apps.json").then(apps=>{
try {
@ -264,9 +265,9 @@ function appNameToApp(appName) {
};
}
function showLoadingIndicator() {
var panelbody = document.querySelector("#myappscontainer .panel-body");
var tab = document.querySelector("#tab-myappscontainer a");
function showLoadingIndicator(id) {
var panelbody = document.querySelector(`#${id} .panel-body`);
var tab = document.querySelector(`#tab-${id} a`);
// set badge up top
tab.classList.add("badge");
tab.setAttribute("data-badge", "");
@ -313,14 +314,21 @@ return `<div class="tile column col-6 col-sm-12 col-xs-12">
}
function getInstalledApps() {
showLoadingIndicator();
// Get apps
return Comms.getInstalledApps().then(appJSON => {
showLoadingIndicator("myappscontainer");
showLoadingIndicator("myfscontainer");
// Get apps and files
return Comms.getInstalledApps()
.then(appJSON => {
appsInstalled = appJSON;
handleConnectionChange(true);
refreshMyApps();
refreshLibrary();
});
})
.then(Comms.listFiles)
.then(list => {
files = list;
refreshMyFS();
})
.then(() => handleConnectionChange(true));
}
var connectMyDeviceBtn = document.getElementById("connectmydevice");
@ -330,11 +338,11 @@ function handleConnectionChange(connected) {
connectMyDeviceBtn.classList.toggle('is-connected', connected);
}
document.getElementById("myappsrefresh").addEventListener("click", () => {
htmlToArray(document.querySelectorAll(".btn.refresh")).map(button => button.addEventListener("click", () => {
getInstalledApps().catch(err => {
showToast("Getting app list failed, "+err,"error");
});
});
}));
connectMyDeviceBtn.addEventListener("click", () => {
if (connectMyDeviceBtn.classList.contains('is-connected')) {
Comms.disconnectDevice();
@ -364,6 +372,45 @@ librarySearchInput.addEventListener('input', evt => {
refreshLibrary();
});
// =========================================== My Files
function refreshMyFS() {
var panelbody = document.querySelector("#myfscontainer .panel-body");
var tab = document.querySelector("#tab-myfscontainer a");
tab.setAttribute("data-badge", files.length);
panelbody.innerHTML = `
<thead>
<tr>
<th>Name</th>
<th>Type</th>
</tr>
</thead>
<tbody>${
files.map(file =>
`<tr data-name="${file}"><td>${escapeHtml(file)}</td><td>${fileType(file).name}</td></li>`
).join("")}
</tbody>`;
htmlToArray(panelbody.getElementsByTagName("tr")).forEach(row => {
row.addEventListener("click",event => {
var name = event.target.closest('tr').dataset.name;
const type = fileType(name);
Comms.readFile(name).then(content => content.length && saveAs(new Blob([content], type), name));
});
});
}
function fileType(file) {
switch (file[0]) {
case "+": return { name: "App descriptor", type: "application/json;charset=utf-8" };
case "*": return { name: "App icon", type: "text/plain;charset=utf-8" };
case "-": return { name: "App code", type: "application/javascript;charset=utf-8" };
case "=": return { name: "Boot-time code", type: "application/javascript;charset=utf-8" };
default: return { name: "Plain", type: "text/plain;charset=utf-8" };
}
}
// =========================================== About
document.getElementById("settime").addEventListener("click",event=>{