feat: chat reactions

This commit is contained in:
gosangam
2026-08-30 01:51:52 +05:30
parent 0f382ed885
commit 8c7afe596d
19 changed files with 662 additions and 53 deletions
+51
View File
@@ -119,6 +119,8 @@ export type ChatMessage = {
reply_to?: ReplyPreview | null;
/** Always an array — media, link cards and shared entities. */
attachments: Attachment[];
/** Emoji chips, grouped with counts. `mine` marks the viewer's own. */
reactions: Reaction[];
/**
* Client-only. Set on a message shown optimistically before the server has
* acknowledged it; never sent by the BFF.
@@ -171,6 +173,21 @@ export type AttachmentKind =
| "entity"
| "quote";
/** One emoji on a message, grouped across everyone who chose it. */
export type Reaction = {
emoji: string;
count: number;
/** The viewer's own choice — highlight the chip, and tapping it removes. */
mine: boolean;
};
/** One person's reaction, for the "who reacted" list. */
export type ReactionDetail = {
user: ChatPeer;
emoji: string;
created_at: string;
};
export type Attachment = {
id: string;
message_id: string;
@@ -411,6 +428,40 @@ export const chat = {
body: JSON.stringify({ seq }),
}),
/** One hydrated message — reaction counts are per viewer, so a client told
* "message X changed" has to ask rather than compute. */
message: (conversationId: string, messageId: string) =>
call<ChatMessage>(
`/chat/conversations/${conversationId}/messages/${messageId}`,
),
/** Tell the thread someone is composing. Ephemeral — nothing is stored. */
typing: (conversationId: string) =>
call<void>(`/chat/conversations/${conversationId}/typing`, {
method: "POST",
}),
/** Who reacted, and with what. Only fetched when the list is opened —
* the thread renders from the grouped counts on each message. */
reactionDetails: (conversationId: string, messageId: string) =>
call<ReactionDetail[]>(
`/chat/conversations/${conversationId}/messages/${messageId}/reactions`,
),
/** Set the caller's reaction, replacing any previous one. */
react: (conversationId: string, messageId: string, emoji: string) =>
call<void>(
`/chat/conversations/${conversationId}/messages/${messageId}/reaction`,
{ method: "PUT", body: JSON.stringify({ emoji }) },
),
/** Remove the caller's reaction. Idempotent. */
unreact: (conversationId: string, messageId: string) =>
call<void>(
`/chat/conversations/${conversationId}/messages/${messageId}/reaction`,
{ method: "DELETE" },
),
markDelivered: (conversationId: string, seq: number) =>
call<void>(`/chat/conversations/${conversationId}/delivered`, {
method: "POST",