feat: add message reliability foundation
All checks were successful
CI / test (push) Successful in 23s

- implement idempotent message creation via client_message_id

- add persistent delivered/read receipts

- expose /messages/status and wire websocket receipt events

- update web client to send client ids and auto-ack delivered/read
This commit is contained in:
2026-03-07 23:57:35 +03:00
parent ff6f409c5a
commit f6ad480973
13 changed files with 382 additions and 28 deletions

View File

@@ -16,6 +16,7 @@ export function useRealtime() {
const prependMessage = useChatStore((s) => s.prependMessage);
const loadChats = useChatStore((s) => s.loadChats);
const chats = useChatStore((s) => s.chats);
const activeChatId = useChatStore((s) => s.activeChatId);
const typingByChat = useRef<Record<number, Set<number>>>({});
const wsUrl = useMemo(() => {
@@ -34,6 +35,12 @@ export function useRealtime() {
const chatId = Number(event.payload.chat_id);
const message = event.payload.message as Message;
prependMessage(chatId, message);
if (message.sender_id !== me?.id) {
ws.send(JSON.stringify({ event: "message_delivered", payload: { chat_id: chatId, message_id: message.id } }));
if (chatId === activeChatId) {
ws.send(JSON.stringify({ event: "message_read", payload: { chat_id: chatId, message_id: message.id } }));
}
}
if (!chats.some((chat) => chat.id === chatId)) {
void loadChats();
}
@@ -59,7 +66,7 @@ export function useRealtime() {
};
return () => ws.close();
}, [wsUrl, prependMessage, loadChats, chats, me?.id]);
}, [wsUrl, prependMessage, loadChats, chats, me?.id, activeChatId]);
return null;
}