Skip to content

Commit

Permalink
refactor: move messageList to it's own component (#721)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dun-sin authored Nov 21, 2024
1 parent 331b5e0 commit e15f38a
Show file tree
Hide file tree
Showing 2 changed files with 271 additions and 227 deletions.
241 changes: 14 additions & 227 deletions client/src/components/Chat.jsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
/* eslint-disable max-len */

import { BsArrow90DegLeft, BsArrow90DegRight } from 'react-icons/bs';

import chatHelper, {
adjustTextareaHeight,
arrayBufferToBase64,
getTime,
arrayBufferToBase64,
pemToArrayBuffer,
} from '../lib/chatHelper';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';

import BadWordsNext from 'bad-words-next';
import DropDownOptions from './Chat/DropDownOption';
import Loading from './Loading';
import MarkdownIt from 'markdown-it';
import MessageInput from './Chat/MessageInput';
import MessageSeen from './Chat/MessageSeen';
import MessageStatus from './MessageStatus';
import PreviousMessages from './Chat/PreviousMessages';
import MessageList from './Chat/MessageList';
import ScrollToBottom from 'react-scroll-to-bottom';
import { createBrowserNotification } from 'src/lib/browserNotification';
import decryptMessage from 'src/lib/decryptMessage';
Expand Down Expand Up @@ -45,18 +38,14 @@ const Chat = () => {
});
const [message, setMessage] = useState('');

const [decryptedMessages, setDecryptedMessages] = useState();
// use the id so we can track what message's previousMessage is open
const [openPreviousMessages, setOpenPreviousMessages] = useState(null);
const [badwordChoices, setBadwordChoices] = useState({});
const [decryptedMessages, setDecryptedMessages] = useState();

const {
messages: state,
addMessage,
updateMessage,
removeMessage,
receiveMessage,
startReply,
receiveMessage,
currentReplyMessageId,
cancelReply,
} = useChat();
Expand All @@ -72,20 +61,14 @@ const Chat = () => {
const { logout } = useKindeAuth();

const { sendMessage, editMessage, emitTyping, setupSocketListeners } = useChatUtils(socket);
const { getMessage, handleResend, scrollToMessage } = chatHelper(state, app);
const { getMessage } = chatHelper(state, app);

const inputRef = useRef('');
const cryptoKeyRef = useRef(null);
cryptoKeyRef.current = cryptoKey;

senderId = authState.loginId;

const md = new MarkdownIt({
html: false,
linkify: true,
typographer: true,
});

const badwords = new BadWordsNext({ data: en });

function logOut() {
Expand Down Expand Up @@ -239,26 +222,6 @@ const Chat = () => {
e.target.style.height = `${e.target.scrollHeight}px`;
}, 500);

const openPreviousEdit = (messageId) => {
if (openPreviousMessages === messageId) {
setOpenPreviousMessages(null);
} else {
setOpenPreviousMessages(messageId);
}
};

const hideBadword = (id) => {
setBadwordChoices({ ...badwordChoices, [id]: 'hide' });
};

const showBadword = (id) => {
setBadwordChoices({ ...badwordChoices, [id]: 'show' });
};

function getRepliedMessage(replyTo) {
return decryptedMessages.find((object) => object.id === replyTo);
}

const onNewMessageHandler = useCallback(
async (message) => {
try {
Expand Down Expand Up @@ -391,191 +354,15 @@ const Chat = () => {
className="h-[100%] md:max-h-full overflow-y-auto w-full scroll-smooth"
>
{!messageIsDecrypting ? (
decryptedMessages &&
decryptedMessages.map(
({
senderId: sender,
id,
message,
time,
status,
isEdited,
oldMessages,
containsBadword,
isRead,
replyTo,
}) => {
const isSender = sender.toString() === senderId.toString();
// original message this message is a reply to
const repliedMessage = replyTo
? (() => {
const messageObj = getRepliedMessage(replyTo);
if (!messageObj) {
return null;
}

return {
...messageObj,
};
})()
: null;

// is this message currently being replied?
const hasActiveReply = currentReplyMessageId === id;
const activeReplyClass = hasActiveReply ? 'bg-[#FF9F1C]/25 border-[#FF9F1C]' : '';
const activeReplySenderClass = hasActiveReply
? isSender
? 'border-r-[3.5px]'
: 'border-l-[3.5px]'
: '';

return (
<div
key={id}
id={`message-${id}`}
className={`
flex flex-col gap-2 py-2 duration-500 transition-all
${activeReplyClass} ${activeReplySenderClass}`}
>
{replyTo && (
<div
className={`
max-w-[80%] md:max-w-[50%] min-w-[10px] flex gap-2 items-center
${sender.toString() === senderId.toString() ? 'self-end' : ''}
${repliedMessage ? 'cursor-pointer' : ''}
`}
onClick={() => scrollToMessage(replyTo)}
>
<div className="truncate border-b-4 border-[#FF9F1C] p-1">
{repliedMessage ? (
typeof repliedMessage.message === 'string' ? (
<div
className="message-reply-container flex flex-nowrap items-center gap-2"
dangerouslySetInnerHTML={{
__html: md.render(repliedMessage.message),
}}
/>
) : (
repliedMessage.message
)
) : (
<p className="text-gray-400 uppercase text-sm italic">
Original Message Deleted
</p>
)}
</div>
<div
className={sender.toString() !== senderId.toString() ? 'order-first' : ''}
>
{sender.toString() === senderId.toString() ? (
<BsArrow90DegLeft className="fill-white text-2xl" />
) : (
<BsArrow90DegRight className="fill-white text-2xl" />
)}
</div>
</div>
)}
<div
className={`w-full flex text-white relative mb-2 ${
isSender ? 'justify-end' : 'justify-start'
}`}
>
<div
className={`flex flex-col mb-[2px] min-w-[10px] mdl:max-w-[80%] max-w-[50%] ${
isSender ? 'items-end' : 'items-start'
}`}
>
{containsBadword && !isSender && !badwordChoices[id] ? (
<div className="dark:text-white text-black flex flex-col border-red border w-full rounded-r-md p-3">
<p>Your buddy is trying to send you a bad word</p>
<div className="flex w-full gap-6">
<span
onClick={() => showBadword(id)}
className="text-sm cursor-pointer"
>
See
</span>
<span
onClick={() => hideBadword(id)}
className="text-red text-sm cursor-pointer"
>
Hide
</span>
</div>
</div>
) : (
<>
<div
className={`chat bg-red p-3 break-all will-change-auto flex gap-6 items-center text ${
isSender
? 'justify-between bg-secondary rounded-l-md'
: 'rounded-r-md'
}`}
>
{typeof message === 'string' ? (
<span
dangerouslySetInnerHTML={{
__html: md.render(
badwordChoices[id] === 'hide'
? badwords.filter(message)
: badwordChoices[id] === 'show'
? message
: message
),
}}
/>
) : badwordChoices[id] === 'hide' ? (
badwords.filter(message)
) : badwordChoices[id] === 'show' ? (
message
) : (
message
)}

<DropDownOptions
isSender={isSender && status !== 'pending'}
id={id}
inputRef={inputRef}
cancelEdit={cancelEdit}
setEditing={setEditing}
setReplyId={startReply}
/>
</div>
<div
className={`flex gap-2 items-center ${
isSender ? 'flex-row' : 'flex-row-reverse'
}`}
>
<div
className={`text-[12px] ${
status === 'failed' ? 'text-red-600' : 'text-white'
}`}
>
<MessageStatus
time={getTime(time)}
status={status ?? 'sent'}
iAmTheSender={isSender}
onResend={() => handleResend(id, doSend, state, app)}
/>
</div>
<PreviousMessages
id={id}
isSender={isSender}
isEdited={isEdited}
openPreviousEdit={openPreviousEdit}
openPreviousMessages={openPreviousMessages}
oldMessages={oldMessages}
/>
</div>
<MessageSeen isRead={isRead} isSender={isSender} />
</>
)}
</div>
</div>
</div>
);
}
)
<MessageList
decryptedMessages={decryptedMessages}
senderId={senderId}
currentReplyMessageId={currentReplyMessageId}
doSend={doSend}
inputRef={inputRef}
cancelEdit={cancelEdit}
setEditing={setEditing}
/>
) : (
<div className="w-full h-full flex flex-col items-center justify-center">
<Loading loading={messageIsDecrypting} />
Expand Down
Loading

0 comments on commit e15f38a

Please sign in to comment.