Skip to content

Commit 4e6f46e

Browse files
committed
[messages][messagegui] Update to use messages.music as in-memory storage; and then save to json on kill
1 parent b5e674c commit 4e6f46e

File tree

3 files changed

+158
-91
lines changed

3 files changed

+158
-91
lines changed

apps/messagegui/app.js

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -67,39 +67,51 @@ if (Bangle.MESSAGES) {
6767
delete Bangle.MESSAGES;
6868
}
6969

70-
// Ensure any music message is always kept at the front of the list
71-
function moveMusicToFront() {
72-
for (var i=0;i<MESSAGES.length;i++){
73-
if (MESSAGES[i] && MESSAGES[i].id=="music"){
74-
if (i!==0) MESSAGES.unshift(MESSAGES.splice(i,1)[0]);
75-
return;
76-
}
70+
// Update or add music message to MESSAGES array at front
71+
function updateMusicMessage(musicMsg) {
72+
var existingMusicIdx = MESSAGES.findIndex(m => m.id === "music");
73+
if (existingMusicIdx >= 0) {
74+
MESSAGES[existingMusicIdx] = musicMsg;
75+
if (existingMusicIdx !== 0) MESSAGES.unshift(MESSAGES.splice(existingMusicIdx, 1)[0]);
76+
} else {
77+
MESSAGES.unshift(musicMsg);
7778
}
7879
}
79-
moveMusicToFront();
80+
81+
// Load music message from storage if it exists
82+
var musicMsg = require("messages").getMusic();
83+
if (musicMsg && (musicMsg.track || musicMsg.artist)) {
84+
updateMusicMessage(musicMsg);
85+
}
8086

8187
var onMessagesModified = function(type,msg) {
8288
if (msg.handled) return;
8389
msg.handled = true;
84-
require("messages").apply(msg, MESSAGES);
85-
// Keep music message at the front so it remains the first item
86-
moveMusicToFront();
87-
// TODO: if new, show this new one
88-
if (msg && msg.id!=="music" && msg.id!=="nav" && msg.new &&
89-
!((require('Storage').readJSON('setting.json', 1) || {}).quiet)) {
90-
require("messages").buzz(msg.src);
91-
}
92-
if (msg && msg.id=="music") {
93-
// Track when music actually played so we can expire old music messages
94-
if (msg.state && msg.state=="play") {
95-
// Update the stored music message (if present) with a last-played timestamp
96-
var mm = MESSAGES.find(m=>m && m.id=="music");
97-
if (mm) mm._lastPlayed = Date.now();
90+
91+
if (msg.id === "music") {
92+
// For music messages, get the complete state from messages module
93+
updateMusicMessage(require("messages").getMusic());
94+
var musicMsg = MESSAGES[0]; // music is now at front
95+
96+
if (musicMsg.state && musicMsg.state=="play") {
9897
openMusic = true;
99-
} else if (msg.state && msg.state!="play") {
98+
} else if (musicMsg.state && musicMsg.state!="play") {
10099
openMusic = false; // no longer playing music to go back to
101100
}
102101
if ((active!=undefined) && (active!="list") && (active!="music")) return; // don't open music over other screens (but do if we're in the main menu)
102+
} else {
103+
require("messages").apply(msg, MESSAGES);
104+
// Move music back to front since apply() may have pushed it down
105+
var musicIdx = MESSAGES.findIndex(m => m.id === "music");
106+
if (musicIdx > 0) {
107+
MESSAGES.unshift(MESSAGES.splice(musicIdx, 1)[0]);
108+
}
109+
}
110+
111+
// TODO: if new, show this new one
112+
if (msg && msg.id!=="music" && msg.id!=="nav" && msg.new &&
113+
!((require('Storage').readJSON('setting.json', 1) || {}).quiet)) {
114+
require("messages").buzz(msg.src);
103115
}
104116
if (msg && msg.id=="nav" && msg.t=="modify" && active!="map")
105117
return; // don't show an updated nav message if we're just in the menu
@@ -539,18 +551,8 @@ function showMessage(msgid, persist) {
539551
*/
540552
function checkMessages(options) {
541553
options=options||{};
542-
// Remove/ignore stale music messages if they haven't played recently.
543-
var musicTimeout = (settings && settings.musicTimeoutMinutes) ? settings.musicTimeoutMinutes : 5;
544-
if (isFinite(musicTimeout) && musicTimeout>0) {
545-
var now = Date.now();
546-
MESSAGES = MESSAGES.filter(function(m) {
547-
if (!m || m.id!="music") return true;
548-
if (m.state=="play" || m.state=="show") return true;
549-
if (m._lastPlayed && (now - m._lastPlayed) <= musicTimeout*60000) return true;
550-
// otherwise drop the stale music message
551-
return false;
552-
});
553-
}
554+
// Remove/ignore stale music messages if they haven't played recently
555+
checkMusicExpired();
554556
// If there's been some user interaction, it's time to stop repeated buzzing
555557
if (!options.dontStopBuzz)
556558
require("messages").stopBuzz();
@@ -650,6 +652,20 @@ function returnToClockIfEmpty() {
650652
checkMessages({clockIfNoMsg:1,clockIfAllRead:0,ignoreUnread:1,openMusic});
651653
}
652654

655+
function checkMusicExpired() {
656+
var musicTimeout = (settings && settings.musicTimeoutMinutes) ? settings.musicTimeoutMinutes : 5;
657+
if (!isFinite(musicTimeout) || musicTimeout <= 0) return;
658+
659+
var now = Date.now();
660+
MESSAGES = MESSAGES.filter(function(m) {
661+
if (!m || m.id!="music") return true;
662+
if (m.state=="play" || m.state=="show") return true;
663+
if (m._lastPlayed && (now - m._lastPlayed) <= musicTimeout*60000) return true;
664+
// otherwise drop the stale music message
665+
return false;
666+
});
667+
}
668+
653669
function cancelReloadTimeout() {
654670
if (!unreadTimeout) return;
655671
clearTimeout(unreadTimeout);

apps/messagegui/lib.js

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@ exports.listener = function(type, msg) {
3737
const appSettings = require("Storage").readJSON("messages.settings.json", 1) || {};
3838
let loadMessages = (Bangle.CLOCK || msg.important); // should we load the messages app?
3939
if (type==="music") {
40+
// Music persistence is handled by messages module via pushMessage
41+
msg.handled = true;
4042
if (Bangle.CLOCK && msg.state && msg.title && appSettings.openMusic) {
4143
loadMessages = true;
4244
} else {
43-
if (persistMusicInfo(msg)) return; // handled
45+
return; // handled
4446
}
4547
}
4648
// Write the message to Bangle.MESSAGES. We'll deal with it in messageTimeout below
@@ -109,40 +111,4 @@ exports.open = function(msg) {
109111
}
110112

111113
Bangle.load((msg && msg.new && msg.id!=="music") ? "messagegui.new.js" : "messagegui.app.js");
112-
};
113-
114-
/**
115-
* Persist info fields from music messages
116-
* @param {*} msg Music message
117-
* @returns true if the message was handled
118-
*/
119-
function persistMusicInfo(msg) {
120-
msg.handled = true;
121-
// if nothing to persist - return that it's handled
122-
if (msg.artist === undefined && msg.track === undefined && msg.album === undefined && msg.dur === undefined) return true;
123-
124-
const a = msg.artist, t = msg.track, al = msg.album, d = msg.dur;
125-
126-
// try to find the last music message
127-
const messagesMod = require("messages");
128-
const messages = messagesMod.getMessages();
129-
const mIdx = messages.findIndex(m => m.id === "music");
130-
const stored = mIdx >= 0 ? messages[mIdx] : null;
131-
132-
if (!stored) {
133-
// new msg, always write
134-
const newEntry = { id: "music", artist: a, track: t, album: al, dur: d };
135-
messages.unshift(Object.assign({}, newEntry));
136-
messagesMod.write(messages);
137-
} else {
138-
// existing msg, only write if something changed
139-
if (stored.artist !== a || stored.track !== t || stored.album !== al || stored.dur !== d) {
140-
stored.artist = a;
141-
stored.track = t;
142-
stored.album = al;
143-
stored.dur = d;
144-
messagesMod.write(messages);
145-
}
146-
}
147-
return true;
148-
}
114+
};

apps/messages/lib.js

Lines changed: 103 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
exports.music = {};
2+
3+
// Track if music has been modified and needs saving
4+
let musicLoaded = false;
5+
let musicDirty = false;
6+
let killHandlerSet = false;
7+
28
/**
39
* Emit "message" event with appropriate type from Bangle
410
* @param {object} msg
@@ -34,24 +40,8 @@ exports.pushMessage = function(event) {
3440

3541
// combine musicinfo and musicstate events
3642
if (event.id==="music") {
37-
if (event.state==="play") event.new = true; // new track, or playback (re)started
38-
// Check if we have music info before combining
39-
const hasMusicInfo = exports.music.track || exports.music.artist || exports.music.album || exports.music.dur;
40-
event = Object.assign(exports.music, event);
41-
42-
// If this is a musicstate message and we don't have any music message stored
43-
// then set track to "Music" so we can trigger displaying music controls
44-
if (event.state && !event.track) {
45-
const messages = exports.getMessages();
46-
const hasMusicMsg = messages.length && messages.findIndex(m => m.id === "music") >= 0;
47-
48-
if (!hasMusicMsg && !hasMusicInfo) {
49-
event.track = "Music";
50-
event.title = event.title || "Music"; // ensure title is set for messagegui/lib.js check
51-
exports.music.track = "Music";
52-
exports.music.title = exports.music.title || "Music";
53-
}
54-
}
43+
setMusic(event);
44+
event = getMusic();
5545
}
5646
}
5747
// reset state (just in case)
@@ -255,3 +245,98 @@ exports.stopBuzz = function() {
255245
if (exports.stopTimeout) clearTimeout(exports.stopTimeout);
256246
delete exports.stopTimeout;
257247
};
248+
249+
/**
250+
* Lazy-load music from messages.music.json if not already loaded
251+
*/
252+
function loadMusic() {
253+
if (musicLoaded) return;
254+
const stored = require("Storage").readJSON("messages.music.json", true);
255+
if (stored) {
256+
exports.music = stored;
257+
}
258+
musicLoaded = true;
259+
}
260+
261+
/**
262+
* Save music info to messages.music.json if dirty
263+
*/
264+
function saveMusicToFlash() {
265+
if (!musicDirty) return;
266+
267+
// Debug counter to track saves
268+
incrementSaveCounter("music");
269+
270+
// Only save if we have actual music data
271+
if (Object.keys(exports.music).length > 0) {
272+
require("Storage").writeJSON("messages.music.json", exports.music);
273+
} else {
274+
require("Storage").erase("messages.music.json");
275+
}
276+
277+
musicDirty = false;
278+
}
279+
280+
/**
281+
* Set music info - merges music message data into exports.music, saves to flash on kill
282+
* @param {object} msg Music message
283+
*/
284+
function setMusic(msg) {
285+
// Lazy-load existing music data
286+
loadMusic();
287+
288+
// Merge in artist/track/album/dur if provided
289+
if (msg.artist !== undefined) exports.music.artist = msg.artist;
290+
if (msg.track !== undefined) exports.music.track = msg.track;
291+
if (msg.album !== undefined) exports.music.album = msg.album;
292+
if (msg.dur !== undefined) exports.music.dur = msg.dur;
293+
294+
// Merge in state and update _lastPlayed timestamp when playing
295+
if (msg.state !== undefined) {
296+
exports.music.state = msg.state;
297+
if (msg.state === "play") {
298+
exports.music._lastPlayed = Date.now();
299+
}
300+
}
301+
302+
// If this is a musicstate message and we don't have track info yet,
303+
// set track to "Music" so we can trigger displaying music controls
304+
if (msg.state && !exports.music.track) {
305+
exports.music.track = "Music";
306+
exports.music.title = exports.music.title || "Music";
307+
}
308+
309+
// Mark as dirty so it gets saved on kill
310+
musicDirty = true;
311+
312+
// Set up kill handler only once
313+
if (!killHandlerSet) {
314+
E.on("kill", saveMusicToFlash);
315+
killHandlerSet = true;
316+
}
317+
}
318+
319+
/**
320+
* Get current music info with event metadata
321+
* @returns {object} Music message object
322+
*/
323+
function getMusic() {
324+
loadMusic();
325+
const event = Object.assign({ id: "music" }, exports.music);
326+
if (event.state === "play") event.new = true; // new track, or playback (re)started
327+
return event;
328+
}
329+
330+
/**
331+
* Get current music message
332+
* @returns {object} Music message object with id, state, track, artist, etc.
333+
*/
334+
exports.getMusic = getMusic;
335+
336+
function incrementSaveCounter(name) {
337+
const storage = require("Storage");
338+
let stats = storage.readJSON("message_stats.json", true) || {};
339+
if (!stats[name]) stats[name] = 0;
340+
stats[name]++;
341+
storage.writeJSON("message_stats.json", stats);
342+
}

0 commit comments

Comments
 (0)