mirror of https://github.com/espruino/BangleApps
folderlauncher: add a way to move apps between folders from web interface
Web interface still can not do everything settings on phone can, still it is very useful to sort applications between folders.pull/3241/head
parent
79444702de
commit
7e4a83f928
|
@ -1,3 +1,4 @@
|
||||||
0.01: New app!
|
0.01: New app!
|
||||||
0.02: Handle files potentially not existing
|
0.02: Handle files potentially not existing
|
||||||
0.03: Add setting to disable vibration
|
0.03: Add setting to disable vibration
|
||||||
|
0.04: Add glue to allow moving applications from web interface
|
||||||
|
|
|
@ -33,4 +33,8 @@ Swiping up and down will scroll. Swiping from the left, using the back button, o
|
||||||
* Move app here: Display a list of apps. Selecting one moves it into the folder.
|
* Move app here: Display a list of apps. Selecting one moves it into the folder.
|
||||||
* One menu entry for each subfolder, which opens the folder management menu for that subfolder.
|
* One menu entry for each subfolder, which opens the folder management menu for that subfolder.
|
||||||
* View apps: Only present if this folder contains apps, Display a menu of all apps in the folder. This is for information only, tapping the apps does nothing.
|
* View apps: Only present if this folder contains apps, Display a menu of all apps in the folder. This is for information only, tapping the apps does nothing.
|
||||||
* Delete folder: Only present if not viewing the root folder. Delete the current folder and move all apps into the parent folder.
|
* Delete folder: Only present if not viewing the root folder. Delete the current folder and move all apps into the parent folder.
|
||||||
|
|
||||||
|
## Web interface
|
||||||
|
|
||||||
|
Limited editing is possible from the web interface. You can add folders and move applications between folders.
|
|
@ -0,0 +1,234 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||||
|
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="../../css/spectre-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin="anonymous">
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css">
|
||||||
|
<script type="module">
|
||||||
|
import vcf from 'https://cdn.jsdelivr.net/npm/vcf@2.1.1/+esm'
|
||||||
|
window.vcf = vcf;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
html, body { height: 100% }
|
||||||
|
.flex-col { display:flex; flex-direction:column; height:100% }
|
||||||
|
#map { width:100%; height:100% }
|
||||||
|
#tab-list { width:100%; height:100% }
|
||||||
|
|
||||||
|
/* https://stackoverflow.com/a/58686215 */
|
||||||
|
.arrow-icon {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
}
|
||||||
|
.arrow-icon > div {
|
||||||
|
margin-left: -1px;
|
||||||
|
margin-top: -3px;
|
||||||
|
transform-origin: center center;
|
||||||
|
font: 12px "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Folder Launcher v0.04</h1>
|
||||||
|
<div class="flex-col">
|
||||||
|
<div id="statusarea">
|
||||||
|
<button id="download" class="btn btn-error">Reload</button> <button id="upload" class="btn btn-primary">Upload</button>
|
||||||
|
<span id="status"></span>
|
||||||
|
<span id="routestatus"></span>
|
||||||
|
</div>
|
||||||
|
<div style="flex: 1">
|
||||||
|
<div id="tab-list">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>App</th>
|
||||||
|
<th>Folder</th>
|
||||||
|
<th>Move to</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="list">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<br>
|
||||||
|
<h4>Add a new folder</h4>
|
||||||
|
<form id="add_folder_form">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column col-3 col-xs-8">
|
||||||
|
<input class="form-input input-sm" type="text" id="add_folder_name" placeholder="Name">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column col-3 col-xs-8">
|
||||||
|
<button id="add_folder_button" class="btn btn-primary btn-sm">Add Folder</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||||
|
<script src="../../core/lib/interface.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var json = [];
|
||||||
|
var folders = [];
|
||||||
|
|
||||||
|
// ==========================================================================
|
||||||
|
/*** status ***/
|
||||||
|
|
||||||
|
function clean() {
|
||||||
|
$('#status').html('<i class="icon icon-check"></i> No pending changes.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function dirty() {
|
||||||
|
$('#status').html('<b><i class="icon icon-edit"></i> Changes have not been sent to the watch.</b>');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** util ***/
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/22706073
|
||||||
|
function escapeHTML(str){
|
||||||
|
return new Option(str).innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Bangle.js ***/
|
||||||
|
|
||||||
|
function gotStored(pts) {
|
||||||
|
json = pts;
|
||||||
|
renderAllApps();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================================================== LIST
|
||||||
|
|
||||||
|
var $name = document.getElementById('add_folder_name')
|
||||||
|
var $form = document.getElementById('add_folder_form')
|
||||||
|
var $add_folder_button = document.getElementById('add_folder_button')
|
||||||
|
var $list = document.getElementById('list')
|
||||||
|
|
||||||
|
$add_folder_button.addEventListener('click', event => {
|
||||||
|
event.preventDefault()
|
||||||
|
var name = $name.value.trim()
|
||||||
|
if(!name) return;
|
||||||
|
|
||||||
|
let n = {};
|
||||||
|
n.folders = {};
|
||||||
|
n.apps = [];
|
||||||
|
json.rootFolder.folders[name] = n;
|
||||||
|
|
||||||
|
renderAllApps()
|
||||||
|
$name.value = ''
|
||||||
|
|
||||||
|
dirty();
|
||||||
|
});
|
||||||
|
|
||||||
|
function moveTo(appId, dst){
|
||||||
|
let config = json;
|
||||||
|
let path = [];
|
||||||
|
path.push(dst);
|
||||||
|
console.log(`Move ${appId} to ${dst}`);
|
||||||
|
|
||||||
|
var folder = config.rootFolder;
|
||||||
|
for (var _i = 0, _a = config.apps[appId].folder; _i < _a.length; _i++) {
|
||||||
|
var folderName = _a[_i];
|
||||||
|
folder = folder.folders[folderName];
|
||||||
|
}
|
||||||
|
console.log(`Move ${appId} from ${folder}`);
|
||||||
|
folder.apps = folder.apps.filter(function (item) { return item != appId; });
|
||||||
|
config.apps[appId].folder = path.slice();
|
||||||
|
folder = config.rootFolder;
|
||||||
|
for (var _b = 0, path_2 = path; _b < path_2.length; _b++) {
|
||||||
|
var folderName = path_2[_b];
|
||||||
|
folder = folder.folders[folderName];
|
||||||
|
}
|
||||||
|
folder.apps.push(appId);
|
||||||
|
console.log(`Move done?`);
|
||||||
|
renderAllApps();
|
||||||
|
dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAppsList(){
|
||||||
|
folders = [];
|
||||||
|
$list.innerHTML = '';
|
||||||
|
Object.entries(json.rootFolder.folders).forEach(([name, params]) => {
|
||||||
|
console.log('Folders:', name);
|
||||||
|
folders.push(name);
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.entries(json.apps).forEach(([name, params]) => {
|
||||||
|
let $contact = document.createElement('tr')
|
||||||
|
let folder = params.folder;
|
||||||
|
console.log('Foreach', name, folder);
|
||||||
|
if (folder != '') {
|
||||||
|
$contact.innerHTML = `<td>${name}</td><td>${folder}</td>`;
|
||||||
|
} else {
|
||||||
|
$contact.innerHTML = `<td>${name}</td><td>/</td>`;
|
||||||
|
}
|
||||||
|
let buttons = "";
|
||||||
|
for (let f of folders) {
|
||||||
|
buttons += `<button class="btn" onclick="moveTo('${name}', '${f}')">${f}</button>`;
|
||||||
|
}
|
||||||
|
$contact.innerHTML += `<td>${buttons}</td>`;
|
||||||
|
$list.appendChild($contact)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderApps() {
|
||||||
|
renderAppsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAllApps() {
|
||||||
|
renderAppsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================================================== UPLOAD/DOWNLOAD
|
||||||
|
|
||||||
|
function downloadJSONfile(fileid, callback) {
|
||||||
|
// TODO: use interface.js-provided stuff?
|
||||||
|
Puck.write(`\x10(function() {
|
||||||
|
var pts = require("Storage").readJSON("${fileid}")||[{name:"NONE"}];
|
||||||
|
Bluetooth.print(JSON.stringify(pts));
|
||||||
|
})()\n`, contents => {
|
||||||
|
if (contents=='[{name:"NONE"}]') contents="[]";
|
||||||
|
var storedpts = JSON.parse(contents);
|
||||||
|
callback(storedpts);
|
||||||
|
clean();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadFile(fileid, contents) {
|
||||||
|
// TODO: use interface.js-provided stuff?
|
||||||
|
Puck.write(`\x10(function() {
|
||||||
|
require("Storage").write("${fileid}",'${contents}');
|
||||||
|
Bluetooth.print("OK");
|
||||||
|
})()\n`, ret => {
|
||||||
|
console.log("uploadFile", ret);
|
||||||
|
if (ret == "OK")
|
||||||
|
clean();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onInit() {
|
||||||
|
downloadJSONfile("folderlaunch.json", gotStored);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#download').on('click', function() {
|
||||||
|
downloadJSONfile("folderlaunch.json", gotStored);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#upload').click(function() {
|
||||||
|
var data = JSON.stringify(json);
|
||||||
|
uploadFile("folderlaunch.json",data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ========================================================================== FINALLY...
|
||||||
|
clean();
|
||||||
|
renderAllApps();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "folderlaunch",
|
"id": "folderlaunch",
|
||||||
"name": "Folder launcher",
|
"name": "Folder launcher",
|
||||||
"version": "0.03",
|
"version": "0.04",
|
||||||
"description": "Launcher that allows you to put your apps into folders",
|
"description": "Launcher that allows you to put your apps into folders",
|
||||||
"icon": "icon.png",
|
"icon": "icon.png",
|
||||||
"type": "launch",
|
"type": "launch",
|
||||||
|
@ -10,6 +10,7 @@
|
||||||
"BANGLEJS2"
|
"BANGLEJS2"
|
||||||
],
|
],
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
|
"interface": "interface.html",
|
||||||
"storage": [
|
"storage": [
|
||||||
{
|
{
|
||||||
"name": "folderlaunch.app.js",
|
"name": "folderlaunch.app.js",
|
||||||
|
|
Loading…
Reference in New Issue