From 372c5110568da7bc19db0cedede09b5b1695dbcb Mon Sep 17 00:00:00 2001 From: ahmedibrahim404 Date: Fri, 15 Jul 2022 01:57:29 +0200 Subject: [PATCH 1/6] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 06f46fb..7c314b1 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ yarn start - [x] Real-time Subscription for messages within each window - [x] Retrieval of messages of each chat with pagination - [x] Message Parsing System (Emojis/Mentions/Markdown/Paragraphs/...etc) -- [ ] Main Chat Window Functionalities (Send/Delete/Edit/Star/...etc) Messages +- [x] Main Chat Window Functionalities (Send/Delete/Edit/...etc) Messages ### Screenshots From d389690d4ecf3cca84af19cdd99681d595e95450 Mon Sep 17 00:00:00 2001 From: ahmedibrahim404 Date: Fri, 15 Jul 2022 01:59:03 +0200 Subject: [PATCH 2/6] feat(message): Implement methods to send/delete/edit message through Realtime API --- src/util/message.util.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/util/message.util.ts diff --git a/src/util/message.util.ts b/src/util/message.util.ts new file mode 100644 index 0000000..dd60d3d --- /dev/null +++ b/src/util/message.util.ts @@ -0,0 +1,26 @@ +import { driver } from '@rocket.chat/sdk'; +import { RealtimeAPIMessage } from '../interfaces/message'; + +async function sendTextMessage(roomId: string | undefined, messageText: string) : Promise { + if(!roomId) throw new Error('Room ID Not Found!'); + let res: RealtimeAPIMessage = await driver.asyncCall("sendMessage", [{ + rid: roomId, + msg: messageText + }]); + return res._id ? true : false; +} + +async function deleteMessageById(messageId: string) { + await driver.asyncCall("deleteMessage", [{ + _id: messageId + }]); +} + +async function editTextMessage(message: RealtimeAPIMessage, newMessageText: string) : Promise { + let newMessage: RealtimeAPIMessage = message; + newMessage.msg = newMessageText; + await driver.asyncCall("updateMessage", [newMessage]); + return true; +} + +export { sendTextMessage, deleteMessageById, editTextMessage }; From e52951dc55e511449f53a56095c8bd9b0d07e569 Mon Sep 17 00:00:00 2001 From: ahmedibrahim404 Date: Fri, 15 Jul 2022 02:00:32 +0200 Subject: [PATCH 3/6] feat(message-actions): Implement Components of the Message Actions(Delete/Edit) --- .../MessageActions/MessageActions.tsx | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/components/ChatWindow/MessageList/MessageRow/components/MessageActions/MessageActions.tsx diff --git a/src/components/ChatWindow/MessageList/MessageRow/components/MessageActions/MessageActions.tsx b/src/components/ChatWindow/MessageList/MessageRow/components/MessageActions/MessageActions.tsx new file mode 100644 index 0000000..ddaf9ce --- /dev/null +++ b/src/components/ChatWindow/MessageList/MessageRow/components/MessageActions/MessageActions.tsx @@ -0,0 +1,68 @@ +import React, { useState } from "react"; +import { hot } from "react-hot-loader/root"; +import styled from "styled-components"; +import { BsThreeDotsVertical } from "react-icons/bs"; + +const Container = styled.div` + position: absolute; + right: 10px; + cursor: pointer; +` + +const ActionsModal = styled.div` + position: fixed; + right: 25px; + background-color: #FFF; + border: 1px solid #eeeff1; + z-index: 100; +` + +const ActionsUl = styled.ul` + list-style-type: none; + padding: 5px; +` + +const ActionsLi = styled.li` + padding: 2px; + &:hover { + background-color:rgba(186, 186, 186, 0.1); + } +` + + +function MessageActions(props : any) { + const [isModalOpen, setModal] = useState(false); + + const toggleActionsModal = () => { + setModal(!isModalOpen); + } + + const onMessageEdit = () => { + props.onMessageEdit(); + toggleActionsModal(); + } + + const onMessageDelete = () => { + props.onMessageDelete(); + toggleActionsModal(); + } + + + return props.show && ( + +
+ +
+ {isModalOpen ? ( + + + Edit Message + Delete Message + + + ): null} +
+ ); +} + +export default hot(MessageActions); From b8b36a8a6dc7718dd75be87674a4b6a6e219edbe Mon Sep 17 00:00:00 2001 From: ahmedibrahim404 Date: Fri, 15 Jul 2022 02:01:28 +0200 Subject: [PATCH 4/6] fix: Fix Import of Room Interface --- src/main/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/main.ts b/src/main/main.ts index 5e5dc92..b2a7394 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -2,7 +2,7 @@ import { app, BrowserWindow, ipcMain } from "electron"; import installExtension, { REACT_DEVELOPER_TOOLS, } from "electron-devtools-installer"; -import Room from "../interfaces/room"; +import { Room } from "../interfaces/room"; declare global { const MAIN_WINDOW_WEBPACK_ENTRY: string; From 2cd381ac4f7d359100ebf2eff47e1e964ff801fa Mon Sep 17 00:00:00 2001 From: ahmedibrahim404 Date: Fri, 15 Jul 2022 02:02:16 +0200 Subject: [PATCH 5/6] feat(messages): Implement a new interface to store messages by Id for fast retrieval --- src/components/ChatWindow/ChatWindow.tsx | 37 +++++++++++++++++++----- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/components/ChatWindow/ChatWindow.tsx b/src/components/ChatWindow/ChatWindow.tsx index f277872..7229b76 100644 --- a/src/components/ChatWindow/ChatWindow.tsx +++ b/src/components/ChatWindow/ChatWindow.tsx @@ -23,10 +23,14 @@ const HeaderFooterContainer = styled.div` height: 4rem; ` +interface MessagesMap { + [_id: string]: RealtimeAPIMessage +} function ChatWindow() { const { id } = useParams(); - const [messages, setMessages] = useState([]); + const [messages, setMessages] = useState({}); + const [messageToEdit, setMessageToEdit] = useState(null); const loginToRoom = async () => { @@ -49,22 +53,39 @@ function ChatWindow() { const showMessages = async () => { let lastMessageDate = null; - if(messages.length){ - lastMessageDate = messages[0].ts; + let messagesKeys = Object.keys(messages); + if(messagesKeys.length){ + lastMessageDate = messages[ messagesKeys[0] ].ts; } - let newMessages: RealtimeAPIMessage[] = await loadMessagesFromRoom(id, 10, lastMessageDate); - setMessages((oldMessages) => [...newMessages, ...oldMessages]); + let newMessages: RealtimeAPIMessage[] = await loadMessagesFromRoom(id, 5, lastMessageDate); + + setMessages((oldMessages) => { + let toBeMessages: MessagesMap = {}; + newMessages.map((message) => { + toBeMessages[message._id] = message; + }); + toBeMessages = {...toBeMessages, ...oldMessages}; + return toBeMessages; + }); } const addMessage = async (message: RealtimeAPIMessage) => { - setMessages((oldMessages) => [...oldMessages, message]); + setMessages((oldMessages) => { + let toBeMessages = {...oldMessages}; + toBeMessages[message._id] = message; + return toBeMessages; + }); } const loadMoreMessages = async () => { showMessages(); } + const onEditMessageAction = (message: RealtimeAPIMessage) => { + setMessageToEdit(message); + } + useEffect(() => { loginToRoom(); }, []); @@ -75,9 +96,9 @@ function ChatWindow() {
- + - + ); From 3d4382ac74bc3cd964bf03b94ec55bb1affbf04f Mon Sep 17 00:00:00 2001 From: ahmedibrahim404 Date: Fri, 15 Jul 2022 02:03:48 +0200 Subject: [PATCH 6/6] feat(messages): Add features to Send/Edit/Delete Messages through the UI --- .../ChatWindow/MessageForm/MessageForm.tsx | 45 +++++++++++++++++-- .../ChatWindow/MessageList/MessageList.tsx | 5 +-- .../MessageList/MessageRow/MessageRow.tsx | 33 ++++++++++++-- 3 files changed, 72 insertions(+), 11 deletions(-) diff --git a/src/components/ChatWindow/MessageForm/MessageForm.tsx b/src/components/ChatWindow/MessageForm/MessageForm.tsx index 0084f51..d71b274 100644 --- a/src/components/ChatWindow/MessageForm/MessageForm.tsx +++ b/src/components/ChatWindow/MessageForm/MessageForm.tsx @@ -1,6 +1,9 @@ import React, { useState, useEffect } from "react"; import { hot } from "react-hot-loader/root"; import styled from "styled-components" +import { sendTextMessage, editTextMessage } from "../../../util/message.util"; +import { useParams } from "react-router-dom"; +import { RealtimeAPIMessage } from '../../../interfaces/message'; const Container = styled.div` position: fixed; @@ -25,11 +28,45 @@ const TextInput = styled.textarea` ` -function MessageForm() { +function MessageForm(props: any) { + const { id: roomId } = useParams(); + const [message, setMessage] = useState(""); + + const onMessageChange = (e: React.ChangeEvent) => { + setMessage(e.target.value); + } + + const onPressSendMessage = async (e: React.KeyboardEvent) => { + if(e.keyCode == 13 && e.shiftKey == false) { + e.preventDefault(); + if(await sendMessage()){ + setMessage(""); + } + } + } + + const sendMessage = async () => { + // Send New Message + if(!props.messageToEdit){ + return await sendTextMessage(roomId, message); + } + + // Edit Existing Message + const messageToEdit: RealtimeAPIMessage = props.messageToEdit; + props.setMessageToEdit(null); + return await editTextMessage(messageToEdit, message); + } + + useEffect(() => { + if(props.messageToEdit){ + setMessage(props.messageToEdit.msg); + } + }, [props.messageToEdit]); + return ( - - - + + + ); } diff --git a/src/components/ChatWindow/MessageList/MessageList.tsx b/src/components/ChatWindow/MessageList/MessageList.tsx index 414f2e1..c02d452 100644 --- a/src/components/ChatWindow/MessageList/MessageList.tsx +++ b/src/components/ChatWindow/MessageList/MessageList.tsx @@ -27,15 +27,14 @@ const ShowMoreMessages = styled.div` function MessageList(props : any) { const messages = props.messages; - return ( Show More Messages - {messages ? messages.map((message: RealtimeAPIMessage) => { + {messages ? Object.keys(messages).map((messageId: string) => { return ( - + ); }) : null} diff --git a/src/components/ChatWindow/MessageList/MessageRow/MessageRow.tsx b/src/components/ChatWindow/MessageList/MessageRow/MessageRow.tsx index cf09b82..518b7a7 100644 --- a/src/components/ChatWindow/MessageList/MessageRow/MessageRow.tsx +++ b/src/components/ChatWindow/MessageList/MessageRow/MessageRow.tsx @@ -1,9 +1,11 @@ import React, { useState, useEffect, MouseEvent } from "react"; import { hot } from "react-hot-loader/root"; import styled from "styled-components"; -import { RealtimeAPIMessage } from '../../../../interfaces/message'; -import { getRoomAvatar } from '../../../../util/chatsList.util'; -import MessageBodyRender from './components/MessageBodyRender'; +import { RealtimeAPIMessage } from "../../../../interfaces/message"; +import { getRoomAvatar } from "../../../../util/chatsList.util"; +import MessageBodyRender from "./components/MessageBodyRender"; +import MessageActions from "./components/MessageActions/MessageActions"; +import { deleteMessageById } from "../../../../util/message.util"; const MessageContainer = styled.div` width:100%; display: flex; @@ -56,16 +58,39 @@ const onChannelMentionClick = (channel: string) => (e: MouseEvent { + props.onEditMessageAction(message); + } + + const deleteMessage = async () => { + await deleteMessageById(message._id); + } + + const showActionsModal = () => { + setActionsModal(true); + } + + const hideActionsModal = () => { + setActionsModal(false); + } + return ( - +
{message.u.name}
{message.u.username}
{messageDate.getHours() + ":" + messageDate.getMinutes()}
+