Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add reply to message functionality #561

Merged
merged 10 commits into from
May 6, 2024
35 changes: 31 additions & 4 deletions packages/react/src/components/ChatInput/ChatInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { useToastBarDispatch } from '../../hooks/useToastBarDispatch';
import { Modal } from '../Modal';
import useSettingsStore from '../../store/settingsStore';
import ChatInfo from '../ChatInfo/ChatInfo';
import QuoteMessage from '../QuoteMessage/QuoteMessage';

const editingMessageCss = css`
background-color: #fff8e0;
Expand Down Expand Up @@ -103,13 +104,17 @@ const ChatInput = ({ scrollToBottom }) => {
const {
editMessage,
setEditMessage,
quoteMessage,
setQuoteMessage,
isRecordingMessage,
upsertMessage,
replaceMessage,
threadId,
} = useMessageStore((state) => ({
editMessage: state.editMessage,
setEditMessage: state.setEditMessage,
quoteMessage: state.quoteMessage,
setQuoteMessage: state.setQuoteMessage,
isRecordingMessage: state.isRecordingMessage,
upsertMessage: state.upsertMessage,
replaceMessage: state.replaceMessage,
Expand Down Expand Up @@ -157,6 +162,12 @@ const ChatInput = ({ scrollToBottom }) => {
}
};

const getMessageLink = async (id) => {
const host = await RCInstance.getHost();
const res = await RCInstance.channelInfo();
return `${host}/channel/${res.room.name}/?msg=${id}`;
};

const sendMessage = async (isAttachmentMode = false) => {
messageRef.current.focus();
messageRef.current.style.height = '44px';
Expand All @@ -183,7 +194,7 @@ const ChatInput = ({ scrollToBottom }) => {

if (!message.length || !isUserAuthenticated) {
messageRef.current.value = '';
if (editMessage.msg) {
if (editMessage.msg || editMessage.attachments) {
setEditMessage({});
}
return;
Expand All @@ -202,9 +213,19 @@ const ChatInput = ({ scrollToBottom }) => {
return;
}
}

messageRef.current.value = '';
const pendingMessage = createPendingMessage(message, user);
let pendingMessage = '';
if (quoteMessage.msg || quoteMessage.attachments) {
const msgLink = await getMessageLink(quoteMessage?._id);
pendingMessage = createPendingMessage(
`[ ](${msgLink})\n ${message}`,
user
);
setQuoteMessage({});
} else {
pendingMessage = createPendingMessage(message, user);
}

if (ECOptions.enableThreads && threadId) {
pendingMessage.tmid = threadId;
}
Expand Down Expand Up @@ -439,7 +460,10 @@ const ChatInput = ({ scrollToBottom }) => {
} else {
e.target.style.height = '150px';
}
} else if (editMessage.msg && e.keyCode === 27) {
} else if (
(editMessage.msg || editMessage.attachments) &&
e.keyCode === 27
) {
messageRef.current.value = '';
setDisableButton(true);
setEditMessage({});
Expand Down Expand Up @@ -490,6 +514,9 @@ const ChatInput = ({ scrollToBottom }) => {
return (
<Box className={`ec-chat-input ${classNames}`} style={styleOverrides}>
<Box>
{(quoteMessage.msg || quoteMessage.attachments) && (
<QuoteMessage message={quoteMessage} />
)}
<ChatInfo
status={
editMessage.msg || editMessage.attachments
Expand Down
15 changes: 15 additions & 0 deletions packages/react/src/components/Icon/icons/Quote.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';

const Quote = (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 32 32"
className="rcx-svg--directional"
fill="currentColor"
{...props}
>
<path d="M5 7C4.63419 7 4.29758 7.19974 4.12229 7.52081C3.947 7.84188 3.96101 8.23305 4.15882 8.54076L7.66834 14H5.5C4.94772 14 4.5 14.4477 4.5 15V24C4.5 24.5523 4.94772 25 5.5 25H13.5C14.0523 25 14.5 24.5523 14.5 24V15C14.5 14.8082 14.4449 14.6205 14.3412 14.4592L9.84118 7.45924C9.65718 7.17302 9.34026 7 9 7H5ZM10.3412 14.4592L6.83167 9H8.45405L12.5 15.2937V23H6.5V16H9.5C9.86581 16 10.2024 15.8003 10.3777 15.4792C10.553 15.1581 10.539 14.767 10.3412 14.4592ZM18 7C17.6342 7 17.2976 7.19974 17.1223 7.52081C16.947 7.84188 16.961 8.23305 17.1588 8.54076L20.6683 14H18.5C17.9477 14 17.5 14.4477 17.5 15V24C17.5 24.5523 17.9477 25 18.5 25H26.5C27.0523 25 27.5 24.5523 27.5 24V15C27.5 14.8082 27.4449 14.6205 27.3412 14.4592L22.8412 7.45924C22.6572 7.17302 22.3403 7 22 7H18ZM23.3412 14.4592L19.8317 9H21.4541L25.5 15.2937V23H19.5V16H22.5C22.8658 16 23.2024 15.8003 23.3777 15.4792C23.553 15.1581 23.539 14.767 23.3412 14.4592Z" />
</svg>
);

export default Quote;
2 changes: 2 additions & 0 deletions packages/react/src/components/Icon/icons/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import Online from './Online';
import Offline from './Offline';
import Away from './Away';
import Busy from './Busy';
import Quote from './Quote';

const icons = {
file: File,
Expand Down Expand Up @@ -108,6 +109,7 @@ const icons = {
offline: Offline,
away: Away,
busy: Busy,
quote: Quote,
};

export default icons;
4 changes: 4 additions & 0 deletions packages/react/src/components/Message/Message.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ const Message = ({
editMessage: state.editMessage,
setEditMessage: state.setEditMessage,
}));

const setQuoteMessage = useMessageStore((state) => state.setQuoteMessage);

const openThread = useMessageStore((state) => state.openThread);

const handleStarMessage = async (msg) => {
Expand Down Expand Up @@ -248,6 +251,7 @@ const Message = ({
setEditMessage(message);
}
}}
handleQuoteMessage={() => setQuoteMessage(message)}
handleEmojiClick={handleEmojiClick}
handlerReportMessage={() => {
setMessageToReport(message._id);
Expand Down
11 changes: 11 additions & 0 deletions packages/react/src/components/Message/MessageToolbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const MessageToolbox = ({
handleDeleteMessage,
handlerReportMessage,
handleEditMessage,
handleQuoteMessage,
isEditing = false,
...props
}) => {
Expand Down Expand Up @@ -98,6 +99,16 @@ export const MessageToolbox = ({
/>
</Tooltip>
) : null}

<Tooltip text="Quote" position="top">
<ActionButton
ghost
size="small"
icon="quote"
onClick={() => handleQuoteMessage(message)}
/>
</Tooltip>

<Tooltip
text={
message.starred &&
Expand Down
80 changes: 80 additions & 0 deletions packages/react/src/components/QuoteMessage/QuoteMessage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React, { useContext } from 'react';
import { css } from '@emotion/react';
import { format } from 'date-fns';
import { Box } from '../Box';
import useComponentOverrides from '../../theme/useComponentOverrides';
import RCContext from '../../context/RCInstance';
import { Avatar } from '../Avatar';
import { ActionButton } from '../ActionButton';
import { Icon } from '../Icon';
import { useMessageStore } from '../../store';

const QuoteMessageContainerCss = css`
margin: 1.25rem 0.5rem 0.25rem 0.5rem;
position: relative;
font-size: 0.85rem;
background-color: #f2f3f5;
padding: 0.5rem;
z-index: 100;
border: 0.5px solid currentColor;
border-radius: 0.15rem;
`;

const AvatarContainerCss = css`
padding: 0.25rem;
display: flex;
gap: 0.5rem;
`;

const MessageCss = css`
padding: 0.25rem;
`;

const ActionButtonCss = css`
position: absolute;
top: 0.25rem;
right: 0.25rem;
`;

const QuoteMessage = ({ className = '', style = {}, message }) => {
const { RCInstance } = useContext(RCContext);
const getUserAvatarUrl = (username) => {
const host = RCInstance.getHost();
const URL = `${host}/avatar/${username}`;
return URL;
};
const setQuoteMessage = useMessageStore((state) => state.setQuoteMessage);

const { classNames, styleOverrides } = useComponentOverrides('QuoteMessage');
return (
<Box
className={`ec-quote-msg ${className} ${classNames}`}
style={{ ...styleOverrides, ...style }}
css={QuoteMessageContainerCss}
>
<Box css={ActionButtonCss}>
<ActionButton ghost onClick={() => setQuoteMessage({})} size="small">
<Icon name="cross" size="0.75rem" />
</ActionButton>
</Box>
<Box css={AvatarContainerCss}>
<Avatar
url={getUserAvatarUrl(message?.u.username)}
alt="avatar"
size="1.5em"
/>
<Box>{message?.u.username}</Box>
<Box>{format(new Date(message.ts), 'h:mm a')}</Box>
</Box>
<Box css={MessageCss}>
{message.msg
? message.msg
: `${message.file?.name} (${
message.file?.size ? (message.file.size / 1024).toFixed(2) : 0
} kB)`}
</Box>
</Box>
);
};

export default QuoteMessage;
2 changes: 2 additions & 0 deletions packages/react/src/store/messageStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const useMessageStore = create((set, get) => ({
threadMessages: [],
filtered: false,
editMessage: {},
quoteMessage: {},
messageToReport: NaN,
showReportMessage: false,
isRecordingMessage: false,
Expand Down Expand Up @@ -65,6 +66,7 @@ const useMessageStore = create((set, get) => ({
}
},
setEditMessage: (editMessage) => set(() => ({ editMessage })),
setQuoteMessage: (quoteMessage) => set(() => ({ quoteMessage })),
setMessageToReport: (messageId) =>
set(() => ({ messageToReport: messageId })),
toggleShowReportMessage: () => {
Expand Down
1 change: 1 addition & 0 deletions packages/react/tools/icons-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const iconsList = [
'chevron-left',
'key',
'attachment',
'quote',
];
const svgDirPath = path.join(
__dirname,
Expand Down
Loading