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: chat box message after order creation #8405

Merged
merged 12 commits into from
Sep 18, 2023
10 changes: 10 additions & 0 deletions packages/p2p/src/components/order-details/order-details.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import MyProfileSeparatorContainer from '../my-profile/my-profile-separator-cont
import { setDecimalPlaces, removeTrailingZeros, roundOffDecimal } from 'Utils/format-value';
import { getDateAfterHours } from 'Utils/date-time';
import { useModalManagerContext } from 'Components/modal-manager/modal-manager-context';
import ChatMessage, { admin_message } from 'Utils/chat-message';
import 'Components/order-details/order-details.scss';

const OrderDetails = observer(() => {
Expand Down Expand Up @@ -65,6 +66,11 @@ const OrderDetails = observer(() => {
} = order_store?.order_information;

const { chat_channel_url } = sendbird_store;
const should_send_admin_message =
buy_sell_store.is_create_order_subscribed &&
sendbird_store.chat_messages.length === 0 &&
sendbird_store.chat_channel_url &&
!sendbird_store.is_chat_loading;

const [should_expand_all, setShouldExpandAll] = React.useState(false);
const [remaining_review_time, setRemainingReviewTime] = React.useState(null);
Expand Down Expand Up @@ -196,6 +202,10 @@ const OrderDetails = observer(() => {
);
const rate_amount = removeTrailingZeros(formatMoney(local_currency, rate, true, 6));

if (should_send_admin_message) {
sendbird_store.sendMessage(admin_message, ChatMessage.TYPE_ADMIN);
}

return (
<OrderDetailsWrapper page_title={page_title}>
{should_show_lost_funds_banner && (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import ChatMessage, { admin_message } from 'Utils/chat-message';
import ChatMessages from '../chat-messages';

const mock_use_store_values = {
sendbird_store: {
active_chat_channel: {},
chat_info: {
user_id: 'test_user_id',
},
chat_messages: [
new ChatMessage({
created_at: 3,
channel_url: 'test',
file_type: '',
id: 1,
message: admin_message,
message_type: 'user',
name: 'test',
sender_user_id: 'test',
url: 'test',
status: 2,
custom_type: 'admin',
}),
],
onMessagesScroll: jest.fn(),
setMessagesRef: jest.fn(),
},
};

jest.mock('Stores', () => ({
...jest.requireActual('Stores'),
useStores: jest.fn(() => mock_use_store_values),
}));

describe('<ChatMessages />', () => {
it('should render the bot message with appropriate styles', () => {
render(<ChatMessages />);
const bot_message = screen.getByText(
/Hello! This is where you can chat with the counterparty to confirm the order details. Note: In case of a dispute, we'll use this chat as a reference./
);
expect(bot_message).toBeInTheDocument();
expect(screen.getByTestId('dt_chat_message')).toHaveClass('order-chat__messages-item--admin');
});
});
3 changes: 0 additions & 3 deletions packages/p2p/src/components/orders/chat/chat-footer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,6 @@ const ChatFooter = observer(() => {

return (
<React.Fragment>
<Text className='order-chat__footer-disclaimer' color='less-prominent' size='xxs'>
<Localize i18n_default_text='In case of a dispute, we will only consider the communication through Deriv P2P chat channel.' />
</Text>
<div
className={classNames('order-chat__footer', {
'order-chat__footer--empty': sendbird_store.chat_messages.length === 0,
Expand Down
4 changes: 0 additions & 4 deletions packages/p2p/src/components/orders/chat/chat-footer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
border-top: 2px solid var(--general-section-1);
max-height: 33.333%;

&-disclaimer {
margin: 0.8rem 2.4rem;
}

&--empty {
margin-top: auto;
}
Expand Down
11 changes: 9 additions & 2 deletions packages/p2p/src/components/orders/chat/chat-message-text.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import React from 'react';
import { Text } from '@deriv/components';
import PropTypes from 'prop-types';
import ChatMessage from 'Utils/chat-message';

const ChatMessageText = React.memo(({ children, color }) => (
const ChatMessageText = React.memo(({ children, color, type = '' }) => (
<div className={`order-chat__messages-item-message`}>
<Text as='div' color={color} line_height='m' size='xs'>
<Text
as='p'
color={color}
line_height={type === ChatMessage.TYPE_ADMIN ? 'xl' : 'm'}
size={type === ChatMessage.TYPE_ADMIN ? 'xxs' : 'xs'}
>
{children}
</Text>
</div>
Expand All @@ -14,6 +20,7 @@ ChatMessageText.displayName = 'ChatMessageText';
ChatMessageText.propTypes = {
children: PropTypes.any,
color: PropTypes.string,
type: PropTypes.string,
};

export default ChatMessageText;
24 changes: 17 additions & 7 deletions packages/p2p/src/components/orders/chat/chat-messages.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,13 @@ const ChatMessages = observer(() => {
onScroll={event => sendbird_store.onMessagesScroll(event)}
>
{sendbird_store.chat_messages.map(chat_message => {
const is_my_message = chat_message.sender_user_id === sendbird_store.chat_info.user_id;
const is_admin_message = chat_message.custom_type === ChatMessage.TYPE_ADMIN;
const is_my_message =
chat_message.sender_user_id === sendbird_store.chat_info.user_id && !is_admin_message;
const message_date = formatMilliseconds(chat_message.created_at, 'MMMM D, YYYY');
const message_color = is_my_message ? 'colored-background' : 'general';
const should_render_date = current_date !== message_date && Boolean((current_date = message_date));
const should_render_date =
current_date !== message_date && Boolean(!is_admin_message && (current_date = message_date));

return (
<React.Fragment key={chat_message.id}>
Expand All @@ -90,18 +93,25 @@ const ChatMessages = observer(() => {
<div
className={classNames(
'order-chat__messages-item',
`order-chat__messages-item--${is_my_message ? 'outgoing' : 'incoming'}`
`order-chat__messages-item--${
is_admin_message ? 'admin' : is_my_message ? 'outgoing' : 'incoming'
}`
)}
data-testid='dt_chat_message'
>
{chat_message.message_type === ChatMessage.TYPE_USER && (
<ChatMessageText color={message_color}>{chat_message.message}</ChatMessageText>
<ChatMessageText color={message_color} type={chat_message.custom_type}>
{chat_message.message}
</ChatMessageText>
)}
{chat_message.message_type === ChatMessage.TYPE_FILE &&
getMessageFormat(chat_message, message_color)}
<div className={`order-chat__messages-item-timestamp`}>
<Text color='less-prominent' line_height='s' size='xxxs'>
{formatMilliseconds(chat_message.created_at, 'HH:mm', true)}
</Text>
{!is_admin_message && (
<Text color='less-prominent' line_height='s' size='xxxs'>
{formatMilliseconds(chat_message.created_at, 'HH:mm', true)}
</Text>
)}
{is_my_message && (
<ChatMessageReceipt
message={chat_message}
Expand Down
8 changes: 8 additions & 0 deletions packages/p2p/src/components/orders/chat/chat-messages.scss
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,13 @@
border-bottom-right-radius: 0;
}
}

&--admin {
align-items: center;
background: var(--status-warning-transparent);
border: 1px solid var(--status-warning);
padding: 0.8rem 0;
border-radius: 0.8rem;
}
}
}
6 changes: 4 additions & 2 deletions packages/p2p/src/stores/sendbird-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ export default class SendbirdStore extends BaseStore {
const is_inclusive_of_timestamp = false;
const result_size = 50;
const reverse_results = false;
const custom_type = [''];
const custom_type = ['', 'admin'];

const messages_timestamp =
timestamp ?? toMoment(this.root_store.general_store.server_time.get()).utc().valueOf();
Expand Down Expand Up @@ -435,7 +435,7 @@ export default class SendbirdStore extends BaseStore {
});
}

sendMessage(message: string) {
sendMessage(message: string, custom_type = '') {
const modified_message = message.trim();

if (modified_message.length === 0) {
Expand All @@ -453,6 +453,7 @@ export default class SendbirdStore extends BaseStore {
message_type: MessageType.USER,
sender_user_id: this.chat_info.user_id,
status: ChatMessage.STATUS_PENDING,
custom_type,
};

this.addChannelMessage(new ChatMessage(placeholder_msg_options));
Expand All @@ -461,6 +462,7 @@ export default class SendbirdStore extends BaseStore {
?.sendUserMessage({
message: modified_message,
data: msg_identifier,
customType: custom_type,
})
.onSucceeded(channel_message => {
const msg_idx = this.chat_messages.findIndex(msg => msg.id === msg_identifier);
Expand Down
9 changes: 9 additions & 0 deletions packages/p2p/src/utils/chat-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { FileMessage, UserMessage } from '@sendbird/chat/message';
type TChatMessageArgs = {
created_at: number;
channel_url: string;
custom_type?: string;
id: string;
file_type?: string;
message?: string;
Expand All @@ -17,6 +18,7 @@ type TChatMessageArgs = {
export default class ChatMessage {
created_at: number;
channel_url: string;
custom_type?: string;
id: string;
file_type?: string;
message?: string;
Expand All @@ -30,6 +32,7 @@ export default class ChatMessage {
constructor({
created_at,
channel_url,
custom_type,
id,
file_type,
message,
Expand All @@ -42,6 +45,7 @@ export default class ChatMessage {
}: TChatMessageArgs) {
this.created_at = created_at;
this.channel_url = channel_url;
this.custom_type = custom_type;
this.file_type = file_type;
this.id = id;
this.message = message;
Expand All @@ -62,6 +66,7 @@ export default class ChatMessage {
// static STATUS_DELIVERED_TO_SERVER = 2;
// static STATUS_READ_BY_RECEIVER = 3;

static TYPE_ADMIN = 'admin';
static TYPE_USER = 'user';
static TYPE_FILE = 'file';
}
Expand All @@ -70,6 +75,7 @@ export const convertFromChannelMessage = (channel_message: UserMessage | FileMes
return new ChatMessage({
created_at: channel_message.createdAt,
channel_url: channel_message.channelUrl,
custom_type: channel_message.customType,
file_type: channel_message.isFileMessage() ? channel_message.type : undefined,
id: channel_message.messageId.toString(),
message: channel_message.isUserMessage() ? channel_message.message : undefined,
Expand All @@ -80,3 +86,6 @@ export const convertFromChannelMessage = (channel_message: UserMessage | FileMes
url: channel_message.isFileMessage() ? channel_message.url : undefined,
});
};

export const admin_message =
"Hello! This is where you can chat with the counterparty to confirm the order details.\nNote: In case of a dispute, we'll use this chat as a reference.";