import React, { useEffect, useState } from "react"; import { Modal, View, Text, TouchableOpacity, StyleSheet, TextInput, StyleProp, ViewStyle, TextStyle, KeyboardAvoidingView, Platform, ScrollView, } from "react-native"; import { useTheme } from "../../../theme"; import { useCometChatTranslation } from "../../resources/CometChatLocalizeNew"; import { Icon } from "../../icons/Icon"; import { CometChat } from "@cometchat/chat-sdk-react-native"; import { useKeyboard } from "../../helper/useKeyboard"; export interface CometChatReportDialogInterface { /** Controls modal visibility */ isOpen?: boolean; /** Message object to be flagged */ message?: CometChat.BaseMessage; /** Hide remark input field */ hideFlagRemarkField?: boolean; /** Callback for cancel */ onCancel?: () => void; /** Modal dismiss callback */ onDismiss?: () => void; /** Optional custom handler after successful flag (receives SDK payload incl. original message) */ onReport?: (payload: { message: CometChat.BaseMessage; reason: CometChat.FlagReason; description: string; }) => void | Promise; /** Override reasons instead of fetching (mainly for testing) */ reasons?: CometChat.FlagReason[]; /** Text overrides / translations */ titleText?: string; messageText?: string; descriptionPlaceholder?: string; cancelButtonText?: string; reportButtonText?: string; /** Styling overrides */ containerStyle?: StyleProp; titleContainerStyle?: StyleProp; titleTextStyle?: StyleProp; messageTextStyle?: StyleProp; pillsContainerStyle?: StyleProp; pillStyle?: StyleProp; pillTextStyle?: StyleProp; textInputContainerStyle?: StyleProp; sectionTitle?: StyleProp; descriptionTextStyle?: StyleProp; buttonContainerStyle?: StyleProp; cancelButtonStyle?: StyleProp; cancelButtonTextStyle?: StyleProp; reportButtonStyle?: StyleProp; reportButtonTextStyle?: StyleProp; /** Error callback */ onError?: (error: CometChat.CometChatException) => void; } export const CometChatReportDialog = (props: CometChatReportDialogInterface) => { const { t } = useCometChatTranslation(); const keyboardHeight = useKeyboard(); const { isOpen, message, hideFlagRemarkField = false, onCancel, onDismiss = () => null, onReport, reasons, titleText = t("Flag_Message_Title"), messageText = t("Flag_Message_Subtitle"), descriptionPlaceholder = t("Flag_Message_Remark_Placeholder"), cancelButtonText = t("Flag_Message_Confirm_No"), reportButtonText = t("Flag_Message_Confirm_Yes"), containerStyle = {}, titleContainerStyle = {}, titleTextStyle = {}, messageTextStyle = {}, pillsContainerStyle = {}, pillStyle = {}, pillTextStyle = {}, buttonContainerStyle = {}, textInputContainerStyle = {}, sectionTitle = {}, descriptionTextStyle = {}, cancelButtonStyle = {}, cancelButtonTextStyle = {}, reportButtonStyle = {}, reportButtonTextStyle = {}, onError, } = props; const theme = useTheme(); const [flagReasons, setFlagReasons] = useState(reasons || []); const [isFetchingReasons, setIsFetchingReasons] = useState(false); const [selectedReason, setSelectedReason] = useState(null); const [description, setDescription] = useState(""); const [errorMessage, setErrorMessage] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); const [errorTimeoutId, setErrorTimeoutId] = useState | null>(null); const [isTextInputFocused, setIsTextInputFocused] = useState(false); const resetStates = () => { setSelectedReason(null); setDescription(""); setErrorMessage(""); setIsSubmitting(false); }; useEffect(() => { if (isOpen && !reasons) { (async () => { try { setIsFetchingReasons(true); const list = await CometChat.getFlagReasons(); setFlagReasons(list); } catch (e: any) { onError?.(e); showError(t("Flag_Error_Text")); } finally { setIsFetchingReasons(false); } })(); } else if (reasons) { setFlagReasons(reasons); } if (!isOpen) { resetStates(); } }, [isOpen, reasons, onError, t]); const handleCancel = () => { resetStates(); onCancel?.(); }; const showError = (message: string) => { // Clear any existing timer if (errorTimeoutId) { clearTimeout(errorTimeoutId); } setErrorMessage(message); const id = setTimeout(() => { setErrorMessage(""); }, 3500); // auto clear after 3.5 seconds setErrorTimeoutId(id); }; const handleReport = async () => { // Validation: missing reason if (!selectedReason) { return; } if (!message) { showError(t("Flag_Error_Text")); return; } setIsSubmitting(true); try { // Trim remark length (max 100 similar to web) const remark = description.trim().substring(0, 100); const messageId = String((message as any).getId?.() ?? ""); if (!messageId) { throw new Error("Invalid message id"); } // SDK call const sdkResult = await CometChat.flagMessage(messageId, { reasonId: selectedReason.id, remark: remark.trim().length > 0 ? remark.trim() : undefined, }); console.log("[ReportDialog] flagMessage() success:", sdkResult); // Fire optional external callback const result = onReport?.({ message, reason: selectedReason, description: remark }); if (result && typeof (result as Promise).then === "function") { await (result as Promise); } // Show success toast then auto-close after 3s setErrorMessage(""); } catch (e: any) { onError?.(e); showError(t("Flag_Error_Text")); console.log("[ReportDialog] flagMessage() error:", e); } finally { setIsSubmitting(false); } }; useEffect(() => { return () => { if (errorTimeoutId) clearTimeout(errorTimeoutId); }; }, [errorTimeoutId]); return ( 0 || isTextInputFocused) && styles.wrapperWithKeyboard, ]} keyboardShouldPersistTaps='handled' showsVerticalScrollIndicator={false} > {titleText} { e.stopPropagation(); handleCancel(); }} > {messageText} {isFetchingReasons && ( {"Loading..."} )} {!isFetchingReasons && flagReasons.map((r) => { const active = r.id === selectedReason?.id; return ( { e.stopPropagation(); // Toggle selection: if already selected, deselect setSelectedReason((prev) => (prev?.id === r.id ? null : r)); setErrorMessage(""); }} style={[ theme.reportDialogStyles.pillsStyle, { backgroundColor: active ? theme.color.extendedPrimary100 : theme.color.background1, borderColor: active ? theme.color.extendedPrimary200 : theme.color.borderDefault, opacity: isSubmitting ? 0.6 : 1, }, pillStyle, ]} > {r.name} ); })} {!hideFlagRemarkField && ( {t("Flag_Message_Remark_Label")}{" "} ({t("Flag_Message_Remark_Optional")}) setIsTextInputFocused(true)} onBlur={() => setIsTextInputFocused(false)} onChangeText={(val) => setDescription(val.substring(0, 100))} editable={!isSubmitting} style={[theme.reportDialogStyles.descriptionTextStyle, descriptionTextStyle]} /> )} {!!errorMessage && ( {errorMessage} )} { e.stopPropagation(); handleCancel(); }} > {cancelButtonText} { e.stopPropagation(); handleReport(); }} > {reportButtonText} ); }; const styles = StyleSheet.create({ keyboardAvoidingView: { flex: 1, }, wrapper: { flexGrow: 1, justifyContent: "center", alignItems: "center", backgroundColor: "#141414CC", paddingHorizontal: 20, minHeight: "100%", }, wrapperWithKeyboard: { justifyContent: "flex-start", paddingTop: Platform.OS === "ios" ? 50 : 20, }, }); export default CometChatReportDialog;