From ba6b1a3d5bde657cbf6933ed9d58c3a16188159d Mon Sep 17 00:00:00 2001
From: MartinCupela <32706194+MartinCupela@users.noreply.github.com>
Date: Tue, 15 Oct 2024 13:09:55 +0200
Subject: [PATCH] fix: get correct lastMessage and latestMessagePreview from
 channel state (#2535)

---
 .../core-components/channel-list.mdx          |  30 +++--
 .../utility-components/channel-preview-ui.mdx |  22 +++-
 .../utility-components/channel-preview.mdx    |   8 ++
 .../customization/channel-list-preview.mdx    |  12 +-
 src/components/ChannelList/ChannelList.tsx    |  10 +-
 .../ChannelList/__tests__/ChannelList.test.js |  34 +++++-
 .../ChannelPreview/ChannelPreview.tsx         |  37 ++++--
 .../ChannelPreviewMessenger.tsx               |   6 +-
 .../__tests__/ChannelPreview.test.js          | 111 +++++++++++++++++-
 .../__tests__/ChannelPreviewMessenger.test.js |   2 +-
 src/components/ChannelPreview/utils.tsx       |   5 +-
 src/mock-builders/event/messageUndeleted.js   |  10 ++
 12 files changed, 241 insertions(+), 46 deletions(-)
 create mode 100644 src/mock-builders/event/messageUndeleted.js

diff --git a/docusaurus/docs/React/components/core-components/channel-list.mdx b/docusaurus/docs/React/components/core-components/channel-list.mdx
index 7c58fa1a7c..52b7e6110e 100644
--- a/docusaurus/docs/React/components/core-components/channel-list.mdx
+++ b/docusaurus/docs/React/components/core-components/channel-list.mdx
@@ -110,7 +110,7 @@ re-setting the list state, you can customize behavior and UI.
 | `channel.truncated`                                                                       | Updates the channel                              | [onChannelTruncated](#onchanneltruncated)     |
 | `channel.updated`                                                                         | Updates the channel                              | [onChannelUpdated](#onchannelupdated)         |
 | `channel.visible`                                                                         | Adds channel to list                             | [onChannelVisible](#onchannelvisible)         |
-| `connection.recovered`                                                                    | Forces a component render                        |  N/A                                          |
+| `connection.recovered`                                                                    | Forces a component render                        | N/A                                           |
 | `message.new`                                                                             | Moves channel to top of list                     | [onMessageNewHandler](#onmessagenewhandler)   |
 | `notification.added_to_channel`                                                           | Moves channel to top of list and starts watching | [onAddedToChannel](#onaddedtochannel)         |
 | `notification.message_new`                                                                | Moves channel to top of list and starts watching | [onMessageNew](#onmessagenew)                 |
@@ -225,13 +225,14 @@ Custom function that handles the channel pagination.
 Takes parameters:
 
 | Parameter         | Description                                                                                                                                                           |
-|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
 | `currentChannels` | The state of loaded `Channel` objects queried thus far. Has to be set with `setChannels` (see below).                                                                 |
 | `queryType`       | A string indicating, whether the channels state has to be reset to the first page ('reload') or newly queried channels should be appended to the `currentChannels`.   |
 | `setChannels`     | Function that allows us to set the channels state reflected in `currentChannels`.                                                                                     |
 | `setHasNextPage`  | Flag indicating whether there are more items to be loaded from the API. Should be infered from the comparison of the query result length and the query options limit. |
 
 The function has to:
+
 1. build / provide own query filters, sort and options parameters
 2. query and append channels to the current channels state
 3. update the `hasNext` pagination flag after each query with `setChannels` function
@@ -239,14 +240,11 @@ The function has to:
 An example below implements a custom query function that uses different filters sequentially once a preceding filter is exhausted:
 
 ```ts
-import uniqBy from "lodash.uniqby";
+import uniqBy from 'lodash.uniqby';
 import throttle from 'lodash.throttle';
-import {useCallback, useRef} from 'react';
-import {ChannelFilters, ChannelOptions, ChannelSort, StreamChat} from 'stream-chat';
-import {
-    CustomQueryChannelParams,
-    useChatContext,
-} from 'stream-chat-react';
+import { useCallback, useRef } from 'react';
+import { ChannelFilters, ChannelOptions, ChannelSort, StreamChat } from 'stream-chat';
+import { CustomQueryChannelParams, useChatContext } from 'stream-chat-react';
 
 const DEFAULT_PAGE_SIZE = 30 as const;
 
@@ -312,7 +310,7 @@ export const useCustomQueryChannels = () => {
 It is recommended to control for duplicate requests by throttling the custom function calls.
 
 | Type                                                                                              |
-|---------------------------------------------------------------------------------------------------|
+| ------------------------------------------------------------------------------------------------- |
 | <GHComponentLink text='CustomQueryChannelsFn' path='/ChannelList/hooks/usePaginatedChannels.ts'/> |
 
 ### EmptyStateIndicator
@@ -332,6 +330,14 @@ for more information.
 | ------ |
 | object |
 
+### getLatestMessagePreview
+
+Custom function that generates the message preview in ChannelPreview component.
+
+| Type                                                                                                                                  |
+| ------------------------------------------------------------------------------------------------------------------------------------- |
+| `(channel: Channel, t: TranslationContextValue['t'], userLanguage: TranslationContextValue['userLanguage']) => string \| JSX.Element` |
+
 ### List
 
 Custom UI component to display the container for the queried channels.
@@ -425,7 +431,7 @@ Function to override the default behavior when a message is received on a channe
 Function to override the default behavior when a message is received on a channel being watched. Handles `message.new` event.
 
 | Type                                                                                                                                |
-|-------------------------------------------------------------------------------------------------------------------------------------|
+| ----------------------------------------------------------------------------------------------------------------------------------- |
 | `(setChannels: React.Dispatch<React.SetStateAction<Array<Channel<StreamChatGenerics>>>>, event: Event<StreamChatGenerics>) => void` |
 
 ### onRemovedFromChannel
@@ -491,7 +497,7 @@ const App = () => (
 ```
 
 | Type   | Default |
-|--------|---------|
+| ------ | ------- |
 | number | 5000    |
 
 ### renderChannels
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..36ed82d36b 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 <span>{latestMessage}</span>;
+  // to display last message of the channel use "latestMessagePreview" property
+  return <span>{latestMessagePreview}</span>;
 };
 
 <ChannelList Preview={CustomChannelPreviewUI} />;
@@ -95,6 +95,14 @@ Title of channel to display.
 | -------- |
 | `string` |
 
+### getLatestMessagePreview
+
+Custom function that generates the message preview in ChannelPreview component.
+
+| Type                                                                                                                                  |
+| ------------------------------------------------------------------------------------------------------------------------------------- |
+| `(channel: Channel, t: TranslationContextValue['t'], userLanguage: TranslationContextValue['userLanguage']) => string \| JSX.Element` |
+
 ### lastMessage
 
 The last message received in a channel.
@@ -105,6 +113,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/components/utility-components/channel-preview.mdx b/docusaurus/docs/React/components/utility-components/channel-preview.mdx
index be38ec6059..e39e6a3b87 100644
--- a/docusaurus/docs/React/components/utility-components/channel-preview.mdx
+++ b/docusaurus/docs/React/components/utility-components/channel-preview.mdx
@@ -55,6 +55,14 @@ Custom class for the channel preview root
 | -------- |
 | `string` |
 
+### getLatestMessagePreview
+
+Custom function that generates the message preview in ChannelPreview component.
+
+| Type                                                                                                                                  |
+| ------------------------------------------------------------------------------------------------------------------------------------- |
+| `(channel: Channel, t: TranslationContextValue['t'], userLanguage: TranslationContextValue['userLanguage']) => string \| JSX.Element` |
+
 ### onSelect
 
 Custom handler invoked when the `ChannelPreview` is clicked. The SDK uses `ChannelPreview` to display items of channel search results. There, behind the scenes, the new active channel is set.
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:
 <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>
 );
@@ -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}
           </time>
         </div>
-        <div className='channel-preview__message'>{latestMessage}</div>
+        <div className='channel-preview__message'>{latestMessagePreview}</div>
       </div>
     </div>
   );
@@ -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}
           </time>
         </div>
-        <div className='channel-preview__message'>{latestMessage}</div>
+        <div className='channel-preview__message'>{latestMessagePreview}</div>
       </div>
     </button>
   );
diff --git a/src/components/ChannelList/ChannelList.tsx b/src/components/ChannelList/ChannelList.tsx
index 7b8c3066d6..0ce8c6075f 100644
--- a/src/components/ChannelList/ChannelList.tsx
+++ b/src/components/ChannelList/ChannelList.tsx
@@ -35,7 +35,7 @@ import { ChannelListContextProvider } from '../../context';
 import { useChatContext } from '../../context/ChatContext';
 
 import type { Channel, ChannelFilters, ChannelOptions, ChannelSort, Event } from 'stream-chat';
-
+import type { TranslationContextValue } from '../../context/TranslationContext';
 import type { DefaultStreamChatGenerics, PaginatorProps } from '../../types/types';
 
 const DEFAULT_FILTERS = {};
@@ -70,6 +70,12 @@ export type ChannelListProps<
   EmptyStateIndicator?: React.ComponentType<EmptyStateIndicatorProps>;
   /** An object containing channel query filters */
   filters?: ChannelFilters<StreamChatGenerics>;
+  /** Custom function that generates the message preview in ChannelPreview component */
+  getLatestMessagePreview?: (
+    channel: Channel<StreamChatGenerics>,
+    t: TranslationContextValue['t'],
+    userLanguage: TranslationContextValue['userLanguage'],
+  ) => string | JSX.Element;
   /** Custom UI component to display the container for the queried channels, defaults to and accepts same props as: [ChannelListMessenger](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelList/ChannelListMessenger.tsx) */
   List?: React.ComponentType<ChannelListMessengerProps<StreamChatGenerics>>;
   /** Custom UI component to display the loading error indicator, defaults to and accepts same props as: [ChatDown](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChatDown/ChatDown.tsx) */
@@ -168,6 +174,7 @@ const UnMemoizedChannelList = <
     customQueryChannels,
     EmptyStateIndicator = DefaultEmptyStateIndicator,
     filters,
+    getLatestMessagePreview,
     LoadingErrorIndicator = ChatDown,
     LoadingIndicator = LoadingChannels,
     List = ChannelListMessenger,
@@ -333,6 +340,7 @@ const UnMemoizedChannelList = <
       channel: item,
       // forces the update of preview component on channel update
       channelUpdateCount,
+      getLatestMessagePreview,
       key: item.cid,
       Preview,
       setActiveChannel,
diff --git a/src/components/ChannelList/__tests__/ChannelList.test.js b/src/components/ChannelList/__tests__/ChannelList.test.js
index d903ca077a..380d1e2186 100644
--- a/src/components/ChannelList/__tests__/ChannelList.test.js
+++ b/src/components/ChannelList/__tests__/ChannelList.test.js
@@ -60,11 +60,11 @@ const channelsQueryStateMock = {
  * to those components might end up breaking tests for ChannelList, which will be quite painful
  * to debug then.
  */
-const ChannelPreviewComponent = ({ channel, channelUpdateCount, latestMessage }) => (
+const ChannelPreviewComponent = ({ channel, channelUpdateCount, latestMessagePreview }) => (
   <div data-testid={channel.id} role='listitem'>
     <div data-testid='channelUpdateCount'>{channelUpdateCount}</div>
     <div>{channel.data.name}</div>
-    <div>{latestMessage}</div>
+    <div>{latestMessagePreview}</div>
   </div>
 );
 
@@ -457,6 +457,36 @@ describe('ChannelList', () => {
     });
   });
 
+  it('allows to customize latest message preview generation', async () => {
+    const previewText = 'custom preview text';
+    const getLatestMessagePreview = () => previewText;
+
+    useMockedApis(chatClient, [queryChannelsApi([testChannel1])]);
+    const { rerender } = render(
+      <Chat client={chatClient}>
+        <ChannelList filters={{}} options={{ limit: 2 }} />
+      </Chat>,
+    );
+
+    await waitFor(() => {
+      expect(screen.getByText('Nothing yet...')).toBeInTheDocument();
+    });
+
+    rerender(
+      <Chat client={chatClient}>
+        <ChannelList
+          filters={{}}
+          getLatestMessagePreview={getLatestMessagePreview}
+          options={{ limit: 2 }}
+        />
+      </Chat>,
+    );
+
+    await waitFor(() => {
+      expect(screen.getByText(previewText)).toBeInTheDocument();
+    });
+  });
+
   describe('Default and custom active channel', () => {
     let setActiveChannel;
     const watchersConfig = { limit: 20, offset: 0 };
diff --git a/src/components/ChannelPreview/ChannelPreview.tsx b/src/components/ChannelPreview/ChannelPreview.tsx
index 8b17bc555b..d368ae3a4a 100644
--- a/src/components/ChannelPreview/ChannelPreview.tsx
+++ b/src/components/ChannelPreview/ChannelPreview.tsx
@@ -4,7 +4,7 @@ import React, { useEffect, useMemo, useState } from 'react';
 import { ChannelPreviewMessenger } from './ChannelPreviewMessenger';
 import { useIsChannelMuted } from './hooks/useIsChannelMuted';
 import { useChannelPreviewInfo } from './hooks/useChannelPreviewInfo';
-import { getLatestMessagePreview } from './utils';
+import { getLatestMessagePreview as defaultGetLatestMessagePreview } from './utils';
 
 import { ChatContextValue, useChatContext } from '../../context/ChatContext';
 import { useTranslationContext } from '../../context/TranslationContext';
@@ -15,7 +15,7 @@ import type { Channel, Event } from 'stream-chat';
 import type { AvatarProps } from '../Avatar/Avatar';
 
 import type { StreamMessage } from '../../context/ChannelStateContext';
-
+import type { TranslationContextValue } from '../../context/TranslationContext';
 import type { DefaultStreamChatGenerics } from '../../types/types';
 
 export type ChannelPreviewUIComponentProps<
@@ -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 */
@@ -50,6 +52,12 @@ export type ChannelPreviewProps<
   channelUpdateCount?: number;
   /** Custom class for the channel preview root */
   className?: string;
+  /** Custom function that generates the message preview in ChannelPreview component */
+  getLatestMessagePreview?: (
+    channel: Channel<StreamChatGenerics>,
+    t: TranslationContextValue['t'],
+    userLanguage: TranslationContextValue['userLanguage'],
+  ) => string | JSX.Element;
   key?: string;
   /** Custom ChannelPreview click handler function */
   onSelect?: (event: React.MouseEvent) => void;
@@ -66,7 +74,12 @@ export const ChannelPreview = <
 >(
   props: ChannelPreviewProps<StreamChatGenerics>,
 ) => {
-  const { channel, Preview = ChannelPreviewMessenger, channelUpdateCount } = props;
+  const {
+    channel,
+    Preview = ChannelPreviewMessenger,
+    channelUpdateCount,
+    getLatestMessagePreview = defaultGetLatestMessagePreview,
+  } = props;
   const { channel: activeChannel, client, setActiveChannel } = useChatContext<StreamChatGenerics>(
     'ChannelPreview',
   );
@@ -123,26 +136,29 @@ export const ChannelPreview = <
   useEffect(() => {
     refreshUnreadCount();
 
-    const handleEvent = (event: Event<StreamChatGenerics>) => {
-      if (event.message) setLastMessage(event.message);
+    const handleEvent = () => {
+      setLastMessage(channel.state.latestMessages[channel.state.latestMessages.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
@@ -151,7 +167,8 @@ export const ChannelPreview = <
       displayImage={displayImage}
       displayTitle={displayTitle}
       lastMessage={lastMessage}
-      latestMessage={latestMessage}
+      latestMessage={latestMessagePreview}
+      latestMessagePreview={latestMessagePreview}
       messageDeliveryStatus={messageDeliveryStatus}
       setActiveChannel={setActiveChannel}
       unread={unread}
diff --git a/src/components/ChannelPreview/ChannelPreviewMessenger.tsx b/src/components/ChannelPreview/ChannelPreviewMessenger.tsx
index 8308dfda4c..18116e0e7b 100644
--- a/src/components/ChannelPreview/ChannelPreviewMessenger.tsx
+++ b/src/components/ChannelPreview/ChannelPreviewMessenger.tsx
@@ -18,7 +18,7 @@ const UnMemoizedChannelPreviewMessenger = <
     className: customClassName = '',
     displayImage,
     displayTitle,
-    latestMessage,
+    latestMessagePreview,
     onSelect: customOnSelectChannel,
     setActiveChannel,
     unread,
@@ -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>
   );
diff --git a/src/components/ChannelPreview/__tests__/ChannelPreview.test.js b/src/components/ChannelPreview/__tests__/ChannelPreview.test.js
index b95e59416b..d78712bca4 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,24 @@ 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>
+  </>
+);
+const PreviewUIComponentWithLatestMessagePreview = (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.latestMessagePreview : EMPTY_CHANNEL_PREVIEW_TEXT}
+    </div>
   </>
 );
 
@@ -65,7 +79,18 @@ describe('ChannelPreview', () => {
 
   beforeEach(async () => {
     client = await getTestClientWithUser(user);
-    useMockedApis(client, [queryChannelsApi([generateChannel(), generateChannel()])]);
+    useMockedApis(client, [
+      queryChannelsApi([
+        generateChannel({
+          channel: { name: 'c0' },
+          messages: Array.from({ length: 5 }, generateMessage),
+        }),
+        generateChannel({
+          channel: { name: 'c1' },
+          messages: Array.from({ length: 5 }, generateMessage),
+        }),
+      ]),
+    ]);
 
     [c0, c1] = await client.queryChannels({}, {});
   });
@@ -128,14 +153,31 @@ describe('ChannelPreview', () => {
     await expectUnreadCountToBe(getByTestId, newUnreadCount);
   });
 
+  it('allows to customize latest message preview generation', async () => {
+    const getLatestMessagePreview = (channel) => channel.data.name;
+
+    const { getByTestId } = renderComponent(
+      {
+        activeChannel: c0,
+        channel: c0,
+        getLatestMessagePreview,
+        Preview: PreviewUIComponentWithLatestMessagePreview,
+      },
+      render,
+    );
+
+    await expectLastEventMessageToBe(getByTestId, c0.data.name);
+  });
+
   const eventCases = [
     ['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 +193,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 +276,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);
@@ -282,6 +380,7 @@ describe('ChannelPreview', () => {
       expectUnreadCountToBe(screen.getByTestId, unreadCount);
     });
   });
+
   describe('notification.mark_unread', () => {
     it('should be ignored if not originated from the current user', () => {
       const unreadCount = 0;
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..1aaa77672b 100644
--- a/src/components/ChannelPreview/utils.tsx
+++ b/src/components/ChannelPreview/utils.tsx
@@ -17,7 +17,7 @@ export const getLatestMessagePreview = <
   t: TranslationContextValue['t'],
   userLanguage: TranslationContextValue['userLanguage'] = 'en',
 ): string | JSX.Element => {
-  const latestMessage = channel.state.messages[channel.state.messages.length - 1];
+  const latestMessage = channel.state.latestMessages[channel.state.latestMessages.length - 1];
 
   const previewTextToRender =
     latestMessage?.i18n?.[`${userLanguage}_text` as `${TranslationLanguages}_text`] ||
@@ -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',
+  });
+};