@armoyu/ui 1.0.0 → 1.0.1
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/app/layout.d.ts.map +1 -1
- package/dist/app/layout.js +16 -13
- package/dist/app/layout.js.map +1 -1
- package/dist/components/Button.d.ts +7 -7
- package/dist/components/Button.js +15 -15
- package/dist/components/GenderStatsBar.d.ts +5 -5
- package/dist/components/GenderStatsBar.js +7 -7
- package/dist/components/RollingNumber.js +3 -3
- package/dist/components/RollingNumber.js.map +1 -1
- package/dist/components/Slider.d.ts +10 -10
- package/dist/components/Slider.js +35 -35
- package/dist/components/StatsGrid.d.ts +5 -5
- package/dist/components/StatsGrid.js +13 -13
- package/dist/components/ViewModeToggle.d.ts +7 -7
- package/dist/components/ViewModeToggle.js +9 -9
- package/dist/components/modules/auth/Dashboard.d.ts +1 -1
- package/dist/components/modules/auth/Dashboard.js +250 -250
- package/dist/components/modules/auth/MediaLightbox.d.ts +13 -13
- package/dist/components/modules/auth/MediaLightbox.js +46 -46
- package/dist/components/modules/auth/PostCard.d.ts +24 -24
- package/dist/components/modules/auth/PostCard.js +112 -112
- package/dist/components/modules/auth/PostInteractionsModal.d.ts +11 -11
- package/dist/components/modules/auth/PostInteractionsModal.js +12 -12
- package/dist/components/modules/auth/RepostModal.d.ts +21 -21
- package/dist/components/modules/auth/RepostModal.js +75 -75
- package/dist/components/modules/auth/SidebarLeft.d.ts +1 -1
- package/dist/components/modules/auth/SidebarLeft.js +40 -40
- package/dist/components/modules/auth/Stories.d.ts +1 -1
- package/dist/components/modules/auth/Stories.js +15 -15
- package/dist/components/modules/auth/StoryViewer.d.ts +9 -9
- package/dist/components/modules/auth/StoryViewer.js +47 -47
- package/dist/components/modules/chat/ChatContainer.d.ts +1 -1
- package/dist/components/modules/chat/ChatContainer.js +196 -196
- package/dist/components/modules/chat/ChatInput.d.ts +4 -4
- package/dist/components/modules/chat/ChatInput.js +30 -30
- package/dist/components/modules/chat/ChatList.d.ts +6 -6
- package/dist/components/modules/chat/ChatList.js +51 -51
- package/dist/components/modules/chat/ChatMessage.d.ts +11 -11
- package/dist/components/modules/chat/ChatMessage.js +6 -6
- package/dist/components/modules/chat/ChatNotes.d.ts +1 -1
- package/dist/components/modules/chat/ChatNotes.js +11 -11
- package/dist/components/modules/community/GroupHeader.d.ts +10 -10
- package/dist/components/modules/community/GroupHeader.js +17 -17
- package/dist/components/modules/community/GroupMenu.d.ts +9 -9
- package/dist/components/modules/community/GroupMenu.js +16 -16
- package/dist/components/modules/community/SchoolCard.d.ts +6 -6
- package/dist/components/modules/community/SchoolCard.js +7 -7
- package/dist/components/modules/community/SurveyCard.d.ts +6 -6
- package/dist/components/modules/community/SurveyCard.js +35 -35
- package/dist/components/modules/forum/ForumBoard.d.ts +16 -16
- package/dist/components/modules/forum/ForumBoard.js +6 -6
- package/dist/components/modules/forum/ForumPost.d.ts +13 -13
- package/dist/components/modules/forum/ForumPost.js +5 -5
- package/dist/components/modules/forum/NewTopicModal.d.ts +7 -7
- package/dist/components/modules/forum/NewTopicModal.js +26 -26
- package/dist/components/modules/forum/TopicItem.d.ts +15 -15
- package/dist/components/modules/forum/TopicItem.js +6 -6
- package/dist/components/modules/galleries/GalleryCard.d.ts +9 -9
- package/dist/components/modules/galleries/GalleryCard.js +5 -5
- package/dist/components/modules/giveaways/GiveawayCard.d.ts +9 -9
- package/dist/components/modules/giveaways/GiveawayCard.js +6 -6
- package/dist/components/modules/groups/ApplicationModal.d.ts +7 -7
- package/dist/components/modules/groups/ApplicationModal.js +27 -27
- package/dist/components/modules/groups/GroupCard.d.ts +12 -12
- package/dist/components/modules/groups/GroupCard.js +6 -6
- package/dist/components/modules/guest/Introduction.d.ts +1 -1
- package/dist/components/modules/guest/Introduction.js +13 -13
- package/dist/components/modules/magaza/BackToStore.d.ts +1 -1
- package/dist/components/modules/magaza/BackToStore.js +10 -10
- package/dist/components/modules/magaza/StoreHeader.d.ts +5 -5
- package/dist/components/modules/magaza/StoreHeader.js +8 -8
- package/dist/components/modules/news/NewsCard.d.ts +11 -11
- package/dist/components/modules/news/NewsCard.js +6 -6
- package/dist/components/modules/news/NewsComments.d.ts +1 -1
- package/dist/components/modules/news/NewsComments.js +52 -52
- package/dist/components/modules/profile/CloudStorageModal.d.ts +8 -8
- package/dist/components/modules/profile/CloudStorageModal.js +31 -31
- package/dist/components/modules/profile/EditProfileModal.d.ts +8 -8
- package/dist/components/modules/profile/EditProfileModal.js +27 -27
- package/dist/components/modules/profile/ProfileContent.d.ts +4 -4
- package/dist/components/modules/profile/ProfileContent.js +70 -70
- package/dist/components/modules/profile/ProfileHeader.d.ts +7 -7
- package/dist/components/modules/profile/ProfileHeader.js +19 -19
- package/dist/components/modules/profile/ProfileStats.js +1 -1
- package/dist/components/modules/profile/ProfileStats.js.map +1 -1
- package/dist/components/modules/profile/TeamSelectorModal.d.ts +10 -10
- package/dist/components/modules/profile/TeamSelectorModal.js +16 -16
- package/dist/components/modules/stations/StationCard.d.ts +2 -2
- package/dist/components/modules/stations/StationCard.js +25 -25
- package/dist/components/modules/stations/StationQRModal.d.ts +9 -9
- package/dist/components/modules/stations/StationQRModal.js +12 -12
- package/dist/components/shared/FloatingChatButton.d.ts +4 -4
- package/dist/components/shared/FloatingChatButton.js +20 -20
- package/dist/components/shared/Footer.d.ts +1 -1
- package/dist/components/shared/Footer.js +9 -9
- package/dist/components/shared/Header.d.ts +1 -1
- package/dist/components/shared/Header.js +97 -97
- package/dist/components/shared/LoginModal.d.ts +4 -4
- package/dist/components/shared/LoginModal.js +69 -69
- package/dist/components/shared/MainLayoutWrapper.d.ts +3 -3
- package/dist/components/shared/MainLayoutWrapper.js +7 -7
- package/dist/components/shared/PageWidth.d.ts +5 -5
- package/dist/components/shared/PageWidth.js +13 -13
- package/dist/context/AuthContext.d.ts +18 -18
- package/dist/context/AuthContext.js +74 -74
- package/dist/context/CartContext.d.ts +16 -16
- package/dist/context/CartContext.js +63 -63
- package/dist/context/ChatContext.d.ts +12 -12
- package/dist/context/ChatContext.js +17 -17
- package/dist/context/LayoutContext.d.ts +10 -10
- package/dist/context/LayoutContext.js +16 -16
- package/dist/context/SocketContext.d.ts +12 -12
- package/dist/context/SocketContext.js +30 -30
- package/dist/context/ThemeContext.d.ts +10 -10
- package/dist/context/ThemeContext.js +39 -39
- package/dist/index.d.ts +65 -65
- package/dist/index.js +80 -80
- package/dist/lib/constants/educationData.d.ts +7 -7
- package/dist/lib/constants/educationData.js +117 -117
- package/dist/lib/constants/punishmentData.d.ts +29 -29
- package/dist/lib/constants/punishmentData.js +183 -183
- package/dist/lib/constants/seedData.d.ts +164 -164
- package/dist/lib/constants/seedData.js +694 -694
- package/dist/lib/constants/stationData.d.ts +13 -13
- package/dist/lib/constants/stationData.js +166 -166
- package/dist/lib/constants/surveyData.d.ts +2 -2
- package/dist/lib/constants/surveyData.js +49 -49
- package/dist/lib/constants/teamData.d.ts +12 -12
- package/dist/lib/constants/teamData.js +65 -65
- package/dist/types/stats.d.ts +17 -17
- package/dist/types/stats.js +1 -1
- package/next.config.ts +13 -13
- package/package.json +45 -46
- package/postcss.config.js +6 -6
- package/src/app/layout.tsx +67 -64
- package/src/app/page.tsx +101 -101
- package/src/components/RollingNumber.tsx +3 -3
- package/src/components/modules/profile/ProfileStats.tsx +4 -4
- package/src/components/showcase/CommunityTab.tsx +22 -22
- package/src/components/showcase/CorporateTab.tsx +38 -38
- package/src/components/showcase/GeneralTab.tsx +41 -41
- package/src/components/showcase/MessagesTab.tsx +26 -26
- package/src/components/showcase/ProfileTab.tsx +20 -20
- package/src/components/showcase/ShopTab.tsx +24 -24
- package/src/components/showcase/SocialTab.tsx +28 -28
- package/src/globals.css +187 -187
- package/src/lib/utils/numberFormat.ts +16 -16
- package/src/lib/utils/odpUtils.ts +51 -51
- package/src/types/index.ts +1 -1
|
@@ -1,197 +1,197 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import React, { useState, useEffect } from 'react';
|
|
4
|
-
import { useAuth } from '../../../context/AuthContext';
|
|
5
|
-
import { ChatList } from './ChatList';
|
|
6
|
-
import { Chat, User, Session, ChatMessage as ChatMessageModel } from '@armoyu/core';
|
|
7
|
-
import { ChatMessage } from './ChatMessage';
|
|
8
|
-
import { ChatInput } from './ChatInput';
|
|
9
|
-
import { useChat } from '../../../context/ChatContext';
|
|
10
|
-
import { useSocket } from '../../../context/SocketContext';
|
|
11
|
-
// Mock Data
|
|
12
|
-
import { userList } from '../../../lib/constants/seedData';
|
|
13
|
-
export function ChatContainer() {
|
|
14
|
-
const { user, session, updateSession } = useAuth();
|
|
15
|
-
const { closeChat } = useChat();
|
|
16
|
-
const { emit, on, isConnected } = useSocket();
|
|
17
|
-
// Eğer null ise liste görünümü açık, ID var ise mesajlaşma açık.
|
|
18
|
-
const [activeContactId, setActiveContactId] = useState(null);
|
|
19
|
-
const [localMessages, setLocalMessages] = useState([]);
|
|
20
|
-
const [localContacts, setLocalContacts] = useState([]);
|
|
21
|
-
const [isTyping, setIsTyping] = useState(false);
|
|
22
|
-
const messagesEndRef = React.useRef(null);
|
|
23
|
-
const scrollToBottom = () => {
|
|
24
|
-
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
25
|
-
};
|
|
26
|
-
// Scroll to bottom when messages or typing status changes
|
|
27
|
-
useEffect(() => {
|
|
28
|
-
scrollToBottom();
|
|
29
|
-
}, [localMessages, isTyping]);
|
|
30
|
-
// Sync with session's chatList (chatList lives on Session, not User)
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
if (session?.chatList) {
|
|
33
|
-
setLocalContacts(session.chatList);
|
|
34
|
-
}
|
|
35
|
-
}, [session?.chatList]);
|
|
36
|
-
// Socket Connection for Real-time Messages & Typing
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
const offMsg = on('message', (incomingMsg) => {
|
|
39
|
-
console.log('[ChatContainer] Incoming socket message:', incomingMsg);
|
|
40
|
-
const msgModel = new ChatMessageModel({
|
|
41
|
-
id: incomingMsg.id,
|
|
42
|
-
sender: incomingMsg.sender ? new User(incomingMsg.sender) : undefined,
|
|
43
|
-
content: incomingMsg.content,
|
|
44
|
-
timestamp: incomingMsg.timestamp,
|
|
45
|
-
isSystem: incomingMsg.isSystem || false
|
|
46
|
-
});
|
|
47
|
-
// Update contacts list
|
|
48
|
-
setLocalContacts(prev => {
|
|
49
|
-
const contactId = incomingMsg.chatId || incomingMsg.sender?.username;
|
|
50
|
-
const contactExists = prev.some(c => c.id === contactId);
|
|
51
|
-
if (!contactExists && incomingMsg.sender?.username !== user?.username) {
|
|
52
|
-
// CREATE NEW CHAT: If sender isn't in our list, create the chat box for them!
|
|
53
|
-
const newChat = new Chat({
|
|
54
|
-
id: contactId,
|
|
55
|
-
name: incomingMsg.sender?.displayName || incomingMsg.sender?.username || 'Bilinmeyen',
|
|
56
|
-
avatar: incomingMsg.sender?.avatar || '',
|
|
57
|
-
lastMessage: msgModel,
|
|
58
|
-
time: msgModel.timestamp,
|
|
59
|
-
updatedAt: Date.now(),
|
|
60
|
-
messages: [msgModel],
|
|
61
|
-
unreadCount: (activeContactId !== contactId) ? 1 : 0,
|
|
62
|
-
isOnline: true // Assume online since they just sent a message
|
|
63
|
-
});
|
|
64
|
-
// PERSIST to Session!
|
|
65
|
-
if (session) {
|
|
66
|
-
const updatedChatList = [newChat, ...(session.chatList || [])];
|
|
67
|
-
updateSession(new Session({ ...session, chatList: updatedChatList }));
|
|
68
|
-
}
|
|
69
|
-
return [newChat, ...prev];
|
|
70
|
-
}
|
|
71
|
-
// UPDATE EXISTING CHAT
|
|
72
|
-
return prev.map(c => {
|
|
73
|
-
if (c.id === contactId) {
|
|
74
|
-
// Only add if not already in messages to avoid duplicates from echo
|
|
75
|
-
const messageExists = c.messages.some(m => m.id === msgModel.id);
|
|
76
|
-
const updatedChat = new Chat({
|
|
77
|
-
...c,
|
|
78
|
-
lastMessage: msgModel,
|
|
79
|
-
time: msgModel.timestamp,
|
|
80
|
-
updatedAt: Date.now(),
|
|
81
|
-
messages: messageExists ? c.messages : [...(c.messages || []), msgModel],
|
|
82
|
-
unreadCount: (activeContactId !== c.id && incomingMsg.sender?.username !== user?.username) ? c.unreadCount + 1 : c.unreadCount
|
|
83
|
-
});
|
|
84
|
-
// Note: We don't necessarily need to update the whole session on every message to avoid excessive renders,
|
|
85
|
-
// as local state handles the UI. But for NEW chats, we must.
|
|
86
|
-
return updatedChat;
|
|
87
|
-
}
|
|
88
|
-
return c;
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
// If this is the active chat, update visible messages
|
|
92
|
-
if (activeContactId === incomingMsg.chatId || activeContactId === incomingMsg.sender?.username) {
|
|
93
|
-
setLocalMessages(prev => {
|
|
94
|
-
if (prev.some(m => m.id === incomingMsg.id))
|
|
95
|
-
return prev;
|
|
96
|
-
return [...prev, msgModel];
|
|
97
|
-
});
|
|
98
|
-
setIsTyping(false); // Stop typing on message receive
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
const offTyping = on('typing', (data) => {
|
|
102
|
-
// console.log('[ChatContainer] Incoming typing event:', data);
|
|
103
|
-
// If we are recipient (data.chatId is US) and the sender IS the one we are currently viewing
|
|
104
|
-
const isChattingWithSender = data.username === activeContactId && data.chatId === user?.username;
|
|
105
|
-
// Special case for system bot
|
|
106
|
-
const isBotTyping = data.username === 'system' && data.chatId === user?.username;
|
|
107
|
-
if (isChattingWithSender || isBotTyping) {
|
|
108
|
-
setIsTyping(data.isTyping);
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
return () => {
|
|
112
|
-
offMsg();
|
|
113
|
-
offTyping();
|
|
114
|
-
};
|
|
115
|
-
}, [on, activeContactId, user?.username]);
|
|
116
|
-
const handleSelectContact = (id) => {
|
|
117
|
-
// Check if the contact is already in our list
|
|
118
|
-
let contact = localContacts.find((c) => c.id === id);
|
|
119
|
-
// If NOT in our list (clicked from "New Contacts" search), we must create it!
|
|
120
|
-
if (!contact) {
|
|
121
|
-
const newUser = userList.find((u) => u.username === id);
|
|
122
|
-
if (newUser) {
|
|
123
|
-
contact = new Chat({
|
|
124
|
-
id: newUser.username,
|
|
125
|
-
name: newUser.displayName,
|
|
126
|
-
avatar: newUser.avatar,
|
|
127
|
-
updatedAt: Date.now(),
|
|
128
|
-
messages: [],
|
|
129
|
-
isOnline: true, // Mock online status for now
|
|
130
|
-
unreadCount: 0
|
|
131
|
-
});
|
|
132
|
-
const updatedContacts = [contact, ...localContacts];
|
|
133
|
-
setLocalContacts(updatedContacts);
|
|
134
|
-
// Persist to session so it stays in the list!
|
|
135
|
-
if (session) {
|
|
136
|
-
const updatedChatList = [contact, ...(session.chatList || [])];
|
|
137
|
-
updateSession(new Session({ ...session, chatList: updatedChatList }));
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
setActiveContactId(id);
|
|
142
|
-
setLocalMessages(contact?.messages || []);
|
|
143
|
-
setIsTyping(false);
|
|
144
|
-
// Clear unread on select (if it was an existing contact)
|
|
145
|
-
if (contact && contact.unreadCount > 0) {
|
|
146
|
-
setLocalContacts(prev => prev.map(c => c.id === id ? { ...c, unreadCount: 0 } : c));
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
const handleSendMessage = (text) => {
|
|
150
|
-
if (!text.trim() || !activeContactId)
|
|
151
|
-
return;
|
|
152
|
-
const messageData = {
|
|
153
|
-
id: Date.now().toString(),
|
|
154
|
-
chatId: activeContactId,
|
|
155
|
-
sender: {
|
|
156
|
-
username: user?.username,
|
|
157
|
-
displayName: user?.displayName,
|
|
158
|
-
avatar: user?.avatar
|
|
159
|
-
},
|
|
160
|
-
content: text,
|
|
161
|
-
timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
|
|
162
|
-
};
|
|
163
|
-
// Update local state immediately for instant feedback
|
|
164
|
-
const newMessage = new ChatMessageModel({
|
|
165
|
-
id: messageData.id,
|
|
166
|
-
sender: user || undefined,
|
|
167
|
-
content: text,
|
|
168
|
-
timestamp: messageData.timestamp,
|
|
169
|
-
isSystem: false
|
|
170
|
-
});
|
|
171
|
-
setLocalMessages(prev => [...prev, newMessage]);
|
|
172
|
-
setLocalContacts(prev => prev.map(c => {
|
|
173
|
-
if (c.id === activeContactId) {
|
|
174
|
-
return new Chat({
|
|
175
|
-
...c,
|
|
176
|
-
lastMessage: newMessage,
|
|
177
|
-
time: newMessage.timestamp,
|
|
178
|
-
updatedAt: Date.now(),
|
|
179
|
-
messages: [...(c.messages || []), newMessage]
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
return c;
|
|
183
|
-
}));
|
|
184
|
-
// Emit via socket for others
|
|
185
|
-
emit('message', messageData);
|
|
186
|
-
};
|
|
187
|
-
if (!user) {
|
|
188
|
-
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." }));
|
|
189
|
-
}
|
|
190
|
-
const activeContact = activeContactId ? localContacts.find((c) => c.id === activeContactId) : null;
|
|
191
|
-
return (_jsxs("div", { className: "flex h-full w-full bg-armoyu-header-bg overflow-hidden relative z-10", children: [!activeContactId && (_jsx("div", { className: "w-full h-full flex flex-col animate-in fade-in slide-in-from-left-4 duration-300", children: _jsx(ChatList, { contacts: localContacts, activeId: '', 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, className: "w-10 h-10 rounded-full border border-black/5 dark:border-white/10 shadow-sm", alt: "" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("h3", { className: "font-bold text-armoyu-text truncate text-base", children: activeContact.name }), _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", { className: "p-2 text-armoyu-text-muted hover:text-blue-500 rounded-full hover:bg-blue-500/10 transition-colors", title: "Ara", children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z" }) }) }), _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: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), _jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto p-4 md:p-6 space-y-2 relative hide-scrollbar", children: [_jsx("div", { className: "text-center mb-8", children: _jsx("span", { className: "inline-block bg-black/5 dark:bg-white/5 text-armoyu-text-muted text-xs font-bold px-3 py-1 rounded-full border border-black/5 dark:border-white/5", children: "Bug\u00FCn" }) }), localMessages.map(msg => (_jsx(ChatMessage, { id: msg.id, sender: {
|
|
192
|
-
name: msg.sender?.displayName || 'Bilinmiyor',
|
|
193
|
-
avatar: msg.sender?.avatar || '',
|
|
194
|
-
isSelf: msg.sender?.username === user?.username
|
|
195
|
-
}, content: msg.content, timestamp: msg.timestamp }, msg.id))), isTyping && (_jsx("div", { className: "flex gap-2 items-center px-4 animate-in fade-in slide-in-from-bottom-2 duration-300", children: _jsxs("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 h-1 rounded-full bg-blue-500 animate-bounce [animation-delay:-0.3s]" }), _jsx("span", { className: "w-1 h-1 rounded-full bg-blue-500 animate-bounce [animation-delay:-0.15s]" }), _jsx("span", { className: "w-1 h-1 rounded-full bg-blue-500 animate-bounce" })] }), _jsx("span", { className: "text-[10px] font-bold text-armoyu-text-muted italic ml-1", children: "Yaz\u0131yor..." })] }) })), _jsx("div", { ref: messagesEndRef, className: "h-2" })] }), _jsx(ChatInput, { onSend: handleSendMessage, chatId: activeContactId })] }))] }));
|
|
196
|
-
}
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React, { useState, useEffect } from 'react';
|
|
4
|
+
import { useAuth } from '../../../context/AuthContext';
|
|
5
|
+
import { ChatList } from './ChatList';
|
|
6
|
+
import { Chat, User, Session, ChatMessage as ChatMessageModel } from '@armoyu/core';
|
|
7
|
+
import { ChatMessage } from './ChatMessage';
|
|
8
|
+
import { ChatInput } from './ChatInput';
|
|
9
|
+
import { useChat } from '../../../context/ChatContext';
|
|
10
|
+
import { useSocket } from '../../../context/SocketContext';
|
|
11
|
+
// Mock Data
|
|
12
|
+
import { userList } from '../../../lib/constants/seedData';
|
|
13
|
+
export function ChatContainer() {
|
|
14
|
+
const { user, session, updateSession } = useAuth();
|
|
15
|
+
const { closeChat } = useChat();
|
|
16
|
+
const { emit, on, isConnected } = useSocket();
|
|
17
|
+
// Eğer null ise liste görünümü açık, ID var ise mesajlaşma açık.
|
|
18
|
+
const [activeContactId, setActiveContactId] = useState(null);
|
|
19
|
+
const [localMessages, setLocalMessages] = useState([]);
|
|
20
|
+
const [localContacts, setLocalContacts] = useState([]);
|
|
21
|
+
const [isTyping, setIsTyping] = useState(false);
|
|
22
|
+
const messagesEndRef = React.useRef(null);
|
|
23
|
+
const scrollToBottom = () => {
|
|
24
|
+
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
25
|
+
};
|
|
26
|
+
// Scroll to bottom when messages or typing status changes
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
scrollToBottom();
|
|
29
|
+
}, [localMessages, isTyping]);
|
|
30
|
+
// Sync with session's chatList (chatList lives on Session, not User)
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (session?.chatList) {
|
|
33
|
+
setLocalContacts(session.chatList);
|
|
34
|
+
}
|
|
35
|
+
}, [session?.chatList]);
|
|
36
|
+
// Socket Connection for Real-time Messages & Typing
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const offMsg = on('message', (incomingMsg) => {
|
|
39
|
+
console.log('[ChatContainer] Incoming socket message:', incomingMsg);
|
|
40
|
+
const msgModel = new ChatMessageModel({
|
|
41
|
+
id: incomingMsg.id,
|
|
42
|
+
sender: incomingMsg.sender ? new User(incomingMsg.sender) : undefined,
|
|
43
|
+
content: incomingMsg.content,
|
|
44
|
+
timestamp: incomingMsg.timestamp,
|
|
45
|
+
isSystem: incomingMsg.isSystem || false
|
|
46
|
+
});
|
|
47
|
+
// Update contacts list
|
|
48
|
+
setLocalContacts(prev => {
|
|
49
|
+
const contactId = incomingMsg.chatId || incomingMsg.sender?.username;
|
|
50
|
+
const contactExists = prev.some(c => c.id === contactId);
|
|
51
|
+
if (!contactExists && incomingMsg.sender?.username !== user?.username) {
|
|
52
|
+
// CREATE NEW CHAT: If sender isn't in our list, create the chat box for them!
|
|
53
|
+
const newChat = new Chat({
|
|
54
|
+
id: contactId,
|
|
55
|
+
name: incomingMsg.sender?.displayName || incomingMsg.sender?.username || 'Bilinmeyen',
|
|
56
|
+
avatar: incomingMsg.sender?.avatar || '',
|
|
57
|
+
lastMessage: msgModel,
|
|
58
|
+
time: msgModel.timestamp,
|
|
59
|
+
updatedAt: Date.now(),
|
|
60
|
+
messages: [msgModel],
|
|
61
|
+
unreadCount: (activeContactId !== contactId) ? 1 : 0,
|
|
62
|
+
isOnline: true // Assume online since they just sent a message
|
|
63
|
+
});
|
|
64
|
+
// PERSIST to Session!
|
|
65
|
+
if (session) {
|
|
66
|
+
const updatedChatList = [newChat, ...(session.chatList || [])];
|
|
67
|
+
updateSession(new Session({ ...session, chatList: updatedChatList }));
|
|
68
|
+
}
|
|
69
|
+
return [newChat, ...prev];
|
|
70
|
+
}
|
|
71
|
+
// UPDATE EXISTING CHAT
|
|
72
|
+
return prev.map(c => {
|
|
73
|
+
if (c.id === contactId) {
|
|
74
|
+
// Only add if not already in messages to avoid duplicates from echo
|
|
75
|
+
const messageExists = c.messages.some(m => m.id === msgModel.id);
|
|
76
|
+
const updatedChat = new Chat({
|
|
77
|
+
...c,
|
|
78
|
+
lastMessage: msgModel,
|
|
79
|
+
time: msgModel.timestamp,
|
|
80
|
+
updatedAt: Date.now(),
|
|
81
|
+
messages: messageExists ? c.messages : [...(c.messages || []), msgModel],
|
|
82
|
+
unreadCount: (activeContactId !== c.id && incomingMsg.sender?.username !== user?.username) ? c.unreadCount + 1 : c.unreadCount
|
|
83
|
+
});
|
|
84
|
+
// Note: We don't necessarily need to update the whole session on every message to avoid excessive renders,
|
|
85
|
+
// as local state handles the UI. But for NEW chats, we must.
|
|
86
|
+
return updatedChat;
|
|
87
|
+
}
|
|
88
|
+
return c;
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
// If this is the active chat, update visible messages
|
|
92
|
+
if (activeContactId === incomingMsg.chatId || activeContactId === incomingMsg.sender?.username) {
|
|
93
|
+
setLocalMessages(prev => {
|
|
94
|
+
if (prev.some(m => m.id === incomingMsg.id))
|
|
95
|
+
return prev;
|
|
96
|
+
return [...prev, msgModel];
|
|
97
|
+
});
|
|
98
|
+
setIsTyping(false); // Stop typing on message receive
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
const offTyping = on('typing', (data) => {
|
|
102
|
+
// console.log('[ChatContainer] Incoming typing event:', data);
|
|
103
|
+
// If we are recipient (data.chatId is US) and the sender IS the one we are currently viewing
|
|
104
|
+
const isChattingWithSender = data.username === activeContactId && data.chatId === user?.username;
|
|
105
|
+
// Special case for system bot
|
|
106
|
+
const isBotTyping = data.username === 'system' && data.chatId === user?.username;
|
|
107
|
+
if (isChattingWithSender || isBotTyping) {
|
|
108
|
+
setIsTyping(data.isTyping);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
return () => {
|
|
112
|
+
offMsg();
|
|
113
|
+
offTyping();
|
|
114
|
+
};
|
|
115
|
+
}, [on, activeContactId, user?.username]);
|
|
116
|
+
const handleSelectContact = (id) => {
|
|
117
|
+
// Check if the contact is already in our list
|
|
118
|
+
let contact = localContacts.find((c) => c.id === id);
|
|
119
|
+
// If NOT in our list (clicked from "New Contacts" search), we must create it!
|
|
120
|
+
if (!contact) {
|
|
121
|
+
const newUser = userList.find((u) => u.username === id);
|
|
122
|
+
if (newUser) {
|
|
123
|
+
contact = new Chat({
|
|
124
|
+
id: newUser.username,
|
|
125
|
+
name: newUser.displayName,
|
|
126
|
+
avatar: newUser.avatar,
|
|
127
|
+
updatedAt: Date.now(),
|
|
128
|
+
messages: [],
|
|
129
|
+
isOnline: true, // Mock online status for now
|
|
130
|
+
unreadCount: 0
|
|
131
|
+
});
|
|
132
|
+
const updatedContacts = [contact, ...localContacts];
|
|
133
|
+
setLocalContacts(updatedContacts);
|
|
134
|
+
// Persist to session so it stays in the list!
|
|
135
|
+
if (session) {
|
|
136
|
+
const updatedChatList = [contact, ...(session.chatList || [])];
|
|
137
|
+
updateSession(new Session({ ...session, chatList: updatedChatList }));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
setActiveContactId(id);
|
|
142
|
+
setLocalMessages(contact?.messages || []);
|
|
143
|
+
setIsTyping(false);
|
|
144
|
+
// Clear unread on select (if it was an existing contact)
|
|
145
|
+
if (contact && contact.unreadCount > 0) {
|
|
146
|
+
setLocalContacts(prev => prev.map(c => c.id === id ? { ...c, unreadCount: 0 } : c));
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
const handleSendMessage = (text) => {
|
|
150
|
+
if (!text.trim() || !activeContactId)
|
|
151
|
+
return;
|
|
152
|
+
const messageData = {
|
|
153
|
+
id: Date.now().toString(),
|
|
154
|
+
chatId: activeContactId,
|
|
155
|
+
sender: {
|
|
156
|
+
username: user?.username,
|
|
157
|
+
displayName: user?.displayName,
|
|
158
|
+
avatar: user?.avatar
|
|
159
|
+
},
|
|
160
|
+
content: text,
|
|
161
|
+
timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
|
|
162
|
+
};
|
|
163
|
+
// Update local state immediately for instant feedback
|
|
164
|
+
const newMessage = new ChatMessageModel({
|
|
165
|
+
id: messageData.id,
|
|
166
|
+
sender: user || undefined,
|
|
167
|
+
content: text,
|
|
168
|
+
timestamp: messageData.timestamp,
|
|
169
|
+
isSystem: false
|
|
170
|
+
});
|
|
171
|
+
setLocalMessages(prev => [...prev, newMessage]);
|
|
172
|
+
setLocalContacts(prev => prev.map(c => {
|
|
173
|
+
if (c.id === activeContactId) {
|
|
174
|
+
return new Chat({
|
|
175
|
+
...c,
|
|
176
|
+
lastMessage: newMessage,
|
|
177
|
+
time: newMessage.timestamp,
|
|
178
|
+
updatedAt: Date.now(),
|
|
179
|
+
messages: [...(c.messages || []), newMessage]
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return c;
|
|
183
|
+
}));
|
|
184
|
+
// Emit via socket for others
|
|
185
|
+
emit('message', messageData);
|
|
186
|
+
};
|
|
187
|
+
if (!user) {
|
|
188
|
+
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." }));
|
|
189
|
+
}
|
|
190
|
+
const activeContact = activeContactId ? localContacts.find((c) => c.id === activeContactId) : null;
|
|
191
|
+
return (_jsxs("div", { className: "flex h-full w-full bg-armoyu-header-bg overflow-hidden relative z-10", children: [!activeContactId && (_jsx("div", { className: "w-full h-full flex flex-col animate-in fade-in slide-in-from-left-4 duration-300", children: _jsx(ChatList, { contacts: localContacts, activeId: '', 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, className: "w-10 h-10 rounded-full border border-black/5 dark:border-white/10 shadow-sm", alt: "" }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("h3", { className: "font-bold text-armoyu-text truncate text-base", children: activeContact.name }), _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", { className: "p-2 text-armoyu-text-muted hover:text-blue-500 rounded-full hover:bg-blue-500/10 transition-colors", title: "Ara", children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z" }) }) }), _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: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), _jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) })] })] }), _jsxs("div", { className: "flex-1 overflow-y-auto p-4 md:p-6 space-y-2 relative hide-scrollbar", children: [_jsx("div", { className: "text-center mb-8", children: _jsx("span", { className: "inline-block bg-black/5 dark:bg-white/5 text-armoyu-text-muted text-xs font-bold px-3 py-1 rounded-full border border-black/5 dark:border-white/5", children: "Bug\u00FCn" }) }), localMessages.map(msg => (_jsx(ChatMessage, { id: msg.id, sender: {
|
|
192
|
+
name: msg.sender?.displayName || 'Bilinmiyor',
|
|
193
|
+
avatar: msg.sender?.avatar || '',
|
|
194
|
+
isSelf: msg.sender?.username === user?.username
|
|
195
|
+
}, content: msg.content, timestamp: msg.timestamp }, msg.id))), isTyping && (_jsx("div", { className: "flex gap-2 items-center px-4 animate-in fade-in slide-in-from-bottom-2 duration-300", children: _jsxs("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 h-1 rounded-full bg-blue-500 animate-bounce [animation-delay:-0.3s]" }), _jsx("span", { className: "w-1 h-1 rounded-full bg-blue-500 animate-bounce [animation-delay:-0.15s]" }), _jsx("span", { className: "w-1 h-1 rounded-full bg-blue-500 animate-bounce" })] }), _jsx("span", { className: "text-[10px] font-bold text-armoyu-text-muted italic ml-1", children: "Yaz\u0131yor..." })] }) })), _jsx("div", { ref: messagesEndRef, className: "h-2" })] }), _jsx(ChatInput, { onSend: handleSendMessage, chatId: activeContactId })] }))] }));
|
|
196
|
+
}
|
|
197
197
|
//# sourceMappingURL=ChatContainer.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export declare function ChatInput({ onSend, chatId }: {
|
|
2
|
-
onSend: (text: string) => void;
|
|
3
|
-
chatId?: string;
|
|
4
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
1
|
+
export declare function ChatInput({ onSend, chatId }: {
|
|
2
|
+
onSend: (text: string) => void;
|
|
3
|
+
chatId?: string;
|
|
4
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
5
5
|
//# sourceMappingURL=ChatInput.d.ts.map
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import { Button } from '../../Button';
|
|
4
|
-
import { useSocket } from '../../../context/SocketContext';
|
|
5
|
-
export function ChatInput({ onSend, chatId }) {
|
|
6
|
-
const [text, setText] = useState('');
|
|
7
|
-
const { emit } = useSocket();
|
|
8
|
-
const handleSend = (e) => {
|
|
9
|
-
e.preventDefault();
|
|
10
|
-
if (text.trim()) {
|
|
11
|
-
onSend(text);
|
|
12
|
-
setText('');
|
|
13
|
-
if (chatId)
|
|
14
|
-
emit('typing', { chatId, isTyping: false });
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
const handleChange = (val) => {
|
|
18
|
-
setText(val);
|
|
19
|
-
if (chatId) {
|
|
20
|
-
const isTyping = val.length > 0;
|
|
21
|
-
// Include username to help receiver filter out self
|
|
22
|
-
emit('typing', {
|
|
23
|
-
chatId,
|
|
24
|
-
isTyping,
|
|
25
|
-
username: typeof window !== 'undefined' ? localStorage.getItem('armoyu_username') : undefined
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
return (_jsxs("form", { onSubmit: handleSend, className: "p-3 border-t border-gray-200 dark:border-white/5 flex items-center gap-3 bg-armoyu-card-bg", children: [_jsx("button", { type: "button", className: "text-gray-400 hover:text-blue-500 p-2 transition-colors shrink-0", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "M8 14s1.5 2 4 2 4-2 4-2" }), _jsx("line", { x1: "9", y1: "9", x2: "9.01", y2: "9" }), _jsx("line", { x1: "15", y1: "9", x2: "15.01", y2: "9" })] }) }), _jsx("button", { type: "button", className: "text-gray-400 hover:text-blue-500 p-2 transition-colors hidden sm:block shrink-0", children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) }) }), _jsx("input", { type: "text", value: text, onChange: (e) => handleChange(e.target.value), placeholder: "Bir mesaj yaz\u0131n...", className: "flex-1 min-w-0 bg-black/5 dark:bg-black/40 border border-black/10 dark:border-white/10 rounded-full px-4 py-2.5 text-sm text-armoyu-text placeholder-armoyu-text-muted focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-all shadow-inner" }), _jsx(Button, { variant: "primary", className: "rounded-full w-10 h-10 p-0 flex items-center justify-center shadow-[0_0_15px_rgba(37,99,235,0.3)] shrink-0 group", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", className: "translate-x-0.5 group-hover:translate-x-1 group-hover:-translate-y-0.5 transition-transform", children: [_jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }), _jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })] }) })] }));
|
|
30
|
-
}
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Button } from '../../Button';
|
|
4
|
+
import { useSocket } from '../../../context/SocketContext';
|
|
5
|
+
export function ChatInput({ onSend, chatId }) {
|
|
6
|
+
const [text, setText] = useState('');
|
|
7
|
+
const { emit } = useSocket();
|
|
8
|
+
const handleSend = (e) => {
|
|
9
|
+
e.preventDefault();
|
|
10
|
+
if (text.trim()) {
|
|
11
|
+
onSend(text);
|
|
12
|
+
setText('');
|
|
13
|
+
if (chatId)
|
|
14
|
+
emit('typing', { chatId, isTyping: false });
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const handleChange = (val) => {
|
|
18
|
+
setText(val);
|
|
19
|
+
if (chatId) {
|
|
20
|
+
const isTyping = val.length > 0;
|
|
21
|
+
// Include username to help receiver filter out self
|
|
22
|
+
emit('typing', {
|
|
23
|
+
chatId,
|
|
24
|
+
isTyping,
|
|
25
|
+
username: typeof window !== 'undefined' ? localStorage.getItem('armoyu_username') : undefined
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
return (_jsxs("form", { onSubmit: handleSend, className: "p-3 border-t border-gray-200 dark:border-white/5 flex items-center gap-3 bg-armoyu-card-bg", children: [_jsx("button", { type: "button", className: "text-gray-400 hover:text-blue-500 p-2 transition-colors shrink-0", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "M8 14s1.5 2 4 2 4-2 4-2" }), _jsx("line", { x1: "9", y1: "9", x2: "9.01", y2: "9" }), _jsx("line", { x1: "15", y1: "9", x2: "15.01", y2: "9" })] }) }), _jsx("button", { type: "button", className: "text-gray-400 hover:text-blue-500 p-2 transition-colors hidden sm:block shrink-0", children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: _jsx("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) }) }), _jsx("input", { type: "text", value: text, onChange: (e) => handleChange(e.target.value), placeholder: "Bir mesaj yaz\u0131n...", className: "flex-1 min-w-0 bg-black/5 dark:bg-black/40 border border-black/10 dark:border-white/10 rounded-full px-4 py-2.5 text-sm text-armoyu-text placeholder-armoyu-text-muted focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500 transition-all shadow-inner" }), _jsx(Button, { variant: "primary", className: "rounded-full w-10 h-10 p-0 flex items-center justify-center shadow-[0_0_15px_rgba(37,99,235,0.3)] shrink-0 group", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", className: "translate-x-0.5 group-hover:translate-x-1 group-hover:-translate-y-0.5 transition-transform", children: [_jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }), _jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })] }) })] }));
|
|
30
|
+
}
|
|
31
31
|
//# sourceMappingURL=ChatInput.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Chat } from '@armoyu/core';
|
|
2
|
-
export declare function ChatList({ contacts, activeId, onSelect }: {
|
|
3
|
-
contacts: Chat[];
|
|
4
|
-
activeId: string;
|
|
5
|
-
onSelect: (id: string) => void;
|
|
6
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
1
|
+
import { Chat } from '@armoyu/core';
|
|
2
|
+
export declare function ChatList({ contacts, activeId, onSelect }: {
|
|
3
|
+
contacts: Chat[];
|
|
4
|
+
activeId: string;
|
|
5
|
+
onSelect: (id: string) => void;
|
|
6
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
//# sourceMappingURL=ChatList.d.ts.map
|