diff --git a/apps/noteify/app.js b/apps/noteify/app.js index 7c3fd9d4c..3ac107f03 100644 --- a/apps/noteify/app.js +++ b/apps/noteify/app.js @@ -1,227 +1,33 @@ +Bangle.loadWidgets(); +Bangle.drawWidgets(); + var notes = require("Storage").readJSON("noteify.json", true) || []; -var alarms = require("Storage").readJSON("alarm.json",1)||[]; +var alarms = require("sched").getAlarms(); +msg = ""; -var note = ""; -var textbox = ""; -var pad = []; -var layer = 0; -var enableNote = 0; -var iniNote = 0; -var editNote; -var textBound = 0; -var msg = ""; -var enableAlarms = (!require('Storage').read("alarm.js")) ? 0 : 1; - -function saveNote() { - return { - note : note - }; +function startNote(idx) { + idx == undefined ? note = "" : note = notes[idx].note; + require("textinput").input({text:note}).then(result => { + idx == undefined ? notes.push({"note" : result}) : notes[idx].note = result; + require("Storage").write("noteify.json",JSON.stringify(notes)); + showMainMenu(); + }); } -class keyPad { - constructor(x1, y1, x2, y2, func) { - this.x1 = x1; - this.y1 = y1; - this.x2 = x2; - this.y2 = y2; - this.func = func; - } - get funcName() { - this.func = func; - } - set funcName(a) { - this.func = a; - } - draw() { - g.drawRect(this.x1, this.y1, this.x2, this.y2).setFont("12x20",1).setFontAlign(0, 0, 0).drawString(this.func, (((this.x2-this.x1)/2)+this.x1), (((this.y2-this.y1)/2)+this.y1)); - } - onTouch() { - //prevent keypad from working on menu - if (enableNote == 1) { - Bangle.on("touch", (button, xy) => { - if ((xy.x >= this.x1) && (xy.x <= this.x2) && (xy.y >= this.y1) && (xy.y <= this.y2) && (this.func) && (enableNote == 1)) { - if (this.func == "space") this.func = " "; - if (this.func == "<-") { - note = note.slice(0, -1); - this.func = ""; - } - else if ((this.func == "SAVE") || this.func == "BACK") { - if (note != "") { - //save edits to existing note - if (editNote != undefined) { - notes[editNote].note = note; - } - //push new note - else notes.push(saveNote()); - require("Storage").write("noteify.json",JSON.stringify(notes)); - } - //exit to menu, reset to default settings - enableNote = 0; - g.clear(); - note = ""; - layer = 0; - textBound = 0; - //prevent main menu from detecting save tap?? - setTimeout(function(){ - (editNote != undefined) ? showEditMenu(editNote) : showMainMenu(); - editNote = undefined; - }, 0); - return; - } - else note += this.func; - //scroll textbox if > 170 pixels or < 165 pixels - textbox = note.substring(textBound, note.length); - while ((g.setFont("12x20", 1).stringWidth(textbox)) > 165) { - textBound++; - textbox = note.substring(textBound, note.length); - } - while (((g.setFont("12x20", 1).stringWidth(textbox)) < 160) && textBound > 0) { - textBound--; - textbox = note.substring(textBound, note.length); - } - renderKeys(); - } - }); - } - } -} - -function renderKeys() { - g.clear(); - if ((editNote == undefined) && (note.length == 0)) { - g.setFont("Vector", 14).setFontAlign(-1, 0).drawString(textbox, 4, 11); - g.setFont("12x20", 1); - } - else g.setFont("12x20", 1).setFontAlign(-1, 0).drawString(textbox, 4, 11); - - if (layer == 0) { - var a = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "SAVE", "space", "<-"]; - if (note == "") a[9] = "BACK"; - for (var i = 0; i < a.length; i++) { - pad[i].funcName = a[i]; - } - } - else if (layer == 1) { - var a = ["J", "K", "L", "M", "N", "O", "P", "Q", "R", "SAVE", "space", "<-"]; - if (note == "") a[9] = "BACK"; - for (var i = 0; i < a.length; i++) { - pad[i].funcName = a[i]; - } - } - else if (layer == 2) { - var a = ["S", "T", "U", "V", "W", "X", "Y", "Z", "0", "SAVE", "space", "<-"]; - if (note == "") a[9] = "BACK"; - for (var i = 0; i < a.length; i++) { - pad[i].funcName = a[i]; - } - } - else if (layer == 3) { - var a = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "SAVE", "space", "<-"]; - if (note == "") a[9] = "BACK"; - for (var i = 0; i < a.length; i++) { - pad[i].funcName = a[i]; - } - } - else if (layer == 4) { - var a = [".", ",", "?", "!", "(", ")", "-", "\'", ":", "SAVE", "space", "<-"]; - if (note == "") a[9] = "BACK"; - for (var i = 0; i < a.length; i++) { - pad[i].funcName = a[i]; - } - } - - for (var j = 0; j < pad.length; j++) { - pad[j].draw(); - } -} - -function startNote() { - //edit note - if (editNote != undefined) { - note = notes[editNote].note; - //textbox start at end of note - textbox = note.substring(textBound - 14); - while ((g.setFont("12x20", 1).stringWidth(textbox)) > 165) { - textBound++; - textbox = note.substring(textBound, note.length); - } - } - //new note - else textbox = "SWIPE ANY DIRECTION"; - //prevent keypad being drawn multiple times - if (iniNote == 0) { - pad[0] = new keyPad(0, 21, 57, 58); - pad[1] = new keyPad(59, 21, 116, 58); - pad[2] = new keyPad(118, 21, 175, 58); - pad[3] = new keyPad(0, 60, 57, 97); - pad[4] = new keyPad(59, 60, 116, 97); - pad[5] = new keyPad(118, 60, 175, 97); - pad[6] = new keyPad(0, 99, 57, 136); - pad[7] = new keyPad(59, 99, 116, 136); - pad[8] = new keyPad(118, 99, 175, 136); - pad[9] = new keyPad(0, 138, 57, 175); - pad[10] = new keyPad(59, 138, 116, 175); - pad[11] = new keyPad(118, 138, 175, 175); - - var drag; - var e; - - Bangle.on("drag", e => { - if (enableNote == 1) { - 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 - if (Math.abs(dx)>Math.abs(dy)+10) { - //swipe left - if (dx<0) layer == 4 ? layer = 0 : layer++; - //swipe right - if (dx>0) layer == 0 ? layer = 4 : layer--; - } - //vertical swipes - else if (Math.abs(dy)>Math.abs(dx)+10) { - //swipe down, hide keypad - if (dy>0) { - enableNote = 2; - g.clear(); - g.setFontAlign(-1, -1).drawString(g.wrapString(note, g.getWidth()).join("\n"), 0, 0); - return; - } - } - } - renderKeys(); - } - else if (enableNote == 2) { - 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; - //vertical swipes - if (Math.abs(dy)>Math.abs(dx)+10) { - //swipe up, reveal keypad - if ((dy<0) && (enableNote == 2)) { - enableNote = 1; - renderKeys(); - } - } - } - } - }); - } - renderKeys(); - for (var i = 0; i < pad.length; i++) { - pad[i].draw(); - //prevent tap zones being activated multiple times - if (iniNote == 0) { - pad[i].onTouch(); - } - } - iniNote = 1; +function viewNote(idx) { + var textY = 30; + var textBound = g.stringMetrics(g.setColor(g.theme.fg).setFont("6x8:2").setFontAlign(-1, -1).drawString(g.wrapString(notes[idx].note, g.getWidth()).join("\n"), 0, textY)).height; + Bangle.setUI({mode:"custom", drag:e=>{ + textY += e.dy; + g.setClipRect(0, 30, g.getWidth(), g.getHeight()); + if (textY > 30) textY = 30; + if (textY < textBound) textY = textBound; + g.clearRect(0, 30, g.getWidth(), g.getHeight()).setColor(g.theme.fg).setFont("6x8:2").setFontAlign(-1, -1).drawString(g.wrapString(notes[idx].note, g.getWidth()).join("\n"), 0, textY); + },back:()=>{ + Bangle.setUI(); + showEditMenu(idx); + }}); + } function showMainMenu() { @@ -229,7 +35,6 @@ function showMainMenu() { "" : { "title" : "Noteify" }, "< Back" : function() { load(); }, "New note" : function() { - enableNote = 1; E.showMenu(); startNote(); }, @@ -237,21 +42,23 @@ function showMainMenu() { }; notes.forEach((a, idx) => { - mainMenu[notes[idx].note.length > 15 ? notes[idx].note.substring(0, 15)+"..." : notes[idx].note] = function () { showEditMenu(idx);}; + mainMenu[notes[idx].note.length > 12 ? notes[idx].note.substring(0, 12)+"..." : notes[idx].note] = function () { showEditMenu(idx);}; }); + msg = ""; E.showMenu(mainMenu); } function showEditMenu(idx) { var moveNote = notes[idx].note; - var newPos = idx+1; var editMenu = { - "" : { "title" : notes[idx].note.length > 15 ? notes[idx].note.substring(0, 15)+"..." : notes[idx].note }, - "View/edit note" : function() { - editNote = idx; - enableNote = 1; + "" : { "title" : notes[idx].note.length > 12 ? notes[idx].note.substring(0, 12)+"..." : notes[idx].note }, + "View note" : function() { E.showMenu(); - startNote(); + viewNote(idx); + }, + "Edit note" : function() { + E.showMenu(); + startNote(idx); }, "Delete note" : function() { notes.splice(idx,1); @@ -268,42 +75,53 @@ function showEditMenu(idx) { editTimer(-1); }, "Change position" : { - value : newPos, + value : idx+1, min : 1, max : notes.length, wrap : true, onchange : function(v) { - newPos = v; + //save changes from change position + if (v-1 != idx) { + notes.splice(v-1, 0, notes.splice(idx, 1)[0]); + require("Storage").write("noteify.json",JSON.stringify(notes)); + } }, }, "< Back" : function() { - //save changes from change position - if (newPos-1 != idx) { - var moveNote = notes[idx]; - notes.splice(newPos-1, 0, notes.splice(idx, 1)[0]); - require("Storage").write("noteify.json",JSON.stringify(notes)); - } showMainMenu(); }, }; E.showMenu(editMenu); } -function getCurrentHr() { - var time = new Date(); - return time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600); +function decodeTime(t) { + t = 0|t; // sanitise + var hrs = 0|(t/3600000); + return { hrs : hrs, mins : Math.round((t-hrs*3600000)/60000) }; +} + +// time in { hrs, mins } -> ms +function encodeTime(o) { + return o.hrs*3600000 + o.mins*60000; } function formatTime(t) { - var hrs = 0|t; - var mins = Math.round((t-hrs)*60); - return hrs+":"+("0"+mins).substr(-2); + var o = decodeTime(t); + return o.hrs+":"+("0"+o.mins).substr(-2); } -function formatMins(t) { - mins = (0|t)%60; - hrs = 0|(t/60); - return hrs+":"+("0"+mins).substr(-2); +function getCurrentTime() { + var time = new Date(); + return ( + time.getHours() * 3600000 + + time.getMinutes() * 60000 + + time.getSeconds() * 1000 + ); +} + +function saveAndReload() { + require("sched").setAlarms(alarms); + require("sched").reload(); } function showAlarmMenu() { @@ -314,148 +132,161 @@ function showAlarmMenu() { 'New Timer': ()=>editTimer(-1) }; alarms.forEach((alarm,idx)=>{ - if (alarm.timer) { - txt = "TIMER "+(alarm.on?"on ":"off ")+formatMins(alarm.timer); - } else { - txt = "ALARM "+(alarm.on?"on ":"off ")+formatTime(alarm.hr); - if (alarm.rp) txt += " (repeat)"; - } - menu[txt] = function() { - if (alarm.timer) editTimer(idx); - else editAlarm(idx); + var txt; // a leading space is currently required (JS error in Espruino 2v12) + if (alarm.timer) + txt = "Timer"+" "+formatTime(alarm.timer); + else + txt = "Alarm"+" "+formatTime(alarm.t); + if (alarm.rp) txt += "\0"+atob("FBaBAAABgAAcAAHn//////wAHsABzAAYwAAMAADAAAAAAwAAMAADGAAzgAN4AD//////54AAOAABgAA="); + menu[txt] = { + value : "\0"+atob(alarm.on?"EhKBAH//v/////////////5//x//j//H+eP+Mf/A//h//z//////////3//g":"EhKBAH//v//8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA///3//g"), + onchange : function() { + if (alarm.timer) editTimer(idx, alarm); + else editAlarm(idx, alarm); + } }; }); - msg = ""; - if (enableAlarms == 1) return E.showMenu(menu); + if (WIDGETS["alarm"]) WIDGETS["alarm"].reload(); + return E.showMenu(menu); } -function editAlarm(alarmIndex) { - var newAlarm = alarmIndex<0; - var hrs = 12; - var mins = 0; - var en = true; - var repeat = true; - var as = false; - if (!newAlarm) { - var a = alarms[alarmIndex]; - hrs = 0|a.hr; - mins = Math.round((a.hr-hrs)*60); - en = a.on; - repeat = a.rp; - as = a.as; - msg = (!a.msg) ? "" : a.msg; - } - var alarmTitle = (msg == "") ? 'Alarm' : (msg.length > 15) ? msg.substring(0, 15)+"..." : msg; +function editDOW(dow, onchange) { + const menu = { + '': { 'title': 'Days of Week' }, + '< Back' : () => onchange(dow) + }; + for (var i = 0; i < 7; i++) (i => { + var dayOfWeek = require("locale").dow({ getDay: () => i }); + menu[dayOfWeek] = { + value: !!(dow&(1< v ? "Yes" : "No", + onchange: v => v ? dow |= 1< 12) ? a.msg.substring(0, 12)+"..." : msg; + const menu = { '': { 'title': alarmTitle }, - '< Back' : showAlarmMenu, + '< Back' : () => showAlarmMenu(), 'Hours': { - value: hrs, min : 0, max : 23, wrap : true, - onchange: v => hrs=v + value: t.hrs, min : 0, max : 23, wrap : true, + onchange: v => t.hrs=v }, 'Minutes': { - value: mins, min : 0, max : 59, wrap : true, - onchange: v => mins=v + value: t.mins, min : 0, max : 59, wrap : true, + onchange: v => t.mins=v }, 'Enabled': { - value: en, + value: a.on, format: v=>v?"On":"Off", - onchange: v=>en=v + onchange: v=>a.on=v }, 'Repeat': { - value: en, + value: a.rp, format: v=>v?"Yes":"No", - onchange: v=>repeat=v + onchange: v=>a.rp=v }, + 'Days': { + value: "SMTWTFS".split("").map((d,n)=>a.dow&(1< editDOW(a.dow, d=>{a.dow=d;editAlarm(alarmIndex,a)}) + }, + 'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ), 'Auto snooze': { - value: as, + value: a.as, format: v=>v?"Yes":"No", - onchange: v=>as=v + onchange: v=>a.as=v } }; - function getAlarm() { - var hr = hrs+(mins/60); - var day = 0; - // If alarm is for tomorrow not today (eg, in the past), set day - if (hr < getCurrentHr()) - day = (new Date()).getDate(); - // Save alarm - return { - on : en, hr : hr, - last : day, rp : repeat, as: as, msg: msg - }; - } - menu["> Save"] = function() { - if (newAlarm) alarms.push(getAlarm()); - else alarms[alarmIndex] = getAlarm(); - require("Storage").write("alarm.json",JSON.stringify(alarms)); - showAlarmMenu(); + menu["Save"] = function() { + a.t = encodeTime(t); + if (a.t < getCurrentTime()) + a.day = (new Date()).getDate(); + if (newAlarm) alarms.push(a); + else alarms[alarmIndex] = a; + saveAndReload(); + showMainMenu(); }; if (!newAlarm) { - menu["> Delete"] = function() { + menu["Delete"] = function() { alarms.splice(alarmIndex,1); - require("Storage").write("alarm.json",JSON.stringify(alarms)); - showAlarmMenu(); + saveAndReload(); + showMainMenu(); }; } - if (enableAlarms == 1) return E.showMenu(menu); + return E.showMenu(menu); } -function editTimer(alarmIndex) { +function editTimer(alarmIndex, alarm) { var newAlarm = alarmIndex<0; - var hrs = 0; - var mins = 5; - var en = true; - if (!newAlarm) { - var a = alarms[alarmIndex]; - mins = (0|a.timer)%60; - hrs = 0|(a.timer/60); - en = a.on; - msg = (!a.msg) ? "" : a.msg; - } - var timerTitle = (msg == "") ? 'Alarm' : (msg.length > 15) ? msg.substring(0, 15)+"..." : msg; + var a = { + timer : 5*60*1000, // 5 minutes + on : true, + rp : false, + as : false, + dow : 0b1111111, + last : 0, + vibrate : ".." + }; + if (msg != "") a["msg"] = msg; + if (!newAlarm) Object.assign(a, alarms[alarmIndex]); + if (alarm) Object.assign(a,alarm); + var t = decodeTime(a.timer); + + var timerTitle = (a.msg == undefined) ? 'Timer' : (a.msg.length > 12) ? a.msg.substring(0, 12)+"..." : msg; + const menu = { '': { 'title': timerTitle }, + '< Back' : () => showMainMenu(), 'Hours': { - value: hrs, min : 0, max : 23, wrap : true, - onchange: v => hrs=v + value: t.hrs, min : 0, max : 23, wrap : true, + onchange: v => t.hrs=v }, 'Minutes': { - value: mins, min : 0, max : 59, wrap : true, - onchange: v => mins=v + value: t.mins, min : 0, max : 59, wrap : true, + onchange: v => t.mins=v }, 'Enabled': { - value: en, + value: a.on, format: v=>v?"On":"Off", - onchange: v=>en=v - } + onchange: v=>a.on=v + }, + 'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ), }; - function getTimer() { - var d = new Date(Date.now() + ((hrs*60)+mins)*60000); - var hr = d.getHours() + (d.getMinutes()/60) + (d.getSeconds()/3600); - // Save alarm - return { - on : en, - timer : (hrs*60)+mins, - hr : hr, - rp : false, as: false, msg: msg - }; - } - menu["> Save"] = function() { - if (newAlarm) alarms.push(getTimer()); - else alarms[alarmIndex] = getTimer(); - require("Storage").write("alarm.json",JSON.stringify(alarms)); - showAlarmMenu(); + menu["Save"] = function() { + a.timer = encodeTime(t); + a.t = getCurrentTime() + a.timer; + if (newAlarm) alarms.push(a); + else alarms[alarmIndex] = a; + saveAndReload(); + showMainMenu(); }; if (!newAlarm) { - menu["> Delete"] = function() { + menu["Delete"] = function() { alarms.splice(alarmIndex,1); - require("Storage").write("alarm.json",JSON.stringify(alarms)); - showAlarmMenu(); + saveAndReload(); + showMainMenu(); }; } - if (enableAlarms == 1) return E.showMenu(menu); + return E.showMenu(menu); } showMainMenu();