@armoyu/ui 1.1.39 → 1.1.41
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/dist/components/modules/chat/ChatLayout.d.ts +7 -1
- package/dist/components/modules/chat/ChatLayout.d.ts.map +1 -1
- package/dist/components/modules/chat/ChatLayout.js +347 -59
- package/dist/components/modules/chat/ChatLayout.js.map +1 -1
- package/dist/components/modules/chat/widgets/ChatInput.d.ts.map +1 -1
- package/dist/components/modules/chat/widgets/ChatInput.js +24 -8
- package/dist/components/modules/chat/widgets/ChatInput.js.map +1 -1
- package/dist/components/modules/chat/widgets/ChatList.d.ts +2 -1
- package/dist/components/modules/chat/widgets/ChatList.d.ts.map +1 -1
- package/dist/components/modules/chat/widgets/ChatList.js +21 -10
- package/dist/components/modules/chat/widgets/ChatList.js.map +1 -1
- package/dist/components/modules/chat/widgets/VoiceCallWidget.d.ts +16 -0
- package/dist/components/modules/chat/widgets/VoiceCallWidget.d.ts.map +1 -0
- package/dist/components/modules/chat/widgets/VoiceCallWidget.js +38 -0
- package/dist/components/modules/chat/widgets/VoiceCallWidget.js.map +1 -0
- package/dist/components/modules/education/pages/EducationPage.d.ts.map +1 -1
- package/dist/components/modules/education/pages/EducationPage.js +0 -2
- package/dist/components/modules/education/pages/EducationPage.js.map +1 -1
- package/dist/components/modules/news/widgets/NewsComments.d.ts +2 -1
- package/dist/components/modules/news/widgets/NewsComments.d.ts.map +1 -1
- package/dist/components/modules/news/widgets/NewsComments.js +13 -3
- package/dist/components/modules/news/widgets/NewsComments.js.map +1 -1
- package/dist/components/modules/posts/widgets/MediaLightbox.d.ts +2 -1
- package/dist/components/modules/posts/widgets/MediaLightbox.d.ts.map +1 -1
- package/dist/components/modules/posts/widgets/MediaLightbox.js +3 -3
- package/dist/components/modules/posts/widgets/MediaLightbox.js.map +1 -1
- package/dist/components/modules/posts/widgets/PostCard.d.ts.map +1 -1
- package/dist/components/modules/posts/widgets/PostCard.js +17 -3
- package/dist/components/modules/posts/widgets/PostCard.js.map +1 -1
- package/dist/components/modules/profile/widgets/ProfileHeader.d.ts.map +1 -1
- package/dist/components/modules/profile/widgets/ProfileHeader.js +66 -5
- package/dist/components/modules/profile/widgets/ProfileHeader.js.map +1 -1
- package/dist/components/modules/social/widgets/PostComposer.d.ts.map +1 -1
- package/dist/components/modules/social/widgets/PostComposer.js +3 -2
- package/dist/components/modules/social/widgets/PostComposer.js.map +1 -1
- package/dist/components/shared/EmojiPicker.d.ts +10 -0
- package/dist/components/shared/EmojiPicker.d.ts.map +1 -0
- package/dist/components/shared/EmojiPicker.js +205 -0
- package/dist/components/shared/EmojiPicker.js.map +1 -0
- package/dist/components/shared/FloatingChatButton.d.ts.map +1 -1
- package/dist/components/shared/FloatingChatButton.js +3 -2
- package/dist/components/shared/FloatingChatButton.js.map +1 -1
- package/dist/components/showcase/GroupTab.d.ts.map +1 -1
- package/dist/components/showcase/GroupTab.js +1 -10
- package/dist/components/showcase/GroupTab.js.map +1 -1
- package/dist/components/showcase/ProfileTab.d.ts.map +1 -1
- package/dist/components/showcase/ProfileTab.js +0 -5
- package/dist/components/showcase/ProfileTab.js.map +1 -1
- package/dist/components/showcase/SocialTab.d.ts.map +1 -1
- package/dist/components/showcase/SocialTab.js +0 -4
- package/dist/components/showcase/SocialTab.js.map +1 -1
- package/dist/context/ChatContext.js +2 -2
- package/dist/context/ChatContext.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/models/auth/User.d.ts +1 -0
- package/dist/models/auth/User.d.ts.map +1 -1
- package/dist/models/auth/User.js +2 -0
- package/dist/models/auth/User.js.map +1 -1
- package/dist/models/social/chat/Chat.d.ts +1 -0
- package/dist/models/social/chat/Chat.d.ts.map +1 -1
- package/dist/models/social/chat/Chat.js +21 -47
- package/dist/models/social/chat/Chat.js.map +1 -1
- package/dist/models/social/chat/Message.d.ts.map +1 -1
- package/dist/models/social/chat/Message.js +26 -1
- package/dist/models/social/chat/Message.js.map +1 -1
- package/dist/types/socket.d.ts +1 -1
- package/dist/types/socket.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import { Chat } from '../../../models/social/chat/Chat';
|
|
2
|
+
interface ChatLayoutProps {
|
|
3
|
+
onVoiceCall?: (contact: Chat) => void;
|
|
4
|
+
onAvatarClick?: (contact: Chat) => void;
|
|
5
|
+
}
|
|
6
|
+
export declare function ChatLayout({ onVoiceCall, onAvatarClick }?: ChatLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
2
8
|
//# sourceMappingURL=ChatLayout.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatLayout.d.ts","sourceRoot":"","sources":["../../../../src/components/modules/chat/ChatLayout.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ChatLayout.d.ts","sourceRoot":"","sources":["../../../../src/components/modules/chat/ChatLayout.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,kCAAkC,CAAC;AAmBxD,UAAU,eAAe;IACvB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;IACtC,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,KAAK,IAAI,CAAC;CACzC;AAED,wBAAgB,UAAU,CAAC,EAAE,WAAW,EAAE,aAAa,EAAE,GAAE,eAAoB,2CA+yB9E"}
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import React, { useState, useEffect } from 'react';
|
|
3
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
4
4
|
import { Chat } from '../../../models/social/chat/Chat';
|
|
5
5
|
import { ChatMessage as ChatMessageModel } from '../../../models/social/chat/Message';
|
|
6
|
-
import { Search, X } from 'lucide-react';
|
|
6
|
+
import { Phone, Search, X } from 'lucide-react';
|
|
7
7
|
import { useArmoyu } from '../../../context/ArmoyuContext';
|
|
8
8
|
// Shared Contexts / Utils from main index
|
|
9
|
-
import { useAuth, useChat, useSocket, userList } from '../../../index';
|
|
9
|
+
import { useAuth, useChat, useSocket, userList, VoiceCallWidget } from '../../../index';
|
|
10
10
|
// Module specific widgets
|
|
11
11
|
import { ChatList } from './widgets/ChatList';
|
|
12
12
|
import { ChatMessage } from './widgets/ChatMessage';
|
|
13
13
|
import { ChatInput } from './widgets/ChatInput';
|
|
14
|
-
export function ChatLayout() {
|
|
14
|
+
export function ChatLayout({ onVoiceCall, onAvatarClick } = {}) {
|
|
15
15
|
const { user, session } = useAuth();
|
|
16
16
|
const { setMockEnabled } = useArmoyu();
|
|
17
|
-
const { closeChat, isLiveMode, setLiveMode, chatList: liveContacts, setChatList, activeMessages, setActiveMessages, fetchMessages, fetchChatList, sendMessage: sendApiMessage } = useChat();
|
|
17
|
+
const { isChatOpen, closeChat, isLiveMode, setLiveMode, chatList: liveContacts, searchResults, setChatList, activeMessages, setActiveMessages, fetchMessages, fetchChatList, sendMessage: sendApiMessage } = useChat();
|
|
18
18
|
const { emit, on, isConnected } = useSocket();
|
|
19
|
+
const isChatOpenRef = React.useRef(isChatOpen);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
isChatOpenRef.current = isChatOpen;
|
|
22
|
+
}, [isChatOpen]);
|
|
19
23
|
const handleFetchFromApi = async () => {
|
|
20
24
|
setMockEnabled(false);
|
|
21
25
|
setLiveMode(true);
|
|
@@ -27,10 +31,114 @@ export function ChatLayout() {
|
|
|
27
31
|
const [localMessages, setLocalMessages] = useState([]);
|
|
28
32
|
const [localContacts, setLocalContacts] = useState([]);
|
|
29
33
|
const [isTyping, setIsTyping] = useState(false);
|
|
34
|
+
const [typingUsers, setTypingUsers] = useState([]);
|
|
30
35
|
const [isSearchVisible, setIsSearchVisible] = useState(false);
|
|
31
36
|
const [msgSearchQuery, setMsgSearchQuery] = useState('');
|
|
32
37
|
const scrollContainerRef = React.useRef(null);
|
|
33
38
|
const isFirstRender = React.useRef(true);
|
|
39
|
+
// Call State
|
|
40
|
+
const [activeCall, setActiveCall] = useState(null);
|
|
41
|
+
// Handle call response callbacks
|
|
42
|
+
const handleAcceptCall = useCallback(() => {
|
|
43
|
+
if (!activeCall)
|
|
44
|
+
return;
|
|
45
|
+
const acceptData = {
|
|
46
|
+
caller: activeCall.caller,
|
|
47
|
+
recipientUsername: activeCall.caller.username,
|
|
48
|
+
recipient: activeCall.recipient,
|
|
49
|
+
status: 'accepted'
|
|
50
|
+
};
|
|
51
|
+
emit('chat_call', acceptData);
|
|
52
|
+
setActiveCall(prev => prev ? { ...prev, status: 'connected' } : null);
|
|
53
|
+
}, [activeCall, emit]);
|
|
54
|
+
const handleDeclineCall = useCallback(() => {
|
|
55
|
+
if (!activeCall)
|
|
56
|
+
return;
|
|
57
|
+
const declineData = {
|
|
58
|
+
caller: activeCall.caller,
|
|
59
|
+
recipientUsername: activeCall.caller.username,
|
|
60
|
+
recipient: activeCall.recipient,
|
|
61
|
+
status: 'rejected'
|
|
62
|
+
};
|
|
63
|
+
emit('chat_call', declineData);
|
|
64
|
+
setActiveCall(prev => prev ? { ...prev, status: 'rejected' } : null);
|
|
65
|
+
setTimeout(() => setActiveCall(null), 1500);
|
|
66
|
+
}, [activeCall, emit]);
|
|
67
|
+
const handleHangUpCall = useCallback(() => {
|
|
68
|
+
if (!activeCall)
|
|
69
|
+
return;
|
|
70
|
+
const targetUsername = activeCall.isIncoming ? activeCall.caller.username : activeCall.recipient.username;
|
|
71
|
+
const hangupData = {
|
|
72
|
+
caller: activeCall.caller,
|
|
73
|
+
recipientUsername: targetUsername,
|
|
74
|
+
recipient: activeCall.recipient,
|
|
75
|
+
status: 'hung_up'
|
|
76
|
+
};
|
|
77
|
+
emit('chat_call', hangupData);
|
|
78
|
+
setActiveCall(prev => prev ? { ...prev, status: 'hung_up' } : null);
|
|
79
|
+
setTimeout(() => setActiveCall(null), 1500);
|
|
80
|
+
}, [activeCall, emit]);
|
|
81
|
+
// Listen for socket call events
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (!on)
|
|
84
|
+
return;
|
|
85
|
+
const offCall = on('chat_call', (data) => {
|
|
86
|
+
console.log('[Socket] Incoming call event:', data);
|
|
87
|
+
if (data.status === 'ringing') {
|
|
88
|
+
if (user && data.recipientUsername === user.username) {
|
|
89
|
+
if (activeCall && activeCall.status === 'connected') {
|
|
90
|
+
emit('chat_call', {
|
|
91
|
+
caller: data.caller,
|
|
92
|
+
recipientUsername: data.caller.username,
|
|
93
|
+
recipient: data.recipient,
|
|
94
|
+
status: 'busy'
|
|
95
|
+
});
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
setActiveCall({
|
|
99
|
+
caller: data.caller,
|
|
100
|
+
recipient: data.recipient,
|
|
101
|
+
isIncoming: true,
|
|
102
|
+
status: 'ringing'
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else if (data.status === 'accepted') {
|
|
107
|
+
if (activeCall && !activeCall.isIncoming && activeCall.status === 'ringing') {
|
|
108
|
+
setActiveCall(prev => prev ? { ...prev, status: 'connected' } : null);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else if (data.status === 'rejected') {
|
|
112
|
+
if (activeCall && !activeCall.isIncoming && activeCall.status === 'ringing') {
|
|
113
|
+
setActiveCall(prev => prev ? { ...prev, status: 'rejected' } : null);
|
|
114
|
+
setTimeout(() => setActiveCall(null), 1500);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else if (data.status === 'busy') {
|
|
118
|
+
if (activeCall && !activeCall.isIncoming && activeCall.status === 'ringing') {
|
|
119
|
+
setActiveCall(prev => prev ? { ...prev, status: 'busy' } : null);
|
|
120
|
+
setTimeout(() => setActiveCall(null), 1500);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else if (data.status === 'hung_up') {
|
|
124
|
+
setActiveCall(prev => prev ? { ...prev, status: 'hung_up' } : null);
|
|
125
|
+
setTimeout(() => setActiveCall(null), 1500);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
return () => {
|
|
129
|
+
offCall();
|
|
130
|
+
};
|
|
131
|
+
}, [on, user, activeCall, emit]);
|
|
132
|
+
// Soket bağlantısı sağlandığında sunucuya kayıt ol
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
if (isConnected && user?.id) {
|
|
135
|
+
emit('register_user', {
|
|
136
|
+
userId: user.id,
|
|
137
|
+
username: user.username
|
|
138
|
+
});
|
|
139
|
+
console.log(`[Socket] Kayıt isteği gönderildi: ${user.username} (${user.id})`);
|
|
140
|
+
}
|
|
141
|
+
}, [isConnected, user, emit]);
|
|
34
142
|
// Tarihleri karşılaştırmak ve "Bugün", "Dün" gibi etiketler üretmek için yardımcı fonksiyon
|
|
35
143
|
const getDateLabel = (dateStr) => {
|
|
36
144
|
try {
|
|
@@ -40,12 +148,8 @@ export function ChatLayout() {
|
|
|
40
148
|
if (isNaN(date.getTime()))
|
|
41
149
|
return dateStr; // Geçersiz tarihse olduğu gibi bırak
|
|
42
150
|
const today = new Date();
|
|
43
|
-
const yesterday = new Date();
|
|
44
|
-
yesterday.setDate(today.getDate() - 1);
|
|
45
151
|
if (date.toDateString() === today.toDateString())
|
|
46
152
|
return 'Bugün';
|
|
47
|
-
if (date.toDateString() === yesterday.toDateString())
|
|
48
|
-
return 'Dün';
|
|
49
153
|
return date.toLocaleDateString('tr-TR', { day: 'numeric', month: 'long', year: 'numeric' });
|
|
50
154
|
}
|
|
51
155
|
catch (e) {
|
|
@@ -96,8 +200,8 @@ export function ChatLayout() {
|
|
|
96
200
|
// Handle Contact Selection in LIVE mode
|
|
97
201
|
useEffect(() => {
|
|
98
202
|
if (isLiveMode && activeContactId) {
|
|
99
|
-
// Find the contact in the list to get its chatType and raw ID
|
|
100
|
-
const contact = (isLiveMode ? liveContacts : localContacts).find(c => c.uid === activeContactId);
|
|
203
|
+
// Find the contact in the list (or search results) to get its chatType and raw ID
|
|
204
|
+
const contact = (isLiveMode ? [...liveContacts, ...searchResults] : localContacts).find(c => c.uid === activeContactId);
|
|
101
205
|
if (contact) {
|
|
102
206
|
const idNum = parseInt(contact.id);
|
|
103
207
|
if (!isNaN(idNum)) {
|
|
@@ -109,30 +213,118 @@ export function ChatLayout() {
|
|
|
109
213
|
}
|
|
110
214
|
}, [activeContactId, isLiveMode, fetchMessages]);
|
|
111
215
|
// Auto-select logic removed to prevent automatic chat opening on load
|
|
216
|
+
const playNotificationSound = () => {
|
|
217
|
+
try {
|
|
218
|
+
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
219
|
+
const osc = audioContext.createOscillator();
|
|
220
|
+
const gainNode = audioContext.createGain();
|
|
221
|
+
osc.connect(gainNode);
|
|
222
|
+
gainNode.connect(audioContext.destination);
|
|
223
|
+
osc.type = 'sine';
|
|
224
|
+
const now = audioContext.currentTime;
|
|
225
|
+
// Ping 1 (587.33Hz - D5)
|
|
226
|
+
osc.frequency.setValueAtTime(587.33, now);
|
|
227
|
+
gainNode.gain.setValueAtTime(0, now);
|
|
228
|
+
gainNode.gain.linearRampToValueAtTime(0.06, now + 0.04);
|
|
229
|
+
gainNode.gain.exponentialRampToValueAtTime(0.001, now + 0.15);
|
|
230
|
+
// Ping 2 (880Hz - A5)
|
|
231
|
+
osc.frequency.setValueAtTime(880, now + 0.1);
|
|
232
|
+
gainNode.gain.setValueAtTime(0.06, now + 0.1);
|
|
233
|
+
gainNode.gain.exponentialRampToValueAtTime(0.001, now + 0.3);
|
|
234
|
+
osc.start(now);
|
|
235
|
+
osc.stop(now + 0.35);
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
console.warn("Ses çalınamadı:", err);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
112
241
|
useEffect(() => {
|
|
113
242
|
const offMsg = on('chat_message', (incomingMsg) => {
|
|
243
|
+
console.log(`[Socket] Yeni mesaj alındı (ID: ${incomingMsg.id})`);
|
|
244
|
+
// Optimistic Update: Ignore if we just sent this message
|
|
245
|
+
if (incomingMsg.sender?.username === user?.username) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
// Check if we are actively chatting with the sender or group
|
|
249
|
+
const currentActiveContact = (isLiveMode ? [...liveContacts, ...searchResults] : localContacts).find(c => c.uid === activeContactId);
|
|
250
|
+
const isGroup = incomingMsg.chatType === 'grup' || String(incomingMsg.chatId) === '0';
|
|
251
|
+
const isChattingWithSender = currentActiveContact && (isGroup
|
|
252
|
+
? (currentActiveContact.chatType === 'grup' && (String(incomingMsg.chatId) === currentActiveContact.id || (incomingMsg.chatName && currentActiveContact.name === incomingMsg.chatName)))
|
|
253
|
+
: (currentActiveContact.chatType !== 'grup' && (String(incomingMsg.sender?.id) === currentActiveContact.id ||
|
|
254
|
+
incomingMsg.sender?.username === currentActiveContact.username ||
|
|
255
|
+
incomingMsg.sender?.username === currentActiveContact.id)));
|
|
256
|
+
// Play sound if not active in this chat or if chat list is closed
|
|
257
|
+
if (!isChatOpenRef.current || !isChattingWithSender) {
|
|
258
|
+
playNotificationSound();
|
|
259
|
+
}
|
|
114
260
|
const msgModel = ChatMessageModel.fromAPI(incomingMsg);
|
|
115
261
|
// 1. UPDATE GLOBAL CHAT LIST CACHE (For both Live and Mock modes)
|
|
116
|
-
setChatList(prev =>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
262
|
+
setChatList(prev => {
|
|
263
|
+
const contactId = String(incomingMsg.chatId);
|
|
264
|
+
const senderUsername = incomingMsg.sender?.username;
|
|
265
|
+
const isGroupMsg = incomingMsg.chatType === 'grup' || contactId === '0';
|
|
266
|
+
// EŞLEŞTİRME MANTIĞI: c.id (örn: 12138) veya c.username (örn: kkadirgumus) eşleşiyorsa bu sohbet mevcuttur!
|
|
267
|
+
const matched = prev.filter(c => {
|
|
268
|
+
if (isGroupMsg) {
|
|
269
|
+
return c.chatType === 'grup' && (c.id === contactId || (incomingMsg.chatName && c.name === incomingMsg.chatName));
|
|
270
|
+
}
|
|
271
|
+
return c.chatType !== 'grup' && (c.id === String(incomingMsg.sender?.id) || c.username === senderUsername || c.id === senderUsername);
|
|
272
|
+
});
|
|
273
|
+
console.log(`[Socket] Etkilenen Sohbet Sayısı: ${matched.length}`);
|
|
274
|
+
matched.forEach(c => {
|
|
275
|
+
console.log(`[Socket] Etkilenen Sohbet Bilgisi -> Ad: ${c.name}, ID: ${c.id}, Tip: ${c.chatType}`);
|
|
276
|
+
});
|
|
277
|
+
const exists = matched.length > 0;
|
|
278
|
+
// Eğer listede yoksa ve kendi mesajımız değilse YENİDEN OLUŞTUR (Sohbeti silenler için)
|
|
279
|
+
if (!exists && senderUsername && senderUsername !== user?.username) {
|
|
280
|
+
const finalId = isGroupMsg ? contactId : (incomingMsg.sender?.id || senderUsername);
|
|
281
|
+
const newChat = new Chat({
|
|
282
|
+
id: finalId,
|
|
283
|
+
uid: isGroupMsg ? `grup-${finalId}` : `ozel-${finalId}`,
|
|
284
|
+
username: isGroupMsg ? '' : senderUsername,
|
|
285
|
+
name: isGroupMsg ? (incomingMsg.chatName || 'Grup Sohbeti') : (incomingMsg.sender?.displayName || senderUsername),
|
|
286
|
+
avatar: isGroupMsg ? '' : (incomingMsg.sender?.avatar || ''),
|
|
121
287
|
lastMessage: msgModel,
|
|
122
288
|
time: msgModel.timestamp,
|
|
123
289
|
updatedAt: Date.now(),
|
|
124
|
-
messages:
|
|
125
|
-
unreadCount: (activeContactId !==
|
|
290
|
+
messages: [msgModel],
|
|
291
|
+
unreadCount: (activeContactId !== (isGroupMsg ? `grup-${finalId}` : `ozel-${finalId}`)) ? 1 : 0,
|
|
292
|
+
isOnline: !isGroupMsg,
|
|
293
|
+
chatType: isGroupMsg ? 'grup' : 'ozel'
|
|
126
294
|
});
|
|
295
|
+
return [newChat, ...prev];
|
|
127
296
|
}
|
|
128
|
-
|
|
129
|
-
|
|
297
|
+
// Varsa mevcut olanı güncelle
|
|
298
|
+
return prev.map(c => {
|
|
299
|
+
const isMatch = isGroupMsg
|
|
300
|
+
? (c.chatType === 'grup' && (c.id === contactId || (incomingMsg.chatName && c.name === incomingMsg.chatName)))
|
|
301
|
+
: (c.chatType !== 'grup' && (c.id === String(incomingMsg.sender?.id) || c.username === senderUsername || c.id === senderUsername));
|
|
302
|
+
if (isMatch) {
|
|
303
|
+
const messageExists = c.messages.some(m => m.id === msgModel.id);
|
|
304
|
+
return new Chat({
|
|
305
|
+
...c,
|
|
306
|
+
lastMessage: msgModel,
|
|
307
|
+
time: msgModel.timestamp,
|
|
308
|
+
updatedAt: Date.now(),
|
|
309
|
+
messages: messageExists ? c.messages : [...(c.messages || []), msgModel],
|
|
310
|
+
unreadCount: (activeContactId !== c.uid && incomingMsg.sender?.username !== user?.username) ? c.unreadCount + 1 : c.unreadCount
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
return c;
|
|
314
|
+
});
|
|
315
|
+
});
|
|
130
316
|
// 2. UPDATE ACTIVE MESSAGES IF OPEN
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
317
|
+
if (isChattingWithSender) {
|
|
318
|
+
setTypingUsers(prev => {
|
|
319
|
+
const filtered = prev.filter(u => u.username !== incomingMsg.sender?.username);
|
|
320
|
+
setIsTyping(filtered.length > 0);
|
|
321
|
+
return filtered;
|
|
322
|
+
});
|
|
323
|
+
setActiveMessages(prev => {
|
|
324
|
+
if (prev.some(m => m.id === msgModel.id))
|
|
325
|
+
return prev;
|
|
326
|
+
return [...prev, msgModel];
|
|
327
|
+
});
|
|
136
328
|
}
|
|
137
329
|
// 3. MOCK MODE UPDATES
|
|
138
330
|
if (!isLiveMode) {
|
|
@@ -170,14 +362,43 @@ export function ChatLayout() {
|
|
|
170
362
|
});
|
|
171
363
|
});
|
|
172
364
|
}
|
|
173
|
-
|
|
365
|
+
if (incomingMsg.sender?.username) {
|
|
366
|
+
setTypingUsers(prev => {
|
|
367
|
+
const filtered = prev.filter(u => u.username !== incomingMsg.sender.username);
|
|
368
|
+
setIsTyping(filtered.length > 0);
|
|
369
|
+
return filtered;
|
|
370
|
+
});
|
|
371
|
+
}
|
|
174
372
|
});
|
|
175
|
-
const offTyping = on('
|
|
176
|
-
const currentActiveContact = (isLiveMode ? liveContacts : localContacts).find(c => c.uid === activeContactId);
|
|
177
|
-
|
|
373
|
+
const offTyping = on('chat_typing', (data) => {
|
|
374
|
+
const currentActiveContact = (isLiveMode ? [...liveContacts, ...searchResults] : localContacts).find(c => c.uid === activeContactId);
|
|
375
|
+
if (!currentActiveContact)
|
|
376
|
+
return;
|
|
377
|
+
const isGroupChat = currentActiveContact.chatType === 'grup' || currentActiveContact.id === '0';
|
|
378
|
+
const isTypingHere = isGroupChat
|
|
379
|
+
? ((String(data.chatId) === currentActiveContact.id || data.chatId === currentActiveContact.username) && data.username !== user?.username)
|
|
380
|
+
: (data.username === currentActiveContact.username && data.chatId === user?.username);
|
|
178
381
|
const isBotTyping = data.username === 'system' && data.chatId === user?.username;
|
|
179
|
-
if (
|
|
180
|
-
|
|
382
|
+
if (isTypingHere || isBotTyping) {
|
|
383
|
+
if (data.isTyping) {
|
|
384
|
+
setTypingUsers(prev => {
|
|
385
|
+
if (prev.some(u => u.username === data.username))
|
|
386
|
+
return prev;
|
|
387
|
+
const updated = [...prev, {
|
|
388
|
+
username: data.username,
|
|
389
|
+
avatar: data.avatar || (isBotTyping ? 'https://api.dicebear.com/7.x/avataaars/svg?seed=System' : currentActiveContact.avatar)
|
|
390
|
+
}];
|
|
391
|
+
setIsTyping(true);
|
|
392
|
+
return updated;
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
setTypingUsers(prev => {
|
|
397
|
+
const updated = prev.filter(u => u.username !== data.username);
|
|
398
|
+
setIsTyping(updated.length > 0);
|
|
399
|
+
return updated;
|
|
400
|
+
});
|
|
401
|
+
}
|
|
181
402
|
}
|
|
182
403
|
});
|
|
183
404
|
return () => {
|
|
@@ -186,7 +407,7 @@ export function ChatLayout() {
|
|
|
186
407
|
};
|
|
187
408
|
}, [on, activeContactId, user?.username, isLiveMode]);
|
|
188
409
|
const handleSelectContact = (uid) => {
|
|
189
|
-
const contact = (isLiveMode ? liveContacts : localContacts).find(c => c.uid === uid);
|
|
410
|
+
const contact = (isLiveMode ? [...liveContacts, ...searchResults] : localContacts).find(c => c.uid === uid);
|
|
190
411
|
// Caching: If we already have messages for this contact, show them immediately
|
|
191
412
|
if (contact && contact.messages && contact.messages.length > 0) {
|
|
192
413
|
setActiveMessages(contact.messages);
|
|
@@ -216,12 +437,37 @@ export function ChatLayout() {
|
|
|
216
437
|
}
|
|
217
438
|
setActiveContactId(uid);
|
|
218
439
|
setIsTyping(false);
|
|
440
|
+
setTypingUsers([]);
|
|
219
441
|
};
|
|
220
442
|
const handleSendMessage = async (text) => {
|
|
221
443
|
if (!text.trim() || !activeContactId)
|
|
222
444
|
return;
|
|
223
445
|
if (isLiveMode) {
|
|
224
|
-
const contact = (isLiveMode ? liveContacts : localContacts).find(c => c.uid === activeContactId);
|
|
446
|
+
const contact = (isLiveMode ? [...liveContacts, ...searchResults] : localContacts).find(c => c.uid === activeContactId);
|
|
447
|
+
// Optimistic Update: Anında UI'a ekle ki bekleme olmasın
|
|
448
|
+
const newMessage = new ChatMessageModel({
|
|
449
|
+
id: Date.now().toString(),
|
|
450
|
+
sender: user,
|
|
451
|
+
content: text,
|
|
452
|
+
timestamp: new Date().toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' }),
|
|
453
|
+
fullDate: new Date().toISOString(),
|
|
454
|
+
isSystem: false,
|
|
455
|
+
isSelf: true
|
|
456
|
+
});
|
|
457
|
+
setActiveMessages(prev => [...prev, newMessage]);
|
|
458
|
+
// Global chat list'in son mesajını da anında güncelle
|
|
459
|
+
setChatList(prev => prev.map(c => {
|
|
460
|
+
if (c.uid === activeContactId) {
|
|
461
|
+
return new Chat({
|
|
462
|
+
...c,
|
|
463
|
+
lastMessage: newMessage,
|
|
464
|
+
time: newMessage.timestamp,
|
|
465
|
+
updatedAt: Date.now(),
|
|
466
|
+
messages: [...(c.messages || []), newMessage]
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
return c;
|
|
470
|
+
}));
|
|
225
471
|
if (contact) {
|
|
226
472
|
const idNum = parseInt(contact.id);
|
|
227
473
|
if (!isNaN(idNum)) {
|
|
@@ -252,48 +498,90 @@ export function ChatLayout() {
|
|
|
252
498
|
return c;
|
|
253
499
|
}));
|
|
254
500
|
}
|
|
255
|
-
const currentContact = (isLiveMode ? liveContacts : localContacts).find(c => c.uid === activeContactId);
|
|
501
|
+
const currentContact = (isLiveMode ? [...liveContacts, ...searchResults] : localContacts).find(c => c.uid === activeContactId);
|
|
502
|
+
// Eğer contact listede yoksa (örn. arama sonuçlarından seçilmişse), activeContactId "ozel-11107" gibi gelir.
|
|
503
|
+
// ID kısmını ayıklayıp göndermeliyiz.
|
|
504
|
+
const finalChatId = currentContact?.id || activeContactId.replace('ozel-', '').replace('grup-', '');
|
|
256
505
|
emit('chat_message', {
|
|
257
506
|
id: Date.now().toString(),
|
|
258
|
-
chatId:
|
|
507
|
+
chatId: finalChatId,
|
|
508
|
+
chatType: currentContact?.chatType || (activeContactId.startsWith('grup-') ? 'grup' : 'ozel'),
|
|
509
|
+
chatName: currentContact?.name || '',
|
|
259
510
|
sender: {
|
|
511
|
+
id: user?.id,
|
|
260
512
|
username: user?.username,
|
|
261
|
-
displayName: user?.displayName,
|
|
513
|
+
displayName: user?.getName?.() || user?.displayName || user?.username,
|
|
262
514
|
avatar: user?.avatar
|
|
263
515
|
},
|
|
264
516
|
content: text,
|
|
265
|
-
timestamp: new Date().toLocaleTimeString(
|
|
517
|
+
timestamp: new Date().toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' }),
|
|
266
518
|
tarih: new Date().toISOString()
|
|
267
519
|
});
|
|
268
520
|
};
|
|
269
521
|
if (!user) {
|
|
270
522
|
return (_jsx("div", { className: "flex items-center justify-center h-full text-armoyu-text-muted bg-armoyu-card-bg rounded-3xl border border-gray-200 dark:border-white/10", children: "Sohbetleri g\u00F6rmek i\u00E7in giri\u015F yapmal\u0131s\u0131n\u0131z." }));
|
|
271
523
|
}
|
|
272
|
-
const currentContacts = isLiveMode ? liveContacts : localContacts;
|
|
524
|
+
const currentContacts = isLiveMode ? [...liveContacts, ...searchResults] : localContacts;
|
|
273
525
|
const activeContact = activeContactId ? currentContacts.find((c) => c.uid === activeContactId) : null;
|
|
274
|
-
return (_jsxs("div", { className: "flex h-full w-full overflow-hidden relative z-10", children: [!activeContactId && (_jsxs("div", { className: "w-full h-full flex flex-col animate-in fade-in slide-in-from-left-4 duration-300", children: [_jsx("div", { className: "p-4 border-b border-gray-200 dark:border-white/5 flex items-center justify-between", children: _jsx("h2", { className: "font-black text-armoyu-text uppercase tracking-widest text-sm", children: "Sohbetler" }) }), _jsx(ChatList, { contacts: currentContacts, activeId: activeContactId || '', onSelect: handleSelectContact })] })), activeContactId && activeContact && (_jsxs("div", { className: "w-full h-full flex flex-col bg-armoyu-bg relative animate-in fade-in slide-in-from-right-4 duration-300", children: [_jsxs("div", { className: "h-[76px] border-b border-gray-200 dark:border-white/5 bg-armoyu-card-bg flex items-center px-4 gap-3 z-10 shrink-0", children: [_jsx("button", { onClick: () => setActiveContactId(null), className: "p-2 -ml-2 text-armoyu-text-muted hover:text-armoyu-text hover:bg-black/5 dark:hover:bg-white/5 transition-colors rounded-full focus:outline-none", children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("polyline", { points: "15 18 9 12 15 6" }) }) }), _jsx("img", { src: activeContact.avatar,
|
|
526
|
+
return (_jsxs("div", { className: "flex h-full w-full overflow-hidden relative z-10", children: [!activeContactId && (_jsxs("div", { className: "w-full h-full flex flex-col animate-in fade-in slide-in-from-left-4 duration-300", children: [_jsx("div", { className: "p-4 border-b border-gray-200 dark:border-white/5 flex items-center justify-between shrink-0", children: _jsx("h2", { className: "font-black text-armoyu-text uppercase tracking-widest text-sm", children: "Sohbetler" }) }), _jsx(ChatList, { contacts: currentContacts, activeId: activeContactId || '', onSelect: handleSelectContact, className: "flex-1 min-h-0" })] })), activeContactId && activeContact && (_jsxs("div", { className: "w-full h-full flex flex-col bg-armoyu-bg relative animate-in fade-in slide-in-from-right-4 duration-300", children: [_jsxs("div", { className: "h-[76px] border-b border-gray-200 dark:border-white/5 bg-armoyu-card-bg flex items-center px-4 gap-3 z-10 shrink-0", children: [_jsx("button", { onClick: () => setActiveContactId(null), className: "p-2 -ml-2 text-armoyu-text-muted hover:text-armoyu-text hover:bg-black/5 dark:hover:bg-white/5 transition-colors rounded-full focus:outline-none", children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("polyline", { points: "15 18 9 12 15 6" }) }) }), _jsx("img", { src: activeContact.avatar, onClick: () => {
|
|
527
|
+
if (onAvatarClick) {
|
|
528
|
+
onAvatarClick(activeContact);
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
alert(`${activeContact.name} profil sayfası açılıyor (ID: ${activeContact.id}, Username: ${activeContact.username}).`);
|
|
532
|
+
}
|
|
533
|
+
}, className: "w-10 h-10 rounded-full border border-black/5 dark:border-white/10 shadow-sm cursor-pointer hover:opacity-80 transition-opacity", alt: "" }), _jsx("div", { className: "flex-1 min-w-0", children: isSearchVisible ? (_jsxs("div", { className: "flex items-center bg-black/5 dark:bg-white/5 border border-black/10 dark:border-white/10 rounded-xl px-3 py-1.5 animate-in fade-in slide-in-from-right-2 duration-300", children: [_jsx(Search, { className: "w-4 h-4 text-armoyu-text-muted mr-2" }), _jsx("input", { type: "text", autoFocus: true, placeholder: "Mesajlarda ara...", value: msgSearchQuery, onChange: (e) => setMsgSearchQuery(e.target.value), className: "flex-1 bg-transparent border-none outline-none text-sm text-armoyu-text placeholder:text-armoyu-text-muted font-medium" }), _jsx("button", { onClick: () => { setIsSearchVisible(false); setMsgSearchQuery(''); }, className: "p-1 hover:bg-black/5 rounded-full transition-colors", children: _jsx(X, { className: "w-4 h-4 text-armoyu-text-muted" }) })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("h3", { className: "font-bold text-armoyu-text truncate text-base", children: activeContact.name }), activeContact.isGroup && (_jsx("span", { className: "px-2 py-0.5 rounded-md bg-armoyu-primary/10 text-armoyu-primary text-[9px] font-black uppercase tracking-widest border border-armoyu-primary/20 flex-shrink-0", children: "Topluluk" }))] }), _jsx("div", { className: "flex items-center gap-1.5 mt-0.5", children: activeContact.isOnline ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "w-2 h-2 rounded-full bg-emerald-500 shadow-[0_0_5px_rgba(16,185,129,0.6)] animate-pulse" }), _jsx("span", { className: "text-xs font-bold text-emerald-500 shadow-sm", children: "\u00C7evrimi\u00E7i" })] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "w-2 h-2 rounded-full bg-gray-400 dark:bg-gray-600" }), _jsxs("span", { className: "text-xs font-bold text-armoyu-text-muted", children: ["Son g\u00F6r\u00FClme ", activeContact.lastSeen] })] })) })] })) }), _jsxs("div", { className: "flex gap-1", children: [_jsx("button", { onClick: () => {
|
|
534
|
+
if (activeContact) {
|
|
535
|
+
const callData = {
|
|
536
|
+
caller: {
|
|
537
|
+
username: user?.username || 'user',
|
|
538
|
+
displayName: user?.displayName || 'Kullanıcı',
|
|
539
|
+
avatar: user?.avatar || 'https://api.dicebear.com/7.x/avataaars/svg'
|
|
540
|
+
},
|
|
541
|
+
recipientUsername: activeContact.username || activeContact.id,
|
|
542
|
+
recipient: {
|
|
543
|
+
username: activeContact.username || activeContact.id,
|
|
544
|
+
displayName: activeContact.name,
|
|
545
|
+
avatar: activeContact.avatar
|
|
546
|
+
},
|
|
547
|
+
status: 'ringing'
|
|
548
|
+
};
|
|
549
|
+
emit('chat_call', callData);
|
|
550
|
+
setActiveCall({
|
|
551
|
+
caller: callData.caller,
|
|
552
|
+
recipient: callData.recipient,
|
|
553
|
+
isIncoming: false,
|
|
554
|
+
status: 'ringing'
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
}, className: "p-2 text-armoyu-text-muted hover:text-emerald-500 rounded-full hover:bg-emerald-500/10 transition-colors", title: "Sesli Arama Ba\u015Flat", children: _jsx(Phone, { className: "w-5 h-5" }) }), _jsx("button", { onClick: () => setIsSearchVisible(!isSearchVisible), className: `p-2 rounded-full transition-colors ${isSearchVisible ? 'text-armoyu-primary bg-armoyu-primary/10' : 'text-armoyu-text-muted hover:text-armoyu-primary hover:bg-armoyu-primary/10'}`, title: "Sohbet \u0130\u00E7i Ara", children: _jsx(Search, { className: "w-5 h-5" }) }), _jsx("button", { onClick: closeChat, className: "p-2 text-armoyu-text-muted hover:text-red-500 rounded-full hover:bg-red-500/10 transition-colors", title: "Sohbeti Kapat", children: _jsx(X, { className: "w-5 h-5" }) })] })] }), _jsxs("div", { ref: scrollContainerRef, className: "flex-1 overflow-y-auto p-4 md:p-6 flex flex-col-reverse gap-2 relative hide-scrollbar scroll-smooth", children: [typingUsers.length > 0 && (_jsxs("div", { className: "flex gap-2 items-center px-4 mb-2 animate-in fade-in slide-in-from-bottom-2 duration-300", children: [_jsxs("div", { className: "flex -space-x-2 overflow-hidden shrink-0", children: [typingUsers.slice(0, 3).map((tUser, idx) => (_jsx("img", { src: tUser.avatar || 'https://api.dicebear.com/7.x/avataaars/svg?seed=Anonymous', alt: tUser.username, className: "inline-block w-6 h-6 rounded-full ring-2 ring-white dark:ring-[#0a0a0e] object-cover bg-white/5", style: { zIndex: 10 - idx }, onError: (e) => { e.target.src = 'https://api.dicebear.com/7.x/avataaars/svg?seed=Anonymous'; } }, tUser.username))), typingUsers.length > 3 && (_jsxs("div", { className: "flex items-center justify-center w-6 h-6 rounded-full bg-armoyu-primary text-white text-[8px] font-black ring-2 ring-white dark:ring-[#0a0a0e] z-0", children: ["+", typingUsers.length - 3] }))] }), _jsx("div", { className: "flex gap-1 items-center bg-black/5 dark:bg-white/5 px-3 py-1.5 rounded-2xl border border-black/5 dark:border-white/5", children: _jsxs("div", { className: "flex gap-0.5 mt-0.5", children: [_jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-armoyu-primary animate-bounce [animation-delay:-0.3s]" }), _jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-armoyu-primary animate-bounce [animation-delay:-0.15s]" }), _jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-armoyu-primary animate-bounce" })] }) })] })), (() => {
|
|
275
558
|
const msgs = (isLiveMode ? activeMessages : localMessages)
|
|
276
559
|
.filter(m => m.content.toLowerCase().includes(msgSearchQuery.toLowerCase()));
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
const currentDate = new Date(msg.fullDate).toDateString();
|
|
290
|
-
const nextMsg = msgs[i + 1];
|
|
291
|
-
const nextDate = nextMsg ? new Date(nextMsg.fullDate).toDateString() : null;
|
|
292
|
-
if (currentDate !== nextDate) {
|
|
293
|
-
renderedItems.push(_jsx("div", { className: "flex justify-center my-6 sticky top-0 z-10", children: _jsx("span", { className: "px-4 py-1.5 rounded-full bg-black/5 dark:bg-white/5 text-[10px] font-black uppercase tracking-widest text-armoyu-text-muted border border-black/5 dark:border-white/5 backdrop-blur-md", children: getDateLabel(msg.fullDate) }) }, `date-${currentDate}`));
|
|
560
|
+
const groups = [];
|
|
561
|
+
let currentGroup = null;
|
|
562
|
+
msgs.forEach(msg => {
|
|
563
|
+
const dateKey = new Date(msg.fullDate).toDateString();
|
|
564
|
+
const label = getDateLabel(msg.fullDate) || '';
|
|
565
|
+
if (!currentGroup || currentGroup.dateKey !== dateKey) {
|
|
566
|
+
currentGroup = {
|
|
567
|
+
dateKey,
|
|
568
|
+
dateLabel: label,
|
|
569
|
+
messages: []
|
|
570
|
+
};
|
|
571
|
+
groups.push(currentGroup);
|
|
294
572
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
573
|
+
currentGroup.messages.push(msg);
|
|
574
|
+
});
|
|
575
|
+
// flex-col-reverse kullandığımız için:
|
|
576
|
+
// Yeni günün grubu DOM'da ilk sırada (dolayısıyla ekranda en altta) olmalı.
|
|
577
|
+
// Bu yüzden grupları ters çevirip basıyoruz.
|
|
578
|
+
return groups.reverse().map(group => (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("div", { className: "flex justify-center my-4 sticky top-0 z-10", children: _jsx("span", { className: "px-4 py-1.5 rounded-full bg-black/5 dark:bg-white/5 text-[10px] font-black uppercase tracking-widest text-armoyu-text-muted border border-black/5 dark:border-white/5 backdrop-blur-md", children: group.dateLabel }) }), group.messages.map(msg => (_jsx(ChatMessage, { id: msg.id, sender: {
|
|
579
|
+
name: msg.sender?.displayName || 'Bilinmiyor',
|
|
580
|
+
avatar: msg.sender?.avatar || '',
|
|
581
|
+
isSelf: msg.isSelf
|
|
582
|
+
}, content: msg.content, timestamp: msg.timestamp }, msg.id)))] }, `group-${group.dateKey}`)));
|
|
583
|
+
})()] }), _jsx(ChatInput, { onSend: handleSendMessage, chatId: (activeContact?.chatType === 'grup' || activeContact?.id === '0')
|
|
584
|
+
? activeContact?.id?.toString()
|
|
585
|
+
: (activeContact?.username || activeContact?.id?.toString()) })] })), activeCall && (_jsx(VoiceCallWidget, { caller: activeCall.caller, recipient: activeCall.recipient, isIncoming: activeCall.isIncoming, status: activeCall.status, onAccept: handleAcceptCall, onDecline: handleDeclineCall, onHangUp: handleHangUpCall }))] }));
|
|
298
586
|
}
|
|
299
587
|
//# sourceMappingURL=ChatLayout.js.map
|