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:
@@ -141,6 +141,9 @@ export function handleClientEvent(
|
||||
case 'reaction_remove':
|
||||
handleReactionRemove(event, userId);
|
||||
break;
|
||||
case 'channel_ack':
|
||||
handleChannelAck(event, userId);
|
||||
break;
|
||||
default:
|
||||
connectionManager.sendToUser(userId, {
|
||||
type: 'error',
|
||||
@@ -677,3 +680,48 @@ function handleReactionRemove(event: Record<string, unknown>, userId: string): v
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleChannelAck(event: Record<string, unknown>, userId: string): void {
|
||||
const channelId = event.channelId as string;
|
||||
const messageId = event.messageId as string;
|
||||
if (!channelId || !messageId) return;
|
||||
|
||||
const db = getDb();
|
||||
|
||||
const existing = db.select()
|
||||
.from(schema.readStates)
|
||||
.where(and(
|
||||
eq(schema.readStates.userId, userId),
|
||||
eq(schema.readStates.channelId, channelId),
|
||||
))
|
||||
.get();
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
if (existing) {
|
||||
// Only update if the new messageId is newer (larger snowflake)
|
||||
if (BigInt(messageId) > BigInt(existing.lastReadMessageId)) {
|
||||
db.update(schema.readStates)
|
||||
.set({ lastReadMessageId: messageId, updatedAt: now })
|
||||
.where(and(
|
||||
eq(schema.readStates.userId, userId),
|
||||
eq(schema.readStates.channelId, channelId),
|
||||
))
|
||||
.run();
|
||||
}
|
||||
} else {
|
||||
db.insert(schema.readStates).values({
|
||||
userId,
|
||||
channelId,
|
||||
lastReadMessageId: messageId,
|
||||
updatedAt: now,
|
||||
}).run();
|
||||
}
|
||||
|
||||
// Echo ack back to all of this user's connections (multi-tab sync)
|
||||
connectionManager.sendToUser(userId, {
|
||||
type: 'channel_ack',
|
||||
channelId,
|
||||
messageId,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user