Source

externalCode/auth.js

import * as React from "react";

/**
 * @typedef {Object} SignupFields
 * @property {?boolean} display
 * @property {string} id Ex: `signup_email`, `field_1`
 * @property {string} label Field's label
 * @property {string} member_type
 * @property {Array<any>} options If field is a checkbox, this contains the checkbox fields
 * @property {boolean} required True if field is required
 * @property {string} type Ex: `email`, `checkbox`
 */

/**
 * @typedef {Function} SignupFieldsFilterCallback
 * @param {Array<SignupFields>} fields Old fields array
 * @return {Array<SignupFields>}  Changed fields array
 */

/**
 * @typedef {Object} LoginButtonProps
 * @property {TranslationFunction} t
 * @property {Object} global App global style
 * @property {Object} colors App colors
 * @property {String} stateUsername Username field state
 * @property {String} statePassword Password field state
 * @property {Function} doLogin Default login function
 * @property {Object} auth
 * @property {Object} screenProps
 */

/**
 * @typedef {Object} SignupButtonProps
 * @property {Boolean} disabled Returns `true` if button should be disabled
 * @property {Object} global App global style
 * @property {Function} doSignup Default signup function
 * @property {String} label Button label
 * @property {Boolean} loading Returns `true` if button is loading
 * @property {Boolean} spinnerColor Returns spinner color in button
 * @property {Array<Object>} visibleFields Visible fields in sign up form
 * @property {Object} fieldValues Default field values. See: {@link https://react-hook-form.com/v5/api#getValues}
 * @property {Object} formState Information about form state. See: {@link https://react-hook-form.com/v5/api#formState}
 * @property {Function} getValues Returns entire form data. See: {@link https://react-hook-form.com/v5/api#getValues}
 */

/**
 * @typedef {Object} RenderAuthInputsProps
 * @property {TranslationFunction} t
 * @property {String} authBg Screen background color
 * @property {Object} global App global style
 * @property {String} backgroundColor input wrap background color
 * @property {String} textColor
 * @property {Number} userIcon Default user icon
 * @property {Function} setUsernameState Helper function for setting the username state
 * @property {String} stateUsername Username field state
 * @property {String} placeholderColor
 * @property {Function} calcFontSize
 * @property {Number} passwordIcon Default password icon
 * @property {Function} setPasswordState Helper function for setting the password state
 * @property {String} statePassword Password field state
 * @property {Function} doLogin Default login function
 * @property {Function} setInputRef Helper function which sets the component reference within the parent component
 
 /**
 * @typedef {Object} UserAgreementTextComponentProps
 * @property {Boolean} register Returns `true` if used for registration
 * @property {Object} global App global style
 * @property {Object} colors App colors
 * @property {TranslationFunction} t
 * @property {Object} termsOfService Terms of service data
 * @property {Object} privacyPolicy Privacy policy data
 * @property {Boolean} withCheckbox Returns `true` if component should have a checkbox
 * @property {Function} onPress Helper function which shows the user agreement content
 * @property {Function} onAgreementChecked Sets the state of `agreementChecked`
 * @property {Function} agreementChecked Returns `true` if agreement is checked
 */

/**
 * @typedef {Object} UserAgreementModalComponentProps
 * @property {Function} calcFontSize Used to calculate font size based on the fixed value you assign and percent set on branding options
 * @property {Boolean} authDarkStyle Returns `true` if component should use dark style
 * @property {Object} config
 * @property {String} modalContent Modal content string
 * @property {String} modalHeader Modal header string
 * @property {Function} onClosed Helper function which resets the modal
 * @property {Function} refModal Modal reference
 * @property {Function} hideModal Helper function which closes the modal
 * @property {Object} global App global style
 * @property {Object} colors App colors
 * @property {TranslationFunction} t
 * @property {Object} termsOfService Terms of service data
 * @property {Object} privacyPolicy Privacy policy data
 * @property {Boolean} withCheckbox Returns `true` if component should have a checkbox
 * @property {Function} onPress Helper function which shows the user agreement content
 * @property {Function} onAgreementChecked Sets the state of `agreementChecked`
 * @property {Function} agreementChecked Returns `true` if agreement is checked
 */

/**
 * @typedef {Object} LoginLogoProps
 * @property {Boolean | undefined} hideLogo Returns `true` if logo should be visible
 * @property {Object} headerStyle Default style of the logo container
 * @property {Object} logoStyle Default style of the logo image
 * @property {Number} source Returns the image for the logo
 */

/**
 * @typedef {Object} SignUpInputComponentProps
 * @property {React.Component} DefaultComponent Default input component
 * @property {String} label Input label
 * @property {Record<any,any>} field Input details
 * @property {Function} setValue Function to handle input changes to the component
 * @property {Record<any,any>} theme Deafult style
 * @property {undefined | string} initialValue Initial value of field
 */

/**
 * @typedef {Function} SignUpInputsValidationCallback
 * @param {Function} doValidation Calls the default validation function
 * @param {String} fieldName Name of the field
 * @param {Object} field Field details
 * @param {TranslationFunction} t
 * @return {void}
 */

/**
 * @class
 * Authentication Hooks.
 * Instance name: authApi
 
 It enables the app to have authentication options during the signup/login process.
 * @example
 * externalCodeSetup.authApi.METHOD_NAME
 */
export class AuthApi {
	/**
	 * @deprecated
	 * @private
	 */
	defaultSubsiteFilter = (defaultSubsite, subsites) => defaultSubsite;
	// @deprecated
	setRenderSignUpScreenAfterInputs = renderFunc => {
		this.renderSignUpScreenAfterInputs = renderFunc;
	};
	// @deprecated
	setDefaultSubsite = defaultSubsiteFilter => {
		this.defaultSubsiteFilter = defaultSubsiteFilter;
	};

	/**
	 * @private
	 * @param {SignupFieldsFilterCallback} fields
	 */
	filterSignUpInputs = fields => fields;

	/**
	 * You can use it to set the function that can filter the input fields to modify the signup process based on your app needs.
	 * @method
	 * @param {SignupFieldsFilterCallback} func
	 * @example <caption>Example: Remove first field in the sign up form</caption>
	 *
	 * externalCodeSetup.authApi.setSignUpInputsFilter(props => {
	 *  props.shift()
	 *  return props;
	 * })
	 */
	setSignUpInputsFilter = func => {
		this.filterSignUpInputs = func;
	};

	LoginButton = null;

	/**
	 * You can use this hook to replace the Login Button on the login screen.
	 * You can also use this hook to change the behavior or styling of the login button on the login screen
	 * @method
	 * @param {React.ComponentType<LoginButtonProps>} LoginButton
	 * @example <caption> Add an extra validation step before logging-in a user </caption>
	 *
	 * ...
	 *
	 * import {Alert} from "react-native"
	 * import AppButton from "@src/components/AppButton";
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *     externalCodeSetup.authApi.setLoginButton(props => {
	 *         const {
	 *             t,
	 *             global,
	 *             colors,
	 *             doLogin,
	 *             stateUsername,
	 *             statePassword,
	 *             auth
	 *         } = props;
	 *
	 *         const customLoginAction = () => {
	 *             let isValid = false;
	 *
	 *             //Call an action to do extra validation such as an OTP request...
	 *             isValid = false;
	 *
	 *             //If successful, do default app login...
	 *             if (isValid) doLogin();
	 *             else Alert.alert("OTP is invalid");
	 *         };
	 *
	 *         return (
	 *             <AppButton
	 *                 accessibilityLabel="login_button"
	 *                 accessible
	 *                 style={[{marginTop: 10}, global.authButtonContainer]}
	 *                 onPress={() => customLoginAction()}
	 *                 label={t("login:login")}
	 *                 global={global}
	 *                 loading={auth.isFetching}
	 *                 labelStyle={global.authButtonLabel}
	 *                 spinnerColor={colors.authButtonTextColor}
	 *             />
	 *         );
	 *     });
	 * }
	 */
	setLoginButton = LoginButton => {
		this.LoginButton = LoginButton;
	};

	renderAuthInputs = null;

	/**
	 * You can use this hook to modify the authentication fields on the login screen.
	 * For example, add a 'Phone' field along with the default email and password fields on the login screen.
	 * @method
	 * @param {React.ComponentType<RenderAuthInputsProps>} renderAuthInput
	 * @example <caption> Add a phone field along with username and password </caption>
	 *
	 * //In custom_code/LoginCustom.js...
	 *
	 * import React, { useEffect, useState, useRef } from 'react';
	 * import { Alert } from "react-native"
	 * import AppButton from "@src/components/AppButton";
	 * import TextInput from "@src/components/AppInput";
	 * import { getExternalCodeSetup } from '../../src/externalCode/externalRepo';
	 *
	 * const externalCodeSetup = getExternalCodeSetup();
	 *
	 * const phoneIcon = require("../assets/img/phone.png");
	 *
	 * const customLoginAction = (doLogin) => {
	 *
	 *     let isValid = false;
	 *
	 *     //Call an action to do extra validation such as an OTP request...
	 *
	 *     isValid = true;
	 *
	 *     //If successful, do default app login...
	 *     if (isValid)
	 *         doLogin()
	 *     else
	 *         Alert.alert("OTP is invalid")
	 * }
	 *
	 * const renderLoginButton = (phoneRef, usernameRef, passwordRef) => {
	 *
	 *     externalCodeSetup.authApi.setLoginButton(props => {
	 *         const {
	 *             t,
	 *             global,
	 *             stateUsername,
	 *             statePassword,
	 *             isSignUpEnabled,
	 *             privacyPolicy,
	 *             termsOfService,
	 *             withUserAgreementCheckbox,
	 *             stateAgreementChecked,
	 *             passwordInput,
	 *             usernameInput,
	 *             doLogin,
	 *             auth,
	 *             colors
	 *         } = props;
	 *
	 *         return <AppButton
	 *             accessibilityLabel="login_button"
	 *             accessible
	 *             style={[{ marginTop: 10 }, global.authButtonContainer]}
	 *             onPress={() => {
	 *                 if (!!!stateUsername || !!!statePassword) {
	 *                     return false;
	 *                 }
	 *                 if (
	 *                     !isSignUpEnabled &&
	 *                     withUserAgreementCheckbox &&
	 *                     (privacyPolicy || termsOfService) &&
	 *                     !stateAgreementChecked
	 *                 ) {
	 *                     return Alert.alert(
	 *                         t("login:user_agreement_title"),
	 *                         t(
	 *                             `login:user_agreement_message${privacyPolicy ? "_privacy" : ""
	 *                             }${termsOfService ? "_terms" : ""}`
	 *                         )
	 *                     );
	 *                 }
	 *                 phoneRef.current.clear()
	 *                 usernameRef.current.clear()
	 *                 passwordRef.current.clear()
	 *                 customLoginAction(doLogin);
	 *             }}
	 *             label="Get OTP"
	 *             global={global}
	 *             loading={auth.isFetching}
	 *             labelStyle={global.authButtonLabel}
	 *             spinnerColor={colors.authButtonTextColor}
	 *         />
	 *     });
	 * }
	 *
	 * const AuthInputs = props => {
	 *     const {
	 *         t,
	 *         authBg,
	 *         global,
	 *         colors,
	 *         backgroundColor,
	 *         textColor,
	 *         userIcon,
	 *         setUsernameState,
	 *         stateUsername,
	 *         placeholderColor,
	 *         calcFontSize,
	 *         passwordIcon,
	 *         setPasswordState,
	 *         statePassword,
	 *         doLogin,
	 *         auth
	 *     } = props;
	 *
	 *     const [phone, setPhone] = useState('');
	 *     const phoneRef = useRef(null);
	 *     const usernameRef = useRef(null);
	 *     const passwordRef = useRef(null);
	 *
	 *     useEffect(() => {
	 *         renderLoginButton(phoneRef, usernameRef, passwordRef);
	 *     }, [])
	 *
	 *     return <>
	 *         <TextInput
	 *             accessibilityLabel="login_phone_input"
	 *             accessible
	 *             ref={phoneRef}
	 *             bodyColor={authBg}
	 *             inputWrapStyle={[global.formInput, { backgroundColor }]}
	 *             style={[
	 *                 global.formInputText,
	 *                 { color: textColor, paddingLeft: 0 }
	 *             ]}
	 *             keyboardType="numeric"
	 *             icon={phoneIcon}
	 *             webIcon={"IconUser"}
	 *             tintColor={textColor}
	 *             onChangeText={phone => setPhone({ phone })}
	 *             value={phone}
	 *             placeholderTextColor={placeholderColor}
	 *             placeholder="Phone"
	 *             autoCapitalize={"none"}
	 *             returnKeyType="next"
	 *             underlineColorAndroid="transparent"
	 *             autoCorrect={false}
	 *             calcFontSize={calcFontSize}
	 *         />
	 *         <TextInput
	 *             accessibilityLabel="login_username_input"
	 *             accessible
	 *             ref={usernameRef}
	 *             bodyColor={authBg}
	 *             inputWrapStyle={[global.formInput, { backgroundColor }]}
	 *             style={[
	 *                 global.formInputText,
	 *                 { color: textColor, paddingLeft: 0 }
	 *             ]}
	 *             icon={userIcon}
	 *             webIcon={"IconUser"}
	 *             tintColor={textColor}
	 *             onChangeText={username => setUsernameState({ username })}
	 *             value={stateUsername}
	 *             placeholderTextColor={placeholderColor}
	 *             placeholder={t("login:username")}
	 *             autoCapitalize={"none"}
	 *             returnKeyType="next"
	 *             underlineColorAndroid="transparent"
	 *             autoCorrect={false}
	 *             calcFontSize={calcFontSize}
	 *         />
	 *
	 *         <TextInput
	 *             accessibilityLabel="login_password_input"
	 *             accessible
	 *             bodyColor={authBg}
	 *             inputWrapStyle={global.inputWrap}
	 *             inputWrapStyle={[global.formInput, { backgroundColor }]}
	 *             style={[
	 *                 global.formInputText,
	 *                 { color: textColor, width: "70%", paddingLeft: 0 }
	 *             ]}
	 *             ref={passwordRef}
	 *             icon={passwordIcon}
	 *             webIcon={"IconLock"}
	 *             eyeTintColor={textColor}
	 *             tintColor={textColor}
	 *             onChangeText={password => setPasswordState({ password })}
	 *             value={statePassword}
	 *             placeholderTextColor={placeholderColor}
	 *             placeholder={t("login:password")}
	 *             secureTextEntry={true}
	 *             secureToggle={true}
	 *             returnKeyType={"done"}
	 *             autoCapitalize={"none"}
	 *             underlineColorAndroid="transparent"
	 *             autoCorrect={false}
	 *             onSubmitEditing={() =>
	 *                 // done key should execute action
	 *                 customLoginAction(doLogin)
	 *             }
	 *             calcFontSize={calcFontSize}
	 *         />
	 *
	 *     </>
	 *
	 * }
	 *
	 * export default AuthInputs;
	 *
	 * //In custom_code/index.js...
	 *
	 * import LoginCustom from "./components/LoginCustom";
	 * export const applyCustomCode = externalCodeSetup => {
	 *   externalCodeSetup.authApi.setRenderAuthInputs(props => <LoginCustom  {...props} />)
	 * }
	 *
	 * @example <caption> Use default implementation of auth inputs in a hook </caption>
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *
	 *  externalCodeSetup.authApi.setRenderAuthInputs((props) => {
	 *
	 *    const {
	 *      t,
	 *      authBg,
	 *      global,
	 *      colors,
	 *      backgroundColor,
	 *      textColor,
	 *      userIcon,
	 *      setUsernameState,
	 *      stateUsername,
	 *      placeholderColor,
	 *      calcFontSize,
	 *      passwordIcon,
	 *      setPasswordState,
	 *      statePassword,
	 *      doLogin,
	 *      setInputRef,
	 *      auth
	 *    } = props;
	 *
	 *    let passwordRef;
	 *
	 *    const setPasswordRef = (component) => {
	 *
	 *      //Set password ref for LoginScreen.js
	 *      setInputRef("password", component);
	 *
	 *      //Set password ref for this component
	 *      passwordRef = component;
	 *    }
	 *
	 *    return <>
	 *      <TextInput
	 *        accessibilityLabel="login_username_input"
	 *        accessible
	 *        ref={component => setInputRef("username", component)}
	 *        bodyColor={authBg}
	 *        inputWrapStyle={[global.formInput, { backgroundColor }]}
	 *        style={[
	 *          global.formInputText,
	 *          { color: textColor, paddingLeft: 0 }
	 *        ]}
	 *        tabIndex={1}
	 *        icon={userIcon}
	 *        webIcon={"IconUser"}
	 *        tintColor={textColor}
	 *        onChangeText={username => setUsernameState({ username })}
	 *        value={stateUsername}
	 *        placeholderTextColor={placeholderColor}
	 *        placeholder={t("login:username")}
	 *        autoCapitalize={"none"}
	 *        returnKeyType="next"
	 *        underlineColorAndroid="transparent"
	 *        autoCorrect={false}
	 *        onSubmitEditing={() => {
	 *          // next key should focus on next input by default
	 *          passwordRef.focus()
	 *        }}
	 *        calcFontSize={calcFontSize}
	 *      />
	 *
	 *      <TextInput
	 *        accessibilityLabel="login_password_input"
	 *        accessible
	 *        bodyColor={authBg}
	 *        inputWrapStyle={global.inputWrap}
	 *        inputWrapStyle={[global.formInput, { backgroundColor }]}
	 *        style={[
	 *          global.formInputText,
	 *          { color: textColor, width: "70%", paddingLeft: 0 }
	 *        ]}
	 *        ref={component => setPasswordRef(component) }
	 *        tabIndex={2}
	 *        icon={passwordIcon}
	 *        webIcon={"IconLock"}
	 *        eyeTintColor={textColor}
	 *        tintColor={textColor}
	 *        onChangeText={password => setPasswordState({ password })}
	 *        value={statePassword}
	 *        placeholderTextColor={placeholderColor}
	 *        placeholder={t("login:password")}
	 *        secureTextEntry={true}
	 *        secureToggle={true}
	 *        returnKeyType={"done"}
	 *        autoCapitalize={"none"}
	 *        underlineColorAndroid="transparent"
	 *        autoCorrect={false}
	 *        onSubmitEditing={() =>
	 *          // done key should execute action
	 *          doLogin()
	 *        }
	 *        calcFontSize={calcFontSize}
	 *      />
	 *    </>
	 *  })
	 * }
	 */
	setRenderAuthInputs = renderFunc => {
		this.renderAuthInputs = renderFunc;
	};

	SignupButton = null;

	/**
	 * You can use this hook to replace the Signup Button on the Signup screen.
	 * You can also use this hook to change the behavior or styling of the signup button on the login screen.
	 * @method
	 * @param {React.ComponentType<SignupButtonProps>} SignupButton
	 * @example
	 *
	 * ...
	 *
	 * import { Alert } from "react-native"
	 * import AppButton from "@src/components/AppButton";
	 *  export const applyCustomCode = externalCodeSetup => {
	 *
	 *   externalCodeSetup.authApi.setSignupButton(props => {
	 *
	 *     const {
	 *       disabled,
	 *       global,
	 *       doSignup,
	 *       label,
	 *       loading,
	 *       spinnerColor,
	 *       visibleFields,
	 *       profileType,
	 *       fieldValues,
	 *       formState,
	 *       getValues
	 *     } = props;
	 *
	 *     const signupAction = () => {
	 *       Alert.alert("OTP", `We will send an OTP to ${getValues.field_1}. Do you want to proceed?`, [
	 *         {
	 *           text: "Cancel",
	 *           onPress: () => console.log("Cancel Pressed"),
	 *           style: "cancel"
	 *         },
	 *         { text: "OK", onPress: () => console.log("OK Pressed") }
	 *       ])
	 *     }
	 *
	 *     return <AppButton
	 *       disabled={disabled}
	 *       style={[{ marginTop: 17 }, global.regButtonContainer]}
	 *       // onPress={doSignup}
	 *       onPress={signupAction}
	 *       label={label}
	 *       global={global}
	 *       loading={loading}
	 *       labelStyle={[
	 *         global.buttonLabel,
	 *         global.regButtonLabel,
	 *         disabled && { opacity: 0.4 }
	 *       ]}
	 *       spinnerColor={spinnerColor}
	 *     />
	 *   });
	 *
	 * }
	 *
	 */
	setSignupButton = SignupButton => {
		this.SignupButton = SignupButton;
	};

	UserAgreementTextComponent = null;
	/**
	 * You can use this hook to customize the User Agreement text component located before the "Create Account" button.
	 * @method
	 * @param {React.ComponentType<UserAgreementTextComponentProps>} UserAgreementTextComponent
	 * @example <caption> Add an alert confirmation if the user has read the Terms of Service </caption>
	 *
	 * //In custom_code/components/UserAgreementText.js...
	 *
	 * import React from "react";
	 * import {Text, View, Alert} from "react-native";
	 * import AppCheckbox from "@src/components/AppCheckbox";
	 * import {Check} from "@src/components/AppCheckbox";
	 * import {getAuthFormTheme, getRegFormTheme} from "@src/components/Forms/utils";
	 *
	 * const confirmAgreement = (onAgreementChecked) => {
	 *
	 *     Alert.alert(
	 *         "Confirmation",
	 *         "Have you read the Terms of Service?",
	 *         [
	 *           {
	 *             text: "No",
	 *             onPress: () => onAgreementChecked(false),
	 *           },
	 *           { text: "Yes", onPress: () => onAgreementChecked(true) }
	 *         ]
	 *       );
	 *
	 * }
	 *
	 * const UserAgreementText = props => {
	 *   const {
	 *     register,
	 *     t,
	 *     termsOfService,
	 *     privacyPolicy,
	 *     withCheckbox,
	 *     global,
	 *     colors,
	 *     onPress,
	 *     agreementChecked,
	 *     onAgreementChecked,
	 *     style = {}
	 *   } = props;
	 *
	 *   if (!termsOfService?.content && !privacyPolicy?.content) return null;
	 *
	 *   const theme = register ? getRegFormTheme(colors) : getAuthFormTheme(colors);
	 *   const {tint, inactiveTint, textColor, labelColor} = theme;
	 *
	 *   const tColor = withCheckbox ? textColor : labelColor;
	 *
	 *   const containerStyle = withCheckbox && {
	 *     borderRadius: 10,
	 *     backgroundColor: theme.backgroundColor,
	 *     paddingVertical: 14,
	 *     paddingHorizontal: 16,
	 *     marginBottom: 15,
	 *     ...style
	 *   };
	 *
	 *   return (
	 *     <View
	 *       style={[
	 *         global.row,
	 *         {alignItems: "center", justifyContent: "center"},
	 *         containerStyle
	 *       ]}
	 *     >
	 *       <Text
	 *         style={[
	 *           global.text,
	 *           {
	 *             textAlign: withCheckbox ? "left" : "center",
	 *             marginTop: withCheckbox ? 0 : 16,
	 *             paddingRight: 4,
	 *             paddingLeft: withCheckbox ? 0 : 4,
	 *             color: tColor,
	 *             flex: 1
	 *           },
	 *           withCheckbox && {
	 *             marginRight: 5
	 *           }
	 *         ]}
	 *       >
	 *         {withCheckbox ? t(`signup:agreementStartingTextWithCheckbox`) : null}
	 *         {!!termsOfService?.content ? (
	 *           <Text
	 *             style={[global.boldText, {color: tColor}]}
	 *             onPress={() =>
	 *               onPress("termsOfService", termsOfService.content.rendered)
	 *             }
	 *           >
	 *             {t("signup:termsOfService")}
	 *           </Text>
	 *         ) : (
	 *           ""
	 *         )}
	 *         {!!termsOfService?.content && privacyPolicy?.content
	 *           ? t("signup:and")
	 *           : ""}
	 *         {!!privacyPolicy?.content ? (
	 *           <Text
	 *             style={[global.boldText, {color: tColor}]}
	 *             onPress={() =>
	 *               onPress("privacyPolicy", privacyPolicy.content.rendered)
	 *             }
	 *           >
	 *             {t("signup:privacyPolicy")}
	 *           </Text>
	 *         ) : (
	 *           ""
	 *         )}
	 *         {withCheckbox ? t("signup:agreementEndingText") : null}
	 *       </Text>
	 *       {withCheckbox && (
	 *         <Check
	 *           inactiveTint={inactiveTint}
	 *           tintColor={tint}
	 *           isChecked={agreementChecked}
	 *           onPress={() => confirmAgreement(onAgreementChecked)}
	 *           // onPress={() => onAgreementChecked(!agreementChecked)}
	 *         />
	 *       )}
	 *     </View>
	 *   );
	 * };
	 *
	 * export default UserAgreementText;
	 *
	 * //In custom_code/index.js...
	 *
	 * import UserAgreementText from "./components/UserAgreementText";
	 * export const applyCustomCode = externalCodeSetup => {
	 *  externalCodeSetup.authApi.setUserAgreementTextComponent(props => <UserAgreementText {...props} />);
	 * }
	 *
	 */
	setUserAgreementTextComponent = UserAgreementTextComponent => {
		this.UserAgreementTextComponent = UserAgreementTextComponent;
	};

	UserAgreementModalComponent = null;
	/**
	 * You can use this hook to customize the User Agreement modal.
	 * For example, you can add a checkbox at the bottom of the modal which will require users to scroll to the bottom to agree to the Terms of Service.
	 * @param {React.ComponentType<UserAgreementModalComponentProps>} UserAgreementModalComponent
	 * @example <caption> Use default BB implementation and add the User Agreement Text inside the modal </caption>
	 *
	 * //In custom_code/components/UserAgreementModal.js...
	 *
	 * import React from "react";
	 * import { View, Text, Platform } from "react-native";
	 * import { wrapHtml } from "@src/utils";
	 * import { DEVICE_HEIGHT, globalStyle } from "@src/styles/global";
	 * import ScrollableModal from "@src/components/Modals/ScrollableModal";
	 * import BottomSheetCloseButton from "@src/components/BottomSheet/BottomSheetCloseButton";
	 * import ContentPlaceholder from "@src/components/ContentPlaceholder";
	 * import ResizingWebView from "@src/components/ResizingWebView";
	 * import { generateAssetsFontCss } from "@src/utils/jsUtils";
	 * import { UserAgreementText } from "@src/components/Auth/UserAgreementText";
	 *
	 * const UserAgreementModal = (props) => {
	 *   const {
	 *     config,
	 *     onClosed,
	 *     modalContent,
	 *     modalHeader,
	 *     hideModal,
	 *     refModal,
	 *     colors,
	 *     global,
	 *     t,
	 *     termsOfService,
	 *     privacyPolicy,
	 *     onPress,
	 *     onAgreementChecked,
	 *     agreementChecked,
	 *   } = props;
	 *
	 *   const {
	 *     htmlAdjustedCss,
	 *     htmlContentCss,
	 *     htmlStylesCss,
	 *     typography,
	 *     headerStyle
	 *   } = globalStyle(config.styles);
	 *
	 *   const renderModalHeader = () => (
	 *     <View
	 *       style={[
	 *         global.row,
	 *         global.panelHeader,
	 *         headerStyle,
	 *         { borderColor: colors.borderColor, borderBottomWidth: 0.5 }
	 *       ]}
	 *     >
	 *       {!!modalHeader && <Text style={global.filterTitle}>{modalHeader}</Text>}
	 *
	 *       <BottomSheetCloseButton onClose={hideModal} colors={colors} />
	 *     </View>
	 *   );
	 *
	 *   return (
	 *     <ScrollableModal
	 *       ref={refModal}
	 *       onClosed={onClosed}
	 *       HeaderComponent={renderModalHeader(global, colors)}
	 *       adjustToContentHeight={false}
	 *       scrollViewProps={{ backgroundColor: colors.bodyFrontBg }}
	 *     >
	 *       <View style={{flex: 1, padding: 20, borderRadius: 12}}>
	 *         <UserAgreementText
	 *           {...{
	 *             register: true,
	 *             colors,
	 *             global,
	 *             t,
	 *             termsOfService,
	 *             privacyPolicy,
	 *             withCheckbox: true,
	 *             onPress,
	 *             onAgreementChecked,
	 *             agreementChecked
	 *           }}
	 *         />
	 *         <ResizingWebView
	 *           defaultHeight={DEVICE_HEIGHT}
	 *           width={"100%"}
	 *           startInLoadingState={true}
	 *           scrollEnabled={Platform.OS === "android"}
	 *           webViewStyle={Platform.OS === "android" ? { marginBottom: 150 } : {}}
	 *           androidEnableScrollingInScrollView
	 *           autoHeight={Platform.OS === "ios"}
	 *           fixAndroid={true}
	 *           contentId={"userAgreementContent"}
	 *           source={{
	 *             html: wrapHtml(
	 *               String(htmlStylesCss).replace(";overflow: hidden", "") +
	 *               htmlAdjustedCss +
	 *               htmlContentCss +
	 *               generateAssetsFontCss(
	 *                 typography.bodyText.family,
	 *                 typography.bodyText.type
	 *               )
	 *             )(modalContent)
	 *           }}
	 *           renderLoading={() => (
	 *             <View
	 *               style={{
	 *                 position: "absolute",
	 *                 top: 0,
	 *                 left: 0,
	 *                 bottom: 0,
	 *                 right: 0,
	 *                 zIndex: 1
	 *               }}
	 *             >
	 *               <ContentPlaceholder global={global} base={colors.headingsColor} />
	 *             </View>
	 *           )}
	 *         />
	 *       </View>
	 *     </ScrollableModal>
	 *   );
	 * };
	 *
	 * export default UserAgreementModal;
	 *
	 * //In custom_code/index.js...
	 *
	 * ...
	 *
	 * import UserAgreeementModal from "./components/UserAgreementModal"
	 * export const applyCustomCode = externalCodeSetup => {
	 *   externalCodeSetup.authApi.setUserAgreementModalComponent(props => <UserAgreeementModal {...props} />)
	 * }
	 */
	setUserAgreementModalComponent = UserAgreementModalComponent => {
		this.UserAgreementModalComponent = UserAgreementModalComponent;
	};

	LoginLogo = null;

	/**
	 * You can use this hook to replace the logo in the Login Screen.
	 * For example, you can change the logo's image, dimensions, or position.
	 * @method
	 * @param {React.ComponentType<LoginLogoProps>} LoginLogo
	 * @example
	 *
	 * ...
	 *
	 * import AppImage from "@src/components/AppImage";
	 * export const applyCustomCode = (externalCodeSetup: ExternalCodeSetup) => {
	 *
	 *     externalCodeSetup.authApi.setLoginLogo(({
	 *         hideLogo,
	 *         headerStyle,
	 *         logoStyle,
	 *         source
	 *     }) => {
	 *
	 *         return !hideLogo && (
	 *             <View style={headerStyle}>
	 *                 <AppImage
	 *                     id="logo"
	 *                     resizeMode={"contain"}
	 *                     style={logoStyle}
	 *                     source={source}
	 *                 />
	 *             </View>
	 *         )
	 *     })
	 * }
	 *
	 */
	setLoginLogo = LoginLogo => {
		this.LoginLogo = LoginLogo;
	};

	SignUpInputComponent = null;

	/**
	 * You can use this hook to replace input components in the sign-up form.
	 * @method
	 * @param {React.ComponentType<SignUpInputComponentProps>} SignUpInputComponent
	 * @example
	 *
	 * //In custom_code/index.js...
	 * ...
	 *
	 * import MyTextInput from "./components/MyTextInput";
	 *
	 * export const applyCustomCode = (externalCodeSetup: any) => {
	 *
	 *     externalCodeSetup.authApi.setSignUpInputComponent(props => {
	 *         const {DefaultComponent, label} = props;
	 *
	 *         if (label === "Email") {
	 *             return <MyTextInput {...props}/>
	 *         }
	 *
	 *         return DefaultComponent;
	 *     });
	 * }
	 *
	 *
	 *  //In custom_code/components/MyTextInput.js...
	 *
	 * import React, {useState} from "react";
	 * import {View, TextInput, ActivityIndicator, Text} from "react-native";
	 * import axios from "axios";
	 * const MyTextInput = props => {
	 *     const [loading, setLoading] = useState(false);
	 *     const [data, setData] = useState(null);
	 *
	 *     const fetchData = async text => {
	 *         const search = text && text.toLowerCase();
	 *
	 *         try {
	 *             setLoading(true);
	 *             const response = await axios.get(`https://example.api/${search}`);
	 *             setData(response.data);
	 *             setLoading(false);
	 *         } catch (error) {
	 *             console.error(error);
	 *         }
	 *     };
	 *
	 *     const handleTextChange = text => {
	 *         fetchData(text);
	 *         props.setValue(props.field.id, text);
	 *     };
	 *
	 *     return (
	 *         <View>
	 *             <TextInput
	 *                 onChangeText={handleTextChange}
	 *                 placeholder="Type something..."
	 *             />
	 *             {loading && <ActivityIndicator />}
	 *             {data && (
	 *                 <Text>
	 *                     Result: {data.name} | {data.abilities[0].ability.name}
	 *                 </Text>
	 *             )}
	 *         </View>
	 *     );
	 * };
	 *
	 * export default MyTextInput;
	 *
	 */
	setSignUpInputComponent = SignUpInputComponent => {
		this.SignUpInputComponent = SignUpInputComponent;
	};

	signUpInputsValidation = null;

	/**
	 * You can use this hook to set the validation rules for the sign-up form.
	 * For example, you can exempt a field so that it will not be required when submitting the form.
	 * @method
	 * @param {SignUpInputsValidationCallback} SignUpInputsValidationCallback
	 * @example
	 *
	 * authApi.setSignUpInputsValidation(props => {
	 *   const {doValidation, fieldName, field, register, t} = props;
	 *
	 *   const changeLabel = false;
	 *   const exemptField = false;
	 *
	 *   if (changeLabel) {
	 *       return register(fieldName, {
	 *           required:
	 *               field.required &&
	 *               field.required &&
	 *               `Custom - ${field.label}` + t("signup:validateFeildModal"),
	 *           validate: value => {
	 *               const {message} = getFieldError(field, value, t);
	 *               return message ?? true;
	 *           }
	 *       });
	 *   }
	 *
	 *   if (exemptField) {
	 *       if (fieldName === "signup_email") {
	 *           return;
	 *       }
	 *   }
	 *
	 *   return doValidation()
	 * });
	 *
	 */
	setSignUpInputsValidation = (validation, fieldName, field, t) => {
		this.signUpInputsValidation = validation;
	};
}