mirror of https://github.com/espruino/BangleApps
Merge remote-tracking branch 'upstream/master'
commit
16f3f4253e
|
@ -12,8 +12,8 @@ body.select div.select,body.export div.export{display:block}
|
||||||
body.select div.export,body.export div.select{display:none}
|
body.select div.export,body.export div.select{display:none}
|
||||||
body.select div#tokens,body.editing div#edit,body.scanning div#scan,body.showqr div#showqr,body.export div#tokens{display:block}
|
body.select div#tokens,body.editing div#edit,body.scanning div#scan,body.showqr div#showqr,body.export div#tokens{display:block}
|
||||||
#tokens th,#tokens td{padding:5px}
|
#tokens th,#tokens td{padding:5px}
|
||||||
#tokens tr:nth-child(odd){background-color:#ccc}
|
#tokens tr:nth-child(odd){background-color:#f1f1fc}
|
||||||
#tokens tr:nth-child(even){background-color:#eee}
|
#tokens tr:nth-child(even){background-color:#fff}
|
||||||
#qr-canvas{margin:auto;width:calc(100%-20px);max-width:400px}
|
#qr-canvas{margin:auto;width:calc(100%-20px);max-width:400px}
|
||||||
#advbtn,#scan,#tokenqr table{text-align:center}
|
#advbtn,#scan,#tokenqr table{text-align:center}
|
||||||
#edittoken tbody#adv{display:none}
|
#edittoken tbody#adv{display:none}
|
||||||
|
@ -226,15 +226,18 @@ function editToken(id) {
|
||||||
markup += selectMarkup('algorithm', otpAlgos, tokens[id].algorithm);
|
markup += selectMarkup('algorithm', otpAlgos, tokens[id].algorithm);
|
||||||
markup += '</td></tr>';
|
markup += '</td></tr>';
|
||||||
markup += '</tbody><tr><td id="advbtn" colspan="2">';
|
markup += '</tbody><tr><td id="advbtn" colspan="2">';
|
||||||
markup += '<button type="button" onclick="document.getElementById(\'edittoken\').classList.toggle(\'showadv\')">Advanced</button>';
|
markup += '<button class="btn" type="button" onclick="document.getElementById(\'edittoken\').classList.toggle(\'showadv\')">Advanced</button>';
|
||||||
markup += '</td></tr></table></form>';
|
markup += '</td></tr></table></form>';
|
||||||
markup += '<button type="button" onclick="updateTokens()">Cancel Edit</button>';
|
markup += '<button class="btn" type="button" onclick="updateTokens()">Cancel Edit</button>';
|
||||||
markup += '<button type="button" onclick="saveEdit(' + id + ', false)">Save Changes</button>';
|
markup += ' ';
|
||||||
|
markup += '<button class="btn" type="button" onclick="saveEdit(' + id + ', false)">Save Changes</button>';
|
||||||
|
markup += ' ';
|
||||||
if (tokens[id].isnew) {
|
if (tokens[id].isnew) {
|
||||||
markup += '<button type="button" onclick="startScan(handleTokenQr,cancelTokenQr)">Scan QR</button>';
|
markup += '<button class="btn" type="button" onclick="startScan(handleTokenQr,cancelTokenQr)">Scan QR</button>';
|
||||||
} else {
|
} else {
|
||||||
markup += '<button type="button" onclick="showTokenQr()">Show QR</button>';
|
markup += '<button class="btn" type="button" onclick="showTokenQr()">Show QR</button>';
|
||||||
markup += '<button type="button" onclick="saveEdit(' + id + ', true)">Forget Token</button>';
|
markup += ' ';
|
||||||
|
markup += '<button class="btn" type="button" onclick="saveEdit(' + id + ', true)">Forget Token</button>';
|
||||||
}
|
}
|
||||||
document.getElementById('edit').innerHTML = markup;
|
document.getElementById('edit').innerHTML = markup;
|
||||||
document.body.className = 'editing';
|
document.body.className = 'editing';
|
||||||
|
@ -304,9 +307,23 @@ function updateTokens() {
|
||||||
return '<input name="exp_' + id + '" type="checkbox" onclick="exportTokens(false, \'' + id + '\')">';
|
return '<input name="exp_' + id + '" type="checkbox" onclick="exportTokens(false, \'' + id + '\')">';
|
||||||
};
|
};
|
||||||
const tokenButton = function(fn, id, label, dir) {
|
const tokenButton = function(fn, id, label, dir) {
|
||||||
return '<button type="button" onclick="' + fn + '(' + id + (dir ? ',' + dir : '') + ')">' + label + '</button>';
|
return '<button class="btn" type="button" onclick="' + fn + '(' + id + (dir ? ',' + dir : '') + ')">' + label + '</button>';
|
||||||
};
|
};
|
||||||
var markup = '<table><tr><th>';
|
var markup = '';
|
||||||
|
markup += '<div class="select">';
|
||||||
|
markup += '<button class="btn" type="button" onclick="addToken()">Add Token</button>';
|
||||||
|
markup += ' ';
|
||||||
|
markup += '<button class="btn" type="button" onclick="saveTokens()">Save to watch</button>';
|
||||||
|
markup += ' ';
|
||||||
|
markup += '<button class="btn" type="button" onclick="startScan(handleImportQr,cancelImportQr)">Import</button>';
|
||||||
|
markup += ' ';
|
||||||
|
markup += '<button class="btn" type="button" onclick="document.body.className=\'export\'">Export</button>';
|
||||||
|
markup += '</div><div class="export">';
|
||||||
|
markup += '<button class="btn" type="button" onclick="document.body.className=\'select\'">Cancel</button>';
|
||||||
|
markup += ' ';
|
||||||
|
markup += '<button class="btn" type="button" onclick="exportTokens(true, null)">Show QR</button>';
|
||||||
|
markup += '</div>';
|
||||||
|
markup += '<table><tr><th>';
|
||||||
markup += tokenSelect('all');
|
markup += tokenSelect('all');
|
||||||
markup += '</th><th>Token</th><th colspan="2">Order</th></tr>';
|
markup += '</th><th>Token</th><th colspan="2">Order</th></tr>';
|
||||||
/* any tokens marked new are cancelled new additions and must be removed */
|
/* any tokens marked new are cancelled new additions and must be removed */
|
||||||
|
@ -331,15 +348,6 @@ function updateTokens() {
|
||||||
markup += '</td></tr>';
|
markup += '</td></tr>';
|
||||||
}
|
}
|
||||||
markup += '</table>';
|
markup += '</table>';
|
||||||
markup += '<div class="select">';
|
|
||||||
markup += '<button type="button" onclick="addToken()">Add Token</button>';
|
|
||||||
markup += '<button type="button" onclick="saveTokens()">Save to watch</button>';
|
|
||||||
markup += '<button type="button" onclick="startScan(handleImportQr,cancelImportQr)">Import</button>';
|
|
||||||
markup += '<button type="button" onclick="document.body.className=\'export\'">Export</button>';
|
|
||||||
markup += '</div><div class="export">';
|
|
||||||
markup += '<button type="button" onclick="document.body.className=\'select\'">Cancel</button>';
|
|
||||||
markup += '<button type="button" onclick="exportTokens(true, null)">Show QR</button>';
|
|
||||||
markup += '</div>';
|
|
||||||
document.getElementById('tokens').innerHTML = markup;
|
document.getElementById('tokens').innerHTML = markup;
|
||||||
document.body.className = 'select';
|
document.body.className = 'select';
|
||||||
}
|
}
|
||||||
|
@ -604,7 +612,7 @@ function qrBack() {
|
||||||
<div id="scan">
|
<div id="scan">
|
||||||
<table>
|
<table>
|
||||||
<tr><td><canvas id="qr-canvas"></canvas></td></tr>
|
<tr><td><canvas id="qr-canvas"></canvas></td></tr>
|
||||||
<tr><td><button type="button" onclick="scanBack()">Cancel</button></td></tr>
|
<tr><td><button class="btn" type="button" onclick="scanBack()">Cancel</button></td></tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -613,7 +621,7 @@ function qrBack() {
|
||||||
|
|
||||||
<div id="showqr">
|
<div id="showqr">
|
||||||
<table><tr><td id="qrcode"></td></tr><tr><td>
|
<table><tr><td id="qrcode"></td></tr><tr><td>
|
||||||
<button type="button" onclick="qrBack()">Back</button>
|
<button class="btn" type="button" onclick="qrBack()">Back</button>
|
||||||
</td></tr></table>
|
</td></tr></table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -4,3 +4,4 @@
|
||||||
0.04: Add setting to switch color schemes. On Bangle 2 non-dithering colors will be used by default. Use localized names for months and days of the week (Language app needed).
|
0.04: Add setting to switch color schemes. On Bangle 2 non-dithering colors will be used by default. Use localized names for months and days of the week (Language app needed).
|
||||||
0.05: Update calendar weekend colors for start on Sunday
|
0.05: Update calendar weekend colors for start on Sunday
|
||||||
0.06: Use larger font for dates
|
0.06: Use larger font for dates
|
||||||
|
0.07: Fix off-by-one-error on previous month
|
||||||
|
|
|
@ -171,7 +171,7 @@ function drawCalendar(date) {
|
||||||
let days = [];
|
let days = [];
|
||||||
let nextMonthDay = 1;
|
let nextMonthDay = 1;
|
||||||
let thisMonthDay = 51;
|
let thisMonthDay = 51;
|
||||||
let prevMonthDay = monthMaxDayMap[month > 0 ? month - 1 : 11] - dowNorm;
|
let prevMonthDay = monthMaxDayMap[month > 0 ? month - 1 : 11] - dowNorm + 1;
|
||||||
for (let i = 0; i < colN * (rowN - 1) + 1; i++) {
|
for (let i = 0; i < colN * (rowN - 1) + 1; i++) {
|
||||||
if (i < dowNorm) {
|
if (i < dowNorm) {
|
||||||
days.push(prevMonthDay);
|
days.push(prevMonthDay);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "calendar",
|
"id": "calendar",
|
||||||
"name": "Calendar",
|
"name": "Calendar",
|
||||||
"version": "0.06",
|
"version": "0.07",
|
||||||
"description": "Simple calendar",
|
"description": "Simple calendar",
|
||||||
"icon": "calendar.png",
|
"icon": "calendar.png",
|
||||||
"screenshots": [{"url":"screenshot_calendar.png"}],
|
"screenshots": [{"url":"screenshot_calendar.png"}],
|
||||||
|
|
|
@ -39,4 +39,4 @@
|
||||||
0.24: Remove left-over debug statement
|
0.24: Remove left-over debug statement
|
||||||
0.25: Fix widget memory usage issues if message received and watch repeatedly calls Bangle.drawWidgets (fix #1550)
|
0.25: Fix widget memory usage issues if message received and watch repeatedly calls Bangle.drawWidgets (fix #1550)
|
||||||
0.26: Setting to auto-open music
|
0.26: Setting to auto-open music
|
||||||
0.27: Option to auto-unlock the watch when a new message arrives
|
0.27: Add 'mark all read' option to popup menu (fix #1624)
|
||||||
|
|
|
@ -302,6 +302,11 @@ function showMessageSettings(msg) {
|
||||||
saveMessages();
|
saveMessages();
|
||||||
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
|
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
|
||||||
},
|
},
|
||||||
|
/*LANG*/"Mark all read" : () => {
|
||||||
|
MESSAGES.forEach(msg => msg.new = false);
|
||||||
|
saveMessages();
|
||||||
|
checkMessages({clockIfNoMsg:0,clockIfAllRead:0,showMsgIfUnread:0,openMusic:0});
|
||||||
|
},
|
||||||
/*LANG*/"Delete all messages" : () => {
|
/*LANG*/"Delete all messages" : () => {
|
||||||
E.showPrompt(/*LANG*/"Are you sure?", {title:/*LANG*/"Delete All Messages"}).then(isYes => {
|
E.showPrompt(/*LANG*/"Are you sure?", {title:/*LANG*/"Delete All Messages"}).then(isYes => {
|
||||||
if (isYes) {
|
if (isYes) {
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: Initial version
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("kMigILIgPAAYMD/ADBwcGhkAwM5wcA/+2//Av/Rn/giFoyFggkUrFggEKlAkCiApCx+AAYNGoADBkU4AYMQj4DBvEICANkAoIPBgE2B4MAiMAH4MAwECAYNALYUgBIISCHYMYAoQWBAIMEgAYBAIMBwEDDQNgDwUf/4eBg4DCAA4"))
|
|
@ -0,0 +1,120 @@
|
||||||
|
var settings = Object.assign(require("Storage").readJSON("quicklaunch.json", true) || {});
|
||||||
|
|
||||||
|
var apps = require("Storage").list(/\.info$/).map(app=>{var a=require("Storage").readJSON(app,1);return a&&{name:a.name,type:a.type,sortorder:a.sortorder,src:a.src};}).filter(app=>app && (app.type=="app" || app.type=="launch" || app.type=="clock" || !app.type));
|
||||||
|
|
||||||
|
apps.sort((a,b)=>{
|
||||||
|
var n=(0|a.sortorder)-(0|b.sortorder);
|
||||||
|
if (n) return n; // do sortorder first
|
||||||
|
if (a.name<b.name) return -1;
|
||||||
|
if (a.name>b.name) return 1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
function save(key, value) {
|
||||||
|
settings[key] = value;
|
||||||
|
require("Storage").write("quicklaunch.json",settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick Launch menu
|
||||||
|
function showMainMenu() {
|
||||||
|
var mainmenu = {
|
||||||
|
"" : { "title" : "Quick Launch" },
|
||||||
|
"< Back" : ()=>{load();}
|
||||||
|
};
|
||||||
|
|
||||||
|
//List all selected apps
|
||||||
|
mainmenu["Left: "+settings.leftapp.name] = function() { E.showMenu(leftmenu); };
|
||||||
|
mainmenu["Right: "+settings.rightapp.name] = function() { E.showMenu(rightmenu); };
|
||||||
|
mainmenu["Up: "+settings.upapp.name] = function() { E.showMenu(upmenu); };
|
||||||
|
mainmenu["Down: "+settings.downapp.name] = function() { E.showMenu(downmenu); };
|
||||||
|
mainmenu["Tap: "+settings.tapapp.name] = function() { E.showMenu(tapmenu); };
|
||||||
|
|
||||||
|
return E.showMenu(mainmenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Left swipe menu
|
||||||
|
var leftmenu = {
|
||||||
|
"" : { "title" : "Left Swipe" },
|
||||||
|
"< Back" : showMainMenu
|
||||||
|
};
|
||||||
|
|
||||||
|
leftmenu["(none)"] = function() {
|
||||||
|
save("leftapp", {"name":"(none)"});
|
||||||
|
showMainMenu();
|
||||||
|
};
|
||||||
|
apps.forEach((a)=>{
|
||||||
|
leftmenu[a.name] = function() {
|
||||||
|
save("leftapp", a);
|
||||||
|
showMainMenu();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
//Right swipe menu
|
||||||
|
var rightmenu = {
|
||||||
|
"" : { "title" : "Right Swipe" },
|
||||||
|
"< Back" : showMainMenu
|
||||||
|
};
|
||||||
|
|
||||||
|
rightmenu["(none)"] = function() {
|
||||||
|
save("rightapp", {"name":"(none)"});
|
||||||
|
showMainMenu();
|
||||||
|
};
|
||||||
|
apps.forEach((a)=>{
|
||||||
|
rightmenu[a.name] = function() {
|
||||||
|
save("rightapp", a);
|
||||||
|
showMainMenu();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
//Up swipe menu
|
||||||
|
var upmenu = {
|
||||||
|
"" : { "title" : "Up Swipe" },
|
||||||
|
"< Back" : showMainMenu
|
||||||
|
};
|
||||||
|
|
||||||
|
upmenu["(none)"] = function() {
|
||||||
|
save("upapp", {"name":"(none)"});
|
||||||
|
showMainMenu();
|
||||||
|
};
|
||||||
|
apps.forEach((a)=>{
|
||||||
|
upmenu[a.name] = function() {
|
||||||
|
save("upapp", a);
|
||||||
|
showMainMenu();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
//Down swipe menu
|
||||||
|
var downmenu = {
|
||||||
|
"" : { "title" : "Down Swipe" },
|
||||||
|
"< Back" : showMainMenu
|
||||||
|
};
|
||||||
|
|
||||||
|
downmenu["(none)"] = function() {
|
||||||
|
save("downapp", {"name":"(none)"});
|
||||||
|
showMainMenu();
|
||||||
|
};
|
||||||
|
apps.forEach((a)=>{
|
||||||
|
downmenu[a.name] = function() {
|
||||||
|
save("downapp", a);
|
||||||
|
showMainMenu();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
//Tap menu
|
||||||
|
var tapmenu = {
|
||||||
|
"" : { "title" : "Tap" },
|
||||||
|
"< Back" : showMainMenu
|
||||||
|
};
|
||||||
|
|
||||||
|
tapmenu["(none)"] = function() {
|
||||||
|
save("tapapp", {"name":"(none)"});
|
||||||
|
showMainMenu();
|
||||||
|
};
|
||||||
|
apps.forEach((a)=>{
|
||||||
|
tapmenu[a.name] = function() {
|
||||||
|
save("tapapp", a);
|
||||||
|
showMainMenu();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
showMainMenu();
|
Binary file not shown.
After Width: | Height: | Size: 321 B |
|
@ -0,0 +1,67 @@
|
||||||
|
(function() {
|
||||||
|
var settings = Object.assign(require("Storage").readJSON("quicklaunch.json", true) || {});
|
||||||
|
|
||||||
|
//list all sources
|
||||||
|
var apps = require("Storage").list(/\.info$/).map(app=>{var a=require("Storage").readJSON(app,1);return a&&{src:a.src};});
|
||||||
|
|
||||||
|
//populate empty app list
|
||||||
|
|
||||||
|
if (!settings.leftapp) {
|
||||||
|
settings["leftapp"] = {"name":"(none)"};
|
||||||
|
require("Storage").write("quicklaunch.json",settings);
|
||||||
|
}
|
||||||
|
if (!settings.rightapp) {
|
||||||
|
settings["rightapp"] = {"name":"(none)"};
|
||||||
|
require("Storage").write("quicklaunch.json",settings);
|
||||||
|
}
|
||||||
|
if (!settings.upapp) {
|
||||||
|
settings["upapp"] = {"name":"(none)"};
|
||||||
|
require("Storage").write("quicklaunch.json",settings);
|
||||||
|
}
|
||||||
|
if (!settings.downapp) {
|
||||||
|
settings["downapp"] = {"name":"(none)"};
|
||||||
|
require("Storage").write("quicklaunch.json",settings);
|
||||||
|
}
|
||||||
|
if (!settings.tapapp) {
|
||||||
|
settings["tapapp"] = {"name":"(none)"};
|
||||||
|
require("Storage").write("quicklaunch.json",settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
//activate on clock faces
|
||||||
|
var sui = Bangle.setUI;
|
||||||
|
Bangle.setUI = function(mode, cb) {
|
||||||
|
sui(mode,cb);
|
||||||
|
if(!mode) return;
|
||||||
|
if ("object"==typeof mode) mode = mode.mode;
|
||||||
|
if (!mode.startsWith("clock")) return;
|
||||||
|
|
||||||
|
function tap() {
|
||||||
|
//tap, check if source exists, launch
|
||||||
|
if ((settings.tapapp.src) && apps.some(e => e.src === settings.tapapp.src)) load (settings.tapapp.src);
|
||||||
|
}
|
||||||
|
|
||||||
|
let drag;
|
||||||
|
let e;
|
||||||
|
|
||||||
|
Bangle.on("touch",tap);
|
||||||
|
Bangle.on("drag", e => {
|
||||||
|
if (!drag) { // start dragging
|
||||||
|
drag = {x: e.x, y: e.y};
|
||||||
|
} else if (!e.b) { // released
|
||||||
|
const dx = e.x-drag.x, dy = e.y-drag.y;
|
||||||
|
drag = null;
|
||||||
|
//horizontal swipes, check if source exists, launch
|
||||||
|
if (Math.abs(dx)>Math.abs(dy)+10) {
|
||||||
|
if ((settings.leftapp.src) && apps.some(e => e.src === settings.leftapp.src) && dx<0) load(settings.leftapp.src);
|
||||||
|
if ((settings.rightapp.src) && apps.some(e => e.src === settings.rightapp.src) && dx>0) load(settings.rightapp.src);
|
||||||
|
}
|
||||||
|
//vertical swipes, check if source exists, launch
|
||||||
|
else if (Math.abs(dy)>Math.abs(dx)+10) {
|
||||||
|
if ((settings.upapp.src) && apps.some(e => e.src === settings.upapp.src) && dy<0) load(settings.upapp.src);
|
||||||
|
if ((settings.downapp.src) && apps.some(e => e.src === settings.downapp.src) && dy>0) load(settings.downapp.src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
})();
|
|
@ -0,0 +1,14 @@
|
||||||
|
{ "id": "quicklaunch",
|
||||||
|
"name": "Quick Launch",
|
||||||
|
"icon": "app.png",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "Tap or swipe left/right/up/down on your clock face to launch up to five apps of your choice.",
|
||||||
|
"tags": "tools, system",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"storage": [
|
||||||
|
{"name":"quicklaunch.app.js","url":"app.js"},
|
||||||
|
{"name":"quicklaunch.boot.js","url":"boot.js"},
|
||||||
|
{"name":"quicklaunch.img","url":"app-icon.js","evaluate":true}
|
||||||
|
],
|
||||||
|
"data": [{"name":"quicklaunch.json"}]
|
||||||
|
}
|
|
@ -9,4 +9,5 @@
|
||||||
0.08: Added support for notifications from exstats. Support all stats from exstats
|
0.08: Added support for notifications from exstats. Support all stats from exstats
|
||||||
0.09: Fix broken start/stop if recording not enabled (fix #1561)
|
0.09: Fix broken start/stop if recording not enabled (fix #1561)
|
||||||
0.10: Don't allow the same setting to be chosen for 2 boxes (fix #1578)
|
0.10: Don't allow the same setting to be chosen for 2 boxes (fix #1578)
|
||||||
0.11: Notifications fixes
|
0.11: Notifications fixes
|
||||||
|
0.12: Fix for recorder not stopping at end of run. Bug introduced in 0.11
|
|
@ -59,7 +59,7 @@ function onStartStop() {
|
||||||
layout.render();
|
layout.render();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} else if (!settings.record && WIDGETS["recorder"]) {
|
} else {
|
||||||
prepPromises.push(
|
prepPromises.push(
|
||||||
WIDGETS["recorder"].setRecording(false)
|
WIDGETS["recorder"].setRecording(false)
|
||||||
);
|
);
|
||||||
|
@ -68,7 +68,7 @@ function onStartStop() {
|
||||||
|
|
||||||
if (!prepPromises.length) // fix for Promise.all bug in 2v12
|
if (!prepPromises.length) // fix for Promise.all bug in 2v12
|
||||||
prepPromises.push(Promise.resolve());
|
prepPromises.push(Promise.resolve());
|
||||||
|
|
||||||
Promise.all(prepPromises)
|
Promise.all(prepPromises)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (running) {
|
if (running) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{ "id": "run",
|
{ "id": "run",
|
||||||
"name": "Run",
|
"name": "Run",
|
||||||
"version":"0.11",
|
"version":"0.12",
|
||||||
"description": "Displays distance, time, steps, cadence, pace and more for runners.",
|
"description": "Displays distance, time, steps, cadence, pace and more for runners.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "run,running,fitness,outdoors,gps",
|
"tags": "run,running,fitness,outdoors,gps",
|
||||||
|
|
|
@ -91,7 +91,7 @@
|
||||||
];
|
];
|
||||||
notificationsMenu[/*LANG*/"Dist Pattern"] = {
|
notificationsMenu[/*LANG*/"Dist Pattern"] = {
|
||||||
value: Math.max(0,vibTimes.findIndex((p) => JSON.stringify(p) === JSON.stringify(settings.notify.dist.notifications))),
|
value: Math.max(0,vibTimes.findIndex((p) => JSON.stringify(p) === JSON.stringify(settings.notify.dist.notifications))),
|
||||||
min: 0, max: vibTimes.length,
|
min: 0, max: vibTimes.length - 1,
|
||||||
format: v => vibPatterns[v]||/*LANG*/"Off",
|
format: v => vibPatterns[v]||/*LANG*/"Off",
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.notify.dist.notifications = vibTimes[v];
|
settings.notify.dist.notifications = vibTimes[v];
|
||||||
|
@ -101,7 +101,7 @@
|
||||||
}
|
}
|
||||||
notificationsMenu[/*LANG*/"Step Pattern"] = {
|
notificationsMenu[/*LANG*/"Step Pattern"] = {
|
||||||
value: Math.max(0,vibTimes.findIndex((p) => JSON.stringify(p) === JSON.stringify(settings.notify.step.notifications))),
|
value: Math.max(0,vibTimes.findIndex((p) => JSON.stringify(p) === JSON.stringify(settings.notify.step.notifications))),
|
||||||
min: 0, max: vibTimes.length,
|
min: 0, max: vibTimes.length - 1,
|
||||||
format: v => vibPatterns[v]||/*LANG*/"Off",
|
format: v => vibPatterns[v]||/*LANG*/"Off",
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.notify.step.notifications = vibTimes[v];
|
settings.notify.step.notifications = vibTimes[v];
|
||||||
|
@ -111,7 +111,7 @@
|
||||||
}
|
}
|
||||||
notificationsMenu[/*LANG*/"Time Pattern"] = {
|
notificationsMenu[/*LANG*/"Time Pattern"] = {
|
||||||
value: Math.max(0,vibTimes.findIndex((p) => JSON.stringify(p) === JSON.stringify(settings.notify.time.notifications))),
|
value: Math.max(0,vibTimes.findIndex((p) => JSON.stringify(p) === JSON.stringify(settings.notify.time.notifications))),
|
||||||
min: 0, max: vibTimes.length,
|
min: 0, max: vibTimes.length - 1,
|
||||||
format: v => vibPatterns[v]||/*LANG*/"Off",
|
format: v => vibPatterns[v]||/*LANG*/"Off",
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.notify.time.notifications = vibTimes[v];
|
settings.notify.time.notifications = vibTimes[v];
|
||||||
|
|
|
@ -2,39 +2,63 @@ Todo List
|
||||||
========
|
========
|
||||||
|
|
||||||
This is a simple Todo List application.
|
This is a simple Todo List application.
|
||||||
|
The content is loaded from a JSON file.
|
||||||
|
A task can be marked as completed or uncompleted.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The content is loaded from a JSON file.
|
Once installed, the list can be modified via the `Download data from app` icon in the [Bangle.js App Store](https://banglejs.com/apps/) (TodoList app).
|
||||||
You can mark a task as completed.
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
JSON file content example:
|
JSON file content example:
|
||||||
```javascript
|
```javascript
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
name: "Pro",
|
"name": "Pro",
|
||||||
children: [
|
"children": [
|
||||||
{
|
{
|
||||||
name: "Read doc",
|
"name": "Read doc",
|
||||||
done: true,
|
"done": true,
|
||||||
children: [],
|
"children": []
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Pers",
|
"name": "Pers",
|
||||||
children: [
|
"children": [
|
||||||
{
|
{
|
||||||
name: "Grocery",
|
"name": "Grocery",
|
||||||
children: [
|
"children": [
|
||||||
{ name: "Milk", done: false, children: [] },
|
{
|
||||||
{ name: "Eggs", done: false, children: [] },
|
"name": "Milk",
|
||||||
{ name: "Cheese", done: false, children: [] },
|
"done": false,
|
||||||
],
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Eggs",
|
||||||
|
"done": false,
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Cheese",
|
||||||
|
"done": false,
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{ name: "Workout", done: false, children: [] },
|
{
|
||||||
{ name: "Learn Rust", done: false, children: [] },
|
"name": "Workout",
|
||||||
],
|
"done": false,
|
||||||
},
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Learn Rust",
|
||||||
|
"done": false,
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
|
@ -0,0 +1,135 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="../../css/spectre.min.css" />
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
.alert {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f44336; /* Red */
|
||||||
|
color: white;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="info"></div>
|
||||||
|
|
||||||
|
<button id="btnReload" class="btn btn-primary">Reload from watch</button>
|
||||||
|
<button id="btnUpload" class="btn btn-primary">Upload to watch</button>
|
||||||
|
<button id="btnDownload" class="btn btn-primary">Download</button>
|
||||||
|
|
||||||
|
<pre id="todos" contenteditable></pre>
|
||||||
|
|
||||||
|
<script src="../../core/lib/interface.js"></script>
|
||||||
|
<script>
|
||||||
|
const fileTodoList = "todolist.json";
|
||||||
|
|
||||||
|
function errorFormat() {
|
||||||
|
var date = new Date();
|
||||||
|
var error =
|
||||||
|
'<p class="alert">' +
|
||||||
|
date.toUTCString() +
|
||||||
|
" : Wrong format, it should be JSON" +
|
||||||
|
"</p>";
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEditableContent() {
|
||||||
|
return document.getElementById("todos").innerHTML.replace(/<[^>]*>/g, '');;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isJsonString(str) {
|
||||||
|
try {
|
||||||
|
JSON.parse(str);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(str)
|
||||||
|
console.log(e)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadFile(fileid, contents) {
|
||||||
|
Puck.write(
|
||||||
|
`\x10(function() {
|
||||||
|
require("Storage").write("${fileid}",'${contents}');
|
||||||
|
Bluetooth.print("OK");
|
||||||
|
})()\n`,
|
||||||
|
(ret) => {
|
||||||
|
console.log("uploadFile", ret);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load settings JSON file from the watch.
|
||||||
|
*/
|
||||||
|
function loadTodos() {
|
||||||
|
document.getElementById("info").innerHTML = "";
|
||||||
|
Util.showModal("Loading...");
|
||||||
|
Puck.eval(`require('Storage').readJSON("${fileTodoList}")`, (data) => {
|
||||||
|
document.getElementById("todos").innerHTML = JSON.stringify(
|
||||||
|
data,
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
Util.hideModal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/* Save settings as a JSON file on the watch.
|
||||||
|
*/
|
||||||
|
function uploadTodos() {
|
||||||
|
document.getElementById("info").innerHTML = "";
|
||||||
|
Util.showModal("Uploading...");
|
||||||
|
let jsonTodos = getEditableContent();
|
||||||
|
if (isJsonString(jsonTodos)) {
|
||||||
|
let shortJsonTodos = JSON.stringify(JSON.parse(jsonTodos));
|
||||||
|
uploadFile(fileTodoList, shortJsonTodos);
|
||||||
|
} else {
|
||||||
|
document.getElementById("info").innerHTML = errorFormat();
|
||||||
|
}
|
||||||
|
Util.hideModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadTodos() {
|
||||||
|
document.getElementById("info").innerHTML = "";
|
||||||
|
Util.showModal("Downloading...");
|
||||||
|
let jsonTodos = getEditableContent();
|
||||||
|
if (isJsonString(jsonTodos)) {
|
||||||
|
var a = document.createElement("a"),
|
||||||
|
file = new Blob([jsonTodos], { type: "application/json" });
|
||||||
|
var url = URL.createObjectURL(file);
|
||||||
|
a.href = url;
|
||||||
|
a.download = fileTodoList;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
setTimeout(function () {
|
||||||
|
document.body.removeChild(a);
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
}, 0);
|
||||||
|
} else {
|
||||||
|
document.getElementById("info").innerHTML = errorFormat();
|
||||||
|
}
|
||||||
|
Util.hideModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
document
|
||||||
|
.getElementById("btnUpload")
|
||||||
|
.addEventListener("click", function () {
|
||||||
|
uploadTodos();
|
||||||
|
});
|
||||||
|
document
|
||||||
|
.getElementById("btnDownload")
|
||||||
|
.addEventListener("click", function () {
|
||||||
|
downloadTodos();
|
||||||
|
});
|
||||||
|
document
|
||||||
|
.getElementById("btnReload")
|
||||||
|
.addEventListener("click", function () {
|
||||||
|
loadTodos();
|
||||||
|
});
|
||||||
|
function onInit() {
|
||||||
|
loadTodos();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -10,6 +10,7 @@
|
||||||
"tags": "tool,todo",
|
"tags": "tool,todo",
|
||||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
|
"interface": "interface.html",
|
||||||
"storage": [
|
"storage": [
|
||||||
{ "name": "todolist.app.js", "url": "app.js" },
|
{ "name": "todolist.app.js", "url": "app.js" },
|
||||||
{ "name": "todolist.img", "url": "app-icon.js", "evaluate": true }
|
{ "name": "todolist.img", "url": "app-icon.js", "evaluate": true }
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 72 KiB |
|
@ -135,7 +135,7 @@ Bangle.on("GPS", function(fix) {
|
||||||
if (stats["dist"]) stats["dist"].emit("changed",stats["dist"]);
|
if (stats["dist"]) stats["dist"].emit("changed",stats["dist"]);
|
||||||
var duration = Date.now() - state.startTime; // in ms
|
var duration = Date.now() - state.startTime; // in ms
|
||||||
state.avrSpeed = state.distance * 1000 / duration; // meters/sec
|
state.avrSpeed = state.distance * 1000 / duration; // meters/sec
|
||||||
state.curSpeed = state.curSpeed*0.8 + fix.speed*0.2/3.6; // meters/sec
|
if (!isNaN(fix.speed)) state.curSpeed = state.curSpeed*0.8 + fix.speed*0.2/3.6; // meters/sec
|
||||||
if (stats["pacea"]) stats["pacea"].emit("changed",stats["pacea"]);
|
if (stats["pacea"]) stats["pacea"].emit("changed",stats["pacea"]);
|
||||||
if (stats["pacec"]) stats["pacec"].emit("changed",stats["pacec"]);
|
if (stats["pacec"]) stats["pacec"].emit("changed",stats["pacec"]);
|
||||||
if (stats["speed"]) stats["speed"].emit("changed",stats["speed"]);
|
if (stats["speed"]) stats["speed"].emit("changed",stats["speed"]);
|
||||||
|
|
Loading…
Reference in New Issue