Add README and app icon.

master
Bryan 2025-08-20 07:51:54 -06:00
parent 09f652df3c
commit d3474965ca
6 changed files with 847 additions and 848 deletions

4
README.md Normal file
View File

@ -0,0 +1,4 @@
# Message Center
- Groups messages from same sender.
- Scroll through all current messages.

BIN
icons8-chat-room-48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

View File

@ -12,7 +12,7 @@
fh: 20, // footer height - leave this much space for a footer
};
let setOptions = function(opts) {
let setOptions = function (opts) {
options = Object.assign(options, opts);
};
@ -29,9 +29,9 @@
g.drawString(str, x + (w / 2), y);
};
HeaderBox = function(msg, messageRect) {
HeaderBox = function (msg, messageRect) {
this.icon = require("messageicons").getImage(msg);
this.color = require("messageicons").getColor(msg, {default:g.theme.fg2});
this.color = require("messageicons").getColor(msg, { default: g.theme.fg2 });
this.im = g.imageMetrics(this.icon);
this.iconSize = this.im.height;
this.titleW = messageRect.w - this.iconSize;
@ -59,7 +59,7 @@
this.new = msg.new ?? false;
}; // HeaderBox
HeaderBox.prototype.draw = function(messageRect) {
HeaderBox.prototype.draw = function (messageRect) {
//const getImgY = () => (this.h - options.padding - this.im.height + (this.h == this.maxH ? this.srcH : 0)) / 2;
const getImgY = () => (this.h - options.padding - (this.im.height / 2)) / 2;
const x = messageRect.x
@ -70,7 +70,7 @@
let y2 = messageRect.y + this.h - 1;
exports.setClipRect(x, y, x2, y2);
g.setBgColor(options.headerBgColor).clearRect(x, y, x2, y2)
.setBgColor(g.theme.bg2).clearRect(x+3, y+3, x2-3, y2-3);
.setBgColor(g.theme.bg2).clearRect(x + 3, y + 3, x2 - 3, y2 - 3);
if (this.new) g.setColor(options.headerHlColor).fillPoly([x, y, x + 20, y, x, y + 20]);
g.setColor(options.headerFgColor)
.setFont(options.titleFont);
@ -97,28 +97,26 @@
g.setColor(this.color).drawImage(this.icon, x + this.imgX, y + getImgY());
};
const TextBox = function(msgGroup, messageRect) {
const TextBox = function (msgGroup, messageRect) {
this.messageGroup = msgGroup;
this.lineH = (g.setFont(options.bodyFont).getFontHeight()) + options.lineSpacing;
this.messages = new Array(this.messageGroup.length);
this.numLines = 0;
let mnum = 0;
for (msg of this.messageGroup.bodies) {
this.messages[mnum] = g.wrapString(msg, messageRect.w - options.margin);
this.numLines += this.messages[mnum].length;
this.messages[mnum] = {
msg: g.wrapString(msg, messageRect.w - options.margin),
offset: this.numLines * this.lineH
}
this.numLines += this.messages[mnum].msg.length;
mnum++;
}
this.scrollY = 0;
console.log("options.fh: ", options.fh);
console.log("lineH: ", this.lineH);
console.log("numLines", this.numLines);
console.log("messageRect.y2", messageRect.y2);
this.minY = (messageRect.y2 - options.fh - (this.lineH * (this.numLines))); // can't for the life of me figure out why I'm having to +1 the num of lines
console.log("minY", this.minY);
if (this.minY > 0) this.minY = 0;
}; // TextBox
TextBox.prototype.draw = function(messageRect, yOffset) {
TextBox.prototype.draw = function (messageRect, yOffset) {
x = messageRect.x;
y = messageRect.y + yOffset;
x2 = messageRect.x2;
@ -129,19 +127,19 @@
.setFont(options.bodyFont).setFontAlign(-1, -1);
let lineY = this.scrollY;
for (msgNum in this.messages) {
g.drawLine(x2-20, y+lineY-5, x2, y+lineY-10)
.drawLine(x2-20, y+lineY-5, x2, y+lineY);
g.drawLine(x2 - 20, y + lineY - 5, x2, y + lineY - 10)
.drawLine(x2 - 20, y + lineY - 5, x2, y + lineY);
//.drawLine(x, y+lineY, x, y+(this.lineH*this.messages[msgNum].length));
for (let line of this.messages[msgNum]) {
for (let line of this.messages[msgNum].msg) {
if (y + lineY >= y - this.lineH) g.drawString(line, x + options.margin, y + lineY);
lineY += this.lineH;
}
g.drawLine(x+20, y+lineY-5, x, y+lineY-10)
.drawLine(x+20, y+lineY-5, x, y+lineY);
g.drawLine(x + 20, y + lineY - 5, x, y + lineY - 10)
.drawLine(x + 20, y + lineY - 5, x, y + lineY);
}
};
MessageBox = function(msgGroup, yOffset) {
MessageBox = function (msgGroup, yOffset) {
this.messageGroup = msgGroup;
//if (!Array.isArray(msgGroup)) msgGroup = [msgGroup]; // make sure we have an array
this.yOffset = yOffset ?? 0;
@ -176,15 +174,15 @@
MessageBox.prototype.prevOffset = Bangle.appRect.y - Bangle.appRect.h;
MessageBox.prototype.nextOffset = Bangle.appRect.y2 + 1;
MessageBox.prototype.setNew = function() {
MessageBox.prototype.setNew = function () {
this.headerBox.new = true;
}
MessageBox.prototype.clearNew = function() {
MessageBox.prototype.clearNew = function () {
this.headerBox.new = false;
}
MessageBox.prototype.reset = function(yOffset) {
MessageBox.prototype.reset = function (yOffset) {
if (!yOffset) yOffset = 0;
this.yOffset = yOffset;
this.headerBox.h = this.headerBox.maxH;
@ -194,7 +192,7 @@
else this.bottom = false;
};
MessageBox.prototype.draw = function() {
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;
@ -207,13 +205,13 @@
this.drawScrollbar();
};
MessageBox.prototype.drawScrollbar = function() {
MessageBox.prototype.drawScrollbar = function () {
if (this.oneScreen) return;
let sbY = this.rect.y + (this.sbRatio * Math.abs(this.textBox.scrollY));
g.setColor(g.theme.fg).drawLine(this.rect.x, sbY, this.rect.x, sbY + this.sbH);
};
MessageBox.prototype.scroll = function(dy) {
MessageBox.prototype.scroll = function (dy) {
if (this.oneScreen) return;
if (this.headerBox.h > this.headerBox.minH && dy < 0) {
this.headerBox.h += dy;

View File

@ -1,20 +1,20 @@
//{
// let settings = require('Storage').readJSON("messages.settings.json", true) || {};
// let settings = () => require("messagegui").settings();
// let fontTiny = "6x8"; // fixed size, don't use this for important things
// let fontNormal;
// setFont() is also called after we close the settings screen
// let setFont = function() {
// let 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();
// let settings = require('Storage').readJSON("messages.settings.json", true) || {};
// let settings = () => require("messagegui").settings();
// let fontTiny = "6x8"; // fixed size, don't use this for important things
// let fontNormal;
// setFont() is also called after we close the settings screen
// let setFont = function() {
// let 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();
let haveNewMessage = false;
let active;
@ -82,7 +82,7 @@ let goBack = function(timedOut) {
if (m.show) delete m.show;
});
require("messages").write(Bangle.MESSAGES);
Bangle.setUI({mode: "custom", remove: cleanup});
Bangle.setUI({ mode: "custom", remove: cleanup });
Bangle.showClock();
}
switch (backTo) {
@ -108,7 +108,7 @@ let filterMessages = function() {
let showNoMessages = function() {
g.reset().clear()
.setFont("12x20").setFontAlign(0,0)
.setFont("12x20").setFontAlign(0, 0)
.drawString("No Messages!", Bangle.appRect.x + (Bangle.appRect.w / 2), Bangle.appRect.y + (Bangle.appRect.h / 2));
Bangle.setUI({
@ -121,11 +121,11 @@ let showNoMessages = function() {
};
let showMessage = function(textGid) {
if (call) {setActive("call"); return showCall();}
else if (alarm) {setActive("alarm"); return showAlarm();}
else if (map) {setActive("map"); return showMap();}
else if (music) {setActive("music"); return showMusic();}
else if (Bangle.MESSAGES && Bangle.MESSAGES.length){
if (call) { setActive("call"); return showCall(); }
else if (alarm) { setActive("alarm"); return showAlarm(); }
else if (map) { setActive("map"); return showMap(); }
else if (music) { setActive("music"); return showMusic(); }
else if (Bangle.MESSAGES && Bangle.MESSAGES.length) {
Bangle.MESSAGES.every(m => m.handled = true); // set all text messages as handled
setActive("text");
return showText(Bangle.MESSAGES, textGid);
@ -176,7 +176,7 @@ let showCall = function() {
delete msg.new;
if (!msg.name) {
let pn = msg.number.replaceAll(/\D/, ""); // remove any non digit characters
msg.title = `${pn.slice(-10, -7)}-${pn.slice(-7,-4)}-${pn.slice(-4)}`;
msg.title = `${pn.slice(-10, -7)}-${pn.slice(-7, -4)}-${pn.slice(-4)}`;
}
let cmd = msg.cmd;
@ -188,17 +188,17 @@ let showCall = function() {
g.reset();
let drawAcceptArrow = function(xOffset) {
xOffset = xOffset??0;
xOffset = xOffset ?? 0;
imgX = x + 2 + xOffset;
g.setColor(0, 1, 0)
.drawImage(rightImg, imgX, y2-50);
.drawImage(rightImg, imgX, y2 - 50);
};
let drawRejectArrow = function(xOffset) {
xOffset = xOffset??0;
xOffset = xOffset ?? 0;
let imgX = x2 - 50 - xOffset;
g.setColor(1, 0.25, 0.25)
.drawImage(leftImg, imgX, y2-50);
.drawImage(leftImg, imgX, y2 - 50);
};
if (cmd === "end") {
@ -207,13 +207,13 @@ let showCall = function() {
if (Bangle.elapsedString) {
let elapsedString = Bangle.elapsedString;
delete Bangle.elapsedString;
g.drawString(elapsedString, mx, y+25)
g.drawString(elapsedString, mx, y + 25)
.drawString("Call Ended", mx, y);
} else {
g.drawString("Missed Call", mx, y);
}
let done = new Promise((resolve, _reject) => {
setTimeout(() => {resolve();}, 5000);
setTimeout(() => { resolve(); }, 5000);
});
done.then(() => {
call = undefined;
@ -223,22 +223,22 @@ let showCall = function() {
if (cmd === "incoming") {
drawRejectArrow();
drawAcceptArrow();
g.setColor(0,1,0)
.drawImage(ringingImg, mx-25, y+15);
g.setColor(0, 1, 0)
.drawImage(ringingImg, mx - 25, y + 15);
}
if (cmd === "start" || cmd === "outgoing") {
drawRejectArrow();
startTime = parseInt(Date.now()/1000);
startTime = parseInt(Date.now() / 1000);
g.setColor(g.theme.fg)
.drawImage(cmd === "start" ? incomingImg : outgoingImg, x+10, y+20);
.drawImage(cmd === "start" ? incomingImg : outgoingImg, x + 10, y + 20);
let timer = () => {
g.clearRect(mx-45, y+20, mx+70, y+50);
elapsed = parseInt(Date.now()/1000) - startTime;
let h = ("0" + Math.floor((elapsed/3600)%60)).slice(-2);
let m = ("0" + Math.floor((elapsed/60)%60)).slice(-2);
let s = ("0" + Math.floor(elapsed%60)).slice(-2);
g.clearRect(mx - 45, y + 20, mx + 70, y + 50);
elapsed = parseInt(Date.now() / 1000) - startTime;
let h = ("0" + Math.floor((elapsed / 3600) % 60)).slice(-2);
let m = ("0" + Math.floor((elapsed / 60) % 60)).slice(-2);
let s = ("0" + Math.floor(elapsed % 60)).slice(-2);
Bangle.elapsedString = `${h}:${m}:${s}`;
g.setFont("Vector:25").setFontAlign(0, -1).drawString(Bangle.elapsedString, mx + 15, y+25);
g.setFont("Vector:25").setFontAlign(0, -1).drawString(Bangle.elapsedString, mx + 15, y + 25);
timeouts["timer"] = setTimeout(timer, 1000);
};
timer();
@ -255,7 +255,7 @@ let showCall = function() {
let animate = new Promise((resolve, _reject) => {
let swipeAnimation = () => {
xOff += 30;
g.clearRect(x, y2-50, x2, y2);
g.clearRect(x, y2 - 50, x2, y2);
drawRejectArrow(xOff);
if (xOff > 160) return resolve();
else setTimeout(swipeAnimation, 30);
@ -274,7 +274,7 @@ let showCall = function() {
let animate = new Promise((resolve, _reject) => {
let swipeAnimation = () => {
xOff += 30;
g.clearRect(x, y2-50, x2, y2);
g.clearRect(x, y2 - 50, x2, y2);
drawAcceptArrow(xOff);
if (xOff > 160) return resolve();
else setTimeout(swipeAnimation, 30);
@ -289,24 +289,13 @@ let showCall = function() {
};
Bangle.setUI({
mode: "custom",
touch: () => {if (buzzing) return require("messages").stopBuzz()},
touch: () => { if (buzzing) return require("messages").stopBuzz() },
swipe: handler,
btn: goBack,
//remove: cleanup
});
};
// let catMessages = function(messageGroup) {
// let msg = "";
// let first = true;
// messageGroup.forEach((m => {
// msg = msg.subject ? msg.subject + "\n" : "";
// msg = msg + (first ? "" : "\n>\n") + m.body;
// first = false;
// }));
// return msg;
// };
let gid = m => m.title + m.src; // create unique group id
let groupMessages = function(messages) {
@ -316,8 +305,9 @@ let groupMessages = function(messages) {
while (msgs.length) {
let currentMessage = msgs[0];
let mg = msgs.filter(m => gid(m) === gid(currentMessage)); // put messages into group
let messageGroup = Object.assign({}, currentMessage, {id: gid(currentMessage), idList: [], bodies: []});
for (msg of mg) {
let messageGroup = Object.assign({}, currentMessage, { id: gid(currentMessage), idList: [], bodies: [], new: [] });
for (let msg of mg) {
messageGroup.new.unshift(msg.new);
messageGroup.idList.unshift(msg.id);
messageGroup.bodies.unshift(msg.body);
}
@ -343,7 +333,7 @@ let showText = function(messages, id) {
Bangle.setUI(); // make sure to clear setUI from anything previous
let switching = false;
let MessageBox = require("messagebox").MessageBox;
require("messagebox").setOptions({fh: 20});
require("messagebox").setOptions({ fh: 20 });
let step = 42;
let delay = 30;
let mode = "scroll"; // one of "scroll", "next", "prev" (switch to next/prev message), "swipe" (swipe to delete or archive)
@ -365,8 +355,8 @@ let showText = function(messages, id) {
let drawFooter = function() { // adapted from messagelist app
let fh = 20; // footer height
// left hint: swipe from left for main menu
g.reset().setBgColor(g.theme.bg).clearRect(Bangle.appRect.x, Bangle.appRect.y2-fh, Bangle.appRect.x2, Bangle.appRect.y2)
.setColor(g.theme.bgH).drawLine(Bangle.appRect.x, Bangle.appRect.y2-fh, Bangle.appRect.x2, Bangle.appRect.y2-fh)
g.reset().setBgColor(g.theme.bg).clearRect(Bangle.appRect.x, Bangle.appRect.y2 - fh, Bangle.appRect.x2, Bangle.appRect.y2)
.setColor(g.theme.bgH).drawLine(Bangle.appRect.x, Bangle.appRect.y2 - fh, Bangle.appRect.x2, Bangle.appRect.y2 - fh)
.setFont("6x15") // TODO make as option
.setFontAlign(-1, 1).setColor(0, 1, 0) //bottom left
.drawString(">>", Bangle.appRect.x + 1, Bangle.appRect.y2);
@ -374,11 +364,11 @@ let showText = function(messages, id) {
let footer = ` ${messageNum + 1}/${msgBoxes.length} `;
let fw = g.stringWidth(footer);
g.setFontAlign(0, 1).setColor(g.theme.fg) // bottom center
.drawString(footer, Bangle.appRect.x+Bangle.appRect.w/2, Bangle.appRect.y2);
.drawString(footer, Bangle.appRect.x + Bangle.appRect.w / 2, Bangle.appRect.y2);
if (messageNum < Bangle.MESSAGES.length - 1 && msgBoxes[messageNum].bottom)
g.drawString("\0"+atob("CAiBAABBIhRJIhQI"), Bangle.appRect.x+Bangle.appRect.w/2-fw/2 - 20, Bangle.appRect.y2); // v swipe to next
g.drawString("\0" + atob("CAiBAABBIhRJIhQI"), Bangle.appRect.x + Bangle.appRect.w / 2 - fw / 2 - 20, Bangle.appRect.y2); // v swipe to next
if (messageNum > 0 && msgBoxes[messageNum].top)
g.drawString("\0"+atob("CAiBABAoRJIoRIIA"), Bangle.appRect.x+Bangle.appRect.w/2+fw/2 + 20, Bangle.appRect.y2); // ^ swipe to prev
g.drawString("\0" + atob("CAiBABAoRJIoRIIA"), Bangle.appRect.x + Bangle.appRect.w / 2 + fw / 2 + 20, Bangle.appRect.y2); // ^ swipe to prev
// right hint: swipe from right for message actions
g.setFontAlign(1, 1).setColor(1, 0.25, 0.25) // bottom right
.drawString("<<", Bangle.appRect.x2 - 1, Bangle.appRect.y2)
@ -436,13 +426,13 @@ let showText = function(messages, id) {
let dismissOnPhone = function() {
for (let id of msgBoxes[messageNum].messageGroup.idList) {
Bangle.messageResponse({id: id}, false);
Bangle.messageResponse({ id: id }, false);
}
}; //dismissOnPhone
let toNext = function(removeCurrent) {
if (removeCurrent == undefined) removeCurrent = false;
if (timeouts["animID"]){
if (timeouts["animID"]) {
clearTimeout(timeouts["animID"]);
timeouts["animID"] = undefined;
}
@ -477,7 +467,7 @@ let showText = function(messages, id) {
let toPrev = function(removeCurrent) {
if (removeCurrent == undefined) removeCurrent = false;
if (timeouts["animID"]){
if (timeouts["animID"]) {
clearTimeout(timeouts["animID"]);
timeouts["animID"] = undefined;
}
@ -592,6 +582,9 @@ let showText = function(messages, id) {
setBusy(true);
// TODO let idx = msgBoxes.findIndex(mb => mb.msg.id === msgBoxes[messageNum].msg.id); // TODO maybe we don't to do this check every time
// TODO if (idx >= 0) delete Bangle.MESSAGES[idx].new;
// TODO for (msg of msgBoxes[messageNum].m)
// TODO msgBoxes[messageNum].clearNew();
if (Math.abs(e.dy) > Math.abs(e.dx)) {
firstTouch = false;
@ -687,8 +680,8 @@ let newMessage = (type, msg) => {
if (type === "text" && msg.t === "remove") { // TODO
let msgIdx = Bangle.MESSAGES.findIndex(m => m.id === msg.id);
if (Bangle.MESSAGES.length > 1) {
if (msgIdx === 0) msgId = Bangle.MESSAGES[msgIdx+1].id;
else if (msgIdx === Bangle.MESSAGES.length - 1) msgId = Bangle.MESSAGES[msgIdx-1].id;
if (msgIdx === 0) msgId = Bangle.MESSAGES[msgIdx + 1].id;
else if (msgIdx === Bangle.MESSAGES.length - 1) msgId = Bangle.MESSAGES[msgIdx - 1].id;
else msgId = textGid;
} else if (Bangle.MESSAGES.length === 1) msgId = 0;
if (textGid === msg.id) msgId = 0; // just removed the message we were viewing

1
messagecenter.img Normal file
View File

@ -0,0 +1 @@
atob("MDDCAP//viRSJhslAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKqqqqqqqqgAAAAACqqqqqqqqqqAAAAACqqqqqqqqqqAAAAAKqqqqqqqqqqgAAAAKqqqqqqqqqqgAAAAKqqqqqqqqqqgAAABVVVVVVVVeqqgAAAVVVVVVVVVV6qgAAAVVVVVVVVVVaqgAABVVVVVVVVVVeqgAABVVVVVVVVVVWqgAABVVVVVVVVVVWqgAABVVVVVVVVVVWqgAABVVVVVVVVVVWqgAABVVVVVVVVVVWqgAABVVVVVVVVVVWqgAABVVVVVVVVVVWqgAABVVVVVVVVVVWqgAABVVVVVVVVVVWqgAABVVVVVVVVVVWqgAABVVVVVVVVVVWqgAABVVVVVVVVVVWqgAABVVVVVVVVVVWqAAABVVVVVVVVVVWqAAABVVVVVVVVVVWgAAABVVVVVVVVVVUAAAABVVVVVVVVVVUAAAABVVVVVVVVVVUAAAABVVVVVVVVVVQAAAABVVVVVVVVVVQAAAABVVVVVVVVVUAAAAABVUAAAAAAAAAAAAABVQAAAAAAAAAAAAABVAAAAAAAAAAAAAABUAAAAAAAAAAAAAABQAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")

View File

@ -1,5 +1,8 @@
{
"id": "messagecenter",
"name": "Message Center",
"src": "messagecenter.app.js"
"id":"messagecenter",
"name":"Message Center",
"type":"tool",
"version":"0.1",
"tags":"tool,system",
"files":"messages.info, messagebox, messagecenter.app.js, messagecenter.boot.js, messagecenter.notify.js, messagegui"
}