UNPKG

@cometchat/chat-uikit-react-native

Version:

Ready-to-use Chat UI Components for React Native

265 lines 11.7 kB
import { CometChat } from "@cometchat/chat-sdk-react-native"; import React, { useCallback, useEffect, useRef, useState } from "react"; import { Keyboard, Platform, TouchableOpacity } from "react-native"; import { CometChatMessageTemplate, CometChatUIEventHandler, CometChatUIEvents, } from "../../shared"; import { ChatConfigurator, DataSourceDecorator } from "../../shared/framework"; import { CometChatUIKit } from "../../shared/CometChatUiKit/CometChatUIKit"; import { MessageCategoryConstants, ViewAlignment } from "../../shared/constants/UIKitConstants"; import { Icon } from "../../shared/icons/Icon"; import { localize } from "../../shared/resources"; import { getUnixTimestampInMilliseconds } from "../../shared/utils/CometChatMessageHelper"; import { getMessagePreviewInternal } from "../../shared/utils/MessageUtils"; import { useTheme } from "../../theme"; import { ExtensionTypeConstants } from "../ExtensionConstants"; import { CometChatStickerKeyboard } from "./CometChatStickerKeyboard"; import { CometChatStickerBubble } from "./StickersBubble"; /** * StickerButton Component * A button that toggles a sticker panel, allowing users to send stickers as custom messages. * Handles keyboard interactions and panel visibility for smooth user experience. * * @param {Object} props - Component props. * @param {CometChat.User} props.user - User object representing the chat receiver. * @param {CometChat.Group} props.group - Group object representing the chat group. * @param {Map<string, any>} props.id - Additional metadata, like parent message ID. */ const StickerButton = ({ user, group, id, stickerIconStyle, stickerIcon }) => { const [isPanelOpen, setIsPanelOpen] = useState(false); // Tracks sticker panel visibility state. const [keyboardOpen, setKeyboardOpen] = useState(false); // Tracks if the system keyboard is open. const loggedInUser = useRef(null); // Stores the currently logged-in user. const theme = useTheme(); // Retrieves theme configurations for styling. /** * Fetches the logged-in user and sets it to `loggedInUser` ref. */ useEffect(() => { CometChat.getLoggedinUser().then((u) => (loggedInUser.current = u)); }, []); /** * Platform-specific keyboard event names for show and hide events. */ const keyboardShowEvent = Platform.select({ ios: "keyboardWillShow", android: "keyboardDidShow", }); const keyboardHideEvent = Platform.select({ ios: "keyboardWillHide", android: "keyboardDidHide", }); /** * Keyboard event listeners to update keyboard state. * Automatically closes the sticker panel when the keyboard appears. */ useEffect(() => { const keyboardDidShowListener = Keyboard.addListener(keyboardShowEvent, () => { setKeyboardOpen(true); if (isPanelOpen) { closePanel(); } }); const keyboardDidHideListener = Keyboard.addListener(keyboardHideEvent, () => { setKeyboardOpen(false); }); return () => { keyboardDidShowListener.remove(); keyboardDidHideListener.remove(); }; }, [isPanelOpen, keyboardShowEvent, keyboardHideEvent]); /** * Sends a custom sticker message to the receiver (user or group). * * @param {Object} sticker - Sticker object containing sticker details. */ const sendCustomMessage = (sticker) => { // Determine receiver details. let receiverId = user?.getUid() || group?.getGuid(); let receiverType = user ? CometChat.RECEIVER_TYPE.USER : group ? CometChat.RECEIVER_TYPE.GROUP : undefined; if (!receiverType) { console.error("Receiver type is undefined."); return; } // Prepare the custom sticker message. let customType = ExtensionTypeConstants.sticker; let customData = sticker; let parentId = id?.get("parentMessageId") || undefined; let customMessage = new CometChat.CustomMessage(receiverId, receiverType, customType, customData); // Configure message metadata and properties. customMessage.setCategory(CometChat.CATEGORY_CUSTOM); customMessage.setParentMessageId(parentId); customMessage.setMuid(String(getUnixTimestampInMilliseconds())); customMessage.setSender(loggedInUser.current); customMessage.setReceiver(user || group); customMessage.shouldUpdateConversation(true); customMessage.setMetadata({ incrementUnreadCount: true }); // Send the custom message using CometChatUIKit. CometChatUIKit.sendCustomMessage(customMessage) .then((res) => { console.log("Sticker sent successfully:", res); }) .catch((err) => { console.error("Failed to send sticker:", err); }); }; /** * Opens the sticker panel. */ const OpenPanel = useCallback(() => { setIsPanelOpen(true); CometChatUIEventHandler.emitUIEvent(CometChatUIEvents.showPanel, { alignment: ViewAlignment.composerBottom, child: () => <CometChatStickerKeyboard onPress={sendCustomMessage}/>, // Render the sticker keyboard. }); }, []); /** * Closes the sticker panel. */ const closePanel = useCallback(() => { CometChatUIEventHandler.emitUIEvent(CometChatUIEvents.hidePanel, { alignment: ViewAlignment.composerBottom, child: () => null, // Hide the panel content. }); setIsPanelOpen(false); }, []); /** * Toggles the sticker panel visibility. * Handles interactions with the keyboard for a smooth user experience. */ const togglePanel = useCallback(() => { if (isPanelOpen) { closePanel(); } else { if (keyboardOpen) { Keyboard.dismiss(); // Close the keyboard first. setTimeout(() => { OpenPanel(); // Open the sticker panel after a small delay. }, 200); } else { OpenPanel(); // Open the panel directly if the keyboard isn't open. } } }, [isPanelOpen, keyboardOpen, OpenPanel, closePanel]); return (<TouchableOpacity key={"sticker"} onPress={togglePanel} style={{ justifyContent: "center", alignItems: "center", padding: 10 }} accessibilityLabel='Sticker Button' accessibilityHint='Opens the sticker panel'> <Icon name='sticker-fill' width={24} height={24} imageStyle={stickerIconStyle} icon={!isPanelOpen ? stickerIcon?.inactive : stickerIcon?.active} //color={!isPanelOpen ? theme.color.iconSecondary : theme.color.primary} color={!isPanelOpen ? (stickerIconStyle.inactive.tintColor ?? theme.color.iconSecondary) : theme.color.primary}/> </TouchableOpacity>); }; /** * StickersExtensionDecorator Class * Extends the DataSourceDecorator to add support for sticker messages. * Defines sticker message templates, auxiliary options, and category/type handling. */ export class StickersExtensionDecorator extends DataSourceDecorator { configuration; constructor(props) { super(props.dataSource); this.configuration = props.configration ?? {}; // Load configuration if provided. } /** * Checks if the message is deleted. * @param {CometChat.BaseMessage} message - The message to check. * @returns {boolean} - True if the message is deleted, otherwise false. */ isDeletedMessage(message) { return message.getDeletedBy() != null; } /** * Adds sticker templates to the list of message templates. */ getAllMessageTemplates(theme, additionalParams) { let templates = super.getAllMessageTemplates(theme, additionalParams); templates.push(new CometChatMessageTemplate({ type: ExtensionTypeConstants.sticker, category: MessageCategoryConstants.custom, ContentView: (message, alignment) => { if (this.isDeletedMessage(message)) { return ChatConfigurator.dataSource.getDeleteMessageBubble(message, theme); } else { return this.getStickerBubble(message, alignment, theme); } }, options: (loggedInuser, message, theme, group) => { return ChatConfigurator.dataSource.getMessageOptions(loggedInuser, message, theme, group, additionalParams); }, BottomView: (message, alignment) => { return ChatConfigurator.dataSource.getBottomView(message, alignment); }, })); return templates; } /** * Renders a sticker bubble containing the sticker image. */ getStickerBubble(message, alignment, theme) { let url = message?.["data"]?.["customData"]?.["stickerUrl"] || message?.["data"]?.["customData"]?.["sticker_url"]; let loggedInUser = CometChatUIKit.loggedInUser; const _style = message.getSender().getUid() === loggedInUser.getUid() ? theme.messageListStyles.outgoingMessageBubbleStyles : theme.messageListStyles.incomingMessageBubbleStyles; return (<CometChatStickerBubble url={url} name='' style={{ ...this.configuration?.style, ..._style.stickerBubbleStyles?.imageStyle, }}/>); } /** * Adds the sticker button to auxiliary options for message input. */ getAuxiliaryOptions(user, group, id, additionalAuxiliaryParams) { const auxiliaryOptions = super.getAuxiliaryOptions(user, group, id ?? new Map(), additionalAuxiliaryParams); if (additionalAuxiliaryParams?.hideStickersButton) return auxiliaryOptions; auxiliaryOptions.push(<StickerButton key='sticker-button' user={user} group={group} id={id} stickerIcon={additionalAuxiliaryParams?.stickerIcon} stickerIconStyle={additionalAuxiliaryParams?.stickerIconStyle}/>); return auxiliaryOptions; } /** * Ensures that "custom" is included in the list of message categories. */ getAllMessageCategories() { var categoryList = super.getAllMessageCategories(); if (!categoryList.includes(MessageCategoryConstants.custom)) { categoryList.push(MessageCategoryConstants.custom); } return categoryList; } /** * Adds the sticker type to the list of supported message types. */ getAllMessageTypes() { var messagesTypes = super.getAllMessageTypes(); messagesTypes.push(ExtensionTypeConstants.sticker); return messagesTypes; } /** * Returns a unique identifier for the sticker extension. */ getId() { return "stickerExtension"; } /** * Customizes the last conversation message preview for sticker messages. */ getLastConversationMessage(conversation, theme) { const message = conversation.getLastMessage(); if (message != null && message.getType() === ExtensionTypeConstants.sticker && message.getCategory() === MessageCategoryConstants.custom && message.getDeletedAt() === undefined) { return getMessagePreviewInternal("sticker-fill", localize("CUSTOM_MESSAGE_STICKER"), { theme, }); } else { return super.getLastConversationMessage(conversation, theme); } } } //# sourceMappingURL=StickersExtensionDecorator.js.map