Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,12 @@ export const useCreateChannelStateContext = (
const notificationsLength = notifications.length;
const readUsers = Object.values(read);
const readUsersLength = readUsers.length;
const readUsersLastReads = readUsers
.map(({ last_read }) => last_read.toISOString())
.join();
const readUsersLastReadDateStrings: string[] = [];
for (const { last_read } of readUsers) {
if (!lastRead) continue;
readUsersLastReadDateStrings.push(last_read?.toISOString());
}
const readUsersLastReads = readUsersLastReadDateStrings.join();
const threadMessagesLength = threadMessages?.length;

const channelCapabilities: Record<string, boolean> = {};
Expand Down
2 changes: 2 additions & 0 deletions src/components/Message/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ export const Message = (props: MessageProps) => {
handleRetry={handleRetry}
highlighted={highlighted}
initialMessage={props.initialMessage}
lastOwnMessage={props.lastOwnMessage}
lastReceivedId={props.lastReceivedId}
message={message}
Message={props.Message}
Expand All @@ -302,6 +303,7 @@ export const Message = (props: MessageProps) => {
reactionDetailsSort={reactionDetailsSort}
readBy={props.readBy}
renderText={props.renderText}
returnAllReadData={props.returnAllReadData}
sortReactionDetails={sortReactionDetails}
sortReactions={sortReactions}
threadList={props.threadList}
Expand Down
18 changes: 15 additions & 3 deletions src/components/Message/MessageStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,15 @@ const UnMemoizedMessageStatus = (props: MessageStatusProps) => {

const { client } = useChatContext('MessageStatus');
const { Avatar: contextAvatar } = useComponentContext('MessageStatus');
const { deliveredTo, isMyMessage, message, readBy, threadList } =
useMessageContext('MessageStatus');
const {
deliveredTo,
isMyMessage,
lastOwnMessage,
message,
readBy,
returnAllReadData,
threadList,
} = useMessageContext('MessageStatus');
const { t } = useTranslationContext('MessageStatus');
const [referenceElement, setReferenceElement] = useState<HTMLSpanElement | null>(null);

Expand All @@ -64,7 +71,12 @@ const UnMemoizedMessageStatus = (props: MessageStatusProps) => {
const sending = message.status === 'sending';
const read = !!(readBy?.length && !justReadByMe && !threadList);
const delivered = !!(deliveredTo?.length && !deliveredOnlyToMe && !read && !threadList);
const sent = message.status === 'received' && !delivered && !read && !threadList;
const sent =
(returnAllReadData || lastOwnMessage?.id === message.id) &&
message.status === 'received' &&
!delivered &&
!read &&
!threadList;

const readersWithoutOwnUser = read
? readBy.filter((item) => item.id !== client.user?.id)
Expand Down
215 changes: 125 additions & 90 deletions src/components/Message/__tests__/MessageSimple.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,111 +354,146 @@ describe('<MessageSimple />', () => {
expect(results).toHaveNoViolations();
});

it('should render no status when message not from the current user', async () => {
const message = generateBobMessage();
const { container, queryByTestId } = await renderMessageSimple({ message });
expect(queryByTestId(/message-status/)).not.toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
describe('delivery status', () => {
it('should render no status when message not from the current user', async () => {
const message = generateBobMessage();
const { container, queryByTestId } = await renderMessageSimple({ message });
expect(queryByTestId(/message-status/)).not.toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});

it('should not render status when message is an error message', async () => {
const message = generateAliceMessage({ type: 'error' });
const { container, queryByTestId } = await renderMessageSimple({
message,
props: {
readBy: [alice, bob],
},
});
expect(queryByTestId(/message-status/)).not.toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});

it('should not render status when message is an error message', async () => {
const message = generateAliceMessage({ type: 'error' });
const { container, queryByTestId } = await renderMessageSimple({ message });
expect(queryByTestId(/message-status/)).not.toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
it('should render sending status when sending message', async () => {
const message = generateAliceMessage({ status: 'sending' });
const { container, getByTestId } = await renderMessageSimple({ message });
expect(getByTestId('message-status-sending')).toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});

it('should render sending status when sending message', async () => {
const message = generateAliceMessage({ status: 'sending' });
const { container, getByTestId } = await renderMessageSimple({ message });
expect(getByTestId('message-status-sending')).toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
it('should render the "read by" status when the message is not part of a thread and was read by another chat members', async () => {
const message = generateAliceMessage();
const { container, getByTestId } = await renderMessageSimple({
message,
props: {
readBy: [alice, bob],
},
});
expect(getByTestId('message-status-read-by')).toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});

it('should render the "read by" status when the message is not part of a thread and was read by another chat members', async () => {
const message = generateAliceMessage();
const { container, getByTestId } = await renderMessageSimple({
message,
props: {
readBy: [alice, bob],
},
it('should render the "read by many" status when the message is not part of a thread and was read by more than one other chat members', async () => {
const message = generateAliceMessage();
const { container, getByTestId } = await renderMessageSimple({
message,
props: {
readBy: [alice, bob, carol],
},
});
expect(getByTestId('message-status-read-by-many')).toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
expect(getByTestId('message-status-read-by')).toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});

it('should render the "read by many" status when the message is not part of a thread and was read by more than one other chat members', async () => {
const message = generateAliceMessage();
const { container, getByTestId } = await renderMessageSimple({
message,
props: {
readBy: [alice, bob, carol],
},
it('should render a sent status when the message has status "received" and was not delivered to others and returnAllReadData=true', async () => {
const message = generateAliceMessage({ status: 'received' });
const { container, getByTestId } = await renderMessageSimple({
message,
props: {
deliveredTo: [alice],
returnAllReadData: true,
},
});
expect(getByTestId('message-status-sent')).toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
expect(getByTestId('message-status-read-by-many')).toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});

it('should render a sent status when the message has status "received" and was not delivered to others', async () => {
const message = generateAliceMessage({ status: 'received' });
const { container, getByTestId } = await renderMessageSimple({
message,
props: {
deliveredTo: [alice],
},
it('should not render sent status when the message is not lastOwnMessage and returnAllReadData=false', async () => {
const message = generateAliceMessage({ status: 'received' });
const { container } = await renderMessageSimple({
message,
props: {
deliveredTo: [alice],
lastOwnMessage: generateAliceMessage({ status: 'received' }),
},
});
expect(screen.queryByTestId('message-status-sent')).not.toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
expect(getByTestId('message-status-sent')).toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});

it('should render a delivered status when the message was delivered to others but not read', async () => {
const message = generateAliceMessage({ status: 'received' });
const { container, getByTestId } = await renderMessageSimple({
message,
props: {
deliveredTo: [alice, bob],
},
it('should render sent status when the message is not lastOwnMessage and returnAllReadData=false', async () => {
const message = generateAliceMessage({ status: 'received' });
const { container } = await renderMessageSimple({
message,
props: {
deliveredTo: [alice],
lastOwnMessage: message,
},
});
expect(screen.queryByTestId('message-status-sent')).toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
expect(getByTestId('message-status-delivered')).toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});

it('should not render status when rendered in a thread list and was delivered to other members', async () => {
const message = generateAliceMessage();
const { container, queryByTestId } = await renderMessageSimple({
message,
props: {
deliveredTo: [alice, bob],
readBy: [alice],
threadList: true,
},
it('should render a delivered status when the message was delivered to others but not read', async () => {
const message = generateAliceMessage({ status: 'received' });
const { container, getByTestId } = await renderMessageSimple({
message,
props: {
deliveredTo: [alice, bob],
},
});
expect(getByTestId('message-status-delivered')).toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
expect(queryByTestId(/message-status/)).not.toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});

it('should not render status when rendered in a thread list and was read by other members', async () => {
const message = generateAliceMessage();
const { container, queryByTestId } = await renderMessageSimple({
message,
props: {
readBy: [alice, bob, carol],
threadList: true,
},
it('should not render status when rendered in a thread list and was delivered to other members', async () => {
const message = generateAliceMessage();
const { container, queryByTestId } = await renderMessageSimple({
message,
props: {
deliveredTo: [alice, bob],
readBy: [alice],
threadList: true,
},
});
expect(queryByTestId(/message-status/)).not.toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
expect(queryByTestId(/message-status/)).not.toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});

it('should not render status when rendered in a thread list and was read by other members', async () => {
const message = generateAliceMessage();
const { container, queryByTestId } = await renderMessageSimple({
message,
props: {
readBy: [alice, bob, carol],
threadList: true,
},
});
expect(queryByTestId(/message-status/)).not.toBeInTheDocument();
const results = await axe(container);
expect(results).toHaveNoViolations();
});
});
it("should render the message user's avatar", async () => {
const message = generateBobMessage();
const { container } = await renderMessageSimple({
Expand Down
Loading