import React, { PropsWithChildren, useContext } from 'react'; import { PressableProps, ViewProps } from 'react-native'; import type { Attachment, Channel, ChannelState, CommandSuggestion, DeleteMessageOptions, LocalMessage, MessageResponse, } from 'stream-chat'; import type { MessagePressableHandlerPayload } from '../../components/Message/Message'; import type { MarkdownRules } from '../../components/Message/MessageItemView/utils/renderText'; import type { MessageActionsParams } from '../../components/Message/utils/messageActions'; import type { GroupStyle, MessageGroupStylesParams, } from '../../components/MessageList/utils/getGroupStyles'; import type { MessageActionType } from '../../components/MessageMenu/MessageActionListItem'; import { NativeHandlers } from '../../native'; import type { ReactionData } from '../../utils/utils'; import type { Alignment, MessageContextValue } from '../messageContext/MessageContext'; import type { DeepPartial } from '../themeContext/ThemeContext'; import type { Theme } from '../themeContext/utils/theme'; import { DEFAULT_BASE_CONTEXT_VALUE } from '../utils/defaultBaseContextValue'; import { isTestEnvironment } from '../utils/isTestEnvironment'; export type MessageContentType = | 'attachments' | 'files' | 'gallery' | 'quoted_reply' | 'poll' | 'ai_text' | 'text' | 'location'; export type MessageLocationProps = { message: LocalMessage; }; export type MessagesContextValue = Pick & { // FIXME: Remove the signature with optionsOrHardDelete boolean with the next major release deleteMessage: ( message: LocalMessage, optionsOrHardDelete?: boolean | DeleteMessageOptions, ) => Promise; deleteReaction: (type: string, messageId: string) => Promise; /** Should keyboard be dismissed when messaged is touched */ dismissKeyboardOnMessageTouch: boolean; enableMessageGroupingByUser: boolean; /** * The type of URL preview to render. * Defaults to: 'full' */ urlPreviewType: 'compact' | 'full'; FlatList: typeof NativeHandlers.FlatList | undefined; /** * The giphy version to render - check the keys of the [Image Object](https://developers.giphy.com/docs/api/schema#image-object) for possible values. Uses 'fixed_height' by default * */ giphyVersion: keyof NonNullable; /** * When true, messageList will be scrolled at first unread message, when opened. */ initialScrollToFirstUnreadMessage: boolean; /** Order to render the message content */ messageContentOrder: MessageContentType[]; /** * Overlay target id that should be teleported when a message overlay opens. * Custom `MessageOverlayWrapper` usages should pass a matching `targetId`. */ messageOverlayTargetId?: string; removeMessage: (message: { id: string; parent_id?: string }) => Promise; /** * Override the api request for retry message functionality. */ retrySendMessage: (message: LocalMessage) => Promise; sendReaction: (type: string, messageId: string) => Promise; updateMessage: ( updatedMessage: MessageResponse | LocalMessage, extraState?: { commands?: CommandSuggestion[]; messageInput?: string; threadMessages?: ChannelState['threads'][string]; }, throttled?: boolean, ) => void; /** * Provide any additional props for `Pressable` which wraps inner MessageContent component here. * Please check docs for Pressable for supported props - https://reactnative.dev/docs/pressable#props * * @overrideType Object */ additionalPressableProps?: Omit; /** * Custom handler to handle message swipe action. * * The default behaviour is swipe to reply for this. */ customMessageSwipeAction?: ({ channel, message, }: { channel: Channel; message: LocalMessage; }) => void; disableTypingIndicator?: boolean; /** * Enable swipe to reply on messages. */ enableSwipeToReply?: boolean; /** * Whether messages should be aligned to right or left part of screen. * By default, messages will be received messages will be aligned to left and * sent messages will be aligned to right. */ forceAlignMessages?: Alignment | boolean; getMessageGroupStyle?: (params: MessageGroupStylesParams) => GroupStyle[]; /** * Handler to access when a ban user action is invoked. * @param message */ handleBan?: (message: LocalMessage) => Promise; /** Handler to access when a copy message action is invoked */ handleCopy?: (message: LocalMessage) => Promise; /** Handler to access when a delete for me message action is invoked */ handleDeleteForMe?: (message: LocalMessage) => Promise; /** Handler to access when a delete message action is invoked */ handleDelete?: (message: LocalMessage) => Promise; /** Handler to access when an edit message action is invoked */ handleEdit?: (message: LocalMessage) => void; /** Handler to access when a flag message action is invoked */ handleFlag?: (message: LocalMessage) => Promise; /** Handler to access when a mark unread action is invoked */ handleMarkUnread?: (message: LocalMessage) => Promise; /** Handler to access when a mute user action is invoked */ handleMute?: (message: LocalMessage) => Promise; /** Handler to access when a pin/unpin user action is invoked*/ handlePinMessage?: ((message: LocalMessage) => MessageActionType) | null; /** Handler to access when a quoted reply action is invoked */ handleQuotedReply?: (message: LocalMessage) => Promise; /** Handler to process a reaction */ handleReaction?: (message: LocalMessage, reactionType: string) => Promise; /** Handler to access when a retry action is invoked */ handleRetry?: (message: LocalMessage) => Promise; /** Handler to access when a thread reply action is invoked */ handleThreadReply?: (message: LocalMessage) => Promise; /** Handler to access when a blocking user action is invoked */ handleBlockUser?: (user: LocalMessage['user']) => Promise; /** A flag specifying whether the poll creation button is available or not. */ hasCreatePoll?: boolean; /** Handler to deal with custom memoization logic of Attachment */ isAttachmentEqual?: (prevAttachment: Attachment, nextAttachment: Attachment) => boolean; /** Object specifying rules defined within simple-markdown https://github.com/Khan/simple-markdown#adding-a-simple-extension */ markdownRules?: MarkdownRules; /** * Use this prop to override message actions (which pop-up in message overlay). * * You can either completely override the default messageActions object. * * ``` * { someAction() }; * title: "Pin Message"; * icon: PinIcon; * titleStyle: {}; * }, * { * action: () => { someAction() }; * title: "Delete Message"; * icon: PinIcon; * titleStyle: {}; * } * ] * > * * ``` * * Or you can selectly keep certain action and remove some: * * e.g. Lets say you only want to keep threadReply and copyMessage actions * * ``` * ([ * threadReply, copyMessage * ])} * > * * ``` * * @overrideType Function | Array */ messageActions?: (param: MessageActionsParams) => MessageActionType[]; /** * HitSlop for the message swipe to reply gesture */ messageSwipeToReplyHitSlop?: ViewProps['hitSlop']; /** * The number of lines of the message text to be displayed */ messageTextNumberOfLines?: number; /** * Theme provided only to messages that are the current users */ myMessageTheme?: DeepPartial; /** * Override default handler for onLongPress on message. You have access to payload of that handler as param: * * ``` * Promise; * editMessage, // () => void; * quotedReply, // () => void; * resendMessage, // () => Promise; * showMessageOverlay, // () => void; * toggleBanUser, // () => Promise; * toggleMuteUser, // () => Promise; * toggleReaction, // (reactionType: string) => Promise; * }, * defaultHandler, // () => void * event, // any event object corresponding to touchable feedback * emitter, // which component trigged this touchable feedback e.g. card, fileAttachment, gallery, message ... etc * message // message object on which longPress occured * }) => { * // Your custom action * }} * /> * ``` */ onLongPressMessage?: (payload: MessagePressableHandlerPayload) => void; /** * Add onPressIn handler for attachments. You have access to payload of that handler as param: * * ``` * Promise; * editMessage, // () => void; * quotedReply, // () => void; * resendMessage, // () => Promise; * showMessageOverlay, // () => void; * toggleBanUser, // () => Promise; * toggleMuteUser, // () => Promise; * toggleReaction, // (reactionType: string) => Promise; * }, * defaultHandler, // () => void * event, // any event object corresponding to touchable feedback * emitter, // which component trigged this touchable feedback e.g. card, fileAttachment, gallery, message ... etc * message // message object on which longPress occured * }) => { * // Your custom action * }} * /> * ``` */ onPressInMessage?: (payload: MessagePressableHandlerPayload) => void; /** * Override onPress handler for message. You have access to payload of that handler as param: * * ``` * Promise; * editMessage, // () => void; * quotedReply, // () => void; * resendMessage, // () => Promise; * showMessageOverlay, // () => void; * toggleBanUser, // () => Promise; * toggleMuteUser, // () => Promise; * toggleReaction, // (reactionType: string) => Promise; * }, * defaultHandler, // () => void * event, // any event object corresponding to touchable feedback * emitter, // which component trigged this touchable feedback e.g. card, fileAttachment, gallery, message ... etc * message // message object on which longPress occurred * }) => { * // Your custom action * }} * /> * ``` */ onPressMessage?: (payload: MessagePressableHandlerPayload) => void; quotedMessage?: LocalMessage | null; /** * The position of the reaction list in the message */ reactionListPosition?: 'top' | 'bottom'; /** * The alignment of the reaction list */ reactionListType?: 'clustered' | 'segmented'; /** * Full override of the reaction function on Message and Message Overlay * * Please check [cookbook](https://github.com/GetStream/stream-chat-react-native/wiki/Cookbook-v3.0#override-or-intercept-message-actions-edit-delete-reaction-reply-etc) for details. * */ selectReaction?: (message: LocalMessage) => (reactionType: string) => Promise; /** * Boolean to enable/disable the message underlay background when there are unread messages in the Message List. */ shouldShowUnreadUnderlay?: boolean; /** * The supported reactions that the user can use to react to messages. */ supportedReactions?: ReactionData[]; targetedMessage?: string; }; export const MessagesContext = React.createContext( DEFAULT_BASE_CONTEXT_VALUE as MessagesContextValue, ); export const MessagesProvider = ({ children, value, }: PropsWithChildren<{ value?: MessagesContextValue; }>) => ( {children} ); export const useMessagesContext = () => { const contextValue = useContext(MessagesContext) as unknown as MessagesContextValue; if (contextValue === DEFAULT_BASE_CONTEXT_VALUE && !isTestEnvironment()) { throw new Error( 'The useMessagesContext hook was called outside of the MessagesContext provider. Make sure you have configured MessageList component correctly - https://getstream.io/chat/docs/sdk/reactnative/basics/hello_stream_chat/#message-list', ); } return contextValue; };