ソースを参照

329 - Group messages interference

feature/329_group_messages_interference
Ermin Bronja 3年前
コミット
3ab2a47883

+ 1
- 1
Backend/Diligent.WebAPI.Business/Services/CustomerService.cs ファイルの表示



if (notification == null) if (notification == null)
{ {
return false;
return true;
} }


user.Notifications.Remove(notification); user.Notifications.Remove(notification);

+ 7
- 1
Backend/Diligent.WebAPI.Host/Controllers/NotificationController.cs ファイルの表示

using Diligent.WebAPI.Host.Mediator.Notifications.Queries;
using Diligent.WebAPI.Host.DTOs.Notification;
using Diligent.WebAPI.Host.Mediator.Notifications.Commands;
using Diligent.WebAPI.Host.Mediator.Notifications.Queries;
using MediatR; using MediatR;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;


[HttpGet("{id}")] [HttpGet("{id}")]
public async Task<IActionResult> GetNotifications([FromRoute] string id) => public async Task<IActionResult> GetNotifications([FromRoute] string id) =>
Ok(await _mediator.Send(new GetNotificationsQuery(id))); Ok(await _mediator.Send(new GetNotificationsQuery(id)));

[HttpPost]
public async Task<IActionResult> ReadNotifications([FromBody] NotificationDeleteDTO userConnection) =>
Ok(await _mediator.Send(new DeleteNotificationCommand(userConnection)));
} }
} }

+ 6
- 18
Backend/Diligent.WebAPI.Host/Hubs/ChatHub.cs ファイルの表示

_mediator = mediator; _mediator = mediator;
} }


//public override Task OnDisconnectedAsync(Exception? exception)
//{
// if (_connections.TryGetValue(Context.ConnectionId, out UserConnection room))
// {
// _connections.Remove(Context.ConnectionId);
// Clients.Group(room.RoomId).ReceiveMessage(new ChatMessage { User = room.UserId, Message = $"{room.UserId} has left room" });
// }

// return base.OnDisconnectedAsync(exception);
//}

[HubMethodName("SendMessageToGroup")] [HubMethodName("SendMessageToGroup")]
[AuthorizationHubFilter(Roles = "Customer,Support")] [AuthorizationHubFilter(Roles = "Customer,Support")]
public async Task SendMessageToGroup(ChatMessage message) public async Task SendMessageToGroup(ChatMessage message)
// All other users will receive notification // All other users will receive notification
if (_connections.TryGetValue(message.ConnId, out UserConnection room)) if (_connections.TryGetValue(message.ConnId, out UserConnection room))
{ {
await Clients.Group(room.RoomId).ReceiveMessage(new ChatMessage { User = room.UserId, Message = message.Message });
await Clients.OthersInGroup(room.RoomId).ReceiveNotifications(room.UserId, room.RoomId);
await Clients.Group(room.RoomId).ReceiveMessage(new ChatMessage { UserId = room.UserId, User = room.UserId, Message = message.Message, RoomId = room.RoomId });
await _mediator.Send(new AddMessageCommand(room.RoomId, new Message { Content = message.Message, SenderId = message.UserId, Username = room.Username })); await _mediator.Send(new AddMessageCommand(room.RoomId, new Message { Content = message.Message, SenderId = message.UserId, Username = room.Username }));


// Find other users in room and save notification in database // Find other users in room and save notification in database
await _mediator.Send(new AddNotificationCommand(new NotificationSaveDTO { RoomId = room.RoomId, ReceiverId = conn.Value.UserId })); await _mediator.Send(new AddNotificationCommand(new NotificationSaveDTO { RoomId = room.RoomId, ReceiverId = conn.Value.UserId }));
} }
} }

await Clients.OthersInGroup(room.RoomId).ReceiveNotifications(room.UserId, room.RoomId);
} }
} }


{ {
await Groups.AddToGroupAsync(Context.ConnectionId, userConnection.RoomId); await Groups.AddToGroupAsync(Context.ConnectionId, userConnection.RoomId);
_connections[Context.ConnectionId] = userConnection; _connections[Context.ConnectionId] = userConnection;
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 { UserId = userConnection.UserId, User = userConnection.UserId, RoomId = userConnection.RoomId, Message = $"{userConnection.Username} has joined room", ConnId = Context.ConnectionId });
} }
else else
{ {
{ {
if (_connections.TryGetValue(connection.ConnId, out UserConnection room)) if (_connections.TryGetValue(connection.ConnId, out UserConnection room))
{ {
// Find user connection in connections dictionary and delete it
_connections.Remove(Context.ConnectionId); _connections.Remove(Context.ConnectionId);
await Clients.OthersInGroup(room.RoomId).ReceiveMessage(new ChatMessage { User = room.UserId, Message = $"{room.Username} has left room" });
await Clients.OthersInGroup(room.RoomId).ReceiveMessage(new ChatMessage { UserId = room.UserId, User = room.UserId, Message = $"{room.Username} has left room", RoomId = room.RoomId });
await _mediator.Send(new RemoveUserFromGroupCommand(room.RoomId, room.UserId)); await _mediator.Send(new RemoveUserFromGroupCommand(room.RoomId, room.UserId));
} }
} }
public async Task SeeWhoIsTyping(TypingInvokeMessage message) public async Task SeeWhoIsTyping(TypingInvokeMessage message)
{ {
// Find user's room and send message to everyone // Find user's room and send message to everyone
//if (_connections.TryGetValue(Context.ConnectionId, out UserConnection room))
if (_connections.TryGetValue(message.ConnId, out UserConnection room)) if (_connections.TryGetValue(message.ConnId, out UserConnection room))
{ {
// send the typing info to all in group except the caller // send the typing info to all in group except the caller
// viewtyping(username + "is typing") // message, roomId
//await Clients.GroupExcept(message.RoomId, Context.ConnectionId).ViewTyping(new TypingMessage { RoomId = room.RoomId, Message = message.Message });
await Clients.GroupExcept(message.RoomId, message.ConnId).ViewTyping(new TypingMessage { RoomId = room.RoomId, Message = message.Message }); await Clients.GroupExcept(message.RoomId, message.ConnId).ViewTyping(new TypingMessage { RoomId = room.RoomId, Message = message.Message });
} }
} }

+ 1
- 0
Backend/Diligent.WebAPI.Host/Hubs/ChatMessage.cs ファイルの表示



// Context.ConnectionId generated by SignalR // Context.ConnectionId generated by SignalR
public string ConnId { get; set; } public string ConnId { get; set; }
public string RoomId { get; set; }
} }
} }

+ 0
- 8
Backend/Diligent.WebAPI.Host/Hubs/ConnectionHub.cs ファイルの表示

{ {
IDs.Remove(s.ConnId); IDs.Remove(s.ConnId);
var msg = new StatusMessage { Id = s.Id, M = "unsubscription" }; var msg = new StatusMessage { Id = s.Id, M = "unsubscription" };
//breakpoint here !
await Clients.Others.SendAsync("Notify", msg); await Clients.Others.SendAsync("Notify", msg);
} }
//public override async Task OnDisconnectedAsync(Exception exception, string id)
//{
// IDs.Remove(id);
// var msg = new StatusMessage { Id = id, M = "unsubscription" };
// await Clients.Others.SendAsync("Notify", msg);
// await base();
//}
} }
} }

+ 85
- 43
Frontend/src/components/ChatList.js ファイルの表示

import { GiSandsOfTime } from "react-icons/gi"; import { GiSandsOfTime } from "react-icons/gi";
import { CgEnter } from "react-icons/cg"; import { CgEnter } from "react-icons/cg";
import Dialog from "./UI/Dialog"; import Dialog from "./UI/Dialog";
import { chatActions, fetchChatRoomsAsync,fetchSupportRoomsAsync } from "../store/chat-slice";
import {
chatActions,
fetchChatRoomsAsync,
fetchSupportRoomsAsync,
} from "../store/chat-slice";
import { createChatRoomAsync } from "../store/chat-slice"; import { createChatRoomAsync } from "../store/chat-slice";
import { createJoinRequestAsync, requestActions } from "../store/request-slice"; import { createJoinRequestAsync, requestActions } from "../store/request-slice";
import { fetchRequestsAsync } from "../store/request-slice"; import { fetchRequestsAsync } from "../store/request-slice";
import { HubConnectionBuilder } from "@microsoft/signalr"; import { HubConnectionBuilder } from "@microsoft/signalr";
import { loadNotifications } from "../store/chat-slice"; import { loadNotifications } from "../store/chat-slice";
import { readNotificationsAsync } from "../store/chat-slice";


// Ovde ce biti dostupne grupe i razgovori // Ovde ce biti dostupne grupe i razgovori
const ChatList = () => { const ChatList = () => {
const [createChat, setCreateChat] = useState(false); const [createChat, setCreateChat] = useState(false);
// naziv chata koji ce biti dodan
const [chatName, setChatName] = useState(""); const [chatName, setChatName] = useState("");
// grupe kojima je korisnik poslao zahtev za ulazak
const [requestedRooms, setRequestedRooms] = useState([]); const [requestedRooms, setRequestedRooms] = useState([]);
// chats from redux
const { rooms, status, error, notifications } = useSelector(
const { rooms, status: chatStatus, error, notifications, activeRoom } = useSelector(
(state) => state.chat (state) => state.chat
); );
const { const {
status: requestsStatus, status: requestsStatus,
requests, requests,
} = useSelector((state) => state.requests); } = useSelector((state) => state.requests);
// show modal
const [showModal, setShowModal] = useState(false); const [showModal, setShowModal] = useState(false);
const [loadedNotification, setLoadedNotification] = useState(false); const [loadedNotification, setLoadedNotification] = useState(false);
const { user } = useContext(UserContext); const { user } = useContext(UserContext);
// const [connection, setConnection] = useState();
// const [messages, setMessages] = useState([]);
const [myConnection, setMyConnection] = useState(null);
const [chatMessage, setChatMessage] = useState(null);
const [notificationRoom, setNotificationRoom] = useState(null);
const dispatch = useDispatch(); const dispatch = useDispatch();


useEffect(() => { useEffect(() => {
} }
}, [user, dispatch, loadedNotification]); }, [user, dispatch, loadedNotification]);


// Maybe don't work
useEffect(() => { useEffect(() => {
(user !== null && user.roles.includes('Support')) ? dispatch(fetchSupportRoomsAsync(user.id)):
(user !== null && dispatch(fetchChatRoomsAsync(user.id)))
user !== null && user.roles.includes("Support")
? dispatch(fetchSupportRoomsAsync(user.id))
: user !== null && dispatch(fetchChatRoomsAsync(user.id));
dispatch(fetchRequestsAsync()); dispatch(fetchRequestsAsync());
}, [dispatch]);
}, [dispatch, user]);


useEffect(() => { useEffect(() => {
if (requests && rooms) { if (requests && rooms) {
const joinRoom = async (n) => { const joinRoom = async (n) => {
try { try {
const connection = new HubConnectionBuilder() const connection = new HubConnectionBuilder()
.withUrl("http://localhost:5116/chatHub",{accessTokenFactory:() => user.token})
.withUrl("http://localhost:5116/chatHub", {
accessTokenFactory: () => user.token,
})
.withAutomaticReconnect() .withAutomaticReconnect()
.build(); .build();


dispatch( dispatch(
chatActions.saveContextId({ connId: data.connId, userId: user.id }) chatActions.saveContextId({ connId: data.connId, userId: user.id })
); );
setChatMessage(data);
} }
// Every new received message will be stored in redux
dispatch(
chatActions.newMessage({
content: data.message,
createdAtUtc: new Date(),
deletedAtUtc: null,
id: null,
senderId: user.id,
updatedAtUtc: null,
username: user.username,
})
);
}); });


connection.on("ViewTyping", (data) =>{
dispatch(chatActions.addTyping(data));
})
connection.on("ViewTyping", (data) => {
dispatch(chatActions.addTyping(data));
});

connection.on("ReceiveNotifications", (userId, roomId) => { connection.on("ReceiveNotifications", (userId, roomId) => {
if (user.id !== userId) dispatch(chatActions.addNotification(roomId));
if (user.id !== userId) {
dispatch(chatActions.addNotification(roomId));
setNotificationRoom(roomId);
}
}); });
// When user changed room, array with messages from previous room will be deleted from redux // When user changed room, array with messages from previous room will be deleted from redux
dispatch(chatActions.newMessage({ changedRoom: true })); dispatch(chatActions.newMessage({ changedRoom: true }));


connection.onclose((e) => { connection.onclose((e) => {
// setConnection();
// setMessages([]);
// On close connection
}); });


await connection.start(); await connection.start();
}); });
dispatch(chatActions.setRoom(n)); dispatch(chatActions.setRoom(n));
dispatch(chatActions.setConnection(connection)); dispatch(chatActions.setConnection(connection));
// setConnection({ connection });
setMyConnection(connection);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
}; };


// Maybe don't work
useEffect(() => {
if (myConnection) {
myConnection.on("ReceiveMessage", (data) => {
// When user enter room first time after login, generated Context.ConnectionId will be saved in redux
if (data.connId) {
dispatch(
chatActions.saveContextId({ connId: data.connId, userId: user.id })
);
}
setChatMessage(data);
});
}
}, [myConnection, dispatch, user]);

// Maybe don't work
useEffect(() => {
if (chatMessage && activeRoom.id === chatMessage.roomId) {
dispatch(
chatActions.newMessage({
content: chatMessage.message,
createdAtUtc: new Date(),
deletedAtUtc: null,
id: null,
senderId: user.id,
updatedAtUtc: null,
username: user.username,
})
);
}
}, [chatMessage, dispatch, user, activeRoom]);

// Maybe don't work
useEffect(() => {
if (notificationRoom && activeRoom) {
if (notificationRoom === activeRoom.id) {
dispatch(chatActions.readNotifications(activeRoom.id));
dispatch(
readNotificationsAsync({ userId: user.id, roomId: activeRoom.id })
);
}
}
setNotificationRoom(null);
}, [notificationRoom, activeRoom, dispatch, user]);

const openModal = (n) => { const openModal = (n) => {
setShowModal(true); setShowModal(true);
dispatch(requestActions.chooseRoom(n)); dispatch(requestActions.chooseRoom(n));
} }
} }
} }
return (
user !== null && user.roles.includes('Support') ?
return user !== null && user.roles.includes("Support") ? (
<div> <div>
{rooms.map((room, index) => ( {rooms.map((room, index) => (
<div <div
className="border-bottom d-flex" className="border-bottom d-flex"
key={index} key={index}
onClick={() => joinRoom(room)} onClick={() => joinRoom(room)}
>
<button
className="text-start w-100 py-3 px-3 btn btn-light h-100"
onClick={showRoomMessagesHandler.bind(this, room)}
> >
<button
className="text-start w-100 py-3 px-3 btn btn-light h-100"
onClick={showRoomMessagesHandler.bind(this, room)}
>
{room.name}
</button>
{room.name}
</button>
</div> </div>
))} ))}
</div>:
</div>
) : (
<div> <div>
{acceptedRequests.length > 0 && ( {acceptedRequests.length > 0 && (
<div> <div>
)} )}


{/* ovo ce biti zamenjeno konkretnim podacima */} {/* ovo ce biti zamenjeno konkretnim podacima */}
{(!error && requestedRooms && status === "pendingFetchRooms") ||
status === "pendingAddRoom" ? (
{(!error && requestedRooms && chatStatus === "pendingFetchRooms") ||
chatStatus === "pendingAddRoom" ? (
<p>Loading</p> <p>Loading</p>
) : ( ) : (
getView() getView()

+ 11
- 24
Frontend/src/components/ChatWindow.js ファイルの表示

import { UserContext } from "../contexts/userContext"; import { UserContext } from "../contexts/userContext";
import { useSelector, useDispatch } from "react-redux"; import { useSelector, useDispatch } from "react-redux";
import { chatActions } from "../store/chat-slice"; import { chatActions } from "../store/chat-slice";
import { BsCircleFill } from 'react-icons/bs'
import { BsCircleFill } from "react-icons/bs";
import TypingBar from "./TypingBar"; import TypingBar from "./TypingBar";


const ChatWindow = ({ room }) => { const ChatWindow = ({ room }) => {
const messagesEndRef = useRef(null); const messagesEndRef = useRef(null);


const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
};

const [message, setMessage] = useState(""); const [message, setMessage] = useState("");
const { user } = useContext(UserContext); const { user } = useContext(UserContext);
const connection = useSelector((state) => state.chat.connection); const connection = useSelector((state) => state.chat.connection);
const { typings } = useSelector((s) => s.chat)
const connections = useSelector((state) => state.chat.connections); const connections = useSelector((state) => state.chat.connections);
const activeRoom = useSelector((state) => state.chat.activeRoom); const activeRoom = useSelector((state) => state.chat.activeRoom);
const messages = useSelector((state) => state.chat.messages); const messages = useSelector((state) => state.chat.messages);
dispatch(chatActions.setMessages(room.messages)); dispatch(chatActions.setMessages(room.messages));
}, [dispatch, room.messages]); }, [dispatch, room.messages]);


useEffect(() => {
// scrollToBottom();
}, []);

const leftRoomHandler = async () => { const leftRoomHandler = async () => {
const userToFetch = connections.filter( const userToFetch = connections.filter(
(conn) => conn.userId === user.id && conn.roomId === activeRoom.id (conn) => conn.userId === user.id && conn.roomId === activeRoom.id
); );


console.log(userToFetch[0].connId, user.id, activeRoom.id);

await connection.invoke("LeaveRoom", { await connection.invoke("LeaveRoom", {
connId: userToFetch[0].connId, connId: userToFetch[0].connId,
}); });
const status = useSelector((s) => s.status); const status = useSelector((s) => s.status);
const { activeUsers } = status; const { activeUsers } = status;


const messageOnChangeHandler = async (e) =>{
setMessage(e.target.value)
const messageOnChangeHandler = async (e) => {
setMessage(e.target.value);
const userToFetch = connections.filter( const userToFetch = connections.filter(
(conn) => conn.userId === user.id && conn.roomId === activeRoom.id (conn) => conn.userId === user.id && conn.roomId === activeRoom.id
); );


// console.log(userToFetch[0].connId)
// console.log(room.id)
await connection.send("SeeWhoIsTyping", { await connection.send("SeeWhoIsTyping", {
message: `${user.username} is typing...`, message: `${user.username} is typing...`,
connId: userToFetch[0].connId, connId: userToFetch[0].connId,
roomId: activeRoom.id
roomId: activeRoom.id,
}); });
}

};


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 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 {messages
.map((n, index) => ( .map((n, index) => (
<div <div
<div ref={messagesEndRef} /> <div ref={messagesEndRef} />
</div> </div>


<TypingBar id={activeRoom.id}/>
<TypingBar id={activeRoom.id} />


<Form style={{ height: "80px" }} className="d-flex align-items-center">
<Form
style={{ height: "80px" }}
className="d-flex align-items-center"
>
<InputGroup className="mb-3"> <InputGroup className="mb-3">
<FormControl <FormControl
placeholder="Enter your messagge..." placeholder="Enter your messagge..."

+ 0
- 22
Frontend/src/components/CustomerRequest/CustomerRequest.js ファイルの表示

import { FiCheckCircle, FiXCircle } from "react-icons/fi"; import { FiCheckCircle, FiXCircle } from "react-icons/fi";
import styles from "./CustomerRequest.module.css"; import styles from "./CustomerRequest.module.css";
import requestService from "../../services/requestService"; import requestService from "../../services/requestService";
// import { HubConnectionBuilder } from "@microsoft/signalr";


export default function CustomerRequest({ customer, requestHandled }) { export default function CustomerRequest({ customer, requestHandled }) {
const onAcceptRequestHandler = () => { const onAcceptRequestHandler = () => {
}) })
.then((res) => { .then((res) => {
requestHandled(res); requestHandled(res);

// Joining in room
// joinRoom({ userId: customer.senderId, roomId: customer.roomId });
}) })
.catch((e) => console.log(e)); .catch((e) => console.log(e));
}; };


// const joinRoom = async (joiningCredentials) => {
// try {
// const connection = new HubConnectionBuilder()
// .withUrl("http://localhost:5116/chatHub")
// .withAutomaticReconnect()
// .build();

// connection.on("ReceiveMessage", (data) => {
// console.log("CUSTOMER REQUEST", data);
// });

// await connection.start();
// await connection.invoke("JoinRoom", joiningCredentials);
// } catch (e) {
// console.log(e);
// }
// };

const onRejectRequestHandler = () => { const onRejectRequestHandler = () => {
requestService requestService
.rejectCustomerRequest({ .rejectCustomerRequest({

+ 3
- 11
Frontend/src/components/MiddleContainer.js ファイルの表示

import Activity from "./Activity"; import Activity from "./Activity";
import ChatList from "./ChatList"; import ChatList from "./ChatList";
import Requests from "./Requests/Requests"; import Requests from "./Requests/Requests";
import { useContext, useEffect, useState } from "react";
import { useContext, useEffect } from "react";
import { HubConnectionBuilder } from "@microsoft/signalr"; import { HubConnectionBuilder } from "@microsoft/signalr";
import { UserContext } from "../contexts/userContext"; import { UserContext } from "../contexts/userContext";
import { useDispatch, useSelector } from "react-redux";
import { useDispatch } from "react-redux";
import { statusActions } from "../store/status-slice"; import { statusActions } from "../store/status-slice";


const MiddleContainer = ({ showTerm }) => { const MiddleContainer = ({ showTerm }) => {
// const [conn, setConn] = useState('');
// window.addEventListener('beforeunload', ()=>{
// })

const { user } = useContext(UserContext); const { user } = useContext(UserContext);


const dispatch = useDispatch(); const dispatch = useDispatch();
.build(); .build();


dispatch(statusActions.setStatusConn(connection)); dispatch(statusActions.setStatusConn(connection));
// setConn(connection)

// connection.onclose(() => {
// connection.send("Unsubscribe", user.id);
// });


connection.on("Notify", (data) => { connection.on("Notify", (data) => {
if (data.m === "subscription") { if (data.m === "subscription") {
connection.start().then(fulfilled, rejected); connection.start().then(fulfilled, rejected);
} }


// Maybe don't work
useEffect(() => { useEffect(() => {
connect(); connect();
}, []); }, []);

+ 0
- 1
Frontend/src/components/RegisterForm.js ファイルの表示

navigate("/main"); navigate("/main");
}) })
.catch((err) => { .catch((err) => {
// console.log(err)
setError(err.response.data.message.split(".")); setError(err.response.data.message.split("."));
}); });
}; };

+ 0
- 5
Frontend/src/components/SideNavbar.js ファイルの表示



const logOutHandler = () => { const logOutHandler = () => {
if (window.confirm("Are you sure?")) { if (window.confirm("Are you sure?")) {
// ovde ce ici logika za logovanje
// uklanjanje iz state-a i slicno
dispatch(chatActions.deleteActiveRoom()); dispatch(chatActions.deleteActiveRoom());
logOut(); logOut();
} }
<Link <Link
to={'/main'} to={'/main'}
className="btn btn-white w-100 button-block button-block-flex-column" className="btn btn-white w-100 button-block button-block-flex-column"
// onClick={() => onClickHandler("notifications")}
> >
<FiBell className="icon-fs" /> <FiBell className="icon-fs" />
<h5 className="small text-muted text-center fw-light">feed</h5> <h5 className="small text-muted text-center fw-light">feed</h5>
<Link <Link
to={'/chats'} to={'/chats'}
className="btn btn-white button-block mt-4 w-100 button-block-flex-column" className="btn btn-white button-block mt-4 w-100 button-block-flex-column"
// onClick={() => onClickHandler("chats")}
> >
<FiMessageSquare className="icon-fs" /> <FiMessageSquare className="icon-fs" />
<h5 className="small text-muted text-center fw-light">chat</h5> <h5 className="small text-muted text-center fw-light">chat</h5>
<Link <Link
to={'/requests'} to={'/requests'}
className="btn btn-white button-block mt-4 w-100 button-block-flex-column" className="btn btn-white button-block mt-4 w-100 button-block-flex-column"
// onClick={() => onClickHandler("requests")}
> >
<FaArrowAltCircleRight className="icon-fs" /> <FaArrowAltCircleRight className="icon-fs" />
<h5 className="small text-muted text-center fw-light">Requests</h5> <h5 className="small text-muted text-center fw-light">Requests</h5>

+ 1
- 1
Frontend/src/components/TypingBar.js ファイルの表示

return () => clearTimeout(timeout) return () => clearTimeout(timeout)
}, 2500) }, 2500)
}, [rerender]);
}, [rerender, dispatch]);


return ( return (
<div className='text-dark'> <div className='text-dark'>

+ 0
- 1
Frontend/src/components/UI/CustomAccordition.js ファイルの表示

import React, { useState } from "react"; import React, { useState } from "react";
// import { useSelector, useDispatch } from "react-redux";


import Accordion from "@mui/material/Accordion"; import Accordion from "@mui/material/Accordion";
import AccordionSummary from "@mui/material/AccordionSummary"; import AccordionSummary from "@mui/material/AccordionSummary";

+ 0
- 2
Frontend/src/contexts/userContext.js ファイルの表示

window.addEventListener("beforeunload", () => { window.addEventListener("beforeunload", () => {
// that means the user probably shut the browser down without loging out // that means the user probably shut the browser down without loging out
if (localStorage.getItem("activeOnes")) { if (localStorage.getItem("activeOnes")) {
// status.connection.stop();
disconnect(); disconnect();
} }
}); });
connId: status.connection.connectionId, connId: status.connection.connectionId,
}) })
.then(() => console.log("bye")); .then(() => console.log("bye"));
// connection.stop();
}; };


const rejected = () => { const rejected = () => {

+ 3
- 1
Frontend/src/services/notificationService.js ファイルの表示

const methods = { const methods = {
loadNotifications: (userId) => loadNotifications: (userId) =>
axios.get(`/Notification/${userId}`).then(responseBody), axios.get(`/Notification/${userId}`).then(responseBody),
readNotifications: (payload) =>
axios.post("/Notification", payload).then(responseBody),
}; };


export default methods;
export default methods;

+ 43
- 16
Frontend/src/store/chat-slice.js ファイルの表示

rooms: [], rooms: [],
activeRoom: null, activeRoom: null,
error: null, error: null,
// Hub connection
connection: null, connection: null,
// List of room messages
messages: [], messages: [],
// All active user connections to rooms
connections: [], connections: [],
// Notifications
notifications: [], notifications: [],
typings: []
typings: [],
}; };


export const fetchChatRoomsAsync = createAsyncThunk( export const fetchChatRoomsAsync = createAsyncThunk(


export const fetchSupportRoomsAsync = createAsyncThunk( export const fetchSupportRoomsAsync = createAsyncThunk(
"chat/fetchSupportRooms", "chat/fetchSupportRooms",
async (payload,thunkAPI) => {
try{
async (payload, thunkAPI) => {
try {
return await chatService.getSupportRooms(payload); return await chatService.getSupportRooms(payload);
}catch(error){
return thunkAPI.rejectWithValue({error})
} catch (error) {
return thunkAPI.rejectWithValue({ error });
} }
} }
)
);


export const createChatRoomAsync = createAsyncThunk( export const createChatRoomAsync = createAsyncThunk(
"chat/createChatRoomAsync", "chat/createChatRoomAsync",
} }
); );


export const readNotificationsAsync = createAsyncThunk(
"chat/deleteNotificationsAsync",
async (payload, thunkAPI) => {
try {
return await notificationService.readNotifications(payload);
} catch (error) {
return thunkAPI.rejectWithValue({ error });
}
}
);

const chatSlice = createSlice({ const chatSlice = createSlice({
name: "chat", name: "chat",
initialState, initialState,
); );
}, },
addTyping: (state, action) => { addTyping: (state, action) => {
if(!state.typings.some(n => n.message === action.payload.message && n.roomId === action.payload.roomId))
if (
!state.typings.some(
(n) =>
n.message === action.payload.message &&
n.roomId === action.payload.roomId
)
)
state.typings = [...state.typings, action.payload]; state.typings = [...state.typings, action.payload];
else console.log('ima vec')
else console.log("ima vec");
}, },
removeTyping: (state, action) => { removeTyping: (state, action) => {
if(state.typings.length >= 1){
let f = state.typings[0]
state.typings = state.typings.filter(n => n.message !== f.message);
if (state.typings.length >= 1) {
let f = state.typings[0];
state.typings = state.typings.filter((n) => n.message !== f.message);
} }
}
},
}, },
extraReducers: (builder) => { extraReducers: (builder) => {
// Fetch chat rooms // Fetch chat rooms
state.status = "idle"; state.status = "idle";
state.error = action.payload; state.error = action.payload;
}); });

// Read notifications
builder.addCase(readNotificationsAsync.pending, (state) => {
state.status = "pendingReadingNotifications";
state.error = null;
});
builder.addCase(readNotificationsAsync.fulfilled, (state) => {
state.status = "idle";
state.error = null;
});
builder.addCase(readNotificationsAsync.rejected, (state, action) => {
state.status = "idle";
state.error = action.payload;
});
}, },
}); });



+ 0
- 1
Frontend/src/store/status-slice.js ファイルの表示

activeUsers: localStorage.getItem("activeOnes") activeUsers: localStorage.getItem("activeOnes")
? JSON.parse(localStorage.getItem("activeOnes")) ? JSON.parse(localStorage.getItem("activeOnes"))
: [], : [],
//maybe needed later
connection: '' connection: ''
}; };



読み込み中…
キャンセル
保存