import { CometChat } from "@cometchat/chat-sdk-react-native"; import React, { useRef } from "react"; import { ActivityIndicator, FlatList, Image, Text, TouchableOpacity, View } from "react-native"; import { Icon } from "../../../shared/icons/Icon"; import { ErrorEmptyView } from "../../../shared/views/ErrorEmptyView/ErrorEmptyView"; import { useTheme } from "../../../theme"; import { CometChatTheme } from "../../../theme/type"; import { ExtensionConstants } from "../../ExtensionConstants"; import { Hooks } from "./hooks"; import { Skeleton } from "./Skeleton"; import { Styles } from "./style"; import { useCometChatTranslation } from "../../../shared/resources/CometChatLocalizeNew"; /** * Interface defining the props for CometChatStickerKeyboard component */ export interface CometChatStickerKeyboardInterface { loadingText?: string; theme?: CometChatTheme; onPress?: (item: any) => void; // Callback when a sticker is pressed emptyText?: string; // Optional text when no stickers are available errorText?: string; // Optional error text } /** * Props for the StickerItem component */ interface StickerItemProps { stickerItem: any; // Sticker item data or null for placeholders onPress: (item: any) => void; // Callback when the sticker is pressed theme: CometChatTheme; // Theme object } /** * StickerItem Component * Renders individual sticker items within the sticker keyboard. * Handles loading state, error state, and placeholder rendering. */ const StickerItem = ({ stickerItem, onPress, theme }: StickerItemProps) => { // State to manage loading status of the sticker image const [isLoading, setIsLoading] = React.useState(true); // State to manage error status if the image fails to load const [hasError, setHasError] = React.useState(false); // If stickerItem is null, render an empty placeholder to maintain grid structure if (!stickerItem) { return ; } return ( onPress(stickerItem)} accessibilityRole='button' > {hasError ? ( // If there was an error loading the sticker, display a default icon ) : ( // Otherwise, display the sticker image setIsLoading(false)} // Update loading state when image loads onError={() => { setIsLoading(false); setHasError(true); // Update error state if image fails to load }} resizeMode='contain' /> )} {isLoading && !hasError && ( // Show a loading indicator while the image is loading )} ); }; /** * CometChatStickerKeyboard Component * Fetches stickers from the Stickers extension and displays them in a keyboard layout. */ export const CometChatStickerKeyboard = (props: CometChatStickerKeyboardInterface) => { const [stickerList, setStickerList] = React.useState([]); const [stickerSet, setStickerSet] = React.useState>({}); const [activeStickerList, setActiveStickerList] = React.useState([]); const [activeStickerSetName, setActiveStickerSetName] = React.useState( undefined ); const [loading, setLoading] = React.useState(true); const [error, setError] = React.useState(null); const theme = useTheme(); const {t}= useCometChatTranslation() const flatListRef = useRef(null); /** * Function to handle sending a sticker message * @param stickerItem - The selected sticker item */ const sendStickerMessage = (stickerItem: any) => { if (props?.onPress) { // Invoke the onPress callback with the sticker message props.onPress({ ...stickerItem, sticker_url: stickerItem?.stickerUrl, sticker_name: stickerItem?.stickerSetName, }); } }; /** * Function to handle the selection of a sticker set * @param sectionItem - The name of the selected sticker set */ const onStickerSetClicked = (sectionItem: string) => { setActiveStickerList(stickerSet[sectionItem]); setActiveStickerSetName(sectionItem); // Scroll the sticker list to the top when a new set is selected if (flatListRef.current) { flatListRef.current.scrollToOffset({ offset: 0, animated: false }); } }; /** * Function to render the list of stickers based on the current state */ const getStickerList = () => { if (error) { return ( {t("SOMETHING_WENT_WRONG")} ); } // If data is still loading, display the skeleton loader if (loading) { return ( ); } // If there are no stickers available, display an empty state view if (stickerList.length === 0) { return ( } containerStyle={[Styles.stickerContainer, Styles.emptyContainer]} titleStyle={[theme.typography.heading4.bold, { color: theme.color.textPrimary }]} subTitleStyle={[theme.typography.body.regular, { color: theme.color.textSecondary }]} /> ); } // If sticker sets are available, render them in a grid if (stickerSet && Object.keys(stickerSet).length) { const numberOfStickers = activeStickerList.length; const numberOfPlaceholders = numberOfStickers % 3 === 0 ? 0 : 3 - (numberOfStickers % 3); const paddedStickerList = [...activeStickerList]; for (let i = 0; i < numberOfPlaceholders; i++) { paddedStickerList.push(null); } return ( ( )} keyExtractor={(item, index) => (item ? `${item.id}-${index}` : `placeholder-${index}`)} numColumns={3} contentContainerStyle={{ paddingHorizontal: theme.spacing.spacing.s5, }} // Adds horizontal padding showsVerticalScrollIndicator={false} /> ); } return null; }; // Invoke custom hooks to fetch stickers and manage state Hooks( props, stickerList, stickerSet, activeStickerSetName, setStickerList, setStickerSet, setActiveStickerList, setActiveStickerSetName, setLoading, setError ); return ( {/* Header displaying the active sticker set name or a default label */} {activeStickerSetName} {/* Main content area for stickers and category selector */} {/* Render the list of stickers based on current state */} {getStickerList()} {/* If there's no error, display the sticker set category selector */} {!error && ( { // Retrieve the thumbnail for the sticker set (first sticker's URL) const stickerSetThumbnail = stickerSet[item][0][ExtensionConstants.stickerUrl]; // Determine if this sticker set is currently active const isActive = item === activeStickerSetName; return ( onStickerSetClicked(item)} accessibilityLabel={`Sticker category ${item}`} accessibilityRole='button' > {isActive && ( // Highlight the active sticker set )} {/* Display the sticker set thumbnail */} ); }} keyExtractor={(item, index) => `${item}-${index}`} horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ paddingHorizontal: theme.spacing.spacing.s3, }} /> )} ); };