From 98a2274a728dfd90a8742d58fb9b3caf9c5b86f2 Mon Sep 17 00:00:00 2001 From: Tal Jacob Date: Mon, 7 Jul 2025 11:25:23 +0300 Subject: [PATCH 1/4] Add Selection Of Online Users Signed-off-by: Tal Jacob --- .../src/components/DividedList.tsx | 27 +++++++++---------- nextstep-frontend/src/pages/Chat.css | 5 ++++ nextstep-frontend/src/pages/Chat.tsx | 6 +++-- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/nextstep-frontend/src/components/DividedList.tsx b/nextstep-frontend/src/components/DividedList.tsx index a16e074..19a4037 100644 --- a/nextstep-frontend/src/components/DividedList.tsx +++ b/nextstep-frontend/src/components/DividedList.tsx @@ -14,16 +14,14 @@ interface User { email: string; } +interface DividedListProps { + onlineUsers: { id: string, email: string }[]; + onUserClick: (user: { id: string, email: string }) => void; + disabled?: boolean; + selectedUserId?: string | null; +} -const DividedList: React.FC = ({ onlineUsers, onUserClick, disabled = false }) => { - const [selectedUserId, setSelectedUserId] = useState(null); - - const handleUserClick = (user: User) => { - if (disabled) return; - setSelectedUserId(user.id); - onUserClick(user); - }; - +const DividedList: React.FC = ({ onlineUsers, onUserClick, disabled = false, selectedUserId }) => { return ( = ({ onlineUsers, onUserClick, disabled = false
{index !== 0 && } handleUserClick(user)} + className={`divided-list-item${selectedUserId === user.id ? ' selected' : ''}`} + onClick={() => !disabled && onUserClick(user)} disabled={disabled} sx={{ py: 1.5, px: 2, - backgroundColor: user.id === selectedUserId ? 'primary.main' : 'transparent', - color: user.id === selectedUserId ? 'primary.contrastText' : 'text.primary', + backgroundColor: selectedUserId === user.id ? 'primary.main' : 'transparent', + color: selectedUserId === user.id ? 'primary.contrastText' : 'text.primary', transition: 'all 0.3s ease', '&:hover': { - backgroundColor: user.id === selectedUserId ? 'primary.dark' : 'action.hover', + backgroundColor: selectedUserId === user.id ? 'primary.dark' : 'action.hover', transform: 'translateX(4px)', }, '&.Mui-disabled': { @@ -78,7 +77,7 @@ const DividedList: React.FC = ({ onlineUsers, onUserClick, disabled = false primary={user.email} primaryTypographyProps={{ fontSize: '0.875rem', - fontWeight: user.id === selectedUserId ? 600 : 400, + fontWeight: selectedUserId === user.id ? 600 : 400, }} sx={{ transition: 'all 0.3s ease', diff --git a/nextstep-frontend/src/pages/Chat.css b/nextstep-frontend/src/pages/Chat.css index 76144dd..5755c88 100644 --- a/nextstep-frontend/src/pages/Chat.css +++ b/nextstep-frontend/src/pages/Chat.css @@ -95,4 +95,9 @@ .online-users { margin-top: 10px; +} + +.divided-list-item.selected { + font-weight: bold; + border-radius: 20px; } \ No newline at end of file diff --git a/nextstep-frontend/src/pages/Chat.tsx b/nextstep-frontend/src/pages/Chat.tsx index 9a4f743..d2c5cfc 100644 --- a/nextstep-frontend/src/pages/Chat.tsx +++ b/nextstep-frontend/src/pages/Chat.tsx @@ -25,6 +25,7 @@ const Chat: React.FC = () => { const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [isConnected, setIsConnected] = useState(false); + const [selectedUserId, setSelectedUserId] = useState(null); const usersMetadataCacheRef = useRef(new Set()); const socketRef = useRef(null); @@ -138,13 +139,13 @@ const Chat: React.FC = () => { const onUserClick = async (user: LoginResponse) => { if (!userAuthRef.current) return; - + setSelectedUserId(user.id!); try { setIsLoading(true); const response = await axios.get(`${config.app.backend_url()}/room/user/${user.id}`, { headers: { Authorization: `Bearer ${userAuthRef.current.accessToken}` } }); - + setRoom(response.data); socketRef.current?.emit(config.socketMethods.enterRoom, response.data._id); } catch (err) { @@ -442,6 +443,7 @@ const Chat: React.FC = () => { onlineUsers={onlineUsers.map(user => ({ id: user.id, email: user.email }))} onUserClick={onUserClick} disabled={!isConnected} + selectedUserId={selectedUserId} /> From bd2bb981824ae192cee237d043eb354718b96703 Mon Sep 17 00:00:00 2001 From: Tal Jacob Date: Tue, 8 Jul 2025 00:37:05 +0300 Subject: [PATCH 2/4] Use Username And Email From Provided OnlineUsers Socket Signed-off-by: Tal Jacob --- .../src/services/socket_service.ts | 9 ++++++-- .../src/components/DividedList.tsx | 7 +++--- nextstep-frontend/src/pages/Chat.tsx | 22 ++++++++++++++++--- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/nextstep-backend/src/services/socket_service.ts b/nextstep-backend/src/services/socket_service.ts index 3091a2e..0e488c9 100644 --- a/nextstep-backend/src/services/socket_service.ts +++ b/nextstep-backend/src/services/socket_service.ts @@ -28,8 +28,13 @@ const initSocket = async (socketListener: Server) => { await new messageModel(messageToInsert).validate(); const insertedMessage = await messageModel.create(messageToInsert); - // Emit mesage - socketListener.to(roomId).emit(config.socketMethods.messageFromServer, { roomId, message: insertedMessage }); + // Emit message with user info + const messageWithUser = { + ...insertedMessage.toObject(), + email: user.email, + username: user.username, + }; + socketListener.to(roomId).emit(config.socketMethods.messageFromServer, { roomId, message: messageWithUser }); }); // Online users diff --git a/nextstep-frontend/src/components/DividedList.tsx b/nextstep-frontend/src/components/DividedList.tsx index 19a4037..1e04e1e 100644 --- a/nextstep-frontend/src/components/DividedList.tsx +++ b/nextstep-frontend/src/components/DividedList.tsx @@ -11,12 +11,13 @@ import { interface User { id: string; + username?: string; email: string; } interface DividedListProps { - onlineUsers: { id: string, email: string }[]; - onUserClick: (user: { id: string, email: string }) => void; + onlineUsers: { id: string, username?: string, email: string }[]; + onUserClick: (user: { id: string, username?: string, email: string }) => void; disabled?: boolean; selectedUserId?: string | null; } @@ -74,7 +75,7 @@ const DividedList: React.FC = ({ onlineUsers, onUserClick, dis /> { } }, [room.messages]); + // Type guard for users with valid id and email + function isValidUser(user: LoginResponse): user is LoginResponse & { id: string; email: string } { + return Boolean(user.id && user.email); + } + + // Helper to get display name by userId + const getUserDisplayName = (userId: string) => { + const user = onlineUsers.find(u => u.id === userId); + return user?.username || user?.email || 'Unknown User'; + }; + if (isLoading) { return ( @@ -340,7 +351,7 @@ const Chat: React.FC = () => { }} className="message-user" > - {Array.from(usersMetadataCacheRef.current).find(u => u.userId === m?.userId)?.email} + {getUserDisplayName(m?.userId)} { ({ id: user.id, email: user.email }))} - onUserClick={onUserClick} + onlineUsers={onlineUsers.filter(isValidUser).map(user => ({ id: user.id, username: user.username, email: user.email }))} + onUserClick={(user) => { + const fullUser = onlineUsers.find(u => u.id === user.id); + if (fullUser) { + onUserClick(fullUser); + } + }} disabled={!isConnected} selectedUserId={selectedUserId} /> From 3f2234e5af88777ab80ac32bb19047fff52eebf6 Mon Sep 17 00:00:00 2001 From: Tal Jacob Date: Tue, 8 Jul 2025 00:43:48 +0300 Subject: [PATCH 3/4] Show Also Email In DividedList Signed-off-by: Tal Jacob --- nextstep-frontend/src/components/DividedList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextstep-frontend/src/components/DividedList.tsx b/nextstep-frontend/src/components/DividedList.tsx index 1e04e1e..36e7587 100644 --- a/nextstep-frontend/src/components/DividedList.tsx +++ b/nextstep-frontend/src/components/DividedList.tsx @@ -75,7 +75,7 @@ const DividedList: React.FC = ({ onlineUsers, onUserClick, dis /> Date: Tue, 8 Jul 2025 00:47:33 +0300 Subject: [PATCH 4/4] Remove Redundant Imports Signed-off-by: Tal Jacob --- nextstep-frontend/src/components/DividedList.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/nextstep-frontend/src/components/DividedList.tsx b/nextstep-frontend/src/components/DividedList.tsx index 36e7587..2631b8a 100644 --- a/nextstep-frontend/src/components/DividedList.tsx +++ b/nextstep-frontend/src/components/DividedList.tsx @@ -1,4 +1,3 @@ -import { useState } from 'react'; import { Avatar, Box,