Skip to content

Commit

Permalink
add editing messages
Browse files Browse the repository at this point in the history
  • Loading branch information
radekm2000 committed May 6, 2024
1 parent 0646f0a commit d7baa19
Show file tree
Hide file tree
Showing 12 changed files with 536 additions and 106 deletions.
11 changes: 11 additions & 0 deletions client/ecommerce/src/api/axios.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
AdminNotification,
Brand,
Conversation,
EditMessageParams,
ExtendedUserWithProfileAndReviews,
Feedback,
FetchedNotifications,
Expand Down Expand Up @@ -328,3 +329,13 @@ export const findUsersBySearchInput = async (
const response = await axiosApi.get(`users/all/${name}`);
return response.data;
};

export const editMessage = async (
dto: EditMessageParams
) => {
const response = await axiosApi.post(
`conversations/${dto.conversationId}/messages/${dto.messageId}`,
dto
);
return response.data;
};
271 changes: 176 additions & 95 deletions client/ecommerce/src/components/inbox/InboxChatContent.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import AccountCircle from "@mui/icons-material/AccountCircle";
import MoreVertIcon from "@mui/icons-material/MoreVert";
import DeleteOutlineOutlinedIcon from "@mui/icons-material/DeleteOutlineOutlined";
import {
Avatar,
Box,
Button,
CardMedia,
IconButton,
List,
ListItem,
ListItemText,
Popover,
TextField,
Typography,
} from "@mui/material";
import { useEffect, useRef, useState } from "react";
import { Conversation, UserWithFollows } from "../../types/types";
import { Conversation } from "../../types/types";
import { useUserContext } from "../../contexts/UserContext";
import { ImagePreview } from "./ImagePreview";
import { useDeleteMessageMutation } from "../../hooks/useDeleteMessageMutation";
import { RenderAvatar } from "../RenderAvatar";

import EditOutlinedIcon from "@mui/icons-material/EditOutlined";
import { useEditMessageMutation } from "../../hooks/useEditMessageMutation";
export const InboxChatContent = ({
selectedUserConversation,
userId,
Expand All @@ -33,7 +32,22 @@ export const InboxChatContent = ({
() => null
)
);
const [editedMessage, setEditedMessage] = useState<null | number>(null);
const [editedMessageContent, setEditedMessageContent] = useState("");
const [focus, setFocus] = useState<boolean>(false);

const handleEditClick = (index: number) => {
setEditedMessage(index);
setFocus(true);
};

const handleEditSave = (index: number) => {
setEditedMessage(null);
setEditedMessageContent("");
setFocus(false);
};

const editMessageMutation = useEditMessageMutation(userId);
const deleteMessageMutation = useDeleteMessageMutation(userId);

const handleClick = (
Expand Down Expand Up @@ -61,68 +75,66 @@ export const InboxChatContent = ({
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "instant" });
};

console.log(selectedUserConversation?.messages);
useEffect(() => {
scrollToBottom();
}, [selectedUserConversation]);

const sortedMessages =
selectedUserConversation &&
selectedUserConversation.messages.slice().sort((a, b) => a.id - b.id);
return (
<List sx={{}}>
{selectedUserConversation &&
selectedUserConversation.messages.map((message, index) => (
<ListItem
key={index}
sx={{
justifyContent:
message.author.username !== user.username
? "flex-start"
: "flex-end",
alignItems: "center",
{sortedMessages?.map((message, index) => (
<ListItem
key={index}
sx={{
justifyContent:
message.author.username !== user.username
? "flex-start"
: "flex-end",
alignItems: "center",
}}
>
<Box
onMouseOver={() => {
setHoveredIndex(index);
}}
onMouseLeave={() => setHoveredIndex(null)}
sx={{ display: "flex", alignItems: "center" }}
>
<Box
onMouseOver={() => {
setHoveredIndex(index);
}}
onMouseLeave={() => setHoveredIndex(null)}
sx={{ display: "flex", alignItems: "center" }}
<RenderAvatar
width="40px"
height="40px"
user={message.author}
marginRight="5px"
/>
{message.author.username === user.username &&
hoveredIndex === index && (
<IconButton
disableRipple
onClick={(e) => handleClick(e, index)}
sx={{
"&:hover": {
backgroundColor: "white",
},
}}
>
<MoreVertIcon sx={{ cursor: "pointer", color: "grey" }} />
</IconButton>
)}
<Popover
open={open(index)}
anchorEl={anchorEl[index]}
onClose={() => handleClose(index)}
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
>
{/* {message.author.username !== user.username ? (
message.author.avatar ? (
<Avatar
src={message.author.avatar}
sx={{ marginRight: "5px" }}
/>
) : (
<AccountCircle
sx={{
color: "grey",
width: "40px",
height: "40px",
marginRight: "5px",
}}
/>
)
) : null} */}
<RenderAvatar width="40px" height="40px" user={message.author} marginRight="5px"/>
{message.author.username === user.username &&
hoveredIndex === index && (
<IconButton
disableRipple
onClick={(e) => handleClick(e, index)}
sx={{
"&:hover": {
backgroundColor: "white",
},
}}
>
<MoreVertIcon sx={{ cursor: "pointer", color: "grey" }} />
</IconButton>
)}
<Popover
open={open(index)}
anchorEl={anchorEl[index]}
onClose={() => handleClose(index)}
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
<Box
sx={{
display: "block",
alignItems: "flex-start",
justifyContent: "center",
}}
>
<Button
onClick={() => {
Expand All @@ -137,46 +149,115 @@ export const InboxChatContent = ({
Delete
</Typography>
</Button>
</Popover>

{message.imageUrl && (
<ListItemText
sx={{
alignItems: "flex-end",
padding: "8px",
borderRadius: "5px",
textAlign: "left",
border: "1px solid rgba(23, 23, 23, 0.08)",
backgroundColor:
message.author.username === user.username
? "rgba(163, 157, 146, 0.15)"
: null,
}}
>
<ImagePreview image={message.imageUrl} />
</ListItemText>
)}
{message.content && (
<ListItemText
sx={{
alignItems: "flex-end",
padding: "8px",
borderRadius: "5px",
textAlign: "left",
border: "1px solid rgba(23, 23, 23, 0.08)",
backgroundColor:
message.author.username === user.username
? "rgba(163, 157, 146, 0.15)"
: null,
<Button
onClick={() => {
setEditedMessageContent(message.content);
handleEditClick(index);
handleClose(index);
}}
startIcon={<EditOutlinedIcon sx={{ color: "black" }} />}
sx={{ textTransform: "none", p: 2 }}
>
{message.content}
</ListItemText>
<Typography sx={{ color: "black", fontWeight: "600" }}>
Edit
</Typography>
</Button>
</Box>
</Popover>

{message.imageUrl && (
<ListItemText
sx={{
alignItems: "flex-end",
padding: "8px",
borderRadius: "5px",
textAlign: "left",
border: "1px solid rgba(23, 23, 23, 0.08)",
backgroundColor:
message.author.username === user.username
? "rgba(163, 157, 146, 0.15)"
: null,
}}
>
<ImagePreview image={message.imageUrl} />
</ListItemText>
)}
<Box sx={{ display: "flex", flexDirection: "column" }}>
{editedMessage === index && focus ? (
<Box>
<TextField
multiline
maxRows={5}
variant="standard"
InputProps={{
disableUnderline: true,
spellCheck: false,
}}
sx={{
alignItems: "flex-end",
padding: "8px",
borderRadius: "5px",
textAlign: "left",
border: "1px solid rgba(23, 23, 23, 0.08)",
backgroundColor: "rgba(163, 157, 146, 0.5)",
}}
value={editedMessageContent}
onChange={(e) => setEditedMessageContent(e.target.value)}
onKeyDown={(e) => {
if (e.key == "Escape" || e.key == "Esc") {
handleEditSave(index);
console.log(message.content);
console.log(editedMessageContent);
}
if (e.key === "Enter") {
editMessageMutation.mutate({
content: editedMessageContent,
messageId: message.id,
conversationId: selectedUserConversation!.id,
});
handleEditSave(index);
}
}}
/>
<Typography sx={{ fontSize: "10px" }}>
esc to <span style={{ color: "blue" }}>return</span> or
enter to <span style={{ color: "blue" }}>save</span>
</Typography>
</Box>
) : (
<Box>
{message.content && (
<>
<ListItemText
sx={{
alignItems: "flex-end",
padding: "8px",
borderRadius: "5px",
textAlign: "left",
border: "1px solid rgba(23, 23, 23, 0.08)",
backgroundColor:
message.author.username === user.username
? "rgba(163, 157, 146, 0.15)"
: null,
}}
>
{message.content}
</ListItemText>
{message.edited && (
<Typography sx={{ fontSize: "9px" }}>
(edited)
</Typography>
)}
</>
)}
</Box>
)}
<Typography ref={messagesEndRef}></Typography>
</Box>
</ListItem>
))}

<Typography ref={messagesEndRef}></Typography>
</Box>
</ListItem>
))}
</List>
);
};
21 changes: 21 additions & 0 deletions client/ecommerce/src/hooks/useEditMessageMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { editMessage } from "../api/axios";
import { EditMessageParams } from "../types/types";
import toast from "react-hot-toast";

export const useEditMessageMutation = (userId: number) => {
const queryClient = useQueryClient();
return useMutation<unknown, Error, EditMessageParams>({
mutationFn: editMessage,

onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [`conversations/users/${userId}`],
});
toast.success("Message edited!");
},
onError: (err) => {
toast.error(err.message || "Something went wrong");
},
});
};
6 changes: 6 additions & 0 deletions client/ecommerce/src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ export type RegisterInput = {
password: string;
confirmPassword: string;
};
export type EditMessageParams = {
content: string;
messageId: number;
conversationId: number;
}

export type LoginInput = {
username: string;
Expand Down Expand Up @@ -129,6 +134,7 @@ export type Message = {
author: User;
imageName?: string;
imageUrl?: string;
edited?: boolean
};
export type ProductNotification = {
id: number;
Expand Down
1 change: 0 additions & 1 deletion server/ecommerce/src/auth/utils/DiscordStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,4 @@ export class DiscordStrategy extends PassportStrategy(Strategy, 'discord') {
private get profileErrorMessage() {
return 'Failed to parse user discord profile';
}
F;
}
Loading

0 comments on commit d7baa19

Please sign in to comment.