The Phantom Unread Badge — JavaScript Bug Hunt
Inspired by the eternally-wrong unread badges of chat apps. The socket re-delivers messages after reconnects, and marking a channel read somehow leaves…
- Language: JavaScript
- Layer: Frontend
- Difficulty: Medium
- Concepts: State, Dedupe
- Modelled on: Slack
- Visible tests: re-delivered messages count once; read clears everything up to and including upToId; messages after the read marker stay unread
- Reward: 50 XP for a complete fix
Briefing
Inspired by the eternally-wrong unread badges of chat apps. The socket re-delivers messages after reconnects, and marking a channel read somehow leaves exactly one message behind.
unread.js folds the event stream into a badge count — two bugs, one function.
Bug report
BUG-1337 · Reported by: mobile team
Event stream contract:
- { type: "message", id, channel } — ids are unique but MAY be re-delivered
- { type: "read", channel, upToId } — clears every message with id <= upToId
Observed: badge shows 5 after a reconnect that re-sent 2 messages, and after "mark as read" one unread always remains.
Logs
[badge] general: events=6 unique=4 badge=6
[badge] after read upTo=42 -> badge=1 (expected 0)The code as shipped
src/chat/unread.js (editable)
// Folds the socket event stream into unread counts per channel.
exports.unreadCounts = function (events) {
var perChannel = {};
for (var i = 0; i < events.length; i++) {
var e = events[i];
if (e.type === "message") {
if (!perChannel[e.channel]) perChannel[e.channel] = [];
perChannel[e.channel].push(e.id);
} else if (e.type === "read") {
var list = perChannel[e.channel] || [];
perChannel[e.channel] = list.filter(function (id) {
return id > e.upToId + 1;
});
}
}
var out = {};
for (var ch in perChannel) out[ch] = perChannel[ch].length;
return out;
};
Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More JavaScript bug hunts.