@pubuduth-aplicy/chat-ui 2.2.13 → 2.2.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/Chat.tsx +3 -6
- package/src/components/messages/MessageContainer.tsx +31 -3
- package/src/components/messages/MessageInput.tsx +17 -5
- package/src/components/sidebar/Conversation.tsx +10 -7
- package/src/components/sidebar/Conversations.tsx +96 -91
- package/src/stores/Zustant.ts +17 -0
package/package.json
CHANGED
package/src/components/Chat.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import { Sidebar } from "./sidebar/Sidebar";
|
|
|
5
5
|
import { useChatContext } from "../providers/ChatProvider";
|
|
6
6
|
|
|
7
7
|
export const Chat = () => {
|
|
8
|
-
const {
|
|
8
|
+
const { selectedConversation, updateMessageStatus } = useChatUIStore();
|
|
9
9
|
|
|
10
10
|
const { socket, sendMessage } = useChatContext();
|
|
11
11
|
|
|
@@ -25,7 +25,7 @@ export const Chat = () => {
|
|
|
25
25
|
event: "confirmDelivery",
|
|
26
26
|
data: {
|
|
27
27
|
messageIds: [message._id],
|
|
28
|
-
chatId: message.conversationId,
|
|
28
|
+
chatId: message.conversationId,
|
|
29
29
|
senderRole: message.senderRole,
|
|
30
30
|
receiverRole: message.receiverRole,
|
|
31
31
|
senderId: message.senderId,
|
|
@@ -38,9 +38,6 @@ export const Chat = () => {
|
|
|
38
38
|
|
|
39
39
|
// Only append to the message list if the message belongs to the currently open conversation
|
|
40
40
|
if (message.conversationId === selectedConversation?._id) {
|
|
41
|
-
setMessages((prev) => [...prev, message]);
|
|
42
|
-
|
|
43
|
-
// Send read receipt only when the user is viewing this conversation
|
|
44
41
|
sendMessage({
|
|
45
42
|
event: "messageRead",
|
|
46
43
|
data: {
|
|
@@ -61,7 +58,7 @@ export const Chat = () => {
|
|
|
61
58
|
|
|
62
59
|
socket.addEventListener("message", handleMessage);
|
|
63
60
|
return () => socket.removeEventListener("message", handleMessage);
|
|
64
|
-
}, [socket,
|
|
61
|
+
}, [socket, sendMessage, updateMessageStatus, selectedConversation]);
|
|
65
62
|
|
|
66
63
|
return (
|
|
67
64
|
<div className="container mx-auto mb-5">
|
|
@@ -16,8 +16,9 @@ const MessageContainer = () => {
|
|
|
16
16
|
const { role } = getChatConfig();
|
|
17
17
|
|
|
18
18
|
useEffect(() => {
|
|
19
|
-
if (selectedConversation?._id
|
|
20
|
-
|
|
19
|
+
if (!socket || !selectedConversation?._id) return;
|
|
20
|
+
|
|
21
|
+
const sendJoinChat = () => {
|
|
21
22
|
socket.send(
|
|
22
23
|
JSON.stringify({
|
|
23
24
|
event: "joinChat",
|
|
@@ -28,8 +29,35 @@ const MessageContainer = () => {
|
|
|
28
29
|
},
|
|
29
30
|
})
|
|
30
31
|
);
|
|
32
|
+
|
|
33
|
+
// Bulk-mark all existing unread messages as read when the conversation is opened
|
|
34
|
+
const unreadIds = selectedConversation.unreadMessageIds ?? [];
|
|
35
|
+
const lastMsg = selectedConversation.lastMessage;
|
|
36
|
+
if (unreadIds.length > 0 && lastMsg) {
|
|
37
|
+
socket.send(
|
|
38
|
+
JSON.stringify({
|
|
39
|
+
event: "messageRead",
|
|
40
|
+
data: {
|
|
41
|
+
messageIds: unreadIds,
|
|
42
|
+
chatId: selectedConversation._id,
|
|
43
|
+
senderId: lastMsg.senderId,
|
|
44
|
+
receiverId: userId,
|
|
45
|
+
// roles are not always available here — backend should infer from chatId if needed
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
if (socket.readyState === WebSocket.OPEN) {
|
|
53
|
+
// Socket already open — fire immediately
|
|
54
|
+
sendJoinChat();
|
|
55
|
+
} else if (socket.readyState === WebSocket.CONNECTING) {
|
|
56
|
+
// Socket still connecting — wait for it to open, then fire once
|
|
57
|
+
socket.addEventListener("open", sendJoinChat, { once: true });
|
|
58
|
+
return () => socket.removeEventListener("open", sendJoinChat);
|
|
31
59
|
}
|
|
32
|
-
}, [selectedConversation?._id, socket]);
|
|
60
|
+
}, [selectedConversation?._id, socket, userId, role]);
|
|
33
61
|
|
|
34
62
|
useEffect(() => {
|
|
35
63
|
if (!socket) return;
|
|
@@ -39,7 +39,7 @@ const MessageInput = () => {
|
|
|
39
39
|
const apiClient = getApiClient();
|
|
40
40
|
const { role } = getChatConfig();
|
|
41
41
|
const { socket, sendMessage, userId } = useChatContext();
|
|
42
|
-
const { selectedConversation, setMessages } = useChatUIStore();
|
|
42
|
+
const { selectedConversation, setMessages, setLastSentMessage } = useChatUIStore();
|
|
43
43
|
|
|
44
44
|
const [message, setMessage] = useState("");
|
|
45
45
|
const [message1, setMessage1] = useState("");
|
|
@@ -47,8 +47,6 @@ const MessageInput = () => {
|
|
|
47
47
|
|
|
48
48
|
const [typingUser, setTypingUser] = useState<string | null>(null);
|
|
49
49
|
const [isSending, setIsSending] = useState(false);
|
|
50
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
51
|
-
const [isTyping, setIsTyping] = useState(false);
|
|
52
50
|
const [attachments, setAttachments] = useState<Attachment[]>([]);
|
|
53
51
|
const [showAttachmentOptions, setShowAttachmentOptions] = useState(false);
|
|
54
52
|
|
|
@@ -474,6 +472,8 @@ const MessageInput = () => {
|
|
|
474
472
|
},
|
|
475
473
|
{
|
|
476
474
|
onSuccess: (data) => {
|
|
475
|
+
const confirmedMessage = data[1];
|
|
476
|
+
|
|
477
477
|
setMessages((prev) => {
|
|
478
478
|
const filtered = prev.filter(
|
|
479
479
|
(msg) => msg._id !== tempMessageId
|
|
@@ -481,20 +481,32 @@ const MessageInput = () => {
|
|
|
481
481
|
return [
|
|
482
482
|
...filtered,
|
|
483
483
|
{
|
|
484
|
-
...
|
|
484
|
+
...confirmedMessage,
|
|
485
485
|
isUploading: false,
|
|
486
486
|
isOptimistic: false,
|
|
487
487
|
},
|
|
488
488
|
];
|
|
489
489
|
});
|
|
490
490
|
|
|
491
|
+
// Update the sidebar last message for the sender immediately
|
|
492
|
+
setLastSentMessage({
|
|
493
|
+
_id: confirmedMessage._id,
|
|
494
|
+
conversationId: selectedConversation?._id ?? "",
|
|
495
|
+
message: message1,
|
|
496
|
+
senderId: userId,
|
|
497
|
+
media: successfulUploads,
|
|
498
|
+
status: confirmedMessage.status ?? "sent",
|
|
499
|
+
createdAt: confirmedMessage.createdAt ?? new Date().toISOString(),
|
|
500
|
+
updatedAt: confirmedMessage.updatedAt ?? new Date().toISOString(),
|
|
501
|
+
});
|
|
502
|
+
|
|
491
503
|
// Send message via WebSocket
|
|
492
504
|
sendMessage({
|
|
493
505
|
event: "sendMessage",
|
|
494
506
|
data: {
|
|
495
507
|
chatId: selectedConversation?._id,
|
|
496
508
|
message: message1,
|
|
497
|
-
messageId:
|
|
509
|
+
messageId: confirmedMessage._id,
|
|
498
510
|
attachments: successfulUploads,
|
|
499
511
|
senderId: userId,
|
|
500
512
|
receiverId: otherParticipant._id,
|
|
@@ -160,13 +160,16 @@ const Conversation = ({ conversation }: ConversationProps) => {
|
|
|
160
160
|
</div>
|
|
161
161
|
{/* <span className="text-xs text-gray-500">{lastMessageTimestamp}</span> */}
|
|
162
162
|
</div>
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
163
|
+
<p className="text-xs text-gray-600 dark:text-gray-500">
|
|
164
|
+
{conversation.lastMessage?.status === "deleted"
|
|
165
|
+
? "This message was deleted"
|
|
166
|
+
: conversation.lastMessage?.media?.length > 0
|
|
167
|
+
? "Attachment"
|
|
168
|
+
: conversation.lastMessage?.message?.length > 30
|
|
169
|
+
? conversation.lastMessage.message.slice(0, 30) + "..."
|
|
170
|
+
: conversation.lastMessage?.message}
|
|
171
|
+
</p>
|
|
172
|
+
|
|
170
173
|
</div>
|
|
171
174
|
</div>
|
|
172
175
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
2
|
+
import { useCallback, useEffect, useState } from "react";
|
|
3
3
|
import { useGetConversations } from "../../hooks/queries/useChatApi";
|
|
4
4
|
import { useChatContext } from "../../providers/ChatProvider";
|
|
5
5
|
import {
|
|
@@ -22,7 +22,7 @@ type TabType = "personal" | "service";
|
|
|
22
22
|
const Conversations = () => {
|
|
23
23
|
const { userId, socket } = useChatContext();
|
|
24
24
|
const { data: participantGroups } = useGetConversations(userId);
|
|
25
|
-
const { searchTerm } = useChatUIStore();
|
|
25
|
+
const { searchTerm, lastSentMessage, setLastSentMessage } = useChatUIStore();
|
|
26
26
|
const [activeTab, setActiveTab] = useState<TabType>("personal");
|
|
27
27
|
const [conversations, setConversations] = useState<{
|
|
28
28
|
personalChats: ConversationType[];
|
|
@@ -77,6 +77,68 @@ const Conversations = () => {
|
|
|
77
77
|
setConversations(processConversations(participantGroups));
|
|
78
78
|
}, [participantGroups]);
|
|
79
79
|
|
|
80
|
+
// ── Hoisted to component scope so both the socket listener AND the
|
|
81
|
+
// lastSentMessage effect can call it ─────────────────────────────
|
|
82
|
+
const handleNewMessage = useCallback((newMessage: any) => {
|
|
83
|
+
if (!newMessage?.conversationId) return;
|
|
84
|
+
|
|
85
|
+
setConversations((prev) => {
|
|
86
|
+
const personalChats = [...prev.personalChats];
|
|
87
|
+
const groupedServiceChats = { ...prev.groupedServiceChats };
|
|
88
|
+
|
|
89
|
+
const updateConversation = (convo: ConversationType) => ({
|
|
90
|
+
...convo,
|
|
91
|
+
lastMessage: newMessage,
|
|
92
|
+
updatedAt: new Date().toISOString(),
|
|
93
|
+
unreadMessageIds:
|
|
94
|
+
userId !== newMessage.senderId
|
|
95
|
+
? [...(convo.unreadMessageIds || []), newMessage._id]
|
|
96
|
+
: convo.unreadMessageIds || [],
|
|
97
|
+
unreadMessageCount:
|
|
98
|
+
userId !== newMessage.senderId
|
|
99
|
+
? (convo.unreadMessageCount || 0) + 1
|
|
100
|
+
: convo.unreadMessageCount || 0,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const personalIndex = personalChats.findIndex(
|
|
104
|
+
(c) => c._id === newMessage.conversationId
|
|
105
|
+
);
|
|
106
|
+
if (personalIndex >= 0) {
|
|
107
|
+
personalChats[personalIndex] = updateConversation(personalChats[personalIndex]);
|
|
108
|
+
return { personalChats, groupedServiceChats };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
for (const serviceId in groupedServiceChats) {
|
|
112
|
+
const serviceIndex = groupedServiceChats[serviceId].conversations.findIndex(
|
|
113
|
+
(c) => c._id === newMessage.conversationId
|
|
114
|
+
);
|
|
115
|
+
if (serviceIndex >= 0) {
|
|
116
|
+
const updatedConversations = [...groupedServiceChats[serviceId].conversations];
|
|
117
|
+
updatedConversations[serviceIndex] = updateConversation(updatedConversations[serviceIndex]);
|
|
118
|
+
groupedServiceChats[serviceId] = {
|
|
119
|
+
...groupedServiceChats[serviceId],
|
|
120
|
+
conversations: updatedConversations,
|
|
121
|
+
};
|
|
122
|
+
return { personalChats, groupedServiceChats };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Fallback: brand-new conversation not yet in local state
|
|
127
|
+
personalChats.push({
|
|
128
|
+
_id: newMessage.conversationId,
|
|
129
|
+
participants: [newMessage.senderId, newMessage.receiverId],
|
|
130
|
+
lastMessage: newMessage,
|
|
131
|
+
updatedAt: new Date().toISOString(),
|
|
132
|
+
unreadMessageIds: userId !== newMessage.senderId ? [newMessage._id] : [],
|
|
133
|
+
unreadMessageCount: userId !== newMessage.senderId ? 1 : 0,
|
|
134
|
+
type: "personal",
|
|
135
|
+
createdAt: new Date().toISOString(),
|
|
136
|
+
} as any);
|
|
137
|
+
|
|
138
|
+
return { personalChats, groupedServiceChats };
|
|
139
|
+
});
|
|
140
|
+
}, [userId]);
|
|
141
|
+
|
|
80
142
|
// Real-time update listeners
|
|
81
143
|
useEffect(() => {
|
|
82
144
|
if (!socket) return;
|
|
@@ -129,87 +191,18 @@ const Conversations = () => {
|
|
|
129
191
|
});
|
|
130
192
|
};
|
|
131
193
|
|
|
132
|
-
const handleNewMessage = (newMessage: any) => {
|
|
133
|
-
if (!newMessage?.conversationId) return;
|
|
134
|
-
|
|
135
|
-
setConversations((prev) => {
|
|
136
|
-
const personalChats = [...prev.personalChats];
|
|
137
|
-
const groupedServiceChats = { ...prev.groupedServiceChats };
|
|
138
|
-
|
|
139
|
-
const updateConversation = (convo: ConversationType) => ({
|
|
140
|
-
...convo,
|
|
141
|
-
lastMessage: newMessage,
|
|
142
|
-
updatedAt: new Date().toISOString(),
|
|
143
|
-
unreadMessageIds:
|
|
144
|
-
userId !== newMessage.senderId
|
|
145
|
-
? [...(convo.unreadMessageIds || []), newMessage._id]
|
|
146
|
-
: convo.unreadMessageIds || [],
|
|
147
|
-
unreadMessageCount:
|
|
148
|
-
userId !== newMessage.senderId
|
|
149
|
-
? (convo.unreadMessageCount || 0) + 1
|
|
150
|
-
: convo.unreadMessageCount || 0,
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
const personalIndex = personalChats.findIndex(
|
|
154
|
-
(c) => c._id === newMessage.conversationId
|
|
155
|
-
);
|
|
156
|
-
if (personalIndex >= 0) {
|
|
157
|
-
personalChats[personalIndex] = updateConversation(
|
|
158
|
-
personalChats[personalIndex]
|
|
159
|
-
);
|
|
160
|
-
return { personalChats, groupedServiceChats };
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
for (const serviceId in groupedServiceChats) {
|
|
164
|
-
const serviceIndex = groupedServiceChats[
|
|
165
|
-
serviceId
|
|
166
|
-
].conversations.findIndex((c) => c._id === newMessage.conversationId);
|
|
167
|
-
if (serviceIndex >= 0) {
|
|
168
|
-
const updatedConversations = [
|
|
169
|
-
...groupedServiceChats[serviceId].conversations,
|
|
170
|
-
];
|
|
171
|
-
updatedConversations[serviceIndex] = updateConversation(
|
|
172
|
-
updatedConversations[serviceIndex]
|
|
173
|
-
);
|
|
174
|
-
groupedServiceChats[serviceId] = {
|
|
175
|
-
...groupedServiceChats[serviceId],
|
|
176
|
-
conversations: updatedConversations,
|
|
177
|
-
};
|
|
178
|
-
return { personalChats, groupedServiceChats };
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
personalChats.push({
|
|
183
|
-
_id: newMessage.conversationId,
|
|
184
|
-
participants: [newMessage.senderId, newMessage.receiverId],
|
|
185
|
-
lastMessage: newMessage,
|
|
186
|
-
updatedAt: new Date().toISOString(),
|
|
187
|
-
unreadMessageIds:
|
|
188
|
-
userId !== newMessage.senderId ? [newMessage._id] : [],
|
|
189
|
-
unreadMessageCount: userId !== newMessage.senderId ? 1 : 0,
|
|
190
|
-
type: "personal",
|
|
191
|
-
readReceipts: [],
|
|
192
|
-
createdAt: new Date().toISOString(),
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
return { personalChats, groupedServiceChats };
|
|
196
|
-
});
|
|
197
|
-
};
|
|
198
|
-
|
|
199
194
|
const messageListener = (event: MessageEvent) => {
|
|
200
195
|
try {
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
data
|
|
206
|
-
|
|
207
|
-
|
|
196
|
+
const parsed = JSON.parse(event.data);
|
|
197
|
+
|
|
198
|
+
if (parsed.event === "newMessage") {
|
|
199
|
+
// Actual message is nested at parsed.data.data — same structure as Messages.tsx
|
|
200
|
+
handleNewMessage(parsed?.data?.data);
|
|
201
|
+
} else if (parsed.event === "messageStatusUpdated" && parsed.data?.status === "read") {
|
|
202
|
+
const rawId = parsed.data.messageId ?? parsed.data.messageIds;
|
|
208
203
|
handleMessageReadAck({
|
|
209
|
-
messageIds: Array.isArray(
|
|
210
|
-
|
|
211
|
-
: [data.data.messageId],
|
|
212
|
-
chatId: data.data.chatId,
|
|
204
|
+
messageIds: Array.isArray(rawId) ? rawId : [rawId].filter(Boolean),
|
|
205
|
+
chatId: parsed.data.chatId,
|
|
213
206
|
});
|
|
214
207
|
}
|
|
215
208
|
} catch (e) {
|
|
@@ -219,7 +212,14 @@ const Conversations = () => {
|
|
|
219
212
|
|
|
220
213
|
socket.addEventListener("message", messageListener);
|
|
221
214
|
return () => socket.removeEventListener("message", messageListener);
|
|
222
|
-
}, [socket,
|
|
215
|
+
}, [socket, handleNewMessage]);
|
|
216
|
+
|
|
217
|
+
// React to messages sent by the current user (no server echo needed)
|
|
218
|
+
useEffect(() => {
|
|
219
|
+
if (!lastSentMessage) return;
|
|
220
|
+
handleNewMessage(lastSentMessage);
|
|
221
|
+
setLastSentMessage(null);
|
|
222
|
+
}, [lastSentMessage, handleNewMessage, setLastSentMessage]);
|
|
223
223
|
|
|
224
224
|
// const isEmpty =
|
|
225
225
|
// activeTab === "personal"
|
|
@@ -231,11 +231,12 @@ const Conversations = () => {
|
|
|
231
231
|
// Filter personal chats
|
|
232
232
|
const filteredPersonalChats = !lowerSearch
|
|
233
233
|
? conversations.personalChats
|
|
234
|
-
: conversations.personalChats.filter((convo) =>
|
|
235
|
-
convo.participantDetails
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
234
|
+
: conversations.personalChats.filter((convo) => {
|
|
235
|
+
const details = Array.isArray(convo.participantDetails)
|
|
236
|
+
? convo.participantDetails
|
|
237
|
+
: [convo.participantDetails].filter(Boolean);
|
|
238
|
+
return details.some((p: any) => p?.name?.toLowerCase().includes(lowerSearch));
|
|
239
|
+
});
|
|
239
240
|
|
|
240
241
|
// Filter service chats
|
|
241
242
|
const filteredGroupedServiceChats: GroupedServiceChats = !lowerSearch
|
|
@@ -243,17 +244,21 @@ const Conversations = () => {
|
|
|
243
244
|
: Object.fromEntries(
|
|
244
245
|
Object.entries(conversations.groupedServiceChats)
|
|
245
246
|
.map(([serviceId, group]) => {
|
|
247
|
+
const details = (convo: ConversationType) => {
|
|
248
|
+
const d = Array.isArray(convo.participantDetails)
|
|
249
|
+
? convo.participantDetails
|
|
250
|
+
: [convo.participantDetails].filter(Boolean);
|
|
251
|
+
return d.some((p: any) => p?.name?.toLowerCase().includes(lowerSearch));
|
|
252
|
+
};
|
|
246
253
|
const filteredConvos = group.conversations.filter(
|
|
247
254
|
(convo) =>
|
|
248
|
-
convo
|
|
249
|
-
p?.name?.toLowerCase().includes(lowerSearch)
|
|
250
|
-
) ||
|
|
255
|
+
details(convo) ||
|
|
251
256
|
group.serviceTitle?.toLowerCase().includes(lowerSearch) ||
|
|
252
257
|
convo.serviceTitle?.toLowerCase().includes(lowerSearch)
|
|
253
258
|
);
|
|
254
|
-
return [serviceId, { ...group, conversations: filteredConvos }];
|
|
259
|
+
return [serviceId, { ...group, conversations: filteredConvos }] as [string, GroupedServiceChats[string]];
|
|
255
260
|
})
|
|
256
|
-
.filter(([
|
|
261
|
+
.filter(([, group]) => group.conversations.length > 0)
|
|
257
262
|
);
|
|
258
263
|
|
|
259
264
|
// Improved empty state logic
|
package/src/stores/Zustant.ts
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
import { FileType } from "../components/common/FilePreview";
|
|
3
3
|
import { create } from "zustand";
|
|
4
4
|
|
|
5
|
+
/** Minimal shape needed to update the sidebar after a send */
|
|
6
|
+
export interface SentMessageSignal {
|
|
7
|
+
_id: string;
|
|
8
|
+
conversationId: string;
|
|
9
|
+
message: string;
|
|
10
|
+
senderId: string;
|
|
11
|
+
media?: any[];
|
|
12
|
+
status: string;
|
|
13
|
+
createdAt: string;
|
|
14
|
+
updatedAt: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
5
17
|
interface ChatUIState {
|
|
6
18
|
isChatOpen: boolean;
|
|
7
19
|
unreadCount: number;
|
|
@@ -51,6 +63,9 @@ interface ChatUIState {
|
|
|
51
63
|
setOnlineUsers: (users: string[]) => void;
|
|
52
64
|
searchTerm: string;
|
|
53
65
|
setSearchTerm: (searchTerm: string) => void;
|
|
66
|
+
/** Signal published by MessageInput after a successful send so the sidebar can update */
|
|
67
|
+
lastSentMessage: SentMessageSignal | null;
|
|
68
|
+
setLastSentMessage: (msg: SentMessageSignal | null) => void;
|
|
54
69
|
}
|
|
55
70
|
|
|
56
71
|
const useChatUIStore = create<ChatUIState>((set) => ({
|
|
@@ -74,6 +89,8 @@ const useChatUIStore = create<ChatUIState>((set) => ({
|
|
|
74
89
|
setOnlineUsers: (users) => set({ onlineUsers: users }),
|
|
75
90
|
searchTerm: "",
|
|
76
91
|
setSearchTerm: (searchTerm) => set({ searchTerm }),
|
|
92
|
+
lastSentMessage: null,
|
|
93
|
+
setLastSentMessage: (msg) => set({ lastSentMessage: msg }),
|
|
77
94
|
}));
|
|
78
95
|
|
|
79
96
|
export default useChatUIStore;
|