From 636de09d70b3495db1d78c512dbead61c73c2344 Mon Sep 17 00:00:00 2001 From: bang9 Date: Thu, 11 Apr 2024 17:54:32 +0900 Subject: [PATCH 1/4] fix: update open channel interface properly --- .../hooks/useSendFileMessageCallback.ts | 5 ++++ .../components/OpenChannelMessage/index.tsx | 21 +++---------- .../OpenChannelMessageList/index.tsx | 2 +- .../components/OpenChannelUI/index.tsx | 30 ++++++++----------- .../context/hooks/useFileUploadCallback.tsx | 5 ++++ src/modules/OpenChannel/index.tsx | 1 + .../context/hooks/useSendFileMessage.ts | 5 ++++ .../hooks/useSendVoiceMessageCallback.ts | 5 ++++ src/utils/index.ts | 8 ++--- 9 files changed, 42 insertions(+), 40 deletions(-) diff --git a/src/modules/Channel/context/hooks/useSendFileMessageCallback.ts b/src/modules/Channel/context/hooks/useSendFileMessageCallback.ts index 60e8becc2..8fe8368d6 100644 --- a/src/modules/Channel/context/hooks/useSendFileMessageCallback.ts +++ b/src/modules/Channel/context/hooks/useSendFileMessageCallback.ts @@ -47,11 +47,16 @@ export default function useSendFileMessageCallback( pubSub.publish(topics.SEND_MESSAGE_START, { /* pubSub is used instead of messagesDispatcher to avoid redundantly calling `messageActionTypes.SEND_MESSAGE_START` */ + // TODO: remove data pollution message: { ...pendingMessage, url: URL.createObjectURL(compressedFile), // pending thumbnail message seems to be failed requestState: 'pending', + isUserMessage: pendingMessage.isUserMessage, + isFileMessage: pendingMessage.isFileMessage, + isAdminMessage: pendingMessage.isAdminMessage, + isMultipleFilesMessage: pendingMessage.isMultipleFilesMessage, } as unknown as FileMessage, channel: currentGroupChannel, publishingModules: [PublishingModuleType.CHANNEL], diff --git a/src/modules/OpenChannel/components/OpenChannelMessage/index.tsx b/src/modules/OpenChannel/components/OpenChannelMessage/index.tsx index 85f33bbef..21f0f29af 100644 --- a/src/modules/OpenChannel/components/OpenChannelMessage/index.tsx +++ b/src/modules/OpenChannel/components/OpenChannelMessage/index.tsx @@ -24,9 +24,7 @@ import { useLocalization } from '../../../../lib/LocalizationContext'; import { CoreMessageType, SendableMessageType } from '../../../../utils'; export type OpenChannelMessageProps = { - renderMessage?: ( - props: RenderMessageProps - ) => React.ElementType; + renderMessage?: (props: RenderMessageProps) => React.ReactElement; message: CoreMessageType; chainTop?: boolean; chainBottom?: boolean; @@ -34,7 +32,7 @@ export type OpenChannelMessageProps = { editDisabled?: boolean; }; -export default function MessagOpenChannelMessageeHoc( +export default function OpenChannelMessage( props: OpenChannelMessageProps, ): ReactElement { const { message, chainTop, chainBottom, hasSeparator, renderMessage } = props; @@ -57,13 +55,6 @@ export default function MessagOpenChannelMessageeHoc( sender = (message as SendableMessageType)?.sender; } - const RenderedMessage = useMemo( - () => (props: RenderMessageProps) => { - return <>{renderMessage ? renderMessage(props) : null}; - }, - [message, renderMessage], - ); - const [showEdit, setShowEdit] = useState(false); const [showRemove, setShowRemove] = useState(false); const [showFileViewer, setShowFileViewer] = useState(false); @@ -80,14 +71,10 @@ export default function MessagOpenChannelMessageeHoc( === SendingMessageStatus.FAILED; } - if (renderMessage && RenderedMessage) { + if (renderMessage) { return (
- + {renderMessage({ message, chainTop, chainBottom })}
); } diff --git a/src/modules/OpenChannel/components/OpenChannelMessageList/index.tsx b/src/modules/OpenChannel/components/OpenChannelMessageList/index.tsx index c8a10c63b..dfc976c7c 100644 --- a/src/modules/OpenChannel/components/OpenChannelMessageList/index.tsx +++ b/src/modules/OpenChannel/components/OpenChannelMessageList/index.tsx @@ -16,7 +16,7 @@ import { useHandleOnScrollCallback } from '../../../../hooks/useHandleOnScrollCa import { compareMessagesForGrouping } from '../../../../utils/messages'; export type OpenchannelMessageListProps = { - renderMessage?: (props: RenderMessageProps) => React.ElementType; + renderMessage?: (props: RenderMessageProps) => React.ReactElement; renderPlaceHolderEmptyList?: () => React.ReactElement; }; diff --git a/src/modules/OpenChannel/components/OpenChannelUI/index.tsx b/src/modules/OpenChannel/components/OpenChannelUI/index.tsx index 940d2f083..8fac6c632 100644 --- a/src/modules/OpenChannel/components/OpenChannelUI/index.tsx +++ b/src/modules/OpenChannel/components/OpenChannelUI/index.tsx @@ -12,12 +12,15 @@ import OpenChannelMessageList from '../OpenChannelMessageList'; import { RenderMessageProps } from '../../../../types'; export interface OpenChannelUIProps { - renderMessage?: (props: RenderMessageProps) => React.ElementType; + renderMessage?: (props: RenderMessageProps) => React.ReactElement; renderHeader?: () => React.ReactElement; - renderInput?: () => React.ReactElement; + renderMessageInput?: () => React.ReactElement; renderPlaceHolderEmptyList?: () => React.ReactElement; renderPlaceHolderError?: () => React.ReactElement; renderPlaceHolderLoading?: () => React.ReactElement; + + /** @deprecated Please use renderMessageInput instead **/ + renderInput?: () => React.ReactElement; } const COMPONENT_CLASS_NAME = 'sendbird-openchannel-conversation'; @@ -25,10 +28,11 @@ const COMPONENT_CLASS_NAME = 'sendbird-openchannel-conversation'; const OpenChannelUI: React.FC = ({ renderMessage, renderHeader, - renderInput, renderPlaceHolderEmptyList, renderPlaceHolderError, renderPlaceHolderLoading, + renderMessageInput, + renderInput, }: OpenChannelUIProps) => { const { currentOpenChannel, @@ -58,28 +62,18 @@ const OpenChannelUI: React.FC = ({ ); } + const renderInputComponent = renderMessageInput || renderInput; + return (
- { - renderHeader?.() || ( - - ) - } - { - currentOpenChannel?.isFrozen && ( - - ) - } + {renderHeader?.() || } + {currentOpenChannel?.isFrozen && } - { - renderInput?.() || ( - - ) - } + {renderInputComponent?.() || }
); }; diff --git a/src/modules/OpenChannel/context/hooks/useFileUploadCallback.tsx b/src/modules/OpenChannel/context/hooks/useFileUploadCallback.tsx index 9f162f361..5b020c097 100644 --- a/src/modules/OpenChannel/context/hooks/useFileUploadCallback.tsx +++ b/src/modules/OpenChannel/context/hooks/useFileUploadCallback.tsx @@ -104,11 +104,16 @@ function useFileUploadCallback({ messagesDispatcher({ type: messageActionTypes.SENDING_MESSAGE_START, payload: { + // TODO: remove data pollution message: { ...pendingMessage, url: URL.createObjectURL(file), // pending thumbnail message seems to be failed requestState: 'pending', + isUserMessage: pendingMessage.isUserMessage, + isFileMessage: pendingMessage.isFileMessage, + isAdminMessage: pendingMessage.isAdminMessage, + isMultipleFilesMessage: pendingMessage.isMultipleFilesMessage, }, channel: currentOpenChannel, }, diff --git a/src/modules/OpenChannel/index.tsx b/src/modules/OpenChannel/index.tsx index 94a1ff446..170b1a680 100644 --- a/src/modules/OpenChannel/index.tsx +++ b/src/modules/OpenChannel/index.tsx @@ -23,6 +23,7 @@ const OpenChannel: React.FC = (props: OpenChannelProps) => { renderMessage={props?.renderMessage} renderHeader={props?.renderHeader} renderInput={props?.renderInput} + renderMessageInput={props?.renderMessageInput} renderPlaceHolderEmptyList={props?.renderPlaceHolderEmptyList} renderPlaceHolderError={props?.renderPlaceHolderError} renderPlaceHolderLoading={props?.renderPlaceHolderLoading} diff --git a/src/modules/Thread/context/hooks/useSendFileMessage.ts b/src/modules/Thread/context/hooks/useSendFileMessage.ts index 23ddecb6f..b243cbd38 100644 --- a/src/modules/Thread/context/hooks/useSendFileMessage.ts +++ b/src/modules/Thread/context/hooks/useSendFileMessage.ts @@ -56,11 +56,16 @@ export default function useSendFileMessageCallback({ payload: { /* pubSub is used instead of messagesDispatcher to avoid redundantly calling `messageActionTypes.SEND_MESSAGE_START` */ + // TODO: remove data pollution message: { ...pendingMessage, url: URL.createObjectURL(file), // pending thumbnail message seems to be failed requestState: 'pending', + isUserMessage: pendingMessage.isUserMessage, + isFileMessage: pendingMessage.isFileMessage, + isAdminMessage: pendingMessage.isAdminMessage, + isMultipleFilesMessage: pendingMessage.isMultipleFilesMessage, }, }, }); diff --git a/src/modules/Thread/context/hooks/useSendVoiceMessageCallback.ts b/src/modules/Thread/context/hooks/useSendVoiceMessageCallback.ts index 59d372fd4..8cfbd3e82 100644 --- a/src/modules/Thread/context/hooks/useSendVoiceMessageCallback.ts +++ b/src/modules/Thread/context/hooks/useSendVoiceMessageCallback.ts @@ -73,11 +73,16 @@ export const useSendVoiceMessageCallback = ({ payload: { /* pubSub is used instead of messagesDispatcher to avoid redundantly calling `messageActionTypes.SEND_MESSAGE_START` */ + // TODO: remove data pollution message: { ...pendingMessage, url: URL.createObjectURL(file), // pending thumbnail message seems to be failed requestState: 'pending', + isUserMessage: pendingMessage.isUserMessage, + isFileMessage: pendingMessage.isFileMessage, + isAdminMessage: pendingMessage.isAdminMessage, + isMultipleFilesMessage: pendingMessage.isMultipleFilesMessage, }, }, }); diff --git a/src/utils/index.ts b/src/utils/index.ts index 0dbb6a914..10efaa135 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -233,21 +233,21 @@ export const isSentStatus = (state: string): boolean => ( || state === OutgoingMessageStates.READ ); -export const isAdminMessage = (message: CoreMessageType): boolean => ( +export const isAdminMessage = (message: CoreMessageType): message is AdminMessage => ( message && ( message['isAdminMessage'] && typeof message.isAdminMessage === 'function' ? message.isAdminMessage() : message?.messageType === 'admin' ) ); -export const isUserMessage = (message: CoreMessageType): boolean => ( +export const isUserMessage = (message: CoreMessageType): message is UserMessage => ( message && ( message['isUserMessage'] && typeof message.isUserMessage === 'function' ? message.isUserMessage() : message?.messageType === 'user' ) ); -export const isFileMessage = (message: CoreMessageType): boolean => ( +export const isFileMessage = (message: CoreMessageType): message is FileMessage => ( message && ( message['isFileMessage'] && typeof message.isFileMessage === 'function' ? message.isFileMessage() @@ -256,7 +256,7 @@ export const isFileMessage = (message: CoreMessageType): boolean => ( ); export const isMultipleFilesMessage = ( message: CoreMessageType, -): boolean => ( +): message is MultipleFilesMessage => ( message && ( message['isMultipleFilesMessage'] && typeof message.isMultipleFilesMessage === 'function' ? message.isMultipleFilesMessage() From f510881193f9504aa86ffbe9c15b6e94d2bed4de Mon Sep 17 00:00:00 2001 From: bang9 Date: Thu, 11 Apr 2024 17:58:05 +0900 Subject: [PATCH 2/4] fix: typo --- .../components/OpenChannelMessageList/index.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/modules/OpenChannel/components/OpenChannelMessageList/index.tsx b/src/modules/OpenChannel/components/OpenChannelMessageList/index.tsx index dfc976c7c..27812f79d 100644 --- a/src/modules/OpenChannel/components/OpenChannelMessageList/index.tsx +++ b/src/modules/OpenChannel/components/OpenChannelMessageList/index.tsx @@ -15,15 +15,16 @@ import useSendbirdStateContext from '../../../../hooks/useSendbirdStateContext'; import { useHandleOnScrollCallback } from '../../../../hooks/useHandleOnScrollCallback'; import { compareMessagesForGrouping } from '../../../../utils/messages'; -export type OpenchannelMessageListProps = { + +export type OpenChannelMessageListProps = { renderMessage?: (props: RenderMessageProps) => React.ReactElement; renderPlaceHolderEmptyList?: () => React.ReactElement; }; -function OpenchannelMessageList( - props: OpenchannelMessageListProps, - ref: React.RefObject, -): ReactElement { +/** @deprecated **/ +export type OpenchannelMessageListProps = OpenChannelMessageListProps + +function OpenChannelMessageList(props: OpenChannelMessageListProps, ref: React.RefObject): ReactElement { const { isMessageGroupingEnabled = true, allMessages, @@ -129,4 +130,4 @@ function OpenchannelMessageList( ); } -export default React.forwardRef(OpenchannelMessageList); +export default React.forwardRef(OpenChannelMessageList); From 62c29e4d7331612fcd8df2439489c07030704eba Mon Sep 17 00:00:00 2001 From: bang9 Date: Thu, 11 Apr 2024 17:59:18 +0900 Subject: [PATCH 3/4] chore: lint fix --- .../OpenChannel/components/OpenChannelMessage/index.tsx | 2 +- .../OpenChannel/components/OpenChannelMessageList/index.tsx | 5 ++--- src/modules/OpenChannel/components/OpenChannelUI/index.tsx | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/modules/OpenChannel/components/OpenChannelMessage/index.tsx b/src/modules/OpenChannel/components/OpenChannelMessage/index.tsx index 21f0f29af..ef9986abd 100644 --- a/src/modules/OpenChannel/components/OpenChannelMessage/index.tsx +++ b/src/modules/OpenChannel/components/OpenChannelMessage/index.tsx @@ -1,4 +1,4 @@ -import React, { useState, useRef, ReactElement, useMemo } from 'react'; +import React, { useState, useRef, ReactElement } from 'react'; import { AdminMessage, FileMessage, UserMessage } from '@sendbird/chat/message'; import { User } from '@sendbird/chat'; import format from 'date-fns/format'; diff --git a/src/modules/OpenChannel/components/OpenChannelMessageList/index.tsx b/src/modules/OpenChannel/components/OpenChannelMessageList/index.tsx index 27812f79d..911a708fc 100644 --- a/src/modules/OpenChannel/components/OpenChannelMessageList/index.tsx +++ b/src/modules/OpenChannel/components/OpenChannelMessageList/index.tsx @@ -15,14 +15,13 @@ import useSendbirdStateContext from '../../../../hooks/useSendbirdStateContext'; import { useHandleOnScrollCallback } from '../../../../hooks/useHandleOnScrollCallback'; import { compareMessagesForGrouping } from '../../../../utils/messages'; - export type OpenChannelMessageListProps = { renderMessage?: (props: RenderMessageProps) => React.ReactElement; renderPlaceHolderEmptyList?: () => React.ReactElement; }; -/** @deprecated **/ -export type OpenchannelMessageListProps = OpenChannelMessageListProps +/** @deprecated * */ +export type OpenchannelMessageListProps = OpenChannelMessageListProps; function OpenChannelMessageList(props: OpenChannelMessageListProps, ref: React.RefObject): ReactElement { const { diff --git a/src/modules/OpenChannel/components/OpenChannelUI/index.tsx b/src/modules/OpenChannel/components/OpenChannelUI/index.tsx index 8fac6c632..6e227e2f0 100644 --- a/src/modules/OpenChannel/components/OpenChannelUI/index.tsx +++ b/src/modules/OpenChannel/components/OpenChannelUI/index.tsx @@ -19,7 +19,7 @@ export interface OpenChannelUIProps { renderPlaceHolderError?: () => React.ReactElement; renderPlaceHolderLoading?: () => React.ReactElement; - /** @deprecated Please use renderMessageInput instead **/ + /** @deprecated Please use renderMessageInput instead * */ renderInput?: () => React.ReactElement; } From 07af5948ef5627b2c0c1e1a6e8c53578bb54744a Mon Sep 17 00:00:00 2001 From: bang9 Date: Thu, 11 Apr 2024 18:34:39 +0900 Subject: [PATCH 4/4] refactor: refactor isXXXMessage utils and removed redundant type-casting --- src/ui/MessageContent/index.tsx | 73 +++++++++---------- .../QuoteMessageThumbnail.tsx | 26 ++----- src/utils/index.ts | 73 ++++++++++++------- 3 files changed, 89 insertions(+), 83 deletions(-) diff --git a/src/ui/MessageContent/index.tsx b/src/ui/MessageContent/index.tsx index 8c13bd790..05dc37ee2 100644 --- a/src/ui/MessageContent/index.tsx +++ b/src/ui/MessageContent/index.tsx @@ -8,7 +8,7 @@ import { MessageEmojiMenu, MessageEmojiMenuProps } from '../MessageItemReactionM import Label, { LabelColors, LabelTypography } from '../Label'; import EmojiReactions, { EmojiReactionsProps } from '../EmojiReactions'; -import ClientAdminMessage from '../AdminMessage'; +import AdminMessage from '../AdminMessage'; import QuoteMessage from '../QuoteMessage'; import type { OnBeforeDownloadFileMessageType } from '../../modules/GroupChannel/context/GroupChannelProvider'; @@ -16,8 +16,9 @@ import { CoreMessageType, getClassName, getMessageContentMiddleClassNameByContainerType, + isAdminMessage, isMultipleFilesMessage, - isOGMessage, + isOGMessage, isSendableMessage, isTemplateMessage, isThumbnailMessage, SendableMessageType, @@ -27,7 +28,7 @@ import { LocalizationContext, useLocalization } from '../../lib/LocalizationCont import useSendbirdStateContext from '../../hooks/useSendbirdStateContext'; import { GroupChannel } from '@sendbird/chat/groupChannel'; import { EmojiContainer } from '@sendbird/chat'; -import { AdminMessage, Feedback, FeedbackRating, FileMessage, UserMessage } from '@sendbird/chat/message'; +import { Feedback, FeedbackRating } from '@sendbird/chat/message'; import useLongPress from '../../hooks/useLongPress'; import MobileMenu from '../MobileMenu'; import { useMediaQueryContext } from '../../lib/MediaQueryContext'; @@ -258,8 +259,8 @@ export default function MessageContent(props: MessageContentProps): ReactElement shouldPreventDefault: false, }); - if (message?.isAdminMessage?.() || message?.messageType === 'admin') { - return (); + if (isAdminMessage(message)) { + return (); } return ( @@ -287,16 +288,16 @@ export default function MessageContent(props: MessageContentProps): ReactElement {showOutgoingMenu && (
{renderMessageMenu({ - channel: channel, - message: message as SendableMessageType, - isByMe: isByMe, - replyType: replyType, - disabled: disabled, - showEdit: showEdit, - showRemove: showRemove, - resendMessage: resendMessage, - setQuoteMessage: setQuoteMessage, - setSupposedHover: setSupposedHover, + channel, + message, + isByMe, + replyType, + disabled, + showEdit, + showRemove, + resendMessage, + setQuoteMessage, + setSupposedHover, onReplyInThread: ({ message }) => { if (threadReplySelectType === ThreadReplySelectType.THREAD) { onReplyInThread({ message }); @@ -308,11 +309,11 @@ export default function MessageContent(props: MessageContentProps): ReactElement })} {isReactionEnabledInChannel && ( renderEmojiMenu({ - message: message as SendableMessageType, - userId: userId, - emojiContainer: emojiContainer, - toggleReaction: toggleReaction, - setSupposedHover: setSupposedHover, + message, + userId, + emojiContainer, + toggleReaction, + setSupposedHover, }) )}
@@ -338,13 +339,13 @@ export default function MessageContent(props: MessageContentProps): ReactElement className={getClassName(['sendbird-message-content__middle__quote-message', isByMe ? 'outgoing' : 'incoming', useReplyingClassName])}> (message.parentMessage?.createdAt ?? 0)} onClick={() => { if (replyType === 'THREAD' && threadReplySelectType === ThreadReplySelectType.THREAD) { - onQuoteMessageClick?.({ message: message as SendableMessageType }); + onQuoteMessageClick?.({ message }); } if ( (replyType === 'QUOTE_REPLY' || (replyType === 'THREAD' && threadReplySelectType === ThreadReplySelectType.PARENT)) @@ -377,7 +378,7 @@ export default function MessageContent(props: MessageContentProps): ReactElement >
@@ -402,16 +403,16 @@ export default function MessageContent(props: MessageContentProps): ReactElement {(isReactionEnabledInChannel && message?.reactions?.length > 0) && (
{ renderEmojiReactions({ userId, - message: message as SendableMessageType, + message, channel, isByMe, emojiContainer, @@ -454,7 +455,7 @@ export default function MessageContent(props: MessageContentProps): ReactElement onReplyInThread?.({ message: message as SendableMessageType })} + onClick={() => onReplyInThread?.({ message })} ref={threadRepliesRef} /> )} @@ -526,17 +527,17 @@ export default function MessageContent(props: MessageContentProps): ReactElement {isReactionEnabledInChannel && ( renderEmojiMenu({ className: 'sendbird-message-content-menu__reaction-menu', - message: message as SendableMessageType, - userId: userId, - emojiContainer: emojiContainer, - toggleReaction: toggleReaction, - setSupposedHover: setSupposedHover, + message, + userId, + emojiContainer, + toggleReaction, + setSupposedHover, }) )} {renderMessageMenu({ className: 'sendbird-message-content-menu__normal-menu', channel, - message: message as SendableMessageType, + message, isByMe, replyType, disabled, @@ -558,9 +559,7 @@ export default function MessageContent(props: MessageContentProps): ReactElement )} { - showMenu && ( - message?.isUserMessage?.() || message?.isFileMessage?.() || message?.isMultipleFilesMessage?.() - ) && renderMobileMenuOnLongPress({ + showMenu && isSendableMessage(message) && renderMobileMenuOnLongPress({ parentRef: contentRef, channel, hideMenu: () => { setShowMenu(false); }, @@ -589,7 +588,7 @@ export default function MessageContent(props: MessageContentProps): ReactElement return null; } try { - const allowDownload = await onBeforeDownloadFileMessage({ message: message as FileMessage }); + const allowDownload = await onBeforeDownloadFileMessage({ message }); if (!allowDownload) { e.preventDefault(); logger?.info?.('MessageContent: Not allowed to download.'); diff --git a/src/ui/QuoteMessageInput/QuoteMessageThumbnail.tsx b/src/ui/QuoteMessageInput/QuoteMessageThumbnail.tsx index 189899093..6973d51a3 100644 --- a/src/ui/QuoteMessageInput/QuoteMessageThumbnail.tsx +++ b/src/ui/QuoteMessageInput/QuoteMessageThumbnail.tsx @@ -5,7 +5,8 @@ import Icon, { IconTypes, IconColors } from '../Icon'; import ImageRenderer from '../ImageRenderer'; import { isAudioMessage, - isFileMessage, isImageFileInfo, + isFileMessage, + isImageFileInfo, isImageMessage, isMultipleFilesMessage, isThumbnailMessage, @@ -22,30 +23,19 @@ interface Props { const componentClassname = 'sendbird-quote_message_input__avatar'; export default function QuoteMessageThumbnail({ message }: Props): ReactElement { - if (!isFileMessage(message) && !isMultipleFilesMessage(message) || isVoiceMessage(message as FileMessage)) { + if (!isFileMessage(message) && !isMultipleFilesMessage(message) || isVoiceMessage(message)) { return null; } let thumbnailUrl = getMessageFirstFileThumbnailUrl(message); if (!thumbnailUrl) { - if ( - message.isFileMessage?.() - && ( - isImageMessage(message) - || isVideoMessage(message) - ) - ) { + if (isImageMessage(message) || isVideoMessage(message)) { thumbnailUrl = getMessageFirstFileUrl(message); - } else if ( - message.isMultipleFilesMessage?.() - && ( - message.fileInfoList.length > 0 - && isImageFileInfo((message).fileInfoList[0]) - ) - ) { + } else if (isMultipleFilesMessage(message) && isImageFileInfo(message.fileInfoList?.[0])) { thumbnailUrl = message.fileInfoList[0].url; } } - if (isVideoMessage(message as FileMessage) && thumbnailUrl) { + + if (isVideoMessage(message) && thumbnailUrl) { return (