@cometchat/chat-uikit-react-native
Version:
Ready-to-use Chat UI Components for React Native
265 lines • 11.7 kB
JavaScript
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