Skip to content

Commit

Permalink
fix: sync ChannelPreview's lastMessage with latestMessagePreview
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinCupela committed Oct 11, 2024
1 parent c852fe4 commit cc78d20
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 <span>{latestMessage}</span>;
// to display last message of the channel use "latestMessagePreview" property
return <span>{latestMessagePreview}</span>;
};

<ChannelList Preview={CustomChannelPreviewUI} />;
Expand Down Expand Up @@ -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 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ Let's implement a simple custom preview:
<TabItem value="js" label="React">

```jsx
const CustomChannelPreview = ({ displayImage, displayTitle, latestMessage }) => (
const CustomChannelPreview = ({ displayImage, displayTitle, latestMessagePreview }) => (
<div className='channel-preview'>
<img className='channel-preview__avatar' src={displayImage} alt='' />
<div className='channel-preview__main'>
<div className='channel-preview__header'>{displayTitle}</div>
<div className='channel-preview__message'>{latestMessage}</div>
<div className='channel-preview__message'>{latestMessagePreview}</div>
</div>
</div>
);
Expand Down Expand Up @@ -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;

Expand All @@ -146,7 +146,7 @@ const CustomChannelPreview = (props) => {
{timestamp}
</time>
</div>
<div className='channel-preview__message'>{latestMessage}</div>
<div className='channel-preview__message'>{latestMessagePreview}</div>
</div>
</div>
);
Expand Down Expand Up @@ -217,7 +217,7 @@ const CustomChannelPreview = (props) => {
activeChannel,
displayImage,
displayTitle,
latestMessage,
latestMessagePreview,
setActiveChannel,
} = props;
const latestMessageAt = channel.state.last_message_at;
Expand Down Expand Up @@ -252,7 +252,7 @@ const CustomChannelPreview = (props) => {
{timestamp}
</time>
</div>
<div className='channel-preview__message'>{latestMessage}</div>
<div className='channel-preview__message'>{latestMessagePreview}</div>
</div>
</button>
);
Expand Down
20 changes: 13 additions & 7 deletions src/components/ChannelPreview/ChannelPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ export type ChannelPreviewUIComponentProps<
displayTitle?: string;
/** The last message received in a channel */
lastMessage?: StreamMessage<StreamChatGenerics>;
/** 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 */
Expand Down Expand Up @@ -123,26 +125,29 @@ export const ChannelPreview = <
useEffect(() => {
refreshUnreadCount();

const handleEvent = (event: Event<StreamChatGenerics>) => {
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 (
<Preview
Expand All @@ -151,7 +156,8 @@ export const ChannelPreview = <
displayImage={displayImage}
displayTitle={displayTitle}
lastMessage={lastMessage}
latestMessage={latestMessage}
latestMessage={latestMessagePreview}
latestMessagePreview={latestMessagePreview}
messageDeliveryStatus={messageDeliveryStatus}
setActiveChannel={setActiveChannel}
unread={unread}
Expand Down
6 changes: 4 additions & 2 deletions src/components/ChannelPreview/ChannelPreviewMessenger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const UnMemoizedChannelPreviewMessenger = <
className: customClassName = '',
displayImage,
displayTitle,
latestMessage,
latestMessagePreview,
onSelect: customOnSelectChannel,
setActiveChannel,
unread,
Expand Down Expand Up @@ -70,7 +70,9 @@ const UnMemoizedChannelPreviewMessenger = <
</div>
)}
</div>
<div className='str-chat__channel-preview-messenger--last-message'>{latestMessage}</div>
<div className='str-chat__channel-preview-messenger--last-message'>
{latestMessagePreview}
</div>
</div>
</button>
);
Expand Down
79 changes: 73 additions & 6 deletions src/components/ChannelPreview/__tests__/ChannelPreview.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Chat } from '../../Chat';
import { ChatContext } from '../../../context/ChatContext';

import {
dispatchChannelTruncatedEvent,
dispatchMessageDeletedEvent,
dispatchMessageNewEvent,
dispatchMessageUpdatedEvent,
Expand All @@ -24,11 +25,15 @@ import {
useMockedApis,
} from 'mock-builders';

const EMPTY_CHANNEL_PREVIEW_TEXT = 'Empty channel';

const PreviewUIComponent = (props) => (
<>
<div data-testid='channel-id'>{props.channel.id}</div>
<div data-testid='unread-count'>{props.unread}</div>
<div data-testid='last-event-message'>{props.lastMessage && props.lastMessage.text}</div>
<div data-testid='last-event-message'>
{props.lastMessage ? props.lastMessage.text : EMPTY_CHANNEL_PREVIEW_TEXT}
</div>
</>
);

Expand Down Expand Up @@ -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({}, {});
});
Expand Down Expand Up @@ -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;

Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
3 changes: 1 addition & 2 deletions src/components/ChannelPreview/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ export const getLatestMessagePreview = <
}

if (previewTextToRender) {
const renderedText = renderPreviewText(previewTextToRender);
return renderedText;
return renderPreviewText(previewTextToRender);
}

if (latestMessage.command) {
Expand Down
10 changes: 10 additions & 0 deletions src/mock-builders/event/messageUndeleted.js
Original file line number Diff line number Diff line change
@@ -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',
});
};

0 comments on commit cc78d20

Please sign in to comment.