feat(web-core): implement unread counters and new-messages divider
Some checks failed
CI / test (push) Failing after 19s
Some checks failed
CI / test (push) Failing after 19s
backend: - add unread_count to ChatRead serialization - compute unread_count per chat using message_receipts and hidden messages web: - add unread badges in chat list - track unread boundary per chat in store - show 'New messages' divider in message list - update realtime flow to increment/clear unread on incoming events
This commit is contained in:
@@ -8,6 +8,7 @@ interface ChatState {
|
||||
messagesByChat: Record<number, Message[]>;
|
||||
typingByChat: Record<number, number[]>;
|
||||
replyToByChat: Record<number, Message | null>;
|
||||
unreadBoundaryByChat: Record<number, number>;
|
||||
loadChats: (query?: string) => Promise<void>;
|
||||
setActiveChatId: (chatId: number | null) => void;
|
||||
loadMessages: (chatId: number) => Promise<void>;
|
||||
@@ -24,6 +25,8 @@ interface ChatState {
|
||||
setMessageDeliveryStatus: (chatId: number, messageId: number, status: DeliveryStatus) => void;
|
||||
removeMessage: (chatId: number, messageId: number) => void;
|
||||
clearChatMessages: (chatId: number) => void;
|
||||
incrementUnread: (chatId: number) => void;
|
||||
clearUnread: (chatId: number) => void;
|
||||
setTypingUsers: (chatId: number, userIds: number[]) => void;
|
||||
setReplyToMessage: (chatId: number, message: Message | null) => void;
|
||||
updateChatPinnedMessage: (chatId: number, pinnedMessageId: number | null) => void;
|
||||
@@ -35,6 +38,7 @@ export const useChatStore = create<ChatState>((set, get) => ({
|
||||
messagesByChat: {},
|
||||
typingByChat: {},
|
||||
replyToByChat: {},
|
||||
unreadBoundaryByChat: {},
|
||||
loadChats: async (query) => {
|
||||
const chats = await getChats(query);
|
||||
const currentActive = get().activeChatId;
|
||||
@@ -43,13 +47,19 @@ export const useChatStore = create<ChatState>((set, get) => ({
|
||||
},
|
||||
setActiveChatId: (chatId) => set({ activeChatId: chatId }),
|
||||
loadMessages: async (chatId) => {
|
||||
const unreadCount = get().chats.find((c) => c.id === chatId)?.unread_count ?? 0;
|
||||
const messages = await getMessages(chatId);
|
||||
const sorted = [...messages].reverse();
|
||||
set((state) => ({
|
||||
messagesByChat: {
|
||||
...state.messagesByChat,
|
||||
[chatId]: sorted
|
||||
}
|
||||
},
|
||||
unreadBoundaryByChat: {
|
||||
...state.unreadBoundaryByChat,
|
||||
[chatId]: unreadCount
|
||||
},
|
||||
chats: state.chats.map((chat) => (chat.id === chatId ? { ...chat, unread_count: 0 } : chat))
|
||||
}));
|
||||
const lastMessage = sorted[sorted.length - 1];
|
||||
if (lastMessage) {
|
||||
@@ -153,6 +163,25 @@ export const useChatStore = create<ChatState>((set, get) => ({
|
||||
messagesByChat: {
|
||||
...state.messagesByChat,
|
||||
[chatId]: []
|
||||
},
|
||||
unreadBoundaryByChat: {
|
||||
...state.unreadBoundaryByChat,
|
||||
[chatId]: 0
|
||||
},
|
||||
chats: state.chats.map((chat) => (chat.id === chatId ? { ...chat, unread_count: 0 } : chat))
|
||||
})),
|
||||
incrementUnread: (chatId) =>
|
||||
set((state) => ({
|
||||
chats: state.chats.map((chat) =>
|
||||
chat.id === chatId ? { ...chat, unread_count: (chat.unread_count ?? 0) + 1 } : chat
|
||||
)
|
||||
})),
|
||||
clearUnread: (chatId) =>
|
||||
set((state) => ({
|
||||
chats: state.chats.map((chat) => (chat.id === chatId ? { ...chat, unread_count: 0 } : chat)),
|
||||
unreadBoundaryByChat: {
|
||||
...state.unreadBoundaryByChat,
|
||||
[chatId]: 0
|
||||
}
|
||||
})),
|
||||
setTypingUsers: (chatId, userIds) =>
|
||||
|
||||
Reference in New Issue
Block a user