feature/316_ability_to_leave_chat naar development 3 jaren geleden
| @@ -60,5 +60,28 @@ namespace Diligent.WebAPI.Business.Services | |||
| public async Task<List<Room>> GetRoomsForSupport(string supportId) => | |||
| 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; | |||
| } | |||
| } | |||
| } | |||
| @@ -68,7 +68,7 @@ namespace Diligent.WebAPI.Host.Controllers | |||
| }; | |||
| var result = await _customerManager.CreateAsync(customer, customerCreateDTO.Password); | |||
| await _customerManager.AddToRoleAsync(customer, "Customer"); | |||
| await _customerManager.AddToRoleAsync(customer, "Support"); | |||
| if (!result.Succeeded) | |||
| { | |||
| @@ -0,0 +1,7 @@ | |||
| namespace Diligent.WebAPI.Host.DTOs.Chat | |||
| { | |||
| public class LeaveChatRoomDTO | |||
| { | |||
| public string ConnId { get; set; } | |||
| } | |||
| } | |||
| @@ -1,4 +1,6 @@ | |||
| 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 MediatR; | |||
| using Microsoft.AspNetCore.Authorization; | |||
| @@ -63,5 +65,16 @@ namespace Diligent.WebAPI.Host.Hubs | |||
| 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)); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,16 @@ | |||
| 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; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,33 @@ | |||
| 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(); | |||
| } | |||
| } | |||
| } | |||
| @@ -5,11 +5,11 @@ import { useSelector, useDispatch } from "react-redux"; | |||
| import { chatActions } from "../store/chat-slice"; | |||
| const ChatWindow = ({ room }) => { | |||
| const messagesEndRef = useRef(null) | |||
| const messagesEndRef = useRef(null); | |||
| const scrollToBottom = () => { | |||
| messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }) | |||
| } | |||
| messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); | |||
| }; | |||
| const [message, setMessage] = useState(""); | |||
| const { user } = useContext(UserContext); | |||
| @@ -23,9 +23,24 @@ const ChatWindow = ({ room }) => { | |||
| dispatch(chatActions.setMessages(room.messages)); | |||
| }, [dispatch, room.messages]); | |||
| useEffect(()=>{ | |||
| useEffect(() => { | |||
| 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 userToFetch = connections.filter( | |||
| @@ -38,38 +53,54 @@ const ChatWindow = ({ room }) => { | |||
| connId: userToFetch[0].connId, | |||
| }); | |||
| setMessage('') | |||
| setMessage(""); | |||
| }; | |||
| return ( | |||
| <div className="px-3 bg-light-transparent rounded h-100 d-flex flex-column"> | |||
| <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 className="messages p-3 border d-flex flex-column-reverse"> | |||
| {/* 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> | |||
| @@ -89,6 +89,17 @@ const chatSlice = createSlice({ | |||
| setMessages: (state, action) => { | |||
| 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) => { | |||
| // Fetch chat rooms | |||