feat: unread indicators + DM bug fixes + data-driven isDmChannel
- Fix stale message cache: add force param to loadMessages, clearAllMessages action - Fix reload race condition: URL-based isDmChannel fallback before WS ready - Add read_states DB table for persistent unread tracking - Add channel_ack WS event (client→server→echo) with BigInt comparison - Wire up unread state in chatStore (readStates, unreadChannels, ackChannel) - Auto-ack channels on MessageList view (200ms debounced) - Unread pill indicators on server icons in ServerSidebar - Bold text + white dot on unread channels/DMs in ChannelSidebar - Replace all showDms reads with data-driven isDmChannel() across 8 files - Design system, UI polish, and component fixes from previous sessions
This commit is contained in:
@@ -21,6 +21,18 @@ function handleEvent(event) {
|
||||
if (currentServerId) {
|
||||
loadServerDetail(currentServerId);
|
||||
}
|
||||
// Clear stale message cache on reconnect, then re-fetch current channel
|
||||
{
|
||||
const { clearAllMessages, loadMessages: reloadMessages, currentChannelId, setReadStates } = useChatStore.getState();
|
||||
clearAllMessages();
|
||||
if (currentChannelId) {
|
||||
reloadMessages(currentChannelId, true);
|
||||
}
|
||||
const { channelLastMessageIds } = useServerStore.getState();
|
||||
if (event.readStates) {
|
||||
setReadStates(event.readStates, channelLastMessageIds);
|
||||
}
|
||||
}
|
||||
// Clear stale voice state, then populate from server truth
|
||||
clearAllVoiceUsers();
|
||||
if (event.voiceStates) {
|
||||
@@ -31,6 +43,12 @@ function handleEvent(event) {
|
||||
break;
|
||||
case 'message_created':
|
||||
addMessage(event.message.channelId, event.message);
|
||||
{
|
||||
const { currentChannelId, markChannelUnread } = useChatStore.getState();
|
||||
if (event.message.channelId !== currentChannelId) {
|
||||
markChannelUnread(event.message.channelId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'message_updated':
|
||||
updateMessage(event.message);
|
||||
@@ -73,6 +91,12 @@ function handleEvent(event) {
|
||||
return bTime - aTime;
|
||||
});
|
||||
setDmChannels(updatedDms);
|
||||
{
|
||||
const { currentChannelId, markChannelUnread } = useChatStore.getState();
|
||||
if (event.message.dmChannelId !== currentChannelId) {
|
||||
markChannelUnread(event.message.dmChannelId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'dm_message_updated':
|
||||
@@ -100,6 +124,11 @@ function handleEvent(event) {
|
||||
addFriendFromAccepted(event.friend, event.requestId);
|
||||
break;
|
||||
}
|
||||
case 'channel_ack': {
|
||||
const { onChannelAck } = useChatStore.getState();
|
||||
onChannelAck(event.channelId, event.messageId);
|
||||
break;
|
||||
}
|
||||
case 'error':
|
||||
console.error('WebSocket error:', event.message);
|
||||
break;
|
||||
|
||||
@@ -25,6 +25,19 @@ function handleEvent(event: ServerEvent): void {
|
||||
if (currentServerId) {
|
||||
loadServerDetail(currentServerId);
|
||||
}
|
||||
// Clear stale message cache on reconnect, then re-fetch current channel
|
||||
{
|
||||
const { clearAllMessages, loadMessages: reloadMessages, currentChannelId, setReadStates } = useChatStore.getState();
|
||||
clearAllMessages();
|
||||
if (currentChannelId) {
|
||||
reloadMessages(currentChannelId, true);
|
||||
}
|
||||
// Initialize unread tracking from ready payload
|
||||
const { channelLastMessageIds } = useServerStore.getState();
|
||||
if (event.readStates) {
|
||||
setReadStates(event.readStates, channelLastMessageIds);
|
||||
}
|
||||
}
|
||||
// Clear stale voice state, then populate from server truth
|
||||
clearAllVoiceUsers();
|
||||
if (event.voiceStates) {
|
||||
@@ -36,6 +49,12 @@ function handleEvent(event: ServerEvent): void {
|
||||
|
||||
case 'message_created':
|
||||
addMessage(event.message.channelId, event.message);
|
||||
{
|
||||
const { currentChannelId, markChannelUnread } = useChatStore.getState();
|
||||
if (event.message.channelId !== currentChannelId) {
|
||||
markChannelUnread(event.message.channelId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'message_updated':
|
||||
@@ -86,6 +105,13 @@ function handleEvent(event: ServerEvent): void {
|
||||
return bTime - aTime;
|
||||
});
|
||||
setDmChannels(updatedDms);
|
||||
// Mark DM as unread if not currently viewing it
|
||||
{
|
||||
const { currentChannelId, markChannelUnread } = useChatStore.getState();
|
||||
if (event.message.dmChannelId !== currentChannelId) {
|
||||
markChannelUnread(event.message.dmChannelId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -121,6 +147,12 @@ function handleEvent(event: ServerEvent): void {
|
||||
break;
|
||||
}
|
||||
|
||||
case 'channel_ack': {
|
||||
const { onChannelAck } = useChatStore.getState();
|
||||
onChannelAck(event.channelId, event.messageId);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'error':
|
||||
console.error('WebSocket error:', event.message);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user