Переглянути джерело

Messages loading bug fixed

feature/fixing_load_messages_bug
Ermin Bronja 3 роки тому
джерело
коміт
8a26246e41

+ 0
- 7
Backend/Diligent.WebAPI.Host/Hubs/Attachment.cs Переглянути файл

@@ -1,7 +0,0 @@
namespace Diligent.WebAPI.Host.Hubs
{
public class Attachment
{
public string Name { get; set; }
}
}

+ 3
- 3
Backend/Diligent.WebAPI.Host/Hubs/ChatHub.cs Переглянути файл

@@ -30,7 +30,7 @@ namespace Diligent.WebAPI.Host.Hubs
// All other users will receive notification
if (_connections.TryGetValue(message.ConnId, out UserConnection room))
{
await Clients.Group(room.RoomId).ReceiveMessage(new ChatMessage { UserId = room.UserId, User = room.UserId, Message = message.Message, RoomId = room.RoomId });
await Clients.Group(room.RoomId).ReceiveMessage(new ChatMessage { UserId = room.UserId, User = room.UserId, Message = message.Message, RoomId = room.RoomId, 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
@@ -59,7 +59,7 @@ namespace Diligent.WebAPI.Host.Hubs
{
await Groups.AddToGroupAsync(Context.ConnectionId, userConnection.RoomId);
_connections[Context.ConnectionId] = userConnection;
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,IsAccessMessage = true });
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,IsAccessMessage = true, Username = userConnection.Username });
}
else
{
@@ -75,7 +75,7 @@ namespace Diligent.WebAPI.Host.Hubs
{
// Find user connection in connections dictionary and delete it
_connections.Remove(Context.ConnectionId);
await Clients.OthersInGroup(room.RoomId).ReceiveMessage(new ChatMessage { UserId = room.UserId, User = room.UserId, Message = $"{room.Username} has left room", RoomId = room.RoomId,IsAccessMessage = true });
await Clients.OthersInGroup(room.RoomId).ReceiveMessage(new ChatMessage { UserId = room.UserId, User = room.UserId, Message = $"{room.Username} has left room", RoomId = room.RoomId,IsAccessMessage = true, Username = room.Username });
await _mediator.Send(new RemoveUserFromGroupCommand(room.RoomId, room.UserId));
}
}

+ 1
- 1
Backend/Diligent.WebAPI.Host/Hubs/ChatMessage.cs Переглянути файл

@@ -6,10 +6,10 @@
public string? User { get; set; }
public string Message { get; set; }
public DateTime Created { get; set; } = DateTime.Now;
public Attachment? Attachment { get; set; }
public bool IsAccessMessage { get; set; } = false;
// Context.ConnectionId generated by SignalR
public string ConnId { get; set; }
public string RoomId { get; set; }
public string Username { get; set; }
}
}

+ 46
- 0
Frontend/src/Helpers.js Переглянути файл

@@ -0,0 +1,46 @@
export const getMonth = (monthNumber) => {
switch (monthNumber) {
case 0:
return "January";

case 1:
return "February";

case 2:
return "March";

case 3:
return "April";

case 4:
return "May";

case 5:
return "June";

case 6:
return "July";

case 7:
return "August";

case 8:
return "September";

case 9:
return "October";

case 10:
return "November";

case 11:
return "December";

default:
return "January";
}
};

export const getDate = (date) => {
return new Date(Date.parse(date));
};

+ 25
- 15
Frontend/src/components/ChatList.js Переглянути файл

@@ -78,6 +78,7 @@ const ChatList = () => {
};

const showRoomMessagesHandler = (n) => {
console.log("??");
dispatch(chatActions.readNotifications(n.id));
dispatch(chatActions.setRoom(n));
};
@@ -97,7 +98,6 @@ const ChatList = () => {
dispatch(
chatActions.saveContextId({ connId: data.connId, userId: user.id })
);
console.log("Join room", data);
setChatMessage(data);
}
});
@@ -113,7 +113,9 @@ const ChatList = () => {
}
});
// 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, room: activeRoom.id })
// );

connection.onclose((e) => {
// On close connection
@@ -143,7 +145,6 @@ const ChatList = () => {
chatActions.saveContextId({ connId: data.connId, userId: user.id })
);
}
console.log("Send group message", data);
setChatMessage(data);
});
}
@@ -154,14 +155,17 @@ const ChatList = () => {
if (chatMessage && activeRoom.id === chatMessage.roomId) {
dispatch(
chatActions.newMessage({
content: chatMessage.message,
createdAtUtc: new Date(),
deletedAtUtc: null,
id: null,
senderId: chatMessage.userId,
updatedAtUtc: null,
username: user.username,
isAccessMessage: chatMessage.isAccessMessage,
message: {
content: chatMessage.message,
createdAtUtc: new Date(),
deletedAtUtc: null,
id: null,
senderId: chatMessage.userId,
updatedAtUtc: null,
username: chatMessage.username,
isAccessMessage: chatMessage.isAccessMessage,
},
room: activeRoom.id,
})
);
}
@@ -233,11 +237,14 @@ const ChatList = () => {
<div
className="border-bottom roomsBtn d-flex bg-light"
key={index}
onClick={() => joinRoom(room)}
onClick={() => {
joinRoom(room);
showRoomMessagesHandler(room);
}}
>
<button
className="text-start w-100 py-2 px-3 btn btn-light h-100"
onClick={showRoomMessagesHandler.bind(this, room)}
// onClick={showRoomMessagesHandler.bind(this, room)}
>
{room.name}
</button>
@@ -255,7 +262,10 @@ const ChatList = () => {
<div
className="border-bottom roomsBtn d-flex bg-light"
key={index}
onClick={() => joinRoom(n)}
onClick={() => {
joinRoom(n);
showRoomMessagesHandler(n);
}}
>
{notificationCounter(n) && (
<div className="notification rounded-circle my-auto ms-3">
@@ -264,7 +274,7 @@ const ChatList = () => {
)}
<button
className="text-start w-100 py-2 px-3 btn btn-light h-100"
onClick={showRoomMessagesHandler.bind(this, n)}
// onClick={showRoomMessagesHandler.bind(this, n)}
>
{n.name}
</button>

+ 179
- 84
Frontend/src/components/ChatWindow.js Переглянути файл

@@ -5,6 +5,8 @@ import { useSelector, useDispatch } from "react-redux";
import { chatActions } from "../store/chat-slice";
import { BsCircleFill } from "react-icons/bs";
import TypingBar from "./TypingBar";
import { fetchChatRoomsAsync } from "../store/chat-slice";
import { getDate, getMonth } from "../Helpers";

const ChatWindow = ({ room }) => {
const messagesEndRef = useRef(null);
@@ -14,12 +16,81 @@ const ChatWindow = ({ room }) => {
const connection = useSelector((state) => state.chat.connection);
const connections = useSelector((state) => state.chat.connections);
const activeRoom = useSelector((state) => state.chat.activeRoom);
const roomStatus = useSelector((state) => state.chat.status);
const messages = useSelector((state) => state.chat.messages);
const trackedRoom = useSelector((state) => state.chat.trackedRoom);
const [proba, setProba] = useState([]);
const dispatch = useDispatch();

useEffect(() => {
dispatch(chatActions.setMessages(room.messages));
}, [dispatch, room.messages]);
if (trackedRoom) {
showDate();
}
}, [trackedRoom, messages]);

const showDate = () => {
let temp = [...trackedRoom.messages];

if (temp[0] !== undefined) {
let date = getDate(temp[0].createdAtUtc);

let d = date.getDate();

let m = date.getMonth();

let y = date.getFullYear();

temp.splice(0, 0, {
content: getMonth(m) + " " + d.toString() + ", " + y.toString(),

senderId: undefined, //is information about date not message

username: undefined,

id: null,

createdAtUtc: undefined,
});
for (let index = 0; index < temp.length; index++) {
const element = temp[index];
let day = getDate(element.createdAtUtc).getDate();
if (temp[index + 1] !== undefined) {
if (temp[index + 1].createdAtUtc !== undefined) {
if (temp[index].isAccessMessage === undefined) {
if (getDate(temp[index + 1].createdAtUtc).getDate() > day) {
let elem = temp[index + 1].createdAtUtc;
let d = getDate(elem).getDate();
let m = getDate(elem).getMonth();
let y = getDate(elem).getFullYear();
temp.splice(index + 1, 0, {
content:
getMonth(m) + " " + d.toString() + ", " + y.toString(),
senderId: undefined, //is information about date not message
username: undefined,
id: null,
createdAtUtc: undefined,
});
}
}
}
}
}
}
setProba(temp);
};

useEffect(() => {
// dispatch(chatActions.setMessages(room.messages));
if (user) {
dispatch(fetchChatRoomsAsync(user.id));
}
}, [dispatch, room, user]);

useEffect(() => {
if (roomStatus.includes("fetchRoomsFulfilled")) {
dispatch(chatActions.setTrackedRoom(room.id));
}
}, [roomStatus]);

const leftRoomHandler = async () => {
const userToFetch = connections.filter(
@@ -67,92 +138,116 @@ const ChatWindow = ({ room }) => {
});
};

const calculateMinutes = (createdAt) => {
const date = getDate(createdAt);

const minutes = date.getMinutes();

return minutes < 10 ? "0" + minutes : minutes;
};

return (
<div className="p-2 position-relative bg-main rounded h-100 d-flex flex-column">
<div className="p-2 px-3 pt-3 position-relative bg-light rounded h-100 d-flex flex-column">
<div style={{ height: "60px" }}>
<h2 className="p-0 m-0">{room.name}</h2>
<button
onClick={leftRoomHandler}
className='btn btn-danger mb-2 leave-btn'
<div className="p-2 px-3 pt-3 position-relative bg-light rounded h-100 d-flex flex-column">
<div style={{ height: "60px" }}>
<h2 className="p-0 m-0">{room.name}</h2>
<button
onClick={leftRoomHandler}
className="btn btn-danger mb-2 leave-btn"
>
Leave Room
</button>
</div>

<div className="messages bg-white border ">
<div className="overlay p-3 px-4 d-flex flex-column-reverse">
{/* mapirane poruke */}
{user &&
trackedRoom &&
proba
.map((n, index) => (
<div
key={index}
className={
n.isAccessMessage === true || n.senderId === undefined
? "d-flex flex-column align-items-center"
: n.senderId === user.id
? "d-flex flex-column align-items-end"
: "d-flex flex-column align-items-start"
}
>
<div
className={`p-2 px-4 mb-0 rounded message ${
n.isAccessMessage === true || n.senderId === undefined
? "text-muted small"
: n.senderId !== user.id
? "bg-main-primary text-light chatMsg"
: "bg-light text-dark chatMsg"
}`}
>
<div style={{ display: "flex", flexDirection: "column" }}>
{n.content}

{n.senderId !== undefined ? (
<div style={{ fontSize: 12, alignSelf: "flex-end" }}>
{getDate(n.createdAtUtc).getHours().toString()}:
{calculateMinutes(n.createdAtUtc)}
</div>
) : (
""
)}
</div>
</div>
<p className="text-muted small m-0 p-0 mb-4">
{n.senderId !== user?.id &&
!n.isAccessMessage &&
n.senderId !== undefined ? (
activeUsers.some((m) => m === n.senderId) ? (
<BsCircleFill className="me-2 text-success" />
) : (
<BsCircleFill className="me-2 text-danger" />
)
) : (
""
)}
{n.isAccessMessage !== true &&
n.senderId !== user?.id &&
n.username}
</p>
</div>
))
.reverse()}
<div ref={messagesEndRef} />
</div>
</div>

<TypingBar id={activeRoom.id} />

<Form
style={{ height: "80px" }}
className="d-flex align-items-center"
onSubmit={onSendMessageToGroupHandler}
>
Leave Room
</button>
</div>

<div className="messages bg-white border ">
<div className="overlay p-3 px-4 d-flex flex-column-reverse">
{/* mapirane poruke */}
{user &&
messages
.map((n, index) => (
<div
key={index}
className={
n.isAccessMessage === true
? "d-flex flex-column align-items-center"
: n.senderId === user.id
? "d-flex flex-column align-items-end"
: "d-flex flex-column align-items-start"
}
>
{console.log("Message", n, "User id", user.id)}
<p
className={`p-2 px-4 mb-0 rounded message ${
n.isAccessMessage === true
? "text-muted small"
: n.senderId !== user.id
? "bg-main-primary text-light chatMsg"
: "bg-light text-dark chatMsg"
}`}
>
{n.content}
</p>
<p className="text-muted small m-0 p-0 mb-4">
{n.senderId !== user?.id ? (
activeUsers.some((m) => m === n.senderId) ? (
<BsCircleFill className="me-2 text-success" />
) : (
<BsCircleFill className="me-2 text-danger" />
)
) : (
""
)}
{n.isAccessMessage !== true && n.senderId !== user?.id && n.username}
</p>
</div>
))
.reverse()}
<div ref={messagesEndRef} /></div>
<InputGroup className="mb-3">
<FormControl
placeholder="Enter your messagge..."
aria-label="Enter your messagge..."
aria-describedby="basic-addon2"
onChange={(e) => messageOnChangeHandler(e)}
value={message}
/>

<Button
className="px-5"
variant="outline-secondary"
id="button-addon2"
type="submit"
>
Send
</Button>
</InputGroup>
</Form>
</div>

<TypingBar id={activeRoom.id} />

<Form
style={{ height: "80px" }}
className="d-flex align-items-center"
onSubmit={onSendMessageToGroupHandler}
>
<InputGroup className="mb-3">
<FormControl
placeholder="Enter your messagge..."
aria-label="Enter your messagge..."
aria-describedby="basic-addon2"
onChange={(e) => messageOnChangeHandler(e)}
value={message}
/>

<Button
className="px-5"
variant="outline-secondary"
id="button-addon2"
type="submit"
>
Send
</Button>
</InputGroup>
</Form>
</div>
</div>
);
};

+ 19
- 10
Frontend/src/store/chat-slice.js Переглянути файл

@@ -12,6 +12,8 @@ const initialState = {
connections: [],
notifications: [],
typings: [],

trackedRoom: null,
};

export const fetchChatRoomsAsync = createAsyncThunk(
@@ -88,11 +90,19 @@ const chatSlice = createSlice({
},
// New message sent from user
newMessage: (state, action) => {
if (action.payload.changedRoom) {
state.messages = [];
} else {
state.messages = [...state.messages, action.payload];
}
// if (action.payload.changedRoom) {
// state.messages = [];
// } else {
// state.messages = [...state.messages, action.payload];
// }

console.log(action.payload);
const room = state.rooms.find((r) => r.id === action.payload.room);
room.messages.push(action.payload.message);
state.trackedRoom.messages = [
...state.trackedRoom.messages,
action.payload.message,
];
},
saveContextId: (state, action) => {
// Check is empty array
@@ -123,15 +133,12 @@ const chatSlice = createSlice({
state.messages = action.payload;
},
addNotification: (state, action) => {
console.log(1, action.payload);
if (state.notifications.length !== 0) {
console.log(2);
const room = state.notifications.find(
(notification) => notification.roomId === action.payload
);

if (room) {
console.log(3);
room.notificationCount++;
} else {
state.notifications.push({
@@ -140,7 +147,6 @@ const chatSlice = createSlice({
});
}
} else {
console.log(4);
state.notifications.push({
roomId: action.payload,
notificationCount: 1,
@@ -180,6 +186,9 @@ const chatSlice = createSlice({
state.typings = state.typings.filter((n) => n.message !== f.message);
}
},
setTrackedRoom: (state, action) => {
state.trackedRoom = state.rooms.find((r) => r.id === action.payload);
},
},
extraReducers: (builder) => {
// Fetch chat rooms
@@ -189,7 +198,7 @@ const chatSlice = createSlice({
});
builder.addCase(fetchChatRoomsAsync.fulfilled, (state, action) => {
state.rooms = action.payload;
state.status = "idle";
state.status = "fetchRoomsFulfilled idle";
state.error = null;
});
builder.addCase(fetchChatRoomsAsync.rejected, (state, action) => {

Завантаження…
Відмінити
Зберегти