From cc78d20daa9e8d6702e02c26929e4b20fecca026 Mon Sep 17 00:00:00 2001 From: martincupela Date: Fri, 11 Oct 2024 18:00:37 +0200 Subject: [PATCH] fix: sync ChannelPreview's lastMessage with latestMessagePreview --- .../utility-components/channel-preview-ui.mdx | 14 +++- .../customization/channel-list-preview.mdx | 12 +-- .../ChannelPreview/ChannelPreview.tsx | 20 +++-- .../ChannelPreviewMessenger.tsx | 6 +- .../__tests__/ChannelPreview.test.js | 79 +++++++++++++++++-- .../__tests__/ChannelPreviewMessenger.test.js | 2 +- src/components/ChannelPreview/utils.tsx | 3 +- src/mock-builders/event/messageUndeleted.js | 10 +++ 8 files changed, 119 insertions(+), 27 deletions(-) create mode 100644 src/mock-builders/event/messageUndeleted.js diff --git a/docusaurus/docs/React/components/utility-components/channel-preview-ui.mdx b/docusaurus/docs/React/components/utility-components/channel-preview-ui.mdx index b71511a397..9cb52cdac1 100644 --- a/docusaurus/docs/React/components/utility-components/channel-preview-ui.mdx +++ b/docusaurus/docs/React/components/utility-components/channel-preview-ui.mdx @@ -18,12 +18,12 @@ For even deeper customization of the channel list and channel previews, use the To customize the `ChannelList` item UI simply pass your custom `Preview` component to the `ChannelList`. See [The Preview Prop Component](../../guides/customization/channel-list-preview.mdx#the-preview-prop-component) for the extended guide. ```tsx -const CustomChannelPreviewUI = ({ latestMessage, lastMessage }) => { +const CustomChannelPreviewUI = ({ latestMessagePreview, lastMessage }) => { // "lastMessage" property is for the last // message that has been interacted with (pinned/edited/deleted) - // to display last message of the channel use "latestMessage" property - return {latestMessage}; + // to display last message of the channel use "latestMessagePreview" property + return {latestMessagePreview}; }; ; @@ -105,6 +105,14 @@ The last message received in a channel. ### latestMessage +Deprecated, use `latestMessagePreview` instead. + +| Type | +| ----------------------- | +| `string \| JSX.Element` | + +### latestMessagePreview + Latest message preview to display. Will be either a string or a JSX.Element rendering markdown. | Type | diff --git a/docusaurus/docs/React/guides/customization/channel-list-preview.mdx b/docusaurus/docs/React/guides/customization/channel-list-preview.mdx index 445af9445a..93c556e045 100644 --- a/docusaurus/docs/React/guides/customization/channel-list-preview.mdx +++ b/docusaurus/docs/React/guides/customization/channel-list-preview.mdx @@ -55,12 +55,12 @@ Let's implement a simple custom preview: ```jsx -const CustomChannelPreview = ({ displayImage, displayTitle, latestMessage }) => ( +const CustomChannelPreview = ({ displayImage, displayTitle, latestMessagePreview }) => (
{displayTitle}
-
{latestMessage}
+
{latestMessagePreview}
); @@ -122,7 +122,7 @@ message in the channel: ```jsx const CustomChannelPreview = (props) => { - const { channel, displayImage, displayTitle, latestMessage } = props; + const { channel, displayImage, displayTitle, latestMessagePreview } = props; const { userLanguage } = useTranslationContext(); const latestMessageAt = channel.state.last_message_at; @@ -146,7 +146,7 @@ const CustomChannelPreview = (props) => { {timestamp} -
{latestMessage}
+
{latestMessagePreview}
); @@ -217,7 +217,7 @@ const CustomChannelPreview = (props) => { activeChannel, displayImage, displayTitle, - latestMessage, + latestMessagePreview, setActiveChannel, } = props; const latestMessageAt = channel.state.last_message_at; @@ -252,7 +252,7 @@ const CustomChannelPreview = (props) => { {timestamp} -
{latestMessage}
+
{latestMessagePreview}
); diff --git a/src/components/ChannelPreview/ChannelPreview.tsx b/src/components/ChannelPreview/ChannelPreview.tsx index 8b17bc555b..4c5366e0f5 100644 --- a/src/components/ChannelPreview/ChannelPreview.tsx +++ b/src/components/ChannelPreview/ChannelPreview.tsx @@ -29,8 +29,10 @@ export type ChannelPreviewUIComponentProps< displayTitle?: string; /** The last message received in a channel */ lastMessage?: StreamMessage; - /** Latest message preview to display, will be a string or JSX element supporting markdown. */ + /** @deprecated Use latestMessagePreview prop instead. */ latestMessage?: string | JSX.Element; + /** Latest message preview to display, will be a string or JSX element supporting markdown. */ + latestMessagePreview?: string | JSX.Element; /** Status describing whether own message has been delivered or read by another. If the last message is not an own message, then the status is undefined. */ messageDeliveryStatus?: MessageDeliveryStatus; /** Number of unread Messages */ @@ -123,26 +125,29 @@ export const ChannelPreview = < useEffect(() => { refreshUnreadCount(); - const handleEvent = (event: Event) => { - if (event.message) setLastMessage(event.message); + const handleEvent = () => { + setLastMessage(channel.state.messages[channel.state.messages.length - 1]); refreshUnreadCount(); }; channel.on('message.new', handleEvent); channel.on('message.updated', handleEvent); channel.on('message.deleted', handleEvent); + channel.on('message.undeleted', handleEvent); + channel.on('channel.truncated', handleEvent); return () => { channel.off('message.new', handleEvent); channel.off('message.updated', handleEvent); channel.off('message.deleted', handleEvent); + channel.off('message.undeleted', handleEvent); + channel.off('channel.truncated', handleEvent); }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [refreshUnreadCount, channelUpdateCount]); + }, [channel, refreshUnreadCount, channelUpdateCount]); if (!Preview) return null; - const latestMessage = getLatestMessagePreview(channel, t, userLanguage); + const latestMessagePreview = getLatestMessagePreview(channel, t, userLanguage); return ( )} -
{latestMessage}
+
+ {latestMessagePreview} +
); diff --git a/src/components/ChannelPreview/__tests__/ChannelPreview.test.js b/src/components/ChannelPreview/__tests__/ChannelPreview.test.js index b95e59416b..ccc882d9b0 100644 --- a/src/components/ChannelPreview/__tests__/ChannelPreview.test.js +++ b/src/components/ChannelPreview/__tests__/ChannelPreview.test.js @@ -8,6 +8,7 @@ import { Chat } from '../../Chat'; import { ChatContext } from '../../../context/ChatContext'; import { + dispatchChannelTruncatedEvent, dispatchMessageDeletedEvent, dispatchMessageNewEvent, dispatchMessageUpdatedEvent, @@ -24,11 +25,15 @@ import { useMockedApis, } from 'mock-builders'; +const EMPTY_CHANNEL_PREVIEW_TEXT = 'Empty channel'; + const PreviewUIComponent = (props) => ( <>
{props.channel.id}
{props.unread}
-
{props.lastMessage && props.lastMessage.text}
+
+ {props.lastMessage ? props.lastMessage.text : EMPTY_CHANNEL_PREVIEW_TEXT} +
); @@ -65,7 +70,12 @@ describe('ChannelPreview', () => { beforeEach(async () => { client = await getTestClientWithUser(user); - useMockedApis(client, [queryChannelsApi([generateChannel(), generateChannel()])]); + useMockedApis(client, [ + queryChannelsApi([ + generateChannel({ messages: Array.from({ length: 5 }, generateMessage) }), + generateChannel({ messages: Array.from({ length: 5 }, generateMessage) }), + ]), + ]); [c0, c1] = await client.queryChannels({}, {}); }); @@ -132,10 +142,11 @@ describe('ChannelPreview', () => { ['message.new', dispatchMessageNewEvent], ['message.updated', dispatchMessageUpdatedEvent], ['message.deleted', dispatchMessageDeletedEvent], + ['message.undeleted', dispatchMessageDeletedEvent], ]; describe.each(eventCases)('On %s event', (eventType, dispatcher) => { - it('should update lastMessage', async () => { + it('should update latest message preview', async () => { const newUnreadCount = getRandomInt(1, 10); c0.countUnread = () => newUnreadCount; @@ -151,9 +162,10 @@ describe('ChannelPreview', () => { expect(getByTestId('channel-id')).toBeInTheDocument(); }); - const message = generateMessage(); - act(() => { - dispatcher(client, message, c0); + const message = + eventType === 'message.new' ? generateMessage() : c0.state.messages.slice(-1)[0]; + await act(async () => { + await dispatcher(client, message, c0); }); await expectLastEventMessageToBe(getByTestId, message.text); @@ -233,6 +245,61 @@ describe('ChannelPreview', () => { }); }); + it('on channel.truncated event should update latest message preview', async () => { + const newUnreadCount = getRandomInt(1, 10); + c0.countUnread = () => newUnreadCount; + + const { getByTestId } = renderComponent( + { + activeChannel: c1, + channel: c0, + }, + render, + ); + + await waitFor(() => { + expect(getByTestId('channel-id')).toBeInTheDocument(); + }); + + await act(async () => { + await dispatchChannelTruncatedEvent(client, c0); + }); + + await expectLastEventMessageToBe(getByTestId, EMPTY_CHANNEL_PREVIEW_TEXT); + }); + + it.each([ + ['message.updated', dispatchMessageUpdatedEvent], + ['message.deleted', dispatchMessageDeletedEvent], + ['message.undeleted', dispatchMessageDeletedEvent], + ])( + 'on %s event should not update latest message preview for the non-last message', + async (_, dispatcher) => { + const newUnreadCount = getRandomInt(1, 10); + c0.countUnread = () => newUnreadCount; + + const { getByTestId } = renderComponent( + { + activeChannel: c1, + channel: c0, + }, + render, + ); + + await waitFor(() => { + expect(getByTestId('channel-id')).toBeInTheDocument(); + }); + + const lastMessage = c0.state.messages.slice(-1)[0]; + const penultimateMessage = c0.state.messages.slice(-2)[0]; + await act(async () => { + await dispatcher(client, penultimateMessage, c0); + }); + + await expectLastEventMessageToBe(getByTestId, lastMessage.text); + }, + ); + describe('notification.mark_read', () => { it('should set unread count to 0 for event missing CID', () => { const unreadCount = getRandomInt(1, 10); diff --git a/src/components/ChannelPreview/__tests__/ChannelPreviewMessenger.test.js b/src/components/ChannelPreview/__tests__/ChannelPreviewMessenger.test.js index 36447f8b2c..56b26c7d4f 100644 --- a/src/components/ChannelPreview/__tests__/ChannelPreviewMessenger.test.js +++ b/src/components/ChannelPreview/__tests__/ChannelPreviewMessenger.test.js @@ -30,7 +30,7 @@ describe('ChannelPreviewMessenger', () => { channel={channel} displayImage='https://randomimage.com/src.jpg' displayTitle='Channel name' - latestMessage='Latest message!' + latestMessagePreview='Latest message!' setActiveChannel={jest.fn()} unread={10} {...props} diff --git a/src/components/ChannelPreview/utils.tsx b/src/components/ChannelPreview/utils.tsx index 3efaa3a6f6..a8cc5bd8c5 100644 --- a/src/components/ChannelPreview/utils.tsx +++ b/src/components/ChannelPreview/utils.tsx @@ -32,8 +32,7 @@ export const getLatestMessagePreview = < } if (previewTextToRender) { - const renderedText = renderPreviewText(previewTextToRender); - return renderedText; + return renderPreviewText(previewTextToRender); } if (latestMessage.command) { diff --git a/src/mock-builders/event/messageUndeleted.js b/src/mock-builders/event/messageUndeleted.js new file mode 100644 index 0000000000..5407563fde --- /dev/null +++ b/src/mock-builders/event/messageUndeleted.js @@ -0,0 +1,10 @@ +export default (client, message, channel = {}) => { + const [channel_id, channel_type] = channel.cid.split(':'); + client.dispatchEvent({ + channel_id, + channel_type, + cid: channel.cid, + message, + type: 'message.undeleted', + }); +};