diff --git a/ai-docs/ai-migration-v14-v15.md b/ai-docs/ai-migration-v14-v15.md index 38937f17a..c34c07f20 100644 --- a/ai-docs/ai-migration-v14-v15.md +++ b/ai-docs/ai-migration-v14-v15.md @@ -149,7 +149,7 @@ code '\bMessage=\{|\b(Message|ThreadHeader|HeaderStartContent|ChannelListUI|Load # §11 message lists code '\bmessages=\{|\b(loadMore|loadMoreNewer|hasMore|hasMoreNewer|loadingMoreNewer|jumpToLatestMessage|messageLimit|highlightedMessageId|threadList|disableDateSeparator|renderMessages|RenderMessagesOptions|getIsFirstUnreadMessage|additionalVirtuosoProps|headerPosition|insertIntro|DateSeparatorMessage|useEnrichedMessages|useNewMessageNotification|useScrollLocationLogic|areMessagePropsEqual|useMessageListScrollManager|NewMessageNotification|ScrollToLatestMessageButton|InfiniteScrollPaginator|loadNextDebounceMs|loadMoreDebounceMs|internalInfiniteScrollProps|noGroupByUser|shouldGroupByUser|groupStyles|defaultItemHeight|overscan|scrollSeekPlaceHolder)\b|\bhead=\{' # §12 messages -code '`). The default `ThreadHead` is not exported, so an override renders the whole head (`DateSeparator` for the parent's `created_at`, `` and `ThreadStart`) | -| `autoFocus` (default `true`) | `` in every thread that should focus, whether or not you passed `autoFocus` | -| `enableDateSeparator` | `withDateSeparator` on the list | -| `Message` | A `MessageUI` override scoped with `WithComponents` | -| `messageActions` | Removed ([section 10](#messageactions-and-message_actions-are-removed)) | -| `virtualized` | Render the list you want | +| Removed `ThreadProps` | v15 | +| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `additionalMessageComposerProps` | Props on your `MessageComposer` child | +| `additionalMessageListProps` | Props on your `MessageList` child | +| `additionalVirtualizedMessageListProps` | Props on your `VirtualizedMessageList` child | +| `additionalParentMessageProps` | The list's own message props, which reach the parent message too; for anything else, a `ThreadHead` override on `ComponentContext` (typed `ComponentType`). The default `ThreadHead` is not exported, so an override renders the whole head (`DateSeparator` for the parent's `created_at`, `` and `ThreadStart`) | +| `autoFocus` (default `true`) | `` in every thread that should focus, whether or not you passed `autoFocus` | +| `enableDateSeparator` | `withDateSeparator` on the list | +| `Message` | A `MessageUI` override scoped with `WithComponents` | +| `messageActions` | Removed ([section 10](#messageactions-and-message_actions-are-removed)) | +| `virtualized` | Render the list you want | **Defaults changed.** A `MessageList` inside a thread now shows date separators unless you pass `withDateSeparator={false}` (v14's `Thread` turned them off). `autoFocus` defaulted to `true`, so every v14 thread focused its composer on open, not only threads that passed the prop; in v15 the composer is focused only if you render ``. @@ -2439,6 +2441,28 @@ export const List = () => ( ); ``` +### `initialMessage` is removed; use `useIsRenderedAsThreadParent()` + +`Message` no longer takes `initialMessage`, and `MessageContextValue` no longer has it. The SDK tells the thread's parent message from its replies itself: `useIsRenderedAsThreadParent()` returns whether the message in `MessageContext` is the parent message of the thread in `ThreadContext`, because a `stream-chat` `Thread`'s `id` is its parent message's id. It returns `false` for the thread's replies and outside a thread. Call it in a component that `` renders, such as a `MessageUI` override. + +- Passing `initialMessage` or reading `useMessageContext().initialMessage` is a compile error. In untyped code the prop is ignored and the field is `undefined`, so a check such as `!!useThreadContext() && !initialMessage` takes the parent for a reply. +- The parent message still gets no message actions, and now gets none without the flag too. A `ThreadHead` override renders ``. + +For example, a component under each message that should skip the thread's replies but not its parent: + +```tsx +// v14 +const { initialMessage } = useMessageContext(); +const isThreadReply = !!useThreadContext() && !initialMessage; +``` + +```tsx +import { useIsRenderedAsThreadParent, useThreadContext } from 'stream-chat-react'; + +const isThreadParent = useIsRenderedAsThreadParent(); +const isThreadReply = !!useThreadContext() && !isThreadParent; +``` + ### `renderText` takes `(text, options)` The `mentionedUsers` argument is removed, so `renderText(text, mentionedUsers, options)` becomes `renderText(text, options)`, and so does the `RenderTextFunction` type of the `renderText` prop on `MessageList`, `VirtualizedMessageList`, `Message`, `MessageText`, `QuotedMessage` and `QuotedMessagePreview`. Pass mentions as `options.messageMentionEntities`, built from a message with `getRenderTextMentionEntities(message)`; it covers `@channel`, `@here`, roles and user groups too. A custom `renderText` written for the old order reads the options object as `mentionedUsers` and loses them. @@ -3437,7 +3461,7 @@ Run after every section is applied. 2. **Leftover identifiers.** Every v14 name below was removed or renamed. In a plain-JS app this grep is the only thing that finds them. It must print nothing outside comments, changelogs and app identifiers that only share the name (record those in the report). Uses the `code` / `styles` helpers from [Detection](#detection). ```bash - code '\b(WindowProps|ChannelStateContext|ChannelStateContextValue|ChannelStateProvider|useChannelStateContext|ChannelActionContext|ChannelActionContextValue|ChannelActionProvider|useChannelActionContext|ChannelNotifications|MarkReadWrapperOptions|RetrySendMessage|TypingContext|TypingContextValue|TypingProvider|useTypingContext|ChannelUnreadUiState|channelUnreadUiState|setChannelUnreadUiState|updateChannelUiUnreadState|useThreadsViewContext|ThreadsViewContextValue|ThreadAdapter|activeChatView|setActiveChatView|setActiveChannel|setActiveChannelOnMount|customActiveChannel|channelsQueryState|latestMessageDatesByChannels|useChannelEditMessageHandler|useChannelMentionsHandler|ChannelListUI|ChannelListUIProps|usePaginatedChannels|useConnectionRecoveredListener|CustomQueryChannelParams|CustomQueryChannelsFn|EffectiveQueryParams|moveChannelUpwards|findLastPinnedChannelIndex|extractSortValue|isChannelPinned|isChannelArchived|shouldConsiderPinnedChannels|shouldConsiderArchivedChannels|allowNewMessagesFromUnfilteredChannels|lockChannelOrder|showChannelSearch|sendChannelsToList|renderChannels|channelRenderFilterFn|customQueryChannels|recoveryThrottleIntervalMs|onMessageNewHandler|channelUpdateCount|useLegacyThreadContext|additionalMessageComposerProps|additionalMessageListProps|additionalVirtualizedMessageListProps|additionalParentMessageProps|enableDateSeparator|disableDateSeparator|HeaderStartContent|MESSAGE_ACTIONS|OPTIONAL_MESSAGE_ACTIONS|getMessageActions|MessageActionsArray|actionsEnabled|messageIsUnread|initializeOnMount|channelQueryOptions|markReadOnMount|activeUnreadHandler|skipMessageDataMemoization|allowConcurrentAudioPlayback|allowConcurrentPlayback|doSendMessageRequest|doUpdateMessageRequest|doDeleteMessageRequest|doMarkReadRequest|overrideSubmitHandler|preventClearingOnUnmount|loadMoreNewer|loadMoreThread|loadingMoreNewer|jumpToLatestMessage|jumpToFirstUnreadMessage|highlightedMessageId|threadSuppressAutoscroll|loadingMoreForJumpToChannelMessage|messageLimit|hasPendingUploadState|recoverStateOnReconnect|wsUrlParams|isLocalUnreadCountEnabled|StreamChatOptions|isLanguageSupported|SupportedTranslations|DateFormatterOptions|getTranslators|registerSetLanguageCallback|getAvailableLanguages|geti18Instance|deTranslations|enTranslations|esTranslations|frTranslations|hiTranslations|itTranslations|jaTranslations|koTranslations|nlTranslations|ptTranslations|ruTranslations|trTranslations|ChannelSort|UserSort|ReactionSort|WatcherState|unBlockUser|ReminderResponse|CommandResponse|DefaultCommandData|CustomCommandData|chatContainer|useUserRole|disableQuotedMessages|MentionedUserEventHandler|mentionedUser|noGroupByUser|shouldGroupByUser|defaultItemHeight|scrollSeekPlaceHolder|endOfGroup|firstOfGroup|groupedByUser)\b| ( ); ``` + + +### BC-102: `initialMessage` is removed from `MessageProps` and `MessageContextValue`; `useIsRenderedAsThreadParent()` tells the thread's parent message apart + +- Status: confirmed +- Area: message / thread +- User impact: + - compile error on passing `initialMessage` to `Message` or reading `useMessageContext().initialMessage`. + - silent behavior change in untyped code: the prop is ignored and the context field is `undefined`, so code that read it to tell the thread's parent message from its replies takes the parent for a reply. For example, `!!useThreadContext() && !initialMessage` is `true` for the parent. + - The default message actions still give the thread's parent message no actions, and now detect it themselves. A `ThreadHead` override that renders `` without `initialMessage` no longer gives the parent the full action menu, as it did in v14. +- Old API: + - `MessageProps.initialMessage?: boolean`, passed on as `MessageContextValue.initialMessage`. The default `ThreadHead` rendered ``. + - `useBaseMessageActionSetFilter` returned no actions while `initialMessage` was set. +- Replacement: `useIsRenderedAsThreadParent()`, exported from `stream-chat-react`. It returns whether the message in `MessageContext` is the parent message of the thread in `ThreadContext`: a `stream-chat` `Thread`'s `id` is its parent message's id. It returns `false` for the thread's replies and outside a thread. Call it in a component that `` renders: it reads `useMessageContext()`, which throws outside a `MessageProvider`. A `ThreadHead` override renders ``. +- Evidence: + - `release-v14:src/components/Message/types.ts:36`, `release-v14:src/context/MessageContext.tsx:90`, `release-v14:src/components/Message/Message.tsx:255`, `release-v14:src/components/Thread/ThreadHead.tsx:15`, `release-v14:src/components/MessageActions/hooks/useBaseMessageActionSetFilter.ts:32` + - `master:src/components/Message/hooks/useIsRenderedAsThreadParent.ts`, `master:src/components/MessageActions/hooks/useBaseMessageActionSetFilter.ts` (`isThreadParent`), `master:src/components/Thread/ThreadHead.tsx` + - commit: REACT-1521 +- Docs impact: + - `06-component-reference/04-message/01-message.md` (`initialMessage` prop), `06-component-reference/04-message/02-messagecontext.md` (`initialMessage` field), `07-reference/02-hooks/10-message-hooks.md` (the new hook; `useBaseMessageActionSetFilter`), `05-ui-cookbooks/04-message-list/06-read-receipt-avatars.md`, `08-release-guides/01-upgrade-to-v15.md` +- Example needed: yes + +Before (v14): + +```tsx +const { initialMessage } = useMessageContext(); +const isThreadReply = !!useThreadContext() && !initialMessage; +``` + +After (v15): + +```tsx +import { useIsRenderedAsThreadParent, useThreadContext } from 'stream-chat-react'; + +const isThreadParent = useIsRenderedAsThreadParent(); +const isThreadReply = !!useThreadContext() && !isThreadParent; +``` + ## Message composer @@ -4325,6 +4364,7 @@ Non-gated items checked and ruled out: `Chat.isMessageAIGenerated`, `SummarizedM - `useUserRole.canPin` member-added-required -> ruled-out: additive return field - `useUserRole` removed, `useMessagePermissions` added -> BC-092 - `Message.disableQuotedMessages`, `MessageProps.disableQuotedMessages`, the `useMessagePermissions` second argument -> BC-095 + - `Message.initialMessage`, `MessageProps.initialMessage`, `MessageContextValue.initialMessage` removed, `useIsRenderedAsThreadParent` added -> BC-102 - components/MessageList - `MessageList.disableQuotedMessages`, `MessageListProps.disableQuotedMessages`, `SharedMessageProps.disableQuotedMessages` -> BC-095 - `getIsFirstUnreadMessage` -> BC-054 diff --git a/examples/cookbook/src/recipes/message-list/read-receipt-avatars/ReadReceiptAvatars.tsx b/examples/cookbook/src/recipes/message-list/read-receipt-avatars/ReadReceiptAvatars.tsx index ae15c56e6..16baa8987 100644 --- a/examples/cookbook/src/recipes/message-list/read-receipt-avatars/ReadReceiptAvatars.tsx +++ b/examples/cookbook/src/recipes/message-list/read-receipt-avatars/ReadReceiptAvatars.tsx @@ -5,6 +5,7 @@ import { MessageUI, useChannel, useChatContext, + useIsRenderedAsThreadParent, useMessageContext, useStateStore, useThreadContext, @@ -21,8 +22,9 @@ const readersSelector = ({ readersByMessageId }: MessageReceiptsSnapshot) => ({ const ReadReceiptAvatars = () => { const channel = useChannel(); const { client } = useChatContext(); - const { initialMessage, message } = useMessageContext(); - const isThreadReply = !!useThreadContext() && !initialMessage; + const { message } = useMessageContext(); + const isThreadParent = useIsRenderedAsThreadParent(); + const isThreadReply = !!useThreadContext() && !isThreadParent; const { readersByMessageId } = useStateStore( channel.messageReceiptsTracker.snapshotStore, readersSelector, diff --git a/src/components/Message/Message.tsx b/src/components/Message/Message.tsx index 91dadf58d..d1f9bbc53 100644 --- a/src/components/Message/Message.tsx +++ b/src/components/Message/Message.tsx @@ -181,7 +181,6 @@ export const Message = (props: MessageProps) => { handleReaction={handleReaction} handleRetry={handleRetry} highlighted={props.highlighted} - initialMessage={props.initialMessage} lastOwnMessage={props.lastOwnMessage} lastReceivedId={props.lastReceivedId} message={message} diff --git a/src/components/Message/__tests__/MessageText.test.tsx b/src/components/Message/__tests__/MessageText.test.tsx index e8e3d6e7f..70ad99104 100644 --- a/src/components/Message/__tests__/MessageText.test.tsx +++ b/src/components/Message/__tests__/MessageText.test.tsx @@ -62,7 +62,6 @@ const bob = generateUser({ name: 'bob' }); const onMentionsHoverMock = vi.fn(); const onMentionsClickMock = vi.fn(); const defaultProps = { - initialMessage: false, message: generateMessage(), }; diff --git a/src/components/Message/hooks/__tests__/useIsRenderedAsThreadParent.test.tsx b/src/components/Message/hooks/__tests__/useIsRenderedAsThreadParent.test.tsx new file mode 100644 index 000000000..dabfab6f4 --- /dev/null +++ b/src/components/Message/hooks/__tests__/useIsRenderedAsThreadParent.test.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import type { PropsWithChildren } from 'react'; +import { renderHook } from '@testing-library/react'; +import type { LocalMessage, Thread } from 'stream-chat'; + +import { useIsRenderedAsThreadParent } from '../useIsRenderedAsThreadParent'; +import { MessageProvider } from '../../../../context'; +import { ThreadProvider } from '../../../Threads'; +import { + generateMessage, + initClientWithChannels, + mockMessageContext, +} from '../../../../mock-builders'; + +const parentMessage = generateMessage() as LocalMessage; +const reply = generateMessage({ parent_id: parentMessage.id }) as LocalMessage; + +const renderUseIsRenderedAsThreadParent = ({ + message, + thread, +}: { + message: LocalMessage; + thread?: Thread; +}) => { + const wrapper = ({ children }: PropsWithChildren) => ( + + + {children} + + + ); + return renderHook(() => useIsRenderedAsThreadParent(), { wrapper }).result.current; +}; + +describe('useIsRenderedAsThreadParent', () => { + let thread: Thread; + + beforeEach(async () => { + const { + channels: [channel], + client, + } = await initClientWithChannels(); + thread = client.threads.ensure({ channel, parentMessage }); + }); + + it('is true for the parent message of the thread in context', () => { + expect(renderUseIsRenderedAsThreadParent({ message: parentMessage, thread })).toBe( + true, + ); + }); + + it('is false for a reply in the thread', () => { + expect(renderUseIsRenderedAsThreadParent({ message: reply, thread })).toBe(false); + }); + + it('is false outside a thread, even for a message that has a thread', () => { + expect(renderUseIsRenderedAsThreadParent({ message: parentMessage })).toBe(false); + }); +}); diff --git a/src/components/Message/hooks/index.ts b/src/components/Message/hooks/index.ts index 1029b03f0..2d88b84cd 100644 --- a/src/components/Message/hooks/index.ts +++ b/src/components/Message/hooks/index.ts @@ -1,6 +1,7 @@ export * from './useActionHandler'; export * from './useDeleteHandler'; export * from './useFlagHandler'; +export * from './useIsRenderedAsThreadParent'; export * from './useMentionsHandler'; export * from './useMarkUnreadHandler'; export * from './useMessageAlsoSentInChannelNavigation'; diff --git a/src/components/Message/hooks/useIsRenderedAsThreadParent.ts b/src/components/Message/hooks/useIsRenderedAsThreadParent.ts new file mode 100644 index 000000000..7fd5f44a1 --- /dev/null +++ b/src/components/Message/hooks/useIsRenderedAsThreadParent.ts @@ -0,0 +1,14 @@ +import { useMessageContext } from '../../../context'; +import { useThreadContext } from '../../Threads'; + +/** + * Whether the message in `MessageContext` is the parent message of the thread in `ThreadContext`. + * A thread's id is its parent message's id, so inside a thread only the parent message matches. + * + * @returns True for the thread's parent message, false for its replies and outside a thread. + */ +export const useIsRenderedAsThreadParent = () => { + const thread = useThreadContext(); + const { message } = useMessageContext(); + return !!thread && thread.id === message.id; +}; diff --git a/src/components/Message/types.ts b/src/components/Message/types.ts index 667be71e8..1e6435298 100644 --- a/src/components/Message/types.ts +++ b/src/components/Message/types.ts @@ -40,8 +40,6 @@ export type MessageProps = { groupStyles?: GroupStyle[]; /** Whether to highlight the message. The message lists set it on the focused message. */ highlighted?: boolean; - /** Whether the message is the thread's parent shown at the top of the thread. */ - initialMessage?: boolean; // todo: move to LLC Channel state /** Latest own message in the currently displayed message set. */ lastOwnMessage?: LocalMessage; diff --git a/src/components/MessageActions/__tests__/MessageActions.test.tsx b/src/components/MessageActions/__tests__/MessageActions.test.tsx index bd6141595..c29b4231b 100644 --- a/src/components/MessageActions/__tests__/MessageActions.test.tsx +++ b/src/components/MessageActions/__tests__/MessageActions.test.tsx @@ -27,6 +27,7 @@ import { ResizeObserverMock } from '../../../mock-builders/browser'; import { Message } from '../../Message'; import { Channel } from '../../Channel'; import { Chat } from '../../Chat'; +import { ThreadProvider } from '../../Threads'; import { convertDateToTimestamp } from '../../../mock-builders'; (window as any).ResizeObserver = ResizeObserverMock; @@ -83,6 +84,7 @@ async function renderMessageActions({ customChatContext = {}, customMessageContext = {}, messageActionsProps = {}, + threadParentMessage, }: any = {}) { const client = chatClient || (await getTestClientWithUser(alice)); if (customChatContext.mutes) { @@ -99,19 +101,25 @@ async function renderMessageActions({ }, }); + const thread = threadParentMessage + ? client.threads.ensure({ channel, parentMessage: threadParentMessage }) + : undefined; + return render( - - - - - + + + + + + + , ); @@ -154,13 +162,25 @@ describe('', () => { expect(queryByTestId(MESSAGE_ACTIONS_HOST_TEST_ID)).not.toBeInTheDocument(); }); - it('should not render when message is parent message in a thread', async () => { - const { queryByTestId } = await renderMessageActions({ - customMessageContext: { - initialMessage: true, - }, + it('should not render for the parent message of the thread it is rendered in', async () => { + const message = generateMessage({ user: alice }); + const { container } = await renderMessageActions({ + customMessageContext: { message }, + threadParentMessage: message, }); - expect(queryByTestId(MESSAGE_ACTIONS_HOST_TEST_ID)).not.toBeInTheDocument(); + expect( + container.querySelector('.str-chat__message-options'), + ).not.toBeInTheDocument(); + }); + + it('should render for a reply in a thread', async () => { + const parentMessage = generateMessage({ user: alice }); + const message = generateMessage({ parent_id: parentMessage.id, user: alice }); + const { container } = await renderMessageActions({ + customMessageContext: { message }, + threadParentMessage: parentMessage, + }); + expect(container.querySelector('.str-chat__message-options')).toBeInTheDocument(); }); it('should render correctly when not open', async () => { diff --git a/src/components/MessageActions/hooks/useBaseMessageActionSetFilter.ts b/src/components/MessageActions/hooks/useBaseMessageActionSetFilter.ts index 2d84966a5..a19e813d2 100644 --- a/src/components/MessageActions/hooks/useBaseMessageActionSetFilter.ts +++ b/src/components/MessageActions/hooks/useBaseMessageActionSetFilter.ts @@ -1,7 +1,7 @@ import { useMemo } from 'react'; import { useChannel, useMessageContext } from '../../../context'; -import { useMessagePermissions } from '../../Message/hooks'; +import { useIsRenderedAsThreadParent, useMessagePermissions } from '../../Message/hooks'; import { ACTIONS_NOT_WORKING_IN_THREAD, isMessageBounced, @@ -29,15 +29,16 @@ const userMessageRemindersStateSelector = ({ userMessageReminders }: ChannelConf * @param messageActionSet - Actions to filter. * @param disable - Skips the checks above when true, leaving only each action's `isVisible`. * Defaults to false. - * @returns The actions that apply to the message, none for bounced, initial, system, ephemeral, - * sending or typeless messages. + * @returns The actions that apply to the message, none for bounced, system, ephemeral, sending or + * typeless messages, or for the parent message of the thread it is rendered in. */ export const useBaseMessageActionSetFilter = ( messageActionSet: MessageActionSetItem[], disable = false, ) => { const channel = useChannel(); - const { initialMessage: isInitialMessage, message } = useMessageContext(); + const { message } = useMessageContext(); + const isThreadParent = useIsRenderedAsThreadParent(); const { userMessageRemindersEnabled } = useStateStore( channel.configState, userMessageRemindersStateSelector, @@ -78,7 +79,7 @@ export const useBaseMessageActionSetFilter = ( // These messages get no actions. if ( isBounced || - isInitialMessage || // the thread parent that `ThreadHead` renders above the replies + isThreadParent || !message.type || message.type === 'system' || message.type === 'ephemeral' || @@ -142,7 +143,7 @@ export const useBaseMessageActionSetFilter = ( canSendMessage, userMessageRemindersEnabled, isBounced, - isInitialMessage, + isThreadParent, messageIsDeleted, isMessageThreadReply, isMyMessage, diff --git a/src/components/MessageList/__tests__/MessageList.test.tsx b/src/components/MessageList/__tests__/MessageList.test.tsx index d9c121cf1..8621734d5 100644 --- a/src/components/MessageList/__tests__/MessageList.test.tsx +++ b/src/components/MessageList/__tests__/MessageList.test.tsx @@ -27,6 +27,7 @@ import { import { Chat } from '../../Chat'; import { MessageList } from '../MessageList'; import { Channel } from '../../Channel'; +import { Message } from '../../Message'; import { CHANNEL_CONTAINER_ID } from '../../Channel/constants'; import { MESSAGE_LIST_MAIN_PANEL_CLASS } from '../MessageListMainPanel'; import { ThreadProvider } from '../../Threads'; @@ -394,6 +395,31 @@ describe('MessageList', () => { }); }); + it('should give no actions to the parent message that a ThreadHead override renders', async () => { + const thread = chatClient.threads.ensure({ channel, parentMessage: message1 }); + const CustomThreadHead = (props: MessageProps) => ( +
+ +
+ ); + + await act(() => { + renderComponent({ + channelProps: { channel }, + chatClient, + components: { ThreadHead: CustomThreadHead }, + msgListProps: { messages: [] }, + thread, + }); + }); + + const threadHead = await screen.findByTestId('custom-thread-head'); + expect(within(threadHead).getByText(message1.text)).toBeInTheDocument(); + expect( + threadHead.querySelector('.str-chat__message-options'), + ).not.toBeInTheDocument(); + }); + it('should give the thread parent message the message props of its replies', async () => { const CustomThreadHead = vi.fn<(props: MessageProps) => React.ReactElement>(() => (
diff --git a/src/components/MessageList/renderMessages.tsx b/src/components/MessageList/renderMessages.tsx index dd4420928..d22ca53b0 100644 --- a/src/components/MessageList/renderMessages.tsx +++ b/src/components/MessageList/renderMessages.tsx @@ -46,7 +46,6 @@ type MessagePropsToOmit = | 'channel' | 'deliveredTo' | 'groupStyles' - | 'initialMessage' | 'lastReceivedId' | 'message' | 'readBy'; diff --git a/src/components/Thread/ThreadHead.tsx b/src/components/Thread/ThreadHead.tsx index 8e2d60a98..7ec23299a 100644 --- a/src/components/Thread/ThreadHead.tsx +++ b/src/components/Thread/ThreadHead.tsx @@ -14,7 +14,7 @@ export const ThreadHead = (props: MessageProps) => { return (
{parentCreatedAt ? : null} - +
); diff --git a/src/context/MessageContext.tsx b/src/context/MessageContext.tsx index 02aa5209a..a309537a6 100644 --- a/src/context/MessageContext.tsx +++ b/src/context/MessageContext.tsx @@ -69,8 +69,6 @@ export type MessageContextValue = { groupStyles?: GroupStyle[]; /** Whether to highlight the message. The message lists set it on the focused message. */ highlighted?: boolean; - /** Whether the message is the thread's parent shown at the top of the thread. */ - initialMessage?: boolean; /** Whether a message is AI generated. */ isMessageAIGenerated?: (message: LocalMessage) => boolean; /** Latest own message in the currently displayed message set. */