Skip to content

Instantly share code, notes, and snippets.

@PRA1995SAG
Created February 5, 2024 04:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PRA1995SAG/910d92735e3a13ccc16e771ab87bfb5c to your computer and use it in GitHub Desktop.
Save PRA1995SAG/910d92735e3a13ccc16e771ab87bfb5c to your computer and use it in GitHub Desktop.
Template modal component | messageTemplateSendHandler
/* eslint-disable import/no-cycle */
/* eslint-disable camelcase */
/* eslint-disable react-native/no-raw-text */
import { View, StyleSheet, Keyboard, Text } from "react-native";
import React, {
Dispatch,
SetStateAction,
memo,
useEffect,
useRef,
useState,
} from "react";
import Config from "react-native-config";
import VectorImage from "react-native-vector-image";
import CustomModal from "../../CustomModal.tsx";
import {
COLORS,
IMAGE_PATH,
SCREEN_HEIGHT,
STRINGS,
} from "../../../constants/index.ts";
import TemplateModalFooter from "./TemplateModalFooter.tsx";
import TemplateModalBody from "./TemplateModalBody.tsx";
import ModalHeader from "../../Modal/ModalHeader.tsx";
import { authFetch, showToast } from "../../../utils/index.ts";
import type { Templates } from "../../../types/templates.d.ts";
import CustomButton from "../../CustomButton.tsx";
import TemplateError from "../TemplateError/TemplateError.tsx";
interface ActionsModalType {
isModalVisible: boolean;
setIsModalVisible: Dispatch<SetStateAction<boolean>>;
contactId: number;
contactNumber: string;
onTemplateModalHide?: () => void;
}
const TemplateModal = ({
isModalVisible,
contactId,
setIsModalVisible,
onTemplateModalHide,
contactNumber,
}: ActionsModalType) => {
const [messageTemplateItems, setMessageTemplateItems] = useState<
{ label: string; value: number }[]
>([]);
const [messageTemplateData, setMessageTemplateData] = useState<Templates[]>(
[],
);
const [messageTemplateValue, setMessageTemplateValue] = useState<
number | null
>(null);
const [expandTemplate, setExpandTemplate] = useState(false);
const [sendTemplateLoading, setSendTemplateLoading] = useState(false);
const [headerVarItems, setHeaderVarItems] = useState<[]>([]);
const [headerDrValue, setHeaderDrValue] = useState<string | null>(null);
const [headerMediaCustomUrl, setHeaderMediaCustomUrl] = useState("");
const [bodyVarsItems, setBodyVarsItems] = useState([]);
const [bodyDrValues, setBodyDrValues] = useState([""]);
const [bodyVarsCustomUrls, setBodyVarsCustomUrls] = useState<string[]>([""]);
const [buttonVarItems, setButtonVarItems] = useState<[]>([]);
const [buttonDrValues, setButtonDrValues] = useState([""]);
const [buttonCustomUrls, setButtonCustomUrls] = useState<string[]>([""]);
const [buttonVariableTypes, setButtonVariableTypes] = useState<string[]>([]);
const [errorMessage, setErrorMessage] = useState("");
const [headerError, setHeaderError] = useState({
isDropdownValid: true,
isCustomValValid: true,
});
const [bodyErrors, setBodyErrors] = useState<
{ isDropdownValid: boolean; isCustomValValid: boolean }[]
>([]);
const [buttonErrors, setButtonErrors] = useState<
{
isDropdownValid: boolean;
isCustomValValid: boolean;
}[]
>([]);
const haveFormErrorRef = useRef<boolean>(false);
const modalBodyScrollRef = useRef(null);
const closeTemplateModal = () => {
setIsModalVisible((prevState) => !prevState);
};
const modalStyle = expandTemplate ? { flex: 1 } : undefined;
const hasHeaderVariables = headerVarItems.length > 0;
const hasButtonVariables = buttonVarItems.length > 0;
const bodyVarsItemsLength = bodyVarsItems.length;
const buttonVarsItemsLength = buttonVarItems.length;
const templatePreviewData =
messageTemplateData[
messageTemplateItems.findIndex(
(temp) => temp.value === messageTemplateValue,
)
] || [];
const messageTemplateSendHandler = () => {
Keyboard.dismiss();
if (!messageTemplateValue) {
showToast(STRINGS.SELECT_A_MESSAGE_TEMPLATE);
return;
}
if (hasHeaderVariables && !headerDrValue) {
setHeaderError(() => ({
isCustomValValid: true,
isDropdownValid: false,
}));
return;
}
if (
hasHeaderVariables &&
headerDrValue === "custom" &&
!headerMediaCustomUrl
) {
setHeaderError({ isCustomValValid: false, isDropdownValid: true });
return;
}
const headerMapping = { merge_tag: headerDrValue };
if (headerDrValue === "custom") {
headerMapping.custom_value = headerMediaCustomUrl;
}
const bodyMapping = [];
for (let i = 0; i < bodyVarsItemsLength; i += 1) {
const bodyDrValue = bodyDrValues[i];
if (!bodyDrValue) {
setBodyErrors((prevState) => {
const updatedState = [...prevState];
updatedState[i] = {
isDropdownValid: false,
isCustomValValid: updatedState[i]?.isCustomValValid ?? true,
};
return updatedState;
});
haveFormErrorRef.current = true;
}
const mappingObj = { merge_tag: bodyDrValue };
if (bodyDrValue === "custom") {
if (!bodyVarsCustomUrls[i]) {
setBodyErrors((prevState) => {
const updatedState = [...prevState];
updatedState[i] = {
isCustomValValid: false,
isDropdownValid: true,
};
return updatedState;
});
haveFormErrorRef.current = true;
}
mappingObj.custom_value = bodyVarsCustomUrls[i];
}
bodyMapping.push(mappingObj);
}
const buttonMapping = [];
for (let j = 0; j < buttonVarsItemsLength; j++) {
const buttonDrValue = buttonDrValues[j];
if (!buttonDrValue) {
setButtonErrors((prevState) => {
const updatedState = [...prevState];
updatedState[j] = {
isDropdownValid: false,
isCustomValValid: updatedState[j]?.isCustomValValid ?? true,
};
return updatedState;
});
haveFormErrorRef.current = true;
}
const mappingObj = { merge_tag: buttonDrValue };
if (buttonDrValue === "custom") {
if (!buttonCustomUrls[j]) {
setButtonErrors((prevState) => {
const updatedState = [...prevState];
updatedState[j] = {
isCustomValValid: false,
isDropdownValid: true,
};
return updatedState;
});
haveFormErrorRef.current = true;
}
mappingObj.custom_value = buttonCustomUrls[j];
}
buttonMapping.push(mappingObj);
}
if (haveFormErrorRef.current) {
return;
}
const variable_mapping = {};
if (headerMapping.merge_tag) {
variable_mapping.header = [headerMapping];
}
// console.log("button mapping: ", buttonMapping);
if (buttonMapping.length) {
for (let i = 0; i < buttonMapping?.length; i++) {
variable_mapping[buttonVariableTypes[i]] = [buttonMapping[i]];
}
}
if (bodyMapping.length) {
variable_mapping.body = bodyMapping;
}
if (
hasHeaderVariables &&
templatePreviewData.variable_mapping.header?.type !== "text"
) {
variable_mapping.header = {
media: headerMapping,
};
}
const apiBody = {
type: "template",
message: {
template_id: messageTemplateValue,
variable_mapping,
},
};
setSendTemplateLoading(true);
authFetch(
`${Config.WANOTIFIER_API_ENDPOINT}wanotifier/v1/conversations/${contactId}/message`,
{
method: "POST",
body: JSON.stringify(apiBody),
},
)
.then((res) => res.json())
.then((result) => {
if (!result.error) {
showToast(`Message template sent successfully to ${contactNumber}`);
setIsModalVisible(false);
setErrorMessage("");
} else {
setErrorMessage(result.message);
modalBodyScrollRef?.current?.scrollTo({ x: 0, y: 0, animated: true });
}
})
.catch(() => {
// console.log("template modal request error: ", e);
})
.finally(() => {
setSendTemplateLoading(false);
});
};
useEffect(() => {
if (isModalVisible) {
authFetch(`${Config.WANOTIFIER_API_ENDPOINT}wanotifier/v1/templates`)
.then((res) => res.json())
.then((result: Templates[]) => {
const templates = result.map((template) => ({
label: template.name,
value: template.id,
}));
setMessageTemplateData(result);
setMessageTemplateItems(templates);
});
}
return () => {
setHeaderMediaCustomUrl("");
setBodyVarsCustomUrls([""]);
setButtonCustomUrls([""]);
setMessageTemplateValue(null);
setExpandTemplate(false);
setHeaderDrValue(null);
setHeaderVarItems([]);
setBodyVarsItems([]);
setButtonVarItems([]);
setBodyDrValues([""]);
setButtonDrValues([""]);
};
}, [isModalVisible]);
useEffect(() => {
if (hasHeaderVariables && headerDrValue) {
setHeaderError((prevState) => ({
isCustomValValid: prevState.isCustomValValid,
isDropdownValid: true,
}));
}
if (
hasHeaderVariables &&
headerDrValue === "custom" &&
headerMediaCustomUrl
) {
setHeaderError((prevState) => ({
isCustomValValid: true,
isDropdownValid: prevState.isDropdownValid,
}));
}
for (let i = 0; i < bodyVarsItemsLength; i += 1) {
const bodyDrValue = bodyDrValues[i];
if (bodyDrValue) {
setBodyErrors((prevState) => {
const updatedState = [...prevState];
updatedState[i] = {
isDropdownValid: true,
isCustomValValid: updatedState[i]?.isCustomValValid ?? true,
};
return updatedState;
});
haveFormErrorRef.current = false;
}
if (bodyDrValue === "custom") {
if (bodyVarsCustomUrls[i]) {
setBodyErrors((prevState) => {
const updatedState = [...prevState];
updatedState[i] = {
isCustomValValid: true,
isDropdownValid: updatedState[i]?.isDropdownValid ?? true,
};
return updatedState;
});
haveFormErrorRef.current = false;
return;
}
}
}
for (let j = 0; j < buttonVarsItemsLength; j += 1) {
const buttonDrValue = buttonDrValues[j];
if (buttonDrValue) {
setButtonErrors((prevState) => {
const updatedState = [...prevState];
updatedState[j] = {
isDropdownValid: true,
isCustomValValid: updatedState[j]?.isCustomValValid ?? true,
};
return updatedState;
});
haveFormErrorRef.current = false;
}
if (buttonDrValue === "custom") {
if (bodyVarsCustomUrls[j]) {
setBodyErrors((prevState) => {
const updatedState = [...prevState];
updatedState[j] = {
isCustomValValid: true,
isDropdownValid: updatedState[j]?.isDropdownValid ?? true,
};
return updatedState;
});
haveFormErrorRef.current = false;
return;
}
}
}
}, [
buttonCustomUrls,
buttonDrValues,
headerDrValue,
hasButtonVariables,
hasHeaderVariables,
headerMediaCustomUrl,
bodyDrValues,
bodyVarsCustomUrls,
bodyVarsItemsLength,
buttonVarsItemsLength,
]);
useEffect(() => {
setHeaderDrValue(null);
setHeaderVarItems([]);
setHeaderMediaCustomUrl("");
setBodyVarsItems([]);
setBodyDrValues([""]);
setBodyVarsCustomUrls([""]);
setButtonVarItems([]);
setButtonCustomUrls([""]);
setButtonDrValues([""]);
setHeaderError({
isDropdownValid: true,
isCustomValValid: true,
});
setBodyErrors([]);
setButtonErrors([]);
setErrorMessage("");
haveFormErrorRef.current = false;
}, [messageTemplateValue]);
return (
<CustomModal
animationIn="fadeInDown"
animationOut="fadeOutUp"
propagateSwipe
handleBackdrop={() => closeTemplateModal()}
isVisible={isModalVisible}
modalStyle={modalStyle}
backdropOpacity={0.2}
onModalHide={onTemplateModalHide}
>
<View style={styles.modalContainer}>
<ModalHeader
headerComponent={
<View style={styles.headerCompContainer}>
<Text style={styles.headerCompText}>Send Message Template</Text>
<CustomButton
onPress={closeTemplateModal}
buttonComp={<VectorImage source={IMAGE_PATH.closeGrayIcon} />}
/>
</View>
}
/>
<TemplateError errorMessage={errorMessage} />
<TemplateModalBody
modalBodyScrollRef={modalBodyScrollRef}
headerError={headerError}
buttonErrors={buttonErrors}
bodyErrors={bodyErrors}
templateItems={messageTemplateItems}
templatePreviewData={templatePreviewData}
setExpandTemplate={setExpandTemplate}
headerVarItems={headerVarItems}
setHeaderVarItems={setHeaderVarItems}
headerDrValue={headerDrValue}
setHeaderDrValue={setHeaderDrValue}
headerMediaCustomUrl={headerMediaCustomUrl}
setHeaderMediaCustomUrl={setHeaderMediaCustomUrl}
bodyVarsItems={bodyVarsItems}
setBodyVarsItems={setBodyVarsItems}
bodyDrValues={bodyDrValues}
setBodyDrValues={setBodyDrValues}
bodyVarsCustomUrls={bodyVarsCustomUrls}
setBodyVarsCustomUrls={setBodyVarsCustomUrls}
buttonVarItems={buttonVarItems}
setButtonVarItems={setButtonVarItems}
buttonDrValues={buttonDrValues}
setButtonDrValues={setButtonDrValues}
buttonCustomUrls={buttonCustomUrls}
setButtonCustomUrls={setButtonCustomUrls}
messageTemplateValue={messageTemplateValue}
setMessageTemplateValue={setMessageTemplateValue}
setButtonVariableTypes={setButtonVariableTypes}
/>
<TemplateModalFooter
rightButtonDisabled={!messageTemplateValue}
isSendingTemplate={sendTemplateLoading}
leftButtonDisabled={sendTemplateLoading}
handleClose={() => closeTemplateModal()}
handleSend={messageTemplateSendHandler}
/>
</View>
</CustomModal>
);
};
TemplateModal.defaultProps = { onTemplateModalHide: () => {} };
TemplateModal.Header = ModalHeader;
const styles = StyleSheet.create({
headerCompContainer: {
alignItems: "center",
flexDirection: "row",
justifyContent: "space-between",
marginBottom: 10,
width: "100%",
},
headerCompText: {
color: COLORS.black,
fontFamily: "Roboto-Bold",
fontSize: 16,
},
modalContainer: {
backgroundColor: COLORS.white,
borderRadius: 12,
maxHeight: SCREEN_HEIGHT * 0.85,
overflow: "hidden",
padding: "3%",
},
});
export default memo(TemplateModal);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment