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,
}}
/>
)}
);
};