diff --git a/src/components/Channel/__tests__/Channel.test.js b/src/components/Channel/__tests__/Channel.test.js
index 2295e690e1..75508f28eb 100644
--- a/src/components/Channel/__tests__/Channel.test.js
+++ b/src/components/Channel/__tests__/Channel.test.js
@@ -1,5 +1,5 @@
import React, { useEffect } from 'react';
-import { fireEvent, render, waitFor } from '@testing-library/react';
+import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import { Channel } from '../Channel';
@@ -22,12 +22,16 @@ import {
threadRepliesApi,
useMockedApis,
} from '../../../mock-builders';
+import { MessageList } from '../../MessageList';
+import { Thread } from '../../Thread';
jest.mock('../../Loading', () => ({
LoadingErrorIndicator: jest.fn(() =>
),
LoadingIndicator: jest.fn(() => loading
),
}));
+const MockAvatar = ({ user }) => {user.custom}
;
+
let chatClient;
let channel;
@@ -63,12 +67,12 @@ const ActiveChannelSetter = ({ activeChannel }) => {
return null;
};
-const user = generateUser({ id: 'id', name: 'name' });
+const user = generateUser({ custom: 'custom-value', id: 'id', name: 'name' });
// create a full message state so we can properly test `loadMore`
const messages = [];
for (let i = 0; i < 25; i++) {
- messages.push(generateMessage());
+ messages.push(generateMessage({ user }));
}
const pinnedMessages = [generateMessage({ pinned: true, user })];
@@ -110,7 +114,7 @@ describe('Channel', () => {
});
it('should render the EmptyPlaceholder prop if the channel is not provided by the ChatContext', () => {
- const { getByText } = render(empty}>);
+ const { getByText } = render(empty} />);
expect(getByText('empty')).toBeInTheDocument();
});
@@ -741,6 +745,100 @@ describe('Channel', () => {
await waitFor(() => expect(newThreadMessageWasAdded).toBe(true));
});
+
+ it('should update user data in MessageList based on updated_at', async () => {
+ const updatedAttribute = { custom: 'newCustomValue' };
+ const dispatchUserUpdatedEvent = createChannelEventDispatcher(
+ {
+ user: { ...user, ...updatedAttribute, updated_at: new Date().toISOString() },
+ },
+ 'user.updated',
+ );
+ renderComponent({ Avatar: MockAvatar, children: });
+
+ await waitFor(() =>
+ expect(screen.queryByText(updatedAttribute.custom)).not.toBeInTheDocument(),
+ );
+ act(() => {
+ dispatchUserUpdatedEvent();
+ });
+ await waitFor(() =>
+ expect(screen.queryAllByText(updatedAttribute.custom).length).toBeGreaterThan(0),
+ );
+ });
+
+ it('should not update user data in MessageList if updated_at has not changed', async () => {
+ const updatedAttribute = { custom: 'newCustomValue' };
+ const dispatchUserUpdatedEvent = createChannelEventDispatcher(
+ {
+ user: { ...user, ...updatedAttribute },
+ },
+ 'user.updated',
+ );
+ renderComponent({ Avatar: MockAvatar, children: });
+
+ await waitFor(() =>
+ expect(screen.queryByText(updatedAttribute.custom)).not.toBeInTheDocument(),
+ );
+ act(() => {
+ dispatchUserUpdatedEvent();
+ });
+ await waitFor(() =>
+ expect(screen.queryByText(updatedAttribute.custom)).not.toBeInTheDocument(),
+ );
+ });
+
+ it('should update user data in Thread if updated_at has changed', async () => {
+ const threadMessage = messages[0];
+ const updatedAttribute = { custom: 'newCustomValue' };
+ const dispatchUserUpdatedEvent = createChannelEventDispatcher(
+ {
+ user: { ...user, ...updatedAttribute, updated_at: new Date().toISOString() },
+ },
+ 'user.updated',
+ );
+ renderComponent({ Avatar: MockAvatar, children: }, ({ openThread, thread }) => {
+ if (!thread) {
+ openThread(threadMessage, { preventDefault: () => null });
+ }
+ });
+
+ await waitFor(() =>
+ expect(screen.queryByText(updatedAttribute.custom)).not.toBeInTheDocument(),
+ );
+ act(() => {
+ dispatchUserUpdatedEvent();
+ });
+ await waitFor(() =>
+ expect(screen.queryAllByText(updatedAttribute.custom).length).toBeGreaterThan(0),
+ );
+ });
+
+ it('should not update user data in Thread if updated_at has not changed', async () => {
+ const threadMessage = messages[0];
+ const updatedAttribute = { custom: 'newCustomValue' };
+ const dispatchUserUpdatedEvent = createChannelEventDispatcher(
+ {
+ user: { ...user, ...updatedAttribute },
+ },
+ 'user.updated',
+ );
+ renderComponent({ Avatar: MockAvatar, children: }, ({ openThread, thread }) => {
+ if (!thread) {
+ openThread(threadMessage, { preventDefault: () => null });
+ }
+ });
+
+ await waitFor(() =>
+ expect(screen.queryByText(updatedAttribute.custom)).not.toBeInTheDocument(),
+ );
+ act(() => {
+ dispatchUserUpdatedEvent();
+ });
+ await waitFor(() =>
+ expect(screen.queryByText(updatedAttribute.custom)).not.toBeInTheDocument(),
+ );
+ });
});
});
});
diff --git a/src/components/Channel/hooks/useCreateChannelStateContext.ts b/src/components/Channel/hooks/useCreateChannelStateContext.ts
index d33c9c7be7..d8ae17f739 100644
--- a/src/components/Channel/hooks/useCreateChannelStateContext.ts
+++ b/src/components/Channel/hooks/useCreateChannelStateContext.ts
@@ -73,7 +73,7 @@ export const useCreateChannelStateContext = <
updated_at && (isDayOrMoment(updated_at) || isDate(updated_at))
? updated_at.toISOString()
: updated_at || ''
- }${user?.image}${user?.name}`,
+ }${user?.updated_at}`,
)
.join();
@@ -86,7 +86,7 @@ export const useCreateChannelStateContext = <
updated_at && (isDayOrMoment(updated_at) || isDate(updated_at))
? updated_at.toISOString()
: updated_at || ''
- }${user?.image}${user?.name}`,
+ }${user?.updated_at}`,
)
.join();
diff --git a/src/components/ChannelPreview/ChannelPreview.tsx b/src/components/ChannelPreview/ChannelPreview.tsx
index 5977c101b9..175924ad90 100644
--- a/src/components/ChannelPreview/ChannelPreview.tsx
+++ b/src/components/ChannelPreview/ChannelPreview.tsx
@@ -58,11 +58,12 @@ export const ChannelPreview = <
props: ChannelPreviewProps,
) => {
const { channel, Preview = ChannelPreviewMessenger, channelUpdateCount } = props;
-
const { channel: activeChannel, client, setActiveChannel } = useChatContext(
'ChannelPreview',
);
const { t, userLanguage } = useTranslationContext('ChannelPreview');
+ const [displayTitle, setDisplayTitle] = useState(getDisplayTitle(channel, client.user));
+ const [displayImage, setDisplayImage] = useState(getDisplayImage(channel, client.user));
const [lastMessage, setLastMessage] = useState>(
channel.state.messages[channel.state.messages.length - 1],
@@ -109,10 +110,26 @@ export const ChannelPreview = <
};
}, [refreshUnreadCount, channelUpdateCount]);
+ useEffect(() => {
+ const handleEvent = () => {
+ setDisplayTitle((displayTitle) => {
+ const newDisplayTitle = getDisplayTitle(channel, client.user);
+ return displayTitle !== newDisplayTitle ? newDisplayTitle : displayTitle;
+ });
+ setDisplayImage((displayImage) => {
+ const newDisplayImage = getDisplayImage(channel, client.user);
+ return displayImage !== newDisplayImage ? newDisplayImage : displayImage;
+ });
+ };
+
+ client.on('user.updated', handleEvent);
+ return () => {
+ client.off('user.updated', handleEvent);
+ };
+ }, []);
+
if (!Preview) return null;
- const displayImage = getDisplayImage(channel, client.user);
- const displayTitle = getDisplayTitle(channel, client.user);
const latestMessage = getLatestMessagePreview(channel, t, userLanguage);
return (
diff --git a/src/components/ChannelPreview/__tests__/ChannelPreview.test.js b/src/components/ChannelPreview/__tests__/ChannelPreview.test.js
index a776bea214..9c6af751a5 100644
--- a/src/components/ChannelPreview/__tests__/ChannelPreview.test.js
+++ b/src/components/ChannelPreview/__tests__/ChannelPreview.test.js
@@ -1,22 +1,27 @@
import React from 'react';
-import { act, render, waitFor } from '@testing-library/react';
+import { act, render, screen, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
+import { ChannelPreview } from '../ChannelPreview';
+import { Chat } from '../../Chat';
+
+import { ChatContext } from '../../../context/ChatContext';
+
import {
dispatchMessageDeletedEvent,
dispatchMessageNewEvent,
dispatchMessageUpdatedEvent,
+ dispatchUserUpdatedEvent,
generateChannel,
+ generateMember,
generateMessage,
+ generateUser,
getRandomInt,
getTestClientWithUser,
queryChannelsApi,
useMockedApis,
} from 'mock-builders';
-import { ChatContext } from '../../../context';
-import { ChannelPreview } from '../ChannelPreview';
-
const PreviewUIComponent = (props) => (
<>
{props.channel.id}
@@ -131,7 +136,7 @@ describe('ChannelPreview', () => {
const { getByTestId } = renderComponent(
{
- activeChannel: c1,
+ activteChannel: c1,
channel: c0,
},
render,
@@ -218,4 +223,103 @@ describe('ChannelPreview', () => {
await expectUnreadCountToBe(getByTestId, 0);
});
});
+
+ describe('user.updated', () => {
+ let chatClient;
+ let channels;
+ let channelState;
+ let otherUser;
+ const MockAvatar = ({ image, name }) => (
+ <>
+ {name}
+ {image}
+ >
+ );
+
+ const channelPreviewProps = {
+ Avatar: MockAvatar,
+ };
+
+ beforeEach(async () => {
+ const activeUser = generateUser({
+ custom: 'custom1',
+ id: 'id1',
+ image: 'image1',
+ name: 'name1',
+ });
+ otherUser = generateUser({
+ custom: 'custom2',
+ id: 'id2',
+ image: 'image2',
+ name: 'name2',
+ });
+ channelState = generateChannel({
+ members: [generateMember({ user: activeUser }), generateMember({ user: otherUser })],
+ messages: [generateMessage({ user: activeUser }), generateMessage({ user: otherUser })],
+ });
+ chatClient = await getTestClientWithUser(activeUser);
+ useMockedApis(chatClient, [queryChannelsApi([channelState])]);
+ channels = await chatClient.queryChannels();
+ });
+
+ it("should update the direct messaging channel's preview if other user's name has changed", async () => {
+ const updatedAttribute = { name: 'new-name' };
+ const channel = channels[0];
+ render(
+
+
+ ,
+ );
+
+ await waitFor(() =>
+ expect(screen.queryByText(updatedAttribute.name)).not.toBeInTheDocument(),
+ );
+ act(() => {
+ dispatchUserUpdatedEvent(chatClient, { ...otherUser, ...updatedAttribute });
+ });
+ await waitFor(() =>
+ expect(screen.queryAllByText(updatedAttribute.name).length).toBeGreaterThan(0),
+ );
+ });
+
+ it("should update the direct messaging channel's preview if other user's image has changed", async () => {
+ const updatedAttribute = { image: 'new-image' };
+ const channel = channels[0];
+ render(
+
+
+ ,
+ );
+
+ await waitFor(() =>
+ expect(screen.queryByText(updatedAttribute.image)).not.toBeInTheDocument(),
+ );
+ act(() => {
+ dispatchUserUpdatedEvent(chatClient, { ...otherUser, ...updatedAttribute });
+ });
+ await waitFor(() =>
+ expect(screen.queryAllByText(updatedAttribute.image).length).toBeGreaterThan(0),
+ );
+ });
+
+ it("should not update the direct messaging channel's preview if other user attribute than name or image has changed", async () => {
+ const updatedAttribute = { custom: 'new-custom' };
+ const channel = channels[0];
+ render(
+
+
+ ,
+ );
+
+ await waitFor(() =>
+ expect(screen.queryByText(updatedAttribute.custom)).not.toBeInTheDocument(),
+ );
+ act(() => {
+ dispatchUserUpdatedEvent(chatClient, { ...otherUser, ...updatedAttribute });
+ });
+ await waitFor(() =>
+ expect(screen.queryByText(updatedAttribute.custom)).not.toBeInTheDocument(),
+ );
+ });
+ });
});
diff --git a/src/components/Message/utils.tsx b/src/components/Message/utils.tsx
index 54baf14f7d..d534b0475a 100644
--- a/src/components/Message/utils.tsx
+++ b/src/components/Message/utils.tsx
@@ -246,8 +246,7 @@ export const areMessagePropsEqual = <
prevMessage.text === nextMessage.text &&
prevMessage.type === nextMessage.type &&
prevMessage.updated_at === nextMessage.updated_at &&
- prevMessage.user?.image === nextMessage.user?.image &&
- prevMessage.user?.name === nextMessage.user?.name;
+ prevMessage.user?.updated_at === nextMessage.user?.updated_at;
if (!messagesAreEqual) return false;
@@ -295,7 +294,7 @@ export const areMessageUIPropsEqual = <
return false;
}
- const messagesAreEqual =
+ return (
prevMessage.deleted_at === nextMessage.deleted_at &&
prevMessage.latest_reactions?.length === nextMessage.latest_reactions?.length &&
prevMessage.own_reactions?.length === nextMessage.own_reactions?.length &&
@@ -305,12 +304,8 @@ export const areMessageUIPropsEqual = <
prevMessage.text === nextMessage.text &&
prevMessage.type === nextMessage.type &&
prevMessage.updated_at === nextMessage.updated_at &&
- prevMessage.user?.image === nextMessage.user?.image &&
- prevMessage.user?.name === nextMessage.user?.name;
-
- if (!messagesAreEqual) return false;
-
- return true;
+ prevMessage.user?.updated_at === nextMessage.user?.updated_at
+ );
};
export const messageHasReactions = <
diff --git a/src/mock-builders/event/index.js b/src/mock-builders/event/index.js
index 9478c09af7..573883c13c 100644
--- a/src/mock-builders/event/index.js
+++ b/src/mock-builders/event/index.js
@@ -12,3 +12,4 @@ export { default as dispatchNotificationAddedToChannelEvent } from './notificati
export { default as dispatchNotificationMessageNewEvent } from './notificationMessageNew';
export { default as dispatchNotificationMutesUpdated } from './notificationMutesUpdated';
export { default as dispatchNotificationRemovedFromChannel } from './notificationRemovedFromChannel';
+export { default as dispatchUserUpdatedEvent } from './userUpdated';
diff --git a/src/mock-builders/event/userUpdated.js b/src/mock-builders/event/userUpdated.js
new file mode 100644
index 0000000000..6e88eea0f5
--- /dev/null
+++ b/src/mock-builders/event/userUpdated.js
@@ -0,0 +1,7 @@
+export default (client, user) => {
+ client.dispatchEvent({
+ created_at: new Date().toISOString(),
+ type: 'user.updated',
+ user,
+ });
+};