diff --git a/GUI/js/matcha.js b/GUI/js/matcha.js
index 0d72b9b..3c1e6fc 100644
--- a/GUI/js/matcha.js
+++ b/GUI/js/matcha.js
@@ -684,19 +684,19 @@ function renderFriendsList() {
// Sort: online first, then alphabetical
const sorted = [...friends].sort((a, b) => {
- const aOnline = a.presence !== 'offline' ? 1 : 0;
- const bOnline = b.presence !== 'offline' ? 1 : 0;
+ const aOnline = a.state !== 'offline' ? 1 : 0;
+ const bOnline = b.state !== 'offline' ? 1 : 0;
if (aOnline !== bOnline) return bOnline - aOnline;
return (a.handle || '').localeCompare(b.handle || '');
});
- const onlineCount = sorted.filter(f => f.presence !== 'offline').length;
+ const onlineCount = sorted.filter(f => f.state !== 'offline').length;
let html = `
`;
sorted.forEach(f => {
- const presenceClass = f.presence === 'offline' ? 'offline' : (f.presence === 'in_game' ? 'ingame' : 'online');
- const presenceText = f.presence === 'offline' ? 'Offline' : (f.presence === 'in_game' ? 'In Game' : 'Online');
+ const presenceClass = f.state === 'offline' ? 'offline' : (f.state === 'in_game' ? 'ingame' : 'online');
+ const presenceText = f.state === 'offline' ? 'Offline' : (f.state === 'in_game' ? 'In Game' : 'Online');
const unread = matchaState.unreadDms[f.id] || 0;
const unreadBadge = unread > 0 ? `${unread}` : '';
const avUrl = avatarUrl(f.id);
@@ -925,8 +925,8 @@ function renderDmChat(body, header) {
const target = matchaState.dmTarget;
// Find friend for presence/avatar info
const friend = matchaState.friends.find(f => f.id === target.id);
- const presenceClass = friend?.presence === 'offline' ? 'offline' : (friend?.presence === 'in_game' ? 'ingame' : 'online');
- const presenceText = friend?.presence === 'offline' ? 'Offline' : (friend?.presence === 'in_game' ? 'In Game' : 'Online');
+ const presenceClass = friend?.state === 'offline' ? 'offline' : (friend?.state === 'in_game' ? 'ingame' : 'online');
+ const presenceText = friend?.state === 'offline' ? 'Offline' : (friend?.state === 'in_game' ? 'In Game' : 'Online');
const avUrl = avatarUrl(friend?.id);
header.innerHTML = `
@@ -1598,12 +1598,13 @@ function setupWsListeners() {
});
matcha.onWsMessage((data) => {
- // The WS data has type:"message" + message fields at top level
+ // WS data has: type:"message", convo:"global"|, message:{...}
const msg = data.message || data;
const isOwnMessage = !!msg.fromId && String(msg.fromId) === String(matchaState.user?.id || '');
- const isGlobal = msg.toId === 'global' || msg.to === 'global';
+ // Use data.convo (Butter's WS format) as primary routing, fallback to msg fields
+ const isGlobal = data.convo === 'global' || msg.toId === 'global' || msg.to === 'global';
- mlog.log('WS msg:', msg.fromHandle, '->', msg.toId || msg.to, '| own:', isOwnMessage, '| fromId:', msg.fromId, '| userId:', matchaState.user?.id);
+ mlog.log('WS msg:', msg.fromHandle, '-> convo:', data.convo, 'toId:', msg.toId, '| own:', isOwnMessage, '| fromId:', msg.fromId, '| userId:', matchaState.user?.id);
if (isGlobal) {
// Skip if own message (already rendered optimistically)
@@ -1632,8 +1633,8 @@ function setupWsListeners() {
updateUnreadBadges();
}
} else {
- // DM
- const otherId = isOwnMessage ? msg.toId : msg.fromId;
+ // DM — use data.convo as conversation identifier, fallback to msg fields
+ const otherId = data.convo || (isOwnMessage ? msg.toId : msg.fromId);
// Skip own messages (already rendered optimistically)
if (isOwnMessage) {