commit 241f4d6b07e40d704e88b81fb465988ad853d536 Author: Bryan Date: Wed Jul 31 00:14:48 2024 -0600 Initial commit diff --git a/messagebox.lib.js b/messagebox.lib.js new file mode 100644 index 0000000..cf91897 --- /dev/null +++ b/messagebox.lib.js @@ -0,0 +1,246 @@ + +let options = { + srcFont: "6x8", + titleFont: "12x20", + bodyFont: "12x20", + headerBgColor: g.theme.bgH, + headerFgColor: g.theme.fg, + margin: 3, // space on sides + padding: 8, // space between header and body + fh: 10, // footer height - leave this much space for a footer +}; + +exports.setOptions = function(opts) { + options = Object.assign(options, opts); +}; + +const setClipRect = function (x, y, x2, y2) { + if (y < Bangle.appRect.y) y = Bangle.appRect.y; + if (y2 < Bangle.appRect.y) y2 = Bangle.appRect.y; + if (y > Bangle.appRect.y2 - 10) y = Bangle.appRect.y2 - options.fh - 1; + if (y2 > Bangle.appRect.y2 - 10) y2 = Bangle.appRect.y2 - options.fh - 1; + return g.setClipRect(x, y, x2, y2); +}; + +const centerString = (str, x, y, w) => { + g.setFontAlign(0, -1); + g.drawString(str, x + (w / 2), y); +}; + +const HeaderBox = function(msg, messageRect) { + this.icon = require("messageicons").getImage(msg); + this.color = require("messageicons").getColor(msg, {default:g.theme.fg2}); + this.iconW = 48; + this.titleW = messageRect.w - this.iconW; + if (!msg.body) this.titleLines = ""; + else { + this.titleLines = (() => { + if (msg.title) return g.setFont(options.titleFont).wrapString(msg.title, this.titleW); + else return ""; + })(); + } + let sw = g.stringWidth(msg.title); // string width + let ew = g.stringWidth("..."); // elipsis width + let w = this.titleW - ew - options.margin; // max width for title + let cw = sw / msg.title.length; // character width - will only be an average if not a fixed width font + this.titleOneLine = msg.title.substr(0, Math.floor(w / cw)).concat("..."); + this.titleLineH = g.setFont(options.titleFont).getFontHeight(); + this.titleX = 0; + this.titleY = 0; + this.srcLineH = g.setFont(options.srcFont).getFontHeight(); + this.srcLines = g.wrapString(msg.src, this.iconW); + this.srcH = this.srcLineH * this.srcLines.length; + this.srcX = this.x2 - this.iconW; + this.srcY = 0; + this.im = g.imageMetrics(this.icon); + this.imgX = this.titleW + ((this.iconW - this.im.width) / 2); + this.minH = Math.max(this.titleLineH, this.srcH + this.im.height) + options.padding; + this.maxH = Math.max(this.titleLineH * this.titleLines.length, this.iconW) + options.padding; + this.h = this.maxH; +}; // HeaderBox + +HeaderBox.prototype.draw = function(messageRect) { + const getImgY = () => (this.h - options.padding - this.im.height + (this.h == this.maxH ? this.srcH : 0)) / 2; + const x = messageRect.x + const x2 = messageRect.x2; + const y = messageRect.y; + if (this.h < this.minH) this.h = this.minH; + else if (this.h > this.maxH) this.h = this.maxH; + let y2 = messageRect.y + this.h - 1; + setClipRect(x, y, x2, y2); + g.setBgColor(options.headerBgColor).clearRect(x, y, x2, y2); + g.setColor(options.headerFgColor); + g.setFont(options.titleFont); + if (this.h > this.minH) { // multi-line title + let titleY = y; + if (this.titleLines) { + for (let line of this.titleLines) { + centerString(line, x, titleY, this.titleW); + titleY += this.titleLineH; + } + } + } else { // single-line title + const titleY = Math.floor(((this.h - options.padding - this.titleLineH) / 2) + y); + centerString(this.titleOneLine, x + options.margin, titleY, this.titleW); + } + g.setFont(options.srcFont); + if (this.h == this.maxH) { + let srcY = y; + for (let line of this.srcLines) { + centerString(line, x2 - this.iconW, srcY, this.iconW); + srcY += this.srcLineH; + } + } + g.setColor(this.color).drawImage(this.icon, x + this.imgX, y + getImgY()); + g.setColor(g.theme.bg).fillRect(x, y2 - 6, x2, y2); // bar at bottom of header +}; + +const BodyBox = function(msg, messageRect) { + if (msg.body) body = msg.body; + else body = msg.title; + this.lineH = (g.setFont(options.bodyFont).getFontHeight()) + 2; // plus 2 to add a little spacing between lines + this.lines = g.wrapString(body, messageRect.w - options.margin); + if (msg.subject) { // If there's a subject, add it to the top + lines.splice(0, 0, msg.subject); + } + this.initScrollY = 0; + this.scrollY = this.initScrollY; + this.minY = messageRect.y2 - (this.lineH * (this.lines.length + 3)); + if (this.minY > 0) this.minY = 0; +}; // BodyBox + +BodyBox.prototype.draw = function(messageRect, yOffset) { + x = messageRect.x; + y = messageRect.y + yOffset; + x2 = messageRect.x2; + y2 = messageRect.y2; + g.setColor(g.theme.fg).setBgColor(g.theme.bg); + setClipRect(x, y, x2, y2); + g.clearRect(x, y, x2, y2); + g.setFont(options.bodyFont).setFontAlign(-1, -1); + let lineY = this.scrollY; + for (let line of this.lines) { + if (y + lineY >= y - this.lineH) g.drawString(line, x + options.margin, y + lineY); + lineY += this.lineH; + } +}; + +let MessageBox = function(msg, yOffset) { + this.msg = msg; + this.yOffset = yOffset ?? 0; + this.xOffset = 0; + this.rect = { + w: Bangle.appRect.w, + h: Bangle.appRect.h, + x: Bangle.appRect.x + this.xOffset, + y: Bangle.appRect.y + this.yOffset, + x2:Bangle.appRect.x2 + this.xOffset, + y2: Bangle.appRect.y2 + this.yOffset + }; + this.headerBox = new HeaderBox(msg, this.rect); + this.bodyBox = new BodyBox(msg, this.rect); + let messageH = this.bodyBox.lines.length * this.bodyBox.lineH; + let boxH = this.rect.h - this.headerBox.maxH; + if ( messageH <= boxH) this.oneScreen = true; + else this.oneScreen = false; + this.top = true; + if (this.oneScreen) { + this.bottom = true; + this.bodyBox.initScrollY = Math.round((boxH - messageH) / 2); + } else { + this.bottom = false; + this.bodyBox.initScrollY = 0; + } + this.sbH = this.rect.h / messageH * this.rect.h; // scrollbar height + this.sbRatio = (this.rect.h - this.sbH) / Math.abs(this.bodyBox.minY); // scrollbar ratio +}; // MessageBox + +MessageBox.prototype.prevOffset = Bangle.appRect.y - Bangle.appRect.h; +MessageBox.prototype.nextOffset = Bangle.appRect.y2 + 1; + +MessageBox.prototype.reset = function(yOffset) { + if (!yOffset) yOffset = 0; + this.yOffset = yOffset; + this.headerBox.h = this.headerBox.maxH; + this.bodyBox.scrollY = this.bodyBox.initScrollY; + this.top = true; + if (this.oneScreen) this.bottom = true; + else this.bottom = false; +}; + +MessageBox.prototype.draw = function() { + this.rect.x = Bangle.appRect.x + this.xOffset; + this.rect.x2 = Bangle.appRect.x2 + this.xOffset; + this.rect.y = Bangle.appRect.y + this.yOffset; + this.rect.y2 = Bangle.appRect.y2 + this.yOffset; + setClipRect(this.rect.x, this.rect.y, this.rect.x2, this.rect.y2); + g.clearRect(this.rect.x, this.rect.y, this.rect.x2, this.rect.y2); + this.headerBox.draw(this.rect); + this.bodyBox.draw(this.rect, this.headerBox.h); + setClipRect(this.rect.x, this.rect.y, this.rect.x2, this.rect.y2); + this.drawScrollbar(); + //this.drawFooter(); +}; + +MessageBox.prototype.drawScrollbar = function() { + if (this.oneScreen) return; + let sbY = this.rect.y + (this.sbRatio * Math.abs(this.bodyBox.scrollY)); + g.setColor(g.theme.fg).drawLine(this.rect.x, sbY, this.rect.x, sbY + this.sbH); +}; + +MessageBox.prototype.scroll = function(dy) { + if ((dy > 0 && this.top) || (dy < 0 && this.bottom)) { + return; + } + if (this.headerBox.h > this.headerBox.minH && dy < 0) { + this.headerBox.h += dy; + if (this.headerBox.h < this.headerBox.minH) this.headerBox.h = this.headerBox.minH; + } else if (this.headerBox.h < this.headerBox.maxH && dy > 0) { + this.headerBox.h += dy; + if (this.headerBox.h > this.headerBox.maxH) this.headerBox.h = this.headerBox.maxH; + } + if (this.headerBox.h == this.headerBox.minH && dy < 0) this.bodyBox.scrollY += dy; + else if (dy > 0) this.bodyBox.scrollY += dy; + if (this.bodyBox.scrollY <= this.bodyBox.minY) { + this.bottom = true; + this.bodyBox.scrollY = this.bodyBox.minY; + } else { + this.bottom = false; + } + if (this.bodyBox.scrollY >= 0 && this.headerBox.h == this.headerBox.maxH) { + this.top = true; + this.bodyBox.scrollY = this.bodyBox.initScrollY; + } else { + this.top = false; + } + this.draw(); +}; + +// MessageBox.prototype.drawFooter = function() { +// // left hint: swipe from left for main menu +// g.reset().clearRect(Bangle.appRect.x, Bangle.appRect.y2-options.fh, Bangle.appRect.x2, Bangle.appRect.y2) +// .setFont(fontTiny) +// .setFontAlign(-1, 1) // bottom left +// .drawString( +// "\0"+atob("CAiBAEgkEgkSJEgA"), // >> +// Bangle.appRect.x + 1, Bangle.appRect.y2 +// ); +// // center message count+hints: swipe up/down for next/prev message +// const footer = ` ${messageNum+1}/${MESSAGES.length} `; +// const fw = g.stringWidth(footer); +// g.setFontAlign(0, 1); // bottom center +// g.drawString(footer, Bangle.appRect.x+Bangle.appRect.w/2, Bangle.appRect.y2); +// if (messageNum < MESSAGES.length - 1 && this.bottom) +// g.drawString("\0"+atob("CAiBAABBIhRJIhQI"), Bangle.appRect.x+Bangle.appRect.w/2-fw/2, Bangle.appRect.y2); // v swipe to next +// if (messageNum > 0 && this.top) +// g.drawString("\0"+atob("CAiBABAoRJIoRIIA"), Bangle.appRect.x+Bangle.appRect.w/2+fw/2, Bangle.appRect.y2); // ^ swipe to prev +// // right hint: swipe from right for message actions +// g.setFontAlign(1, 1) // bottom right +// .drawString( +// "\0"+atob("CAiBABIkSJBIJBIA"), // << +// Bangle.appRect.x2 - 1, Bangle.appRect.y2 +// ); +// }; + +module.exports.MessageBox = MessageBox; +module.exports.setOptions = this.setOptions; diff --git a/messagecenter.app.js b/messagecenter.app.js new file mode 100644 index 0000000..ff265d5 --- /dev/null +++ b/messagecenter.app.js @@ -0,0 +1,456 @@ +Bangle.MESSAGES = [ + {"t":"add","id":1720281666,"src":"SMS Message","title":"Hayley Thiessen, Wifey","subject":"", + "body":"5 * short message *5", + "sender":"","positive":true,"negative":true,"new":true,"handled":true}, + + {"t":"add","id":1720281662,"src":"SMS Message","title":"Hayley Thiessen, Wifey","subject":"", + "body":"1 ***** This is a long message from my wife. I want to make it take up more than a screenful. That way we can test out scrolling. ****1", + "sender":"","positive":true,"negative":true,"new":false,"handled":true}, + + {"t":"add","id":1720281663,"src":"SMS Message","title":"Hayley Thiessen, Wifey","subject":"", + "body":"2 ***** This is a long message from my wife. I want to make it take up more than a screenful. That way we can test out scrolling. ****2", + "sender":"","positive":true,"negative":true,"new":false,"handled":true}, + + {"t":"add","id":1720281667,"src":"SMS Message","title":"Hayley Thiessen","subject":"", + "body":"6 * short message *6", + "sender":"","positive":true,"negative":true,"new":false,"handled":true}, + + {"t":"add","id":1720281664,"src":"SMS Message","title":"Hayley Thiessen, Wifey","subject":"", + "body":"3 ***** This is a long message from my wife. I want to make it take up more than a screenful. That way we can test out scrolling. ****3", + "sender":"","positive":true,"negative":true,"new":true,"handled":true}, + + {"t":"add","id":1720281665,"src":"SMS Message","title":"Hayley Thiessen, Wifey","subject":"", + "body":"4 ***** This is a long message from my wife. I want to make it take up more than a screenful. That way we can test out scrolling. ****4", + "sender":"","positive":true,"negative":true,"new":false,"handled":true}, + +]; + + +const goBack = function() { + // if (back==="call" && call) showCall(); + // else if (back==="map" && map) showMap(); + // else if (back==="music" && music) showMusic(); + // else if (back==="messages" && messageList.length) showMessage(); + // else if (back) showMain(); // previous screen was "main", or no longer valid + // else quitApp(); // no previous screen: go back to clock + console.log("go back"); +}; + + //let settings = require('Storage').readJSON("messages.settings.json", true) || {}; + //const settings = () => require("messagegui").settings(); + const fontTiny = "6x8"; // fixed size, don't use this for important things + let fontNormal; + // setFont() is also called after we close the settings screen + // const setFont = function() { + // const fontSize = settings().fontSize; + // if (fontSize===0) // small + // fontNormal = g.getFonts().includes("6x15") ? "6x15" : "6x8:2"; + // else if (fontSize===2) // large + // fontNormal = g.getFonts().includes("6x15") ? "6x15:2" : "6x8:4"; + // else // medium + // fontNormal = g.getFonts().includes("12x20") ? "12x20" : "6x8:3"; + // }; + // setFont(); + + Bangle.loadWidgets(); + require("widget_utils").hide(); +///////////////////////////////// + +setClipRect = function (x, y, x2, y2) { + if (y < Bangle.appRect.y) y = Bangle.appRect.y; + if (y2 < Bangle.appRect.y2) y2 = Bangle.appRect.y2; + return g.setClipRect(x, y, x2, y2); +}; + +let active; + +/** + * @param messageList {array} all messages + * @param showAll {bool} true to show all messages, false to only show new messages, default: false + */ + +const newMessage = (type, msg) => { + require("messages").apply(msg, Bangle.MESSAGES); + if (type === "text" && active === "message") { + Bangle.emit("updateMessages"); + // console.log("new message: ", type, msg); + } +} +Bangle.on("message", newMessage); + +const showMessage = function(allMessages, showAll) { + let switching = false; + let updatingMessages = false; + let shouldUpdateMessages = false; + let idle = false; + + const updateMessages = function() { + console.log("updating messages"); + activeMsgId = msgBoxes[messageNum].msg.id; + let newMessages = Bangle.MESSAGES.filter((m) => { + if (messageList.find((n) => n.id === m.id) === undefined && // not already in messageList + m.new) return true; // and it's new + else return false; + }); + const waitUntilIdle = () => { + if (idle && !switching) { + for (let msg of newMessage) { + msgBoxes.unshift(new MessageBox(msg)); + messageList.unshift(msg); + } + messageNum = messageList.findIndex((m) => m.id === activeMsgId); + refresh(); + shouldUpdateMessages = false; + updatingMessages = false; + return; + } else { + setTimeout(waitUntilIdle, 250); + } + }; + } + + Bangle.on("updateMessages", () => {shouldUpdateMessages = true;}); + active = "message"; + let MessageBox = require("messagebox").MessageBox; + require("messagebox").setOptions({fh: 30}); + const step = 89; + const delay = 30; + const swipeThreshold = 20; + let mode = "scroll"; // one of "scroll", "next", "prev" (switch to next/prev message), "swipe" (swipe to delete or archive) + let messageNum = 0; + let messageList = showAll ? allMessages : allMessages.filter(m => m.new); + if (!messageList.length) { + console.log("No messages") + return; + } + Bangle.setLocked(false); + Bangle.setLCDPower(true); + + let msgBoxes = []; + let i = 0; + for (let msg of messageList) { + msgBoxes[i] = new MessageBox(msg); + if (i == messageNum - 1) msgBoxes[i].yOffset = MessageBox.prototype.prevOffset; + else if (i == messageNum) msgBoxes[i].yOffset = 0; + else if ( i == messageNum + 1) msgBoxes[i].yOffset = MessageBox.prototype.nextOffset; + i++; + } + + + const drawFooter = function() { + let fh = 10; // footer height + // left hint: swipe from left for main menu + g.reset().clearRect(Bangle.appRect.x, Bangle.appRect.y2-fh, Bangle.appRect.x2, Bangle.appRect.y2) + .setFont(fontTiny) + .setFontAlign(-1, 1) // bottom left + .drawString( + "\0"+atob("CAiBAEgkEgkSJEgA"), // >> + Bangle.appRect.x + 1, Bangle.appRect.y2 + ); + // center message count+hints: swipe up/down for next/prev message + const footer = ` ${messageNum+1}/${messageList.length} `; + const fw = g.stringWidth(footer); + g.setFontAlign(0, 1); // bottom center + g.drawString(footer, Bangle.appRect.x+Bangle.appRect.w/2, Bangle.appRect.y2); + if (messageNum < Bangle.MESSAGES.length - 1 && this.bottom) + g.drawString("\0"+atob("CAiBAABBIhRJIhQI"), Bangle.appRect.x+Bangle.appRect.w/2-fw/2, Bangle.appRect.y2); // v swipe to next + if (messageNum > 0 && this.top) + g.drawString("\0"+atob("CAiBABAoRJIoRIIA"), Bangle.appRect.x+Bangle.appRect.w/2+fw/2, Bangle.appRect.y2); // ^ swipe to prev + // right hint: swipe from right for message actions + g.setFontAlign(1, 1) // bottom right + .drawString( + "\0"+atob("CAiBABIkSJBIJBIA"), // << + Bangle.appRect.x2 - 1, Bangle.appRect.y2 + ); + }; + + const refresh = function() { + if (messageNum >= 0 && messageNum < msgBoxes.length) { + msgBoxes[messageNum].reset(); + if (messageNum > 0) msgBoxes[messageNum - 1].reset(MessageBox.prototype.prevOffset); + if (messageNum < msgBoxes.length - 1) msgBoxes[messageNum + 1].reset(MessageBox.prototype.nextOffset); + } + } + + const removeMessage = function() { + const removeAndReset = () => { + allMessages = allMessages.filter((m) => m.id != msgBoxes[messageNum.id]); // remove from messages array + msgBoxes.splice(messageNum, 1); // remove from msgBoxes + if (messageNum == msgBoxes.length) messageNum--; // we removed the last message, go to previous message + // otherwise messageNum is automatically the index of the next message now + if (messageNum < 0) { + Bangle.setUI(); + return goBack(); // no more messages + } + // if (messageNum > 0) msgBoxes[messageNum - 1].reset(MessageBox.prototype.prevOffset); + // msgBoxes[messageNum].reset(); + // if (messageNum < msgBoxes.length - 1) msgBoxes[messageNum + 1].reset(MessageBox.prototype.nextOffset); + refresh(); + msgBoxes[messageNum].draw(); + drawFooter(); + }; + if (messageNum < msgBoxes.length - 1) { + toNext(true).then(() => { + removeAndReset(); + switching = false; + }); + } else if (messageNum > 0) { + toPrev(true).then(() => { + removeAndReset(); + switching = false; + }); + } else { + removeAndReset(); + } + }; + + const dismissOnPhone = function() { + return Bangle.messageResponse(msgBoxes[messageNum].msg, false); // false to dismiss, true to open on phone + }; + + let animID; + const toNext = function(removeCurrent) { + if (removeCurrent == undefined) removeCurrent = false; + if (animID){ + clearTimeout(animID); + animID = undefined; + } + firstTouch = true; + return new Promise((resolve, _reject) => { + let animate = () => { + msgBoxes[messageNum].yOffset -= step; + msgBoxes[messageNum + 1].yOffset -= step; + if (msgBoxes[messageNum + 1].yOffset <= 0) { + if (!removeCurrent) { + if (messageNum < msgBoxes.length) { + messageNum++; + // msgBoxes[messageNum - 1].reset(MessageBox.prototype.prevOffset); + // msgBoxes[messageNum].reset(); + // if (messageNum + 1 < msgBoxes.length) msgBoxes[messageNum + 1].reset(MessageBox.prototype.nextOffset); + refresh(); + } + } + msgBoxes[messageNum].draw(); + drawFooter(); + return resolve(); + } else { + msgBoxes[messageNum].draw(); + msgBoxes[messageNum + 1].draw(); + drawFooter(); + animID = setTimeout(animate, delay); + } + }; + animate(); + }); + }; + + const toPrev = function(removeCurrent) { + if (removeCurrent == undefined) removeCurrent = false; + if (animID){ + clearTimeout(animID); + animID = undefined; + } + firstTouch = true; + return new Promise((resolve, _reject) => { + let animate = () => { + msgBoxes[messageNum].yOffset += step; + msgBoxes[messageNum - 1].yOffset += step; + if (msgBoxes[messageNum - 1].yOffset >= 0) { + if (!removeCurrent) { + if (messageNum > 0) { + messageNum--; + // if (messageNum - 1 > 0) msgBoxes[messageNum - 1].reset(MessageBox.prototype.prevOffset); + // msgBoxes[messageNum].reset(); + // msgBoxes[messageNum + 1].reset(MessageBox.prototype.nextOffset); + refresh(); + } + } + msgBoxes[messageNum].draw(); + drawFooter(); + return resolve(); + } else { + msgBoxes[messageNum].draw(); + msgBoxes[messageNum - 1].draw(); + drawFooter(); + animID = setTimeout(animate, delay); + } + }; + animate(); + }); + }; + + const drawLeftBackground = function() { + g.setBgColor(1, 0.5, 0); // red + g.setClipRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y2); + g.clearRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y2); + g.setBgColor(g.theme.bg); + }; + + const drawRightBackground = function() { + g.setBgColor(0, 0.5, 1); // red + g.setClipRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y2); + g.clearRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y2); + g.setBgColor(g.theme.bg); + }; + + const animateToLeft = function() { + if (animID) { + clearTimeout(animID); + animID = undefined; + } + const msgBox = msgBoxes[messageNum]; + return new Promise((resolve, _reject) => { + let animate = () => { + msgBox.xOffset -= step + drawLeftBackground(); + if (msgBox.xOffset <= Bangle.appRect.x - Bangle.appRect.w) { + return resolve(); + } else { + msgBox.draw(); + drawFooter(); + let animID = setTimeout(animate, delay); + } + }; + animate(); + }); + }; + + const animateToRight = function() { + if (animID) { + clearTimeout(animID); + animID = undefined; + } + const msgBox = msgBoxes[messageNum]; + return new Promise((resolve, _reject) => { + let animate = () => { + msgBox.xOffset += step + drawRightBackground(); + if (msgBox.xOffset >= Bangle.appRect.x2) { + return resolve(); + } else { + msgBox.draw(); + drawFooter(); + animID = setTimeout(animate, delay); + } + }; + animate(); + }); + }; + + let firstTouch = true; + const handler = function(e) { + if (updatingMessages) return; + if (switching) return; // don't repond to touch while we are animating + if (firstTouch) { + idle = false; + if (e.dy > 0 && msgBoxes[messageNum].top) { + firstTouch = false; + mode = "prev"; + } else if (e.dy < 0 && msgBoxes[messageNum].bottom) { + firstTouch = false; + mode = "next"; + } else if (Math.abs(e.dx) > Math.abs(e.dy)) { + firstTouch = false; + if (e.dx < 0) mode = "left"; + else mode = "right"; + } else if (Math.abs(e.dy) > 0) { + firstTouch = false; + mode = "scroll"; + } else { + mode = undefined; + return; + } + } + if (e.b == 0) { // released finger, reset firstTouch for next time + idle = true; + firstTouch = true; + } + delete messageList[messageNum].new; + if (mode == "scroll") scrollHandler(e.dy); + else if (mode == "left") leftHandler(e); // remove from phone and watch + else if (mode == "right") rightHandler(e); // remove from watch only + else if (mode == "next") nextHandler(e.dy); + else if (mode == "prev") prevHandler(e.dy); + }; + + const nextHandler = function(dy) { + if (messageNum == msgBoxes.length - 1) return; // already on last message + switching = true; + toNext().then(() => { + switching = false; + }); + }; + + const prevHandler = function(dy) { + if (messageNum == 0) return; // already on first message + switching = true; + toPrev().then(() => { + switching = false; + }); + }; + + const leftHandler = function(e) { + const msgBox = msgBoxes[messageNum]; + if (e.b == 0) { + if (msgBox.xOffset > -swipeThreshold) { + msgBox.xOffset = 0; + msgBox.draw(); + drawFooter(); + } else { + switching = true; + animateToLeft().then(() => { + dismissOnPhone(); + removeMessage(); + switching = false; + }); + } + return; + } + msgBox.xOffset += e.dx; + if (msgBox.xOffset > 0) msgBox.xOffset = 0; + drawLeftBackground(); + msgBox.draw(); + drawFooter(); + }; + + const rightHandler = function(e) { + const msgBox = msgBoxes[messageNum]; + if (e.b == 0) { + if (msgBox.xOffset < swipeThreshold) { + msgBox.xOffset = 0; + msgBox.draw(); + drawFooter(); + } else { + switching = true; + animateToRight().then(() => { + removeMessage(); + switching = false; + }); + } + return; + } + msgBox.xOffset += e.dx; + if (msgBox.xOffset < 0) msgBox.xOffset = 0; + drawRightBackground(); + msgBox.draw(); + drawFooter(); + } + + const scrollHandler = function(dy) { + msgBoxes[messageNum].scroll(dy); + }; + + Bangle.setUI({ + mode: "custom", + drag: e => handler(e), + btn: _e => goBack(), + }); + + msgBoxes[messageNum].draw(); + drawFooter(); + idle = true; +}; + +showMessage(Bangle.MESSAGES); + + diff --git a/messagegui.lib.js b/messagegui.lib.js new file mode 100644 index 0000000..c3c7e7c --- /dev/null +++ b/messagegui.lib.js @@ -0,0 +1,3 @@ +exports.messageListener = function(_type, _msg) { + return; +}