import { ChatConfigurator, DataSource, DataSourceDecorator } from "../../shared/framework";
import { CometChat } from "@cometchat/chat-sdk-react-native";
import { CometChatMessageOption } from "../../shared/modals";
import { ExtensionConstants, ExtensionURLs } from "../ExtensionConstants";
import React, { JSX } from "react";
import {
CometChatMentionsFormatter,
CometChatTextFormatter,
CometChatUIKit,
CometChatUrlsFormatter,
getCurrentLanguage,
MentionTextStyle,
} from "../../shared";
import { AdditionalParams, MessageBubbleAlignmentType } from "../../shared/base/Types";
import {
MentionsTargetElement,
MessageOptionConstants,
} from "../../shared/constants/UIKitConstants";
import { CometChatUIEvents, MessageEvents } from "../../shared/events";
import { CometChatUIEventHandler } from "../../shared/events/CometChatUIEventHandler/CometChatUIEventHandler";
import { messageStatus } from "../../shared/utils/CometChatMessageHelper";
import { CommonUtils } from "../../shared/utils/CommonUtils";
import { MessageTranslationBubble } from "./MessageTranslationBubble";
import { CometChatTheme } from "../../theme/type";
import { Icon } from "../../shared/icons/Icon";
import { MentionContext } from "../../shared/framework/MessageDataSource";
import { DeepPartial } from "../../shared/helper/types";
import { getCometChatTranslation } from "../../shared/resources/CometChatLocalizeNew/LocalizationManager"
const t = getCometChatTranslation();
/**
* Decorator class for handling message translation in the chat application.
*/
export class MessageTranslationExtensionDecorator extends DataSourceDecorator {
translatedMessage: any = {};
/**
* Creates an instance of MessageTranslationExtensionDecorator.
*
* @param {DataSource} dataSource - The data source to be decorated.
*/
constructor(dataSource: DataSource) {
super(dataSource);
}
/**
* Returns the unique ID for this extension.
*
* @returns {string} The ID string "MessageTranslation".
*/
getId(): string {
return "MessageTranslation";
}
/**
* Gets the text message options for a given message.
*
* @param {CometChat.User} loggedInUser - The currently logged in user.
* @param {CometChat.BaseMessage} messageObject - The message object.
* @param {CometChatTheme} theme - The current theme settings.
* @param {CometChat.Group} group - The group to which the message belongs.
*
* @returns {CometChatMessageOption[]} An array of message options including the translate option.
*/
getTextMessageOptions(
loggedInUser: CometChat.User,
messageObject: CometChat.BaseMessage,
theme: CometChatTheme,
group?: CometChat.Group,
additionalParams?: AdditionalParams
): CometChatMessageOption[] {
// Retrieve default text message options from the decorated data source.
let optionsList: CometChatMessageOption[] = super.getTextMessageOptions(
loggedInUser,
messageObject,
theme,
group,
additionalParams
);
// Add the translate option to the options list.
!additionalParams?.hideTranslateMessageOption &&
optionsList.push(this.getTranslateOption(messageObject, theme));
return optionsList;
}
/**
* Constructs the translation option for a message.
*
* @param {CometChat.BaseMessage} messageObject - The message object to be translated.
* @param {CometChatTheme} theme - The current theme settings.
*
* @returns {CometChatMessageOption} A message option object for translation.
*/
getTranslateOption(
messageObject: CometChat.BaseMessage,
theme: CometChatTheme
): CometChatMessageOption {
return {
id: MessageOptionConstants.translateMessage,
title: t("TRANSLATE"),
icon: (
),
onPress: () => {
// Invoke translation when the option is pressed.
this.translateMessage(messageObject as CometChat.TextMessage);
},
};
}
/**
* Updates the metadata of a message with translation information.
*
* @param {CometChat.TextMessage} messageObj - The text message object.
* @param {any} messageTranslation - The translated text for the message.
*
* @returns {object} An object containing the updated message and translation metadata.
*/
getSetMetaData = (messageObj: CometChat.TextMessage, messageTranslation: any) => {
let metaData: any = messageObj.getMetadata();
if (!metaData) {
metaData = {};
}
// Inject the translate extension if not already present.
if (metaData && !metaData["@injected"]) {
metaData = {
...metaData,
"@injected": { extensions: { translate: {} } },
};
}
if (metaData && metaData["@injected"] && metaData["@injected"]["extensions"]) {
let tempData = metaData["@injected"]["extensions"];
tempData = {
...metaData,
"@injected": {
...metaData["@injected"],
extensions: {
...metaData["@injected"]["extensions"],
translate: { [messageObj.getId()]: messageTranslation },
},
},
};
metaData = tempData;
}
if (metaData && metaData["@injected"] && metaData["@injected"]["extensions"]["translate"]) {
let tempMetaData = {};
let translateData = metaData["@injected"]["extensions"]["translate"];
if (translateData) {
translateData = {
...translateData,
[messageObj.getId()]: messageTranslation,
};
} else {
translateData[messageObj.getId()] = {
[messageObj.getId()]: messageTranslation,
};
}
tempMetaData = {
...metaData["@injected"]["extensions"]["translate"],
...translateData,
};
metaData["@injected"]["extensions"]["translate"] = tempMetaData;
}
// Update the message metadata.
messageObj.setMetadata(metaData);
return {
msg: messageObj,
metaData: metaData["@injected"]["extensions"]["translate"],
};
};
/**
* Translates a given text message by calling the translation extension.
*
* @param {CometChat.TextMessage} message - The text message to be translated.
*/
translateMessage = (message: CometChat.TextMessage) => {
const language = getCurrentLanguage()
const messageId = message.getId();
const messageText = message.getText();
// Get the target language for translation.
let translateToLanguage = language
// Hide the bottom sheet (if visible).
CometChatUIEventHandler.emitUIEvent(CometChatUIEvents.ccToggleBottomSheet, {
isBottomSheetVisible: false,
});
// Modify the text to avoid translating specific tags.
let tempText = messageText.replace(
/<@uid:[a-zA-Z0-9]+>/g,
(match) => `${match}`
);
// Call the translation extension endpoint.
CometChat.callExtension(
ExtensionConstants.messageTranslation,
"POST",
ExtensionURLs.translate,
{
msgId: messageId,
text: tempText,
languages: [translateToLanguage],
}
)
.then((result: any) => {
if (result?.hasOwnProperty("translations") && result["translations"]["length"]) {
let messageTranslation = result["translations"][0];
// Replace span tags to restore original formatting.
messageTranslation["message_translated"] = messageTranslation[
"message_translated"
].replace(
/(<@uid:[a-zA-Z0-9]+>)<\/span>/g,
(_: any, match: any) => match
);
// Update message metadata with translation.
const translatedMsg = this.getSetMetaData(
message,
messageTranslation["message_translated"]
);
if (translatedMsg) {
if (translatedMsg.metaData?.translate)
this.translatedMessage = translatedMsg?.metaData?.translate;
} else {
this.translatedMessage = {
[message.getId()]: `${messageTranslation["message_translated"]}`,
};
}
// Emit an event indicating the message has been edited.
CometChatUIEventHandler.emitMessageEvent(MessageEvents.ccMessageEdited, {
message: translatedMsg.msg,
status: messageStatus.success,
});
}
})
.catch((error) => {
console.log(error);
});
};
/**
* Returns the text message bubble component.
*
* This method renders a MessageTranslationBubble if the message has been translated;
* otherwise, it falls back to the default text message bubble.
*
* @param {string} messageText - The original text of the message.
* @param {CometChat.TextMessage} message - The message object.
* @param {MessageBubbleAlignmentType} alignment - The alignment of the message bubble.
* @param {CometChatTheme} theme - The current theme settings.
* @param {AdditionalParams} [additionalParams] - Additional parameters for rendering.
*
* @returns {JSX.Element} The text message bubble component.
*/
getTextMessageBubble(
messageText: string,
message: CometChat.TextMessage,
alignment: MessageBubbleAlignmentType,
theme: CometChatTheme,
additionalParams?: AdditionalParams
): JSX.Element {
let tempTranslatedMsg: any = {};
let translatedMetaData: any = message.getMetadata();
// Check if translation metadata exists in the message.
if (
translatedMetaData &&
translatedMetaData["@injected"] &&
translatedMetaData["@injected"]["extensions"] &&
translatedMetaData["@injected"]["extensions"]["translate"]
) {
tempTranslatedMsg = translatedMetaData["@injected"]["extensions"]["translate"];
}
let loggedInUser = CometChatUIKit.loggedInUser;
let mentionedUsers = message.getMentionedUsers();
let textFormatters = [...(additionalParams?.textFormatters || [])];
// Determine if the message was sent by the logged-in user.
const isMessageSentByLoggedInUser = message.getSender().getUid() === loggedInUser!.getUid();
// Select the appropriate style based on the sender.
const _style: Partial = isMessageSentByLoggedInUser
? (theme.messageListStyles.outgoingMessageBubbleStyles
.textBubbleStyles as CometChatTheme["textBubbleStyles"])
: (theme.messageListStyles.incomingMessageBubbleStyles
.textBubbleStyles as CometChatTheme["textBubbleStyles"]);
// Create URL formatter and set properties.
let linksTextFormatter = ChatConfigurator.getDataSource().getUrlsFormatter(loggedInUser!);
let mentionsTextFormatter = ChatConfigurator.getDataSource().getMentionsFormatter(
loggedInUser!,
theme
);
linksTextFormatter.setMessage(message);
linksTextFormatter.setId("ccDefaultUrlsFormatterId");
linksTextFormatter.setStyle({ linkTextColor: theme.color.receiveBubbleLink });
if (isMessageSentByLoggedInUser) {
linksTextFormatter.setStyle({ linkTextColor: theme.color.sendBubbleText });
}
mentionsTextFormatter.setContext(isMessageSentByLoggedInUser ? MentionContext.Outgoing : MentionContext.Incoming);
// Configure mentions formatter if mentioned users are present.
if (!additionalParams?.disableMentions && mentionedUsers && mentionedUsers.length) {
mentionsTextFormatter.setLoggedInUser(loggedInUser!);
mentionsTextFormatter.setMessage(message);
mentionsTextFormatter.setId("ccDefaultMentionFormatterId");
}
let finalFormatters: CometChatTextFormatter[] = [];
let urlFormatterExists = false;
let mentionsFormatterExists = false;
// Process the provided text formatters.
for (const formatter of textFormatters) {
if (formatter instanceof CometChatUrlsFormatter) {
urlFormatterExists = true;
}
if (formatter instanceof CometChatMentionsFormatter) {
mentionsFormatterExists = true;
formatter.setMessage(message);
formatter.setTargetElement(MentionsTargetElement.textbubble);
formatter.setLoggedInUser(CometChatUIKit.loggedInUser!);
formatter.setContext(isMessageSentByLoggedInUser ? "outgoing" : "incoming");
}
formatter.setMessage(message);
finalFormatters.push(CommonUtils.clone(formatter));
if (urlFormatterExists && mentionsFormatterExists) {
break;
}
}
// Ensure URL formatter is present.
if (!urlFormatterExists) {
finalFormatters.push(linksTextFormatter);
}
// Ensure mentions formatter is present.
if (!mentionsFormatterExists) {
finalFormatters.push(mentionsTextFormatter);
}
// If a translation exists for the message, render the MessageTranslationBubble.
if (
(tempTranslatedMsg && tempTranslatedMsg[message.getId()]) ||
(this.translatedMessage && this.translatedMessage[message.getId()])
) {
return (
);
}
// Otherwise, return the default text message bubble from the decorated data source.
return super.getTextMessageBubble(messageText, message, alignment, theme, additionalParams);
}
}