| public async Task<List<Room>> GetRoomsForSupport(string supportId) => | public async Task<List<Room>> GetRoomsForSupport(string supportId) => | ||||
| await _mongoCollection.Find(k => k.CreatedBy == supportId).ToListAsync(); | await _mongoCollection.Find(k => k.CreatedBy == supportId).ToListAsync(); | ||||
| public async Task<bool> LeaveChatRoom(string roomId, string userId) | |||||
| { | |||||
| var room = await GetRoomAsync(roomId); | |||||
| if (room == null) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| var userConnection = room.Customers.Where(x => x.CustomerId == userId).FirstOrDefault(); | |||||
| if (userConnection == null) | |||||
| { | |||||
| return false; | |||||
| } | |||||
| room.Customers.Remove(userConnection); | |||||
| await _mongoCollection.ReplaceOneAsync(x => x.Id == roomId, room); | |||||
| return true; | |||||
| } | |||||
| } | } | ||||
| } | } |
| }; | }; | ||||
| var result = await _customerManager.CreateAsync(customer, customerCreateDTO.Password); | var result = await _customerManager.CreateAsync(customer, customerCreateDTO.Password); | ||||
| await _customerManager.AddToRoleAsync(customer, "Customer"); | |||||
| await _customerManager.AddToRoleAsync(customer, "Support"); | |||||
| if (!result.Succeeded) | if (!result.Succeeded) | ||||
| { | { |
| namespace Diligent.WebAPI.Host.DTOs.Chat | |||||
| { | |||||
| public class LeaveChatRoomDTO | |||||
| { | |||||
| public string ConnId { get; set; } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Data.Entities; | using Diligent.WebAPI.Data.Entities; | ||||
| using Diligent.WebAPI.Host.DTOs.Chat; | |||||
| using Diligent.WebAPI.Host.Mediator.Chat.Commands; | |||||
| using Diligent.WebAPI.Host.Mediator.Messages.Commands; | using Diligent.WebAPI.Host.Mediator.Messages.Commands; | ||||
| using MediatR; | using MediatR; | ||||
| using Microsoft.AspNetCore.Authorization; | using Microsoft.AspNetCore.Authorization; | ||||
| await Clients.Group(userConnection.RoomId).ReceiveMessage(new ChatMessage { User = userConnection.UserId, Message = $"{userConnection.Username} has joined room", ConnId = Context.ConnectionId }); | await Clients.Group(userConnection.RoomId).ReceiveMessage(new ChatMessage { User = userConnection.UserId, Message = $"{userConnection.Username} has joined room", ConnId = Context.ConnectionId }); | ||||
| } | } | ||||
| } | } | ||||
| [HubMethodName("LeaveRoom")] | |||||
| public async Task LeaveRoom(LeaveChatRoomDTO connection) | |||||
| { | |||||
| if (_connections.TryGetValue(connection.ConnId, out UserConnection room)) | |||||
| { | |||||
| _connections.Remove(Context.ConnectionId); | |||||
| await Clients.OthersInGroup(room.RoomId).ReceiveMessage(new ChatMessage { User = room.UserId, Message = $"{room.Username} has left room" }); | |||||
| await _mediator.Send(new RemoveUserFromGroupCommand(room.RoomId, room.UserId)); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } |
| using MediatR; | |||||
| namespace Diligent.WebAPI.Host.Mediator.Chat.Commands | |||||
| { | |||||
| public class RemoveUserFromGroupCommand : IRequest<Unit> | |||||
| { | |||||
| public string RoomId { get; } | |||||
| public string UserId { get; } | |||||
| public RemoveUserFromGroupCommand(string roomId, string userId) | |||||
| { | |||||
| RoomId = roomId; | |||||
| UserId = userId; | |||||
| } | |||||
| } | |||||
| } |
| using Diligent.WebAPI.Business.Services; | |||||
| using Diligent.WebAPI.Host.Mediator.Chat.Commands; | |||||
| using MediatR; | |||||
| namespace Diligent.WebAPI.Host.Mediator.Chat.Handlers | |||||
| { | |||||
| public class RemoveUserFromGroupHandler : IRequestHandler<RemoveUserFromGroupCommand, Unit> | |||||
| { | |||||
| private readonly RoomService _roomService; | |||||
| public RemoveUserFromGroupHandler(RoomService roomService) | |||||
| { | |||||
| _roomService = roomService; | |||||
| } | |||||
| public async Task<Unit> Handle(RemoveUserFromGroupCommand request, CancellationToken cancellationToken) | |||||
| { | |||||
| if (request == null) | |||||
| { | |||||
| throw new BadHttpRequestException("Object cannot be null"); | |||||
| } | |||||
| var result = await _roomService.LeaveChatRoom(request.RoomId, request.UserId); | |||||
| if (!result) | |||||
| { | |||||
| throw new Exception("Problem with deleting user from group"); | |||||
| } | |||||
| return new Unit(); | |||||
| } | |||||
| } | |||||
| } |
| import { chatActions } from "../store/chat-slice"; | import { chatActions } from "../store/chat-slice"; | ||||
| const ChatWindow = ({ room }) => { | const ChatWindow = ({ room }) => { | ||||
| const messagesEndRef = useRef(null) | |||||
| const messagesEndRef = useRef(null); | |||||
| const scrollToBottom = () => { | const scrollToBottom = () => { | ||||
| messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }) | |||||
| } | |||||
| messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); | |||||
| }; | |||||
| const [message, setMessage] = useState(""); | const [message, setMessage] = useState(""); | ||||
| const { user } = useContext(UserContext); | const { user } = useContext(UserContext); | ||||
| dispatch(chatActions.setMessages(room.messages)); | dispatch(chatActions.setMessages(room.messages)); | ||||
| }, [dispatch, room.messages]); | }, [dispatch, room.messages]); | ||||
| useEffect(()=>{ | |||||
| useEffect(() => { | |||||
| scrollToBottom(); | scrollToBottom(); | ||||
| },[]) | |||||
| }, []); | |||||
| const leftRoomHandler = async () => { | |||||
| const userToFetch = connections.filter( | |||||
| (conn) => conn.userId === user.id && conn.roomId === activeRoom.id | |||||
| ); | |||||
| console.log(userToFetch[0].connId, user.id, activeRoom.id); | |||||
| await connection.invoke("LeaveRoom", { | |||||
| connId: userToFetch[0].connId, | |||||
| }); | |||||
| dispatch( | |||||
| chatActions.leaveRoom({ roomId: activeRoom.id, customerId: user.id }) | |||||
| ); | |||||
| }; | |||||
| const onSendMessageToGroupHandler = async () => { | const onSendMessageToGroupHandler = async () => { | ||||
| const userToFetch = connections.filter( | const userToFetch = connections.filter( | ||||
| connId: userToFetch[0].connId, | connId: userToFetch[0].connId, | ||||
| }); | }); | ||||
| setMessage('') | |||||
| setMessage(""); | |||||
| }; | }; | ||||
| return ( | return ( | ||||
| <div className="px-3 bg-light-transparent rounded h-100 d-flex flex-column"> | <div className="px-3 bg-light-transparent rounded h-100 d-flex flex-column"> | ||||
| <div style={{ height: "80px" }}> | <div style={{ height: "80px" }}> | ||||
| <h2 className="py-3 m-0">{room.name}</h2> | |||||
| <h2 className="py-1 m-0">{room.name}</h2> | |||||
| <button | |||||
| onClick={leftRoomHandler} | |||||
| style={{ | |||||
| border: 0, | |||||
| backgroundColor: "transparent", | |||||
| padding: 0, | |||||
| margin: 0, | |||||
| textDecoration: "underline", | |||||
| }} | |||||
| > | |||||
| Leave Room | |||||
| </button> | |||||
| </div> | </div> | ||||
| <div className="messages p-3 border d-flex flex-column-reverse"> | <div className="messages p-3 border d-flex flex-column-reverse"> | ||||
| {/* mapirane poruke */} | {/* mapirane poruke */} | ||||
| {messages.map((n, index) => ( | |||||
| <div | |||||
| key={index} | |||||
| className={ | |||||
| n.senderId === user.id | |||||
| ? "d-flex flex-column align-items-end" | |||||
| : "d-flex flex-column align-items-start" | |||||
| } | |||||
| > | |||||
| <p | |||||
| className={`p-2 px-4 mb-0 rounded message ${ | |||||
| n.senderId !== user.id ? "bg-primary text-light" : "bg-light text-dark" | |||||
| }`} | |||||
| {messages | |||||
| .map((n, index) => ( | |||||
| <div | |||||
| key={index} | |||||
| className={ | |||||
| n.senderId === user.id | |||||
| ? "d-flex flex-column align-items-end" | |||||
| : "d-flex flex-column align-items-start" | |||||
| } | |||||
| > | > | ||||
| {/* {n.message} */} | |||||
| {n.content} | |||||
| </p> | |||||
| <p className="text-muted small m-0 p-0 mb-4">{n.username}</p> | |||||
| </div> | |||||
| )).reverse()} | |||||
| <p | |||||
| className={`p-2 px-4 mb-0 rounded message ${ | |||||
| n.senderId !== user.id | |||||
| ? "bg-primary text-light" | |||||
| : "bg-light text-dark" | |||||
| }`} | |||||
| > | |||||
| {/* {n.message} */} | |||||
| {n.content} | |||||
| </p> | |||||
| <p className="text-muted small m-0 p-0 mb-4">{n.username}</p> | |||||
| </div> | |||||
| )) | |||||
| .reverse()} | |||||
| <div ref={messagesEndRef} /> | <div ref={messagesEndRef} /> | ||||
| </div> | </div> | ||||
| setMessages: (state, action) => { | setMessages: (state, action) => { | ||||
| state.messages = action.payload; | state.messages = action.payload; | ||||
| }, | }, | ||||
| leaveRoom: (state, action) => { | |||||
| state.activeRoom = null; | |||||
| const room = state.rooms.find( | |||||
| (room) => room.id === action.payload.roomId | |||||
| ); | |||||
| room.customers = room.customers.filter( | |||||
| (customer) => customer.customerId !== action.payload.customerId | |||||
| ); | |||||
| }, | |||||
| }, | }, | ||||
| extraReducers: (builder) => { | extraReducers: (builder) => { | ||||
| // Fetch chat rooms | // Fetch chat rooms |