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 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() {
- + - + ); 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()}
+
{ + 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); 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; 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 };