Source

externalCode/profileScreen.ts

import * as React from "react";
import {User, TMemberViewModel, TTranslationFunction} from "./types";
import {NavigationService} from "./navigation";

/**
 * @typedef {Object} User Logged-in user's data
 */

/**
 * @typedef {"xprofile" | "courses" | "certificates" | "gamipress_points" | "gamipress_achievements" | "gamipress_ranks" | "badgeos_points" | "badgeos_achievements" | "badgeos_ranks" | "results" | "favourites" | "subscribed" | "replies" | "topics" | "activities" | "forums" | "friends" | "blocked_users" | "groups" | "documents"} ProfileTab
 */

/**
 * @typedef { "just-me" | "friends" | "groups" | "favorites" | "mentions" | "following" } ActivitiesFilter
 */

/**
 * @typedef { "-1" | "new_member" | "new_avatar" | "updated_profile" | "activity_update" | "activity_comment" | "friendship_accepted" | "friendship_created" | "created_group" | "joined_group" | "group_details_updated" | "bbp_topic_create" | "bbp_reply_create"} ActivitiesSubFilter
 */

/**
 * Image resource (via request('...src'))
 * @typedef {number} LocalImageSource
 */

/**
 * @typedef {Object} RemoteImageSource
 */

/**
 * @typedef {LocalImageSource | RemoteImageSource} ImageSource
 */

type TransformProfileIgnoredSubscreensCallback = {
	list:
		| "xprofile"
		| "courses"
		| "certificates"
		| "gamipress_points"
		| "gamipress_achievements"
		| "gamipress_ranks"
		| "badgeos_points"
		| "badgeos_achievements"
		| "badgeos_ranks"
		| "results"
		| "favourites"
		| "subscribed"
		| "replies"
		| "topics"
		| "activities"
		| "forums"
		| "friends"
		| "blocked_users"
		| "groups"
		| "documents",
	/**
	 * Returns `true` if profile displayed in screen is logged-in user's own account
	 */
	isOwnAcccount: boolean
};

/**
 * @typedef {Function} TransformProfileActivityFilters
 * @param {Array<ActivitiesFilter>} list
 * @return {Array<string>} - New list
 */

/**
 * SubscreenObject
 */
type SubscreenObject = {
	icon: number | Record<any, any>,
	label: string,
	count?: number,
	onPress: () => void
};

/**
 * TransformProfileSubscreensListCallback
 */
type TransformProfileSubscreensListCallback = {
	list: SubscreenObject[],
	navigation: NavigationService,
	user: User,
	/**
	 * Returns `true` if profile displayed in screen is logged-in user's own account
	 */
	isOwnAccount: boolean
};
/**
 * BeforeProfileDetailsComponentProps
 */
type BeforeProfileDetailsComponentProps = {
	/**
	 * App global style
	 */
	global: Record<any, any>,
	/**
	 * App colors
	 */
	colors: Record<any, any>,

	user: TMemberViewModel
};

/**
 * AfterProfileDetailsComponentProps
 */
type AfterProfileDetailsComponentProps = {
	user: TMemberViewModel
};

/**
 * UserFullNameComponentProps
 */
type UserFullNameComponentProps = {
	user: TMemberViewModel,
	/**
	 * App global style
	 */
	global: Record<any, any>,

	textStyle: Record<any, any>,

	topicLoading: Record<any, any>
};

/**
 * UserTypeComponentProps
 */
type UserTypeComponentProps = {
	isUserProfile: boolean,
	user: TMemberViewModel,
	/**
	 * App global style
	 */
	global: Record<any, any>,

	textStyle: Record<any, any>
};

/**
 * UserHandleComponentProps
 */
type UserHandleComponentProps = {
	user: TMemberViewModel,
	/**
	 * App global style
	 */
	global: Record<any, any>,

	textStyle: Record<any, any>,
	/**
	 * Returns `true` if a setting to hide the handle was set
	 */
	hideHandle: boolean
};

/**
 * UserRegisteredComponentProps
 */
type UserRegisteredComponentProps = {
	user: TMemberViewModel,
	/**
	 * App global style
	 */
	global: Record<any, any>,

	t: TTranslationFunction,
	/**
	 * Helper function for parsing dates
	 */
	formatDateFunc: Function
};

/**
 * UserFollowingComponentProps
 */
type UserFollowingComponentProps = {
	user: TMemberViewModel,
	/**
	 * App global style
	 */
	global: Record<any, any>,

	/**
	 * App colors
	 */
	colors: Record<any, any>,
	t: TTranslationFunction
};

/**
 * UserAvatarContainerProps
 */
type UserAvatarContainerProps = {
	/**
	 * Default styles
	 */
	styles: Record<any, any>
};

/**
 * UserAvatarProps
 */
type UserAvatarProps = {
	/**
	 * Returns `true` if profile displayed in screen is logged-in user's own account
	 */
	isOwnAccount: boolean,

	t: TTranslationFunction,
	/**
	 * App global style
	 */
	global: Record<any, any>,

	user: TMemberViewModel
};

/**
 * AfterProfileHeaderProps
 */
type AfterProfileHeaderProps = {
	user: TMemberViewModel
};

/**
 * ActionButtonProps
 */
type ActionButtonProps = {
	/** App global style */
	global: Record<any, any>,
	/** Default actions when button is pressed */
	button: Record<any, any>,
	/** Color for icon */
	color: Record<any, any>,
	/** Style for the button */
	style: Record<any, any>,
	/** Translation function */
	t: TTranslationFunction,
	/** Returns `true` if button has label */
	withLabel: boolean,
	/** Style for the label */
	labelStyle: Record<any, any>,
	/** Returns `true` if component is still loading */
	loading: boolean,
	/** Returns `true` if button is first in the list */
	first: boolean,
	/** Returns `true` if button is last in the list */
	last: boolean,
	/** Returns `true` if button is highlighted */
	highlight: boolean
};

/**
 * OnChangeEventListenerCallback
 */
type OnChangeEventListenerCallback = {
	/** Field id */
	id: number,
	/** Group key */
	groupKey: number,
	/** Returns `true` if value for field is required */
	required: boolean,
	/** Returns `true` if input is date */
	isDate: boolean,
	/** Returns value of input */
	newValue: string
};

/**
 * TransformEditProfileFieldSetsCallback
 */
type TransformEditProfileFieldSetsCallback = {
	/** id of field set */
	id: number,
	/** Name of the field set */
	name: string,
	/** Description of the field set */
	description: Record<any, any>,
	/** Order of the group */
	group_order: number,
	/** Returns `true` if field set can be deleted */
	can_delete: boolean,
	/** Returns `true` if repeater is enabled */
	repeater_enabled: boolean,
	/** Fields in a field set */
	fields: Record<any, any>[]
};

/**
 * TransformViewProfileFieldSetsCallback
 */
type TransformViewProfileFieldSetsCallback = {
	/** id of field set */
	id: number,
	/** Name of the field set */
	name: string,
	/** Description of the field set */
	description: Record<any, any>,
	/** Order of the group */
	group_order: number,
	/** Returns `true` if field set can be deleted */
	can_delete: boolean,
	/** Returns `true` if repeater is enabled */
	repeater_enabled: boolean,
	/** Fields in a field set */
	fields: Record<any, any>[]
};

/**
 * EditProfileComponentDidUpdateCallback
 */
type EditProfileComponentDidUpdateCallback = {
	prevProps: Record<any, any>,
	prevState: Record<any, any>,
	props: Record<any, any>,
	state: Record<any, any>,
	/**
	 * Helper function which you can use to update the Edit Profile screen's field values
	 */
	updateStoreValues: Function
};

/**
 * ProfileHeaderWrapperProps
 */
type ProfileHeaderWrapperProps = {
	styles: Record<any, any>,
	children: React.ComponentType
};

/**
 * ProfileItemValueProps
 */
type ProfileItemValueProps = {
	/** App global style */
	global: Record<any, any>,
	/**
	 * Field details
	 */
	data: Record<any, any>,
	/**
	 * Field type
	 */
	type: string,
	/**
	 * Field options
	 */
	options: Record<any, any>[],
	/**
	 * Helper function which can be used to format dates
	 */
	formatDateFunc: Function,
	/**
	 * Helper function which is used for deep linking
	 */
	attemptDeepLink: Function,
	/**
	 * Default component used for rendering the values
	 */
	ProfileItemValue: React.ComponentType<any>
};

/**
 * ProfileItemProps
 */
type ProfileItemProps = {
	/**
	 * Returns `true` if item to be rendered is last in the list
	 */
	isLast: boolean,
	/** App global style */
	global: Record<any, any>,

	/**
	 * App colors
	 */
	colors: Record<any, any>,

	/**
	 * Helper function which can be used to format dates
	 */
	formatDateFunc: Function,

	/**
	 * Helper function which is used for deep linking
	 */
	attemptDeepLink: Function,

	/**
	 * Default styles
	 */
	styles: Record<any, any>,
	/**
	 * Field details
	 */
	field: Record<any, any>
};

/**
 * EditProfileItemProps
 */
type EditProfileItemProps = {
	/**
	 * Form item details
	 */
	formItem: Record<any, any>,

	/**
	 * Profile component details
	 */
	values: Record<any, any>,

	/**
	 * Function to handle input changes to the component
	 */
	handleInput: Function,

	/**
	 * Function to handle visibility changes to the component
	 */
	handleVisibility: Function,

	config: Record<any, any>,
	/** App global style */
	global: Record<any, any>,

	/**
	 * App colors
	 */
	colors: Record<any, any>,
	t: TTranslationFunction,
	/**
	 * Error details
	 */
	errors: Record<any, any>,

	/**
	 * Default styles
	 */
	styles: Record<any, any>
};

/**
 * XProfileUserAvatarProps
 */
type XProfileUserAvatarProps = {
	/**
	 * Returns `true` if profile displayed in screen is logged-in user's own account
	 */
	isOwnAccount: boolean,
	t: TTranslationFunction,
	/** App global style */
	global: Record<any, any>,
	user: TMemberViewModel,
	/**
	 * Default avatar size
	 */
	size: number,
	/**
	 * Default settings for image picker
	 */
	imagePickerProps: Record<any, any>
};

/**
 * XProfileUserTypeComponentProps
 */
type XProfileUserTypeComponentProps = {
	user: TMemberViewModel,
	/**
	 * Default styles
	 */
	styles: Record<any, any>
};

/**
 * XProfileUserFullNameComponentProps
 */
type XProfileUserFullNameComponentProps = {
	user: TMemberViewModel,
	/**
	 * Default styles
	 */
	styles: Record<any, any>
};

/**
 * XProfileUserHandleComponentProps
 */
type XProfileUserHandleComponentProps = {
	/**
	 * Returns `true` if user handle should be hidden
	 */
	hideHandle: boolean,
	/**
	 * User nickname
	 */
	name: string,
	/**
	 * App colors
	 */
	colors: Record<any, any>,
	/**
	 * Default styles
	 */
	styles: Record<any, any>
};

/**
 * XProfileUserRegisteredComponentProps
 */
type XProfileUserRegisteredComponentProps = {
	user: TMemberViewModel,
	/**
	 * App global style
	 */
	global: Record<any, any>,

	t: TTranslationFunction,
	/**
	 * Helper function for parsing dates
	 */
	formatDateFunc: Function
};

/**
 * XProfileUserFollowingComponentProps
 */
type XProfileUserFollowingComponentProps = {
	/**
	 * App global style
	 */
	global: Record<any, any>,

	/**
	 * Default styles
	 */
	styles: Record<any, any>,
	t: TTranslationFunction,
	user: TMemberViewModel
};

/**
 * GroupFieldsTitleProps
 */
type GroupFieldsTitleProps = {
	/**
	 * Fieldset details
	 */
	group: Record<any, any>,
	/**
	 * Default styles
	 */
	styles: Record<any, any>
};

/**
 * ProfileHeaderButtonObject
 */
type ProfileHeaderButtonObject = {
	/**
	 * Button icon
	 */
	icon: number,
	/**
	 * Button label
	 */
	label: string,
	/**
	 * Function to call
	 */
	doFunction: Function,
	/**
	 * Callback if an error occured
	 */
	getError: Function
};

/**
 * EditProfileInputComponentProps
 */
type EditProfileInputComponentProps = {
	/**
	 * Profile component details
	 */
	values: Record<any, any>,
	/**
	 * Function to handle input changes to the component
	 */
	handleInput: Function,
	config: Record<any, any>,
	/**
	 * App global style
	 */
	global: Record<any, any>,
	/**
	 * App colors
	 */
	colors: Record<any, any>,
	t: TTranslationFunction,
	/**
	 * Error details
	 */
	errors: undefined | Record<any, any>,
	/**
	 * Current form component
	 */
	formItem: Record<any, any>
};

/**
 * AchievementsComponentProps
 */
type AchievementsComponentProps = {
	/**
	 * Default styles
	 */
	styles: Record<any, any>,
	/**
	 * Returns the default container component
	 */
	ContainerComponent: React.ComponentType<any>,
	/**
	 * Achievement data
	 */
	achievements: Record<any, any>,
	/**
	 * Navigates to the achievements screen
	 */
	navigateToAchievements: Function
};

/**
 * @typedef {Function} TransformProfileHeaderButtonsCallback
 * @param {Array<ProfileHeaderButtonObject>} profileHeaderButtons Array of objects that renders the header buttons
 * @return {Array<ProfileHeaderButtonObject>} New Array of objects
 */

/**
 * @class
 * ProfileScreen Hooks.
 * Instance name: profileScreenHooksApi
 
   You can use this hook to personalize the profile screen by adding components before/after profile headers, blog details, custom header backgrounds and more.
 * @example
 * externalCodeSetup.profileScreenHooksApi.METHOD_NAME
 */
export class ProfileScreenHooksApi {
	/**
	 * @deprecated
	 */
	//@ts-ignore
	activityFiltersSubFilter = list => list;
	//@ts-ignore
	setActivityFiltersSubFilter = activityFiltersSubFilter => {
		this.activityFiltersSubFilter = activityFiltersSubFilter;
	};

	/**
	 * Sets a default cover
	 * @private
	 */
	//@ts-ignore
	baseCover;
	/**
	 * @ignore
	 * Reason for ignore: Function is not used anywhere in the app
	 * Sets a default cover image. It will override Platform settings.
	 * @param {LocalImageSource} baseCover
	 */
	//@ts-ignore
	setBaseCover = baseCover => {
		this.baseCover = baseCover;
	};

	/**
	 * @private
	 */
	headerBgSource = null;
	/**
	 * @ignore
	 * Reason for ignore: Hook not being used to replace header background.
	 * There are some instances where `headerBgSource` is used to check for a condition but it should be replaced by `customHeaderBackground`.
	 * Set image for Profile Screen header background
	 * @param {ImageSource}
	 */
	//@ts-ignore
	setHeaderBgSource = source => {
		this.headerBgSource = source;
	};
	//@ts-ignore
	profileActivityFiltersFilter = list => list;

	/**
	 * @ignore
	 * Reason for ignore: Variable assignment is incorrect and filter is not being used in profile activities screen
	 * Sets the function that can change filters array
	 * @param {TransformProfileActivityFilters} activityFiltersFilter
	 * @example
	 * externalCodeSetup.profileScreenHooksApi.setProfileActivityFiltersFilter((
	 * 	list,
	 * ) => ([
	 * 	...list,
	 * 	"followers",
	 * ]))
	 */
	//@ts-ignore
	setProfileActivityFiltersFilter = activityFiltersFilter => {
		//@ts-ignore
		this.activityFiltersFilter = activityFiltersFilter;
	};

	customHeaderBackground: string | null = null;

	/**
	 * Replaces cover image for all profiles in the profile screen
	 * @method
	 * @param {String} customHeaderBackground Resource to replace cover image
	 * @example
	 * externalCodeSetup.profileScreenHooksApi.setCustomHeaderBackground('https://link-to-image.png')
	 */
	setCustomHeaderBackground = (customHeaderBackground: string | null) => {
		this.customHeaderBackground = customHeaderBackground;
	};

	tabsList = (
		list: SubscreenObject[],
		navigation: NavigationService,
		user: User,
		isOwnAccount: boolean
	) => list;

	/**
	 * You can use this hook to add or modify existing tabs in the profile screen.
	 * For example, you can add a new route in profile screen tab list
	 * @method
	 * @param {TransformProfileSubscreensListCallback} tabsList
	 * @example <caption> Add a new route in profile screen tab list </caption>
	 * ...
	 * import {CommonActions} from "@react-navigation/native";
	 * export const applyCustomCode = externalCodeSetup => {
	 *
	 *  //Register a new route
	 *  externalCodeSetup.navigationApi.addNavigationRoute(
	 *    "book",
	 *    "BookScreen",
	 *    () => <Text>Custom Book Screen</Text>,
	 *    "All"
	 *  );
	 *
	 *  //Add new menu item to profile screen tab list
	 *  externalCodeSetup.profileScreenHooksApi.setTabsList((
	 *    list,
	 *    navigation,
	 *    user,
	 *    isOwnAccount
	 *  ) => {
	 *    return [
	 *      ...list,
	 *      {
	 *        icon: {fontIconName: 'book', weight: 100}, //Set icon
	 *        label: "Book", //Set label of menu
	 *        onPress: () => navigation.navigate(
	 *          CommonActions.navigate({
	 *            name: "book",
	 *            params: { //Pass params if needed
	 *              user,
	 *              isOwnAccount
	 *            },
	 *            key: user.id
	 *          })
	 *        )
	 *      }
	 *    ]
	 *  })
	 * }
	 */
	setTabsList = (
		tabsList: (
			list: SubscreenObject[],
			navigation: NavigationService,
			user: User,
			isOwnAccount: boolean
		) => SubscreenObject[]
	) => {
		this.tabsList = tabsList;
	};

	hideHandle: boolean = false;

	/**
	 * Hides user handle in profile header by passing it as a `true` value.
	 * @method
	 * @param {Boolean} hideHandle
	 * @example
	 * externalCodeSetup.profileScreenHooksApi.setHideHandle(true)
	 */
	setHideHandle = (hideHandle: boolean) => {
		this.hideHandle = hideHandle;
	};

	hideAchievementPoints: boolean = false;

	/**
	 * Hides the points under each badge on the Achievements screen.
	 * @method
	 * @param {Boolean} hideAchievementPoints
	 * @example
	 * externalCodeSetup.profileScreenHooksApi.sethideAchievementPoints(true)
	 */
	setHideAchievementPoints = (hideAchievementPoints: boolean) => {
		this.hideAchievementPoints = hideAchievementPoints;
	};

	ignoreTabsFilter = (tabs: string[], own: boolean) => tabs;

	/**
	 * You can use this to set a function that is capable of hiding specific profile menu items.
	 * @method
	 * @param {TransformProfileIgnoredSubscreensCallback} ignoreTabsFilter
	 * @example
	 * externalCodeSetup.profileScreenHooksApi.setIgnoreTabsFilter((
	 *   list,
	 *   isOwnAccount
	 * ) => [
	 *     ...list,
	 *     "activities",
	 *     "friends"
	 *   ])
	 */
	setIgnoreTabsFilter = (
		ignoreTabsFilter: (tabs: string[], own: boolean) => string[]
	) => {
		this.ignoreTabsFilter = ignoreTabsFilter;
	};

	BeforeDetailsComponent: React.ComponentType<
		BeforeProfileDetailsComponentProps
	> | null = null;

	/**
	 * You can use it to add a component before the profile details.
	 * @method
	 * @param {React.ComponentType<BeforeProfileDetailsComponentProps>} BeforeDetailsComponent
	 * @example
	 * const BeforeDetailsComponent = ({user}) => (
	 *    <Text>User Type: {user.type}</Text>
	 * )
	 *
	 *  externalCodeSetup.profileScreenHooksApi.setBeforeDetailsComponent(BeforeDetailsComponent)
	 */
	setBeforeDetailsComponent = (
		BeforeDetailsComponent: React.ComponentType<
			BeforeProfileDetailsComponentProps
		> | null
	) => {
		this.BeforeDetailsComponent = BeforeDetailsComponent;
	};

	AfterDetailsComponent: React.ComponentType<
		AfterProfileDetailsComponentProps
	> | null = null;

	/**
	 * You can use this to add a component after the profile details. For example, you can add more information about your profile in the custom component.
	 * @method
	 * @param {React.ComponentType<AfterProfileDetailsComponentProps>} AfterDetailsComponent
	 * @example
	 * const AfterDetailsComponent = ({ user }) => (
	 *  <Text>Last activity: {user.lastActivity}</Text>
	 * )
	 *
	 * externalCodeSetup.profileScreenHooksApi.setAfterDetailsComponent(AfterDetailsComponent)
	 */
	setAfterDetailsComponent = (
		AfterDetailsComponent: React.ComponentType<
			AfterProfileDetailsComponentProps
		> | null
	) => {
		this.AfterDetailsComponent = AfterDetailsComponent;
	};

	UserFullNameComponent: React.ComponentType<
		UserFullNameComponentProps
	> | null = null;

	/**
	 * You can use this to replace the user full name component and customize the name display according to your app preferences.
	 * @method
	 * @param {React.ComponentType<UserFullNameComponentProps>} UserFullNameComponent
	 * @example
	 * const UserFullNameComponent = (props) => (
	 *     <View>
	 *       <Text style={{fontSize: 20, color: "red"}}>
	 *        {props.user.fullname}
	 *       </Text>
	 *     </View>
	 * )
	 *
	 * externalCodeSetup.profileScreenHooksApi.setUserFullNameComponent(UserFullNameComponent)
	 */
	setUserFullNameComponent = (
		UserFullNameComponent: React.ComponentType<
			UserFullNameComponentProps
		> | null
	) => {
		this.UserFullNameComponent = UserFullNameComponent;
	};

	UserTypeComponent: React.ComponentType<UserTypeComponentProps> | null = null;

	/**
	 * You can use this to replace the user type component for the various app users.
	 * @method
	 * @param {React.ComponentType<UserTypeComponentProps>} UserTypeComponent
	 * @example
	 *
	 * const UserTypeComponent = (props) => (
	 *  <View>
	 *    <Text>
	 *      User type: {props.user.type}
	 *    </Text>
	 *  </View>
	 * )
	 * externalCodeSetup.profileScreenHooksApi.setUserTypeComponent(UserTypeComponent);
	 */
	setUserTypeComponent = (
		UserTypeComponent: React.ComponentType<UserTypeComponentProps> | null
	) => {
		this.UserTypeComponent = UserTypeComponent;
	};

	UserHandleComponent: React.ComponentType<
		UserHandleComponentProps
	> | null = null;

	/**
	 * It is used to replace the default user handle component.
	 * @method
	 * @param {React.ComponentType<UserHandleComponentProps>} UserHandleComponent
	 * @example
	 *
	 * const UserHandleComponent = (props) => (
	 *  <Text style={{color: "blue", fontSize: 30}}> @{props.user.nicename} </Text>
	 * )
	 *
	 * externalCodeSetup.profileScreenHooksApi.setUserHandleComponent(UserHandleComponent)
	 */
	setUserHandleComponent = (
		UserHandleComponent: React.ComponentType<UserHandleComponentProps> | null
	) => {
		this.UserHandleComponent = UserHandleComponent;
	};

	UserRegisteredComponent: React.ComponentType<
		UserRegisteredComponentProps
	> | null = null;

	/**
	 * It is used to replace user registration details component.
	 * @method
	 * @param {React.ComponentType<UserRegisteredComponentProps>} UserRegisteredComponent
	 * @example
	 *
	 * const UserRegisteredComponent = (props) => (
	 *  <View style={{ marginVertical: 5 }}>
	 *    <Text>Date registered: {props.formatDateFunc(props.user.registeredDate)}</Text>
	 *  </View>
	 * )
	 *
	 * externalCodeSetup.profileScreenHooksApi.setUserRegisteredComponent(UserRegisteredComponent)
	 */
	setUserRegisteredComponent = (
		UserRegisteredComponent: React.ComponentType<
			UserRegisteredComponentProps
		> | null
	) => {
		this.UserRegisteredComponent = UserRegisteredComponent;
	};

	UserFollowingComponent: React.ComponentType<
		UserFollowingComponentProps
	> | null = null;

	/**
	 * You can use this to replace the user's following and/or follower details component.
	 * @param {React.ComponentType<UserFollowingComponentProps>} UserFollowingComponent
	 * @method
	 * @example
	 *
	 * const UserFollowingComponent = (props) => (
	 *  <View style={{ flexDirection: "row" }}>
	 *    <Text> {props.user.followers} Followers </Text>
	 *    <Text> {props.user.following} Following </Text>
	 *  </View>
	 * )
	 *
	 * externalCodeSetup.profileScreenHooksApi.setUserFollowingComponent(UserFollowingComponent)
	 */
	setUserFollowingComponent = (
		UserFollowingComponent: React.ComponentType<
			UserFollowingComponentProps
		> | null
	) => {
		this.UserFollowingComponent = UserFollowingComponent;
	};

	UserAvatarContainer: React.ComponentType<
		UserAvatarContainerProps
	> | null = null;

	/**
	 * You can use it to replace the user avatar container component.
	 * @method
	 * @param {React.ComponentType<UserAvatarContainerProps>} UserAvatarContainer
	 * @example <caption> Overwrite default container styles </caption>
	 * externalCodeSetup.profileScreenHooksApi.setUserAvatarContainer(({styles, children}) => (
	 *         <View
	 *             style={[
	 *                 styles.container,
	 *                 {
	 *                     borderRadius: 30,
	 *                     top: styles.height + 10 - styles.headerTopOffset - styles.avatarContainerSize / 2,
	 *                     height: styles.avatarContainerSize - 20,
	 *                     width: styles.avatarContainerSize - 20
	 *                 }
	 *             ]}
	 *         >
	 *             {children}
	 *         </View>
	 * ));
	 */
	setUserAvatarContainer = (
		UserAvatarContainer: React.ComponentType<UserAvatarContainerProps> | null
	) => {
		this.UserAvatarContainer = UserAvatarContainer;
	};

	UserAvatar: React.ComponentType<UserAvatarProps> | null = null;

	/**
	 * You can use it to replace the user avatar component.
	 * @method
	 * @param {React.ComponentType<UserAvatarProps>} UserAvatar
	 * @example
	 * ...
	 * import AvatarImageUpload from "@src/components/AvatarImageUpload"
	 * export const applyCustomCode = externalCodeSetup => {
	 *
	 *  const UserAvatar = (props) => {
	 *    return (
	 *      <>
	 *        <Text>Tap avatar to edit</Text>
	 *        <AvatarImageUpload
	 *          isOwnAccount={props.isOwnAccount}
	 *          user={props.user}
	 *          size={100}
	 *          imagePickerProps={{ cropping: true, cropperCircleOverlay: true }}
	 *        />
	 *      </>
	 *    )
	 *  }
	 *
	 *  externalCodeSetup.profileScreenHooksApi.setUserAvatar(UserAvatar)
	 *
	 */
	setUserAvatar = (UserAvatar: React.ComponentType<UserAvatarProps> | null) => {
		this.UserAvatar = UserAvatar;
	};

	AfterProfileHeader: React.ComponentType<
		AfterProfileHeaderProps
	> | null = null;

	/**
	 * It adds a component after the profile header.
	 * @method
	 * @param {React.ComponentType<AfterProfileHeaderProps>} AfterProfileHeader
	 * @example
	 *
	 *  const AfterProfileHeader = (props) => (
	 *    <View style={{ marginLeft: 20 }}>
	 *      <Text> User Points: {props.user.points} </Text>
	 *    </View>
	 *  )
	 *
	 *  externalCodeSetup.profileScreenHooksApi.setAfterProfileHeader(AfterProfileHeader)
	 *
	 */
	setAfterProfileHeader = (
		AfterProfileHeader: React.ComponentType<AfterProfileHeaderProps> | null
	) => {
		this.AfterProfileHeader = AfterProfileHeader;
	};

	HeaderRightComponent: React.ComponentType | null = null;

	/**
	 * You can use it to add a component on the top right corner of the profile screen for users.
	 * @method
	 * @param {React.ComponentType} HeaderRightComponent
	 * @example
	 *
	 * //In custom_code/components/ProfileHeaderButton.js
	 *
	 * ...
	 *
	 * import { Alert } from "react-native";
	 * import IconButton from "@src/components/IconButton";
	 * import { globalStyle } from "@src/styles/global";
	 * import { useSelector } from "react-redux"; //use useSelector to get state from redux
	 *
	 * export const ProfileHeaderButton = () => {
	 *
	 *    const globalStyles = useSelector((state) => globalStyle(state.config.styles)) // Get style from redux
	 *    const user = useSelector((state) => state.user.userObject); // Get user from redux
	 *    const { colors } = globalStyles;
	 *    return (
	 *        <IconButton
	 *            icon={{fontIconName: "plus", weight: 200}}
	 *            pressHandler={() => Alert.alert(`Hello ${user.nicename}!`) }// Do something here such as call api requests
	 *            tintColor={colors.headerIconColor}
	 *            style={{
	 *                height: 28
	 *            }}
	 *        />
	 *    )
	 *
	 * }
	 *
	 *
	 * //In custom_code/index.js
	 *
	 * ...
	 *
	 * import { ProfileHeaderButton } from "./components/ProfileHeaderButton";
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *   externalCodeSetup.profileScreenHooksApi.setHeaderRightComponent(() => <ProfileHeaderButton />)
	 * };
	 */

	setHeaderRightComponent = (
		HeaderRightComponent: React.ComponentType | null
	) => {
		this.HeaderRightComponent = HeaderRightComponent;
	};

	ActionButton: React.ComponentType<ActionButtonProps> | null = null;

	/**
	 * You can use this hook to replace the action button in the profile header component.
	 * @method
	 * @param {React.ComponentType<ActionButtonProps>} ActionButton
	 * @example
	 * ...
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *   const RoundIconButton = ({
	 *     global,
	 *     button,
	 *     color,
	 *     style,
	 *     t,
	 *     withLabel,
	 *     labelStyle,
	 *     loading,
	 *     first,
	 *     last,
	 *     highlight
	 *   }) => (
	 *  <>
	 *    <View
	 *      style={[
	 *        {justifyContent: "center", alignItems: "center"},
	 *        highlight
	 *          ? global.wrappedIconButtonHighlight
	 *          : styles.roundActionButton,
	 *        !first && !withLabel && {marginLeft: 10},
	 *        !last && !withLabel && {marginRight: 10},
	 *        style
	 *      ]}
	 *      activeOpacity={1}
	 *    >
	 *      {!loading &&
	 *        button.icon && (
	 *          <Icon
	 *            tintColor={highlight ? "#fff" : color || "#000"}
	 *            icon={button.icon}
	 *            styles={{height: 26, width: 26}}
	 *          />
	 *        )}
	 *      {loading && (
	 *        <ActivityIndicator
	 *          animating={true}
	 *          style={styles.indicator}
	 *          color={color}
	 *          size="small"
	 *        />
	 *      )}
	 *    </View>
	 *    {withLabel && (
	 *      <View style={{height: 40, paddingTop: 10}}>
	 *        <Text
	 *          ellipsizeMode="tail"
	 *          numberOfLines={2}
	 *          style={
	 *            [global.actionIconText, {textAlign: "center"}, labelStyle]
	 *          }
	 *        >
	 *          {t(button.label)}
	 *        </Text>
	 *      </View>
	 *    )}
	 *   </>
	 *  );
	 *
	 * const styles = StyleSheet.create({
	 *   indicator: {opacity: 0.45},
	 *   roundActionButton: {
	 *   backgroundColor: "#EDEEF2",
	 *   borderRadius: 24,
	 *   width: 48,
	 *   height: 48,
	 *   justifyContent: "center",
	 *   alignItems: "center",
	 *   marginHorizontal: 5
	 *  }
	 * });
	 *
	 *  externalCodeSetup.profileScreenHooksApi.setActionButton((props) => <RoundIconButton {...props} />)
	 * }
	 */
	setActionButton = (
		ActionButton: React.ComponentType<ActionButtonProps> | null
	) => {
		this.ActionButton = ActionButton;
	};

	editProfileFieldSetsFilter = (list: Record<any, any>) => list;
	/**
	 * Sets the filters available on the Edit Profile screen as the filter tabs.
	 * For example, you can use this hook to remove a specific filter. However, this does not remove the corresponding field set of the removed filter.
	 * @method
	 * @param {Record<any,any>} editProfileFieldSetsFilter
	 * @example <caption> Change name of filter with id 1 </caption>
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *
	 *  externalCodeSetup.profileScreenHooksApi.setEditProfileFieldSetsFilter(props => {
	 *    if (Array.isArray(props.filters)){
	 *
	 *      const modifiedFilters = props.filters.map(obj => {
	 *
	 *        if (obj.id === 1){
	 *          return {
	 *            ...obj,
	 *            name: "New name"
	 *          }
	 *        }
	 *
	 *        return obj;
	 *      })
	 *
	 *      return {
	 *        ...props,
	 *        filters: modifiedFilters
	 *      };
	 *
	 *    }
	 *
	 *    return props;
	 *
	 *  });
	 * }
	 */
	setEditProfileFieldSetsFilter = (
		editProfileFieldSetsFilter: (props: Record<any, any>) => Record<any, any>
	) => {
		this.editProfileFieldSetsFilter = editProfileFieldSetsFilter;
	};

	handleInputFieldCallback:
		| null
		| ((props: OnChangeEventListenerCallback) => void) = null;
	/**
	 * You can use this hook to add a callback to a function when an input field on the profile Edit screen is changed.
	 * For example, you can add a function to display a prompt whenever the word 'Bar' is typed in a specific profile field.
	 * @method
	 * @param {OnChangeEventListenerCallback} onHandledInput
	 * @example <caption> If "Food" is selected when editing the value in a profile field, change the available field sets in the edit profile screen </caption>
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *
	 *  externalCodeSetup.profileScreenHooksApi.addHandleInputFieldCallback(props => {
	 *
	 *    const { id, newValue } = props;
	 *
	 *    if (id === 24) {
	 *
	 *      if (newValue.includes("Food")) {
	 *
	 *        //If "Food" is selected, display the first field set only
	 *        externalCodeSetup.profileScreenHooksApi.setEditProfileFieldSets(fieldSets => {
	 *          return [...fieldSets].splice(0,1);
	 *        });
	 *
	 *      } else {
	 *
	 *        //Reset field sets
	 *        externalCodeSetup.profileScreenHooksApi.setEditProfileFieldSets(fieldSets => fieldSets);
	 *      }
	 *
	 *    }
	 *  })
	 * }
	 */
	addHandleInputFieldCallback = (
		onHandledInput: null | ((props: OnChangeEventListenerCallback) => void)
	) => {
		this.handleInputFieldCallback = onHandledInput;
	};

	editProfileFieldSets = (fieldSets: TransformEditProfileFieldSetsCallback[]) =>
		fieldSets;
	/**
	 * Sets the field sets and the filters available on the Edit Profile screen.
	 * @method
	 * @param {TransformEditProfileFieldSetsCallback[]} editProfileFieldSets
	 * @example <caption>Delete a field from a field set</caption>
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *
	 *  externalCodeSetup.profileScreenHooksApi.setEditProfileFieldSets(fieldSets => {
	 *
	 *    const modifiedFieldSets = fieldSets.map(obj => {
	 *
	 *      if (obj.id === 1){
	 *
	 *        //Delete a field from the field set
	 *        const fields = obj.fields.slice(1);
	 *
	 *        return {
	 *          ...obj,
	 *          fields: fields
	 *        }
	 *      }
	 *      return obj;
	 *    });
	 *
	 *    return modifiedFieldSets;
	 *  });
	 * }
	 */
	setEditProfileFieldSets = (
		editProfileFieldSets: (
			fieldSets: TransformEditProfileFieldSetsCallback[]
		) => TransformEditProfileFieldSetsCallback[]
	) => {
		this.editProfileFieldSets = editProfileFieldSets;
	};

	viewProfileFieldSets = (fieldSets: TransformViewProfileFieldSetsCallback[]) =>
		fieldSets;

	/**
	 * Used to set and modify the field sets in the View Profile screen.
	 * You can show/hide the field sets in the View Profile screen.
	 * @method
	 * @param {TransformViewProfileFieldSetsCallback} viewProfileFieldSets
	 * @example
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *
	 *  externalCodeSetup.profileScreenHooksApi.setViewProfileFieldSets(fieldSets => {
	 *
	 *    if (Array.isArray(fieldSets)){
	 *      const modifiedFieldSets = [...fieldSets].slice(1);
	 *
	 *      return modifiedFieldSets;
	 *    }
	 *
	 *    return fieldSets;
	 *
	 *  })
	 * }
	 */
	setViewProfileFieldSets = (
		viewProfileFieldSets: (
			fieldSets: TransformViewProfileFieldSetsCallback[]
		) => TransformViewProfileFieldSetsCallback[]
	) => {
		this.viewProfileFieldSets = viewProfileFieldSets;
	};

	editProfileComponentDidUpdateCallback:
		| null
		| ((props: EditProfileComponentDidUpdateCallback) => void) = null;
	/**
	 * You can use this hook to set a callback function in the Edit Profile screen's componentDidUpdate function.
	 * For example, you can use this to make a network request (or call an API) if a state's value is changed.
	 * @method
	 * @param {EditProfileComponentDidUpdateCallback} onComponentDidUpdate
	 * @example <caption>Change edit profile field values based on WebView's message</caption>
	 *
	 * //In custom_code/MyCustomScreen.js...
	 *
	 * import React, { useState } from 'react';
	 * import { View, Text, StyleSheet, Button, Modal, TouchableOpacity } from "react-native";
	 * import isEqual from 'lodash.isequal';
	 * import { WebView } from 'react-native-webview';
	 * import EditScreen from "@src/containers/Custom/Profile/Xprofile/Edit";
	 * import { backButton } from "@src/utils";
	 * import { getExternalCodeSetup } from '../../src/externalCode/externalRepo';
	 *
	 * const MyHTML = require('./../html/sample.html');
	 *
	 * const MyModal = ({ modalVisible, setModalVisible, updateStoreValues }) => {
	 *
	 *     //You can change this according to the message sent by the webview's source
	 *     const matchedMessage = "Clicked!";
	 *
	 *     return <View style={styles.centeredView}>
	 *         <Modal
	 *             animationType="slide"
	 *             transparent={true}
	 *             visible={modalVisible}
	 *             onRequestClose={() => {
	 *                 setModalVisible(!modalVisible);
	 *             }}
	 *             style={{
	 *                 margin: 15
	 *             }}
	 *         >
	 *             <WebView
	 *                 source={MyHTML}
	 *                 onMessage={event => {
	 *
	 *                     const message = event.nativeEvent.data;
	 *
	 *                     if (message == matchedMessage) {
	 *                         updateStoreValues()
	 *                     }
	 *                 }}
	 *
	 *             />
	 *             <TouchableOpacity
	 *                 style={[styles.button, styles.buttonClose]}
	 *                 onPress={() => setModalVisible(!modalVisible)}
	 *             >
	 *                 <Text style={styles.textStyle}>Hide Modal</Text>
	 *             </TouchableOpacity>
	 *         </Modal>
	 *     </View>
	 * }
	 *
	 * const MyCustomScreen = (props) => {
	 *
	 *     const matchedValue = "gmap";
	 *
	 *     const [modalVisible, setModalVisible] = useState(false);
	 *     const [callbackUpdateStoreValues, setCallbackUpdateStoreValues] = useState(null);
	 *
	 *
	 *     //Set condition when to open modal. If the field matches the value of matchedValue, then it will open the modal.
	 *     getExternalCodeSetup().profileScreenHooksApi.addHandleInputFieldCallback(props => {
	 *         const { id, newValue, groupKey } = props;
	 *         if (groupKey === 1 && id === 3) {
	 *             if (newValue === matchedValue) {
	 *                 setModalVisible(true)
	 *             }
	 *         }
	 *     })
	 *
	 *     getExternalCodeSetup().profileScreenHooksApi.setEditProfileComponentDidUpdateCallback(paramProps => {
	 *
	 *         const {
	 *             prevProps,
	 *             prevState,
	 *             props,
	 *             state,
	 *             updateStoreValues } = paramProps;
	 *
	 *         //Update callbackUpdateStoreValues only when there are updates in the EditProfile component
	 *         if (!isEqual(state.updates, prevState.updates) && Object.keys(prevState.updates).length !== 0) {
	 *
	 *             const updateValues = () => {
	 *
	 *                 //You can get the appropriate groupKey and id by doing a console.log for state and/or prevState.
	 *                 //In this case, we're updating the store values of groupKey = 1 and id = 1 with a the webViewInput value.
	 *                 const groupKey = 1,
	 *                     id = 1,
	 *                     property = "value",
	 *                     webViewInput = "Updated from web view";
	 *
	 *                 updateStoreValues(groupKey, id, property, webViewInput);
	 *
	 *             }
	 *
	 *             setCallbackUpdateStoreValues(() => () => updateValues());
	 *         }
	 *
	 *     });
	 *
	 *     return <>
	 *         <EditScreen />
	 *         <MyModal
	 *             modalVisible={modalVisible}
	 *             setModalVisible={setModalVisible}
	 *             updateStoreValues={callbackUpdateStoreValues}
	 *         />
	 *     </>
	 * }
	 *
	 * MyCustomScreen.navigationOptions = ({ navigation, screenProps }) => {
	 *     const { t, colors, calcFontSize, global } = screenProps;
	 *     const { borderColor } = colors;
	 *     const { params = {} } = navigation.state;
	 *
	 *     const hideBackButton = true;
	 *
	 *     let headerLeft = params.renderHeaderLeft
	 *         ? params.renderHeaderLeft()
	 *         : backButton({
	 *             navigation,
	 *             headerColor: colors.headerIconColor,
	 *             text: t("common:back"),
	 *             textStyle: global.headerText,
	 *             colors
	 *         });
	 *
	 *     if (hideBackButton) {
	 *         headerLeft = null;
	 *     }
	 *
	 *     return {
	 *         headerTitle: (
	 *             <Text
	 *                 ellipsizeMode="tail"
	 *                 numberOfLines={1}
	 *                 style={global.appHeaderTitle}
	 *             >
	 *                 {t("profile:editXprofile")}
	 *             </Text>
	 *         ),
	 *         tabBarVisible: false,
	 *         headerLeft: headerLeft,
	 *         headerRight: params.renderHeaderRight ? params.renderHeaderRight() : null,
	 *         headerStyle: {
	 *             ...StyleSheet.flatten(global.header),
	 *             borderBottomColor: borderColor,
	 *             borderBottomWidth: StyleSheet.hairlineWidth
	 *         }
	 *     };
	 * };
	 *
	 * export default MyCustomScreen;
	 *
	 * const styles = StyleSheet.create({
	 *     centeredView: {
	 *         justifyContent: "center",
	 *         alignItems: "center",
	 *     },
	 *     button: {
	 *         position: "absolute",
	 *         bottom: 80,
	 *         width: "50%",
	 *         borderRadius: 20,
	 *         padding: 10,
	 *         elevation: 2,
	 *         alignSelf: "center"
	 *     },
	 *     buttonClose: {
	 *         backgroundColor: "#2196F3",
	 *     },
	 *     textStyle: {
	 *         color: "white",
	 *         fontWeight: "bold",
	 *         textAlign: "center"
	 *     }
	 * });
	 *
	 * //In custom_code/html/sample.html...
	 * <!DOCTYPE html>
	 * <html>
	 * <meta name="viewport" content="width=device-width, initial-scale=1">
	 *
	 * <head>
	 *     <style>
	 *         body {
	 *             background-color: #18222d;
	 *             height: 100vh;
	 *             display: flex;
	 *             justify-content: center;
	 *             align-items: center;
	 *         }
	 *     </style>
	 * </head>
	 * <body>
	 *     <div>
	 *         <button type="button" onclick="clicked()">Click Me!</button>
	 *     </div>
	 *     <script>
	 *         const OS = "ios";
	 *         const sendMessageToRN = (message) => {
	 *             if (OS === "ios") {
	 *                 if (window.ReactNativeWebView.postMessage.length !== 1) {
	 *                     setTimeout(sendMessageToRN(message), 200);
	 *                 } else {
	 *                     window.ReactNativeWebView.postMessage(message);
	 *                 }
	 *             } else {
	 *                 window.ReactNativeWebView.postMessage(message);
	 *                 setTimeout(() => window.ReactNativeWebView.postMessage(message), 50);
	 *             }
	 *         }
	 *         const clicked = () => {
	 *             sendMessageToRN("Clicked!")
	 *         }
	 *     </script>
	 * </body>
	 *
	 * </html>
	 *
	 *  //In custom_code/index.js...
	 *
	 *  ...
	 *
	 * import MyCustomScreen from "./components/MyCustomScreen";
	 * export const applyCustomCode = externalCodeSetup => {
	 *   externalCodeSetup.navigationApi.replaceScreenComponent("EditXprofile", MyCustomScreen);
	 * }
	 *
	 *
	 */
	setEditProfileComponentDidUpdateCallback = (
		onComponentDidUpdate:
			| null
			| ((props: EditProfileComponentDidUpdateCallback) => void)
	) => {
		this.editProfileComponentDidUpdateCallback = onComponentDidUpdate;
	};

	profileHeaderButtonsFilter = (
		profileHeaderButtons: ProfileHeaderButtonObject[]
	) => profileHeaderButtons;
	/**
	 * You can use this hook to filter the buttons rendered in the header component of the Profile screen.
	 * For example, you can add or remove buttons according to your preference.
	 * @method
	 * @param {ProfileHeaderButtonObject[]} profileHeaderButtons
	 * @example <caption> Add an "Announcement" button </caption>
	 *
	 * externalCodeSetup.profileScreenHooksApi.setProfileHeaderButtonsFilter(
	 *    buttons => {
	 *        const modifiedButtons = [
	 *
	 *            {
	 *                icon: {fontIconName: 'globe', weight: 300},
	 *                label: "Announcements",
	 *                isNavigation: true, //If set to true, the button will not be set to a "loading" state
	 *                useDispatch: false, //If this is not set, `doFunction` will be wrapped in a `dispatch` function which is used to call a redux function
	 *                doFunction: () => Alert.alert("This user is an admin"),
	 *                getError: (err) => console.log('Error', err)
	 *            },
	 *            ...buttons
	 *        ];
	 *
	 *        return modifiedButtons;
	 *    }
	 * );
	 */
	setProfileHeaderButtonsFilter = (
		profileHeaderButtons: (
			profileHeaderButtons: ProfileHeaderButtonObject[]
		) => ProfileHeaderButtonObject[]
	) => {
		this.profileHeaderButtonsFilter = profileHeaderButtons;
	};

	ProfileHeaderWrapper: React.ComponentType<
		ProfileHeaderWrapperProps
	> | null = null;

	/**
	 * You can use this hook to customize the ProfileHeaderWrapper component when viewing the XProfile Screen.
	 * For example, you can use this to change the appearance of the component.
	 * @method
	 * @param {ProfileHeaderWrapperProps} ProfileHeaderWrapper
	 * @example <caption> Change style of the wrapper </caption>
	 *
	 * externalCodeSetup.profileScreenHooksApi.setProfileHeaderWrapper(props => (
	 *   <View style={[props.styles.wrapper, {borderRadius: 0}]}>
	 *       {props.children}
	 *   </View>
	 * ));
	 */
	setProfileHeaderWrapper = (
		ProfileHeaderWrapper: React.ComponentType<ProfileHeaderWrapperProps> | null
	) => {
		this.ProfileHeaderWrapper = ProfileHeaderWrapper;
	};

	ProfileItemValue: React.ComponentType<ProfileItemValueProps> | null = null;

	/**
	 * You can use this hook to customize the component that displays the value of each fields when viewing the XProfile Screen.
	 * @method
	 * @param {ProfileItemValueProps} ProfileItemValue
	 * @example <caption> Use an image when displaying a certain type of field </caption>
	 *
	 *  ...
	 *
	 * import AppImage from "@src/components/AppImage";
	 *
	 * export const applyCustomCode = async (externalCodeSetup) => {
	 *     externalCodeSetup.profileScreenHooksApi.setProfileItemValue(props => {
	 *         const {
	 *             global,
	 *             data,
	 *             type,
	 *             options,
	 *             formatDateFunc,
	 *             attemptDeepLink,
	 *             ProfileItemValue
	 *         } = props;
	 *
	 *         return (
	 *             <>
	 *                 {type === "country" ? (
	 *                     <View>
	 *                         <AppImage
	 *                             source={require("./assets/images/country.png")}
	 *                             style={{
	 *                                 width: "auto",
	 *                                 height: 50
	 *                             }}
	 *                         />
	 *                     </View>
	 *                 ) : (
	 *                     <ProfileItemValue
	 *                         {...{
	 *                             global,
	 *                             data,
	 *                             type,
	 *                             options,
	 *                             formatDateFunc,
	 *                             attemptDeepLink
	 *                         }}
	 *                     />
	 *                 )}
	 *             </>
	 *         );
	 *     });
	 * }
	 *
	 */
	setProfileItemValue = (
		ProfileItemValue: React.ComponentType<ProfileItemValueProps> | null
	) => {
		this.ProfileItemValue = ProfileItemValue;
	};

	ProfileItem: React.ComponentType<ProfileItemProps> | null = null;

	/**
	 * You can use this hook to customize the component that displays the title and value of each fields when viewing the XProfile Screen.
	 * @method
	 * @param {ProfileItemProps} ProfileItem
	 * @example
	 *
	 *  ...
	 *
	 * import {Text, View, StyleSheet} from "react-native";
	 * import {ProfileItemValue} from "@src/components/Profile/Xprofile/View";
	 *
	 * export const applyCustomCode = async (externalCodeSetup) => {
	 *     externalCodeSetup.profileScreenHooksApi.setProfileItem(
	 *         ({
	 *             styles,
	 *             isLast,
	 *             global,
	 *             colors,
	 *             field,
	 *             formatDateFunc,
	 *             attemptDeepLink
	 *         }) => (
	 *             <View
	 *                 style={[
	 *                     styles.xProfileItemNew,
	 *                     !isLast
	 *                         ? {
	 *                                 borderBottomColor: colors.borderColor,
	 *                                 borderBottomWidth: StyleSheet.hairlineWidth
	 *                           }
	 *                         : {paddingBottom: 0}
	 *                 ]}
	 *             >
	 *                 <View style={{flex: 1.5, marginBottom: 2}}>
	 *                     <Text style={[global.xProfileItemTitle]}>{field.name}</Text>
	 *                 </View>
	 *                 <View style={{flex: 2}}>
	 *                     <ProfileItemValue
	 *                         {...{
	 *                             global,
	 *                             data: field.data,
	 *                             type: field.type,
	 *                             options: field.options,
	 *                             formatDateFunc,
	 *                             attemptDeepLink,
	 *                             ProfileItemValue
	 *                         }}
	 *                     />
	 *                 </View>
	 *             </View>
	 *         )
	 *     );
	 * }
	 *
	 */
	setProfileItem = (
		ProfileItem: React.ComponentType<ProfileItemProps> | null
	) => {
		this.ProfileItem = ProfileItem;
	};

	EditProfileItem: React.ComponentType<EditProfileItemProps> | null = null;

	/**
	 * You can use this hook to customize the component that displays the title and input fields when viewing the Edit XProfile Screen.
	 * @method
	 * @param {EditProfileItemProps} EditProfileItem
	 * @example
	 *
	 *
	 * //In custom_code/index.js...
	 *
	 * ...
	 *
	 * import XEditProfileItem from "./components/XEditProfileItem";
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *   externalCodeSetup.profileScreenHooksApi.setEditProfileItem(props => <XEditProfileItem {...props} />);
	 * }
	 *
	 * //In custom_code/components/XEditProfileItem.js...
	 *
	 * import React from 'react';
	 * import {View, Text} from 'react-native';
	 * import ent from "ent";
	 *
	 * import {ProfileItemVisibility, ProfileItemField, ProfileItemError} from '@src/components/Profile/Xprofile/Edit';
	 *
	 * const XEditProfileItem = (props) => {
	 *
	 *     const {styles, global, formItem, errors} = props;
	 *
	 *     return (
	 *         <View style={styles.container}>
	 *             <View style={global.row}>
	 *                 <Text style={styles.title}>
	 *                     {formItem.alternate_name !== ""
	 *                         ? formItem.alternate_name
	 *                         : formItem.name}
	 *                 </Text>
	 *                 {formItem.allow_custom_visibility !== "disabled" && (
	 *                     <ProfileItemVisibility {...props} />
	 *                 )}
	 *             </View>
	 *             <View style={styles.fieldContainer}>
	 *                 <ProfileItemField {...props}/>
	 *             </View>
	 *             {!!formItem.description?.rendered && (
	 *                 <Text
	 *                     style={styles.description}
	 *                 >
	 *                     {ent.decode(formItem.description.rendered || "")}
	 *                 </Text>
	 *             )}
	 *             {!!errors &&
	 *                 !!errors[formItem.id] && (
	 *                     <ProfileItemError {...props} error={errors[formItem.id]} />
	 *                 )}
	 *         </View>
	 *     );
	 * };
	 *
	 * export default XEditProfileItem;
	 *
	 */
	setEditProfileItem = (
		EditProfileItem: React.ComponentType<EditProfileItemProps> | null
	) => {
		this.EditProfileItem = EditProfileItem;
	};

	GroupFieldsTitle: React.ComponentType<GroupFieldsTitleProps> | null = null;

	/**
	 * You can use this hook to customize the component that displays the group title of the fields when viewing the XProfile Screen.
	 * @method
	 * @param {GroupFieldsTitleProps} GroupFieldsTitle
	 * @example
	 *
	 * externalCodeSetup.profileScreenHooksApi.setGroupFieldsTitle(
	 *   ({styles, group}) => (
	 *       <View style={styles.container}>
	 *           <Text style={styles.text}>{group.name}</Text>
	 *       </View>
	 *   )
	 * );
	 *
	 */
	setGroupFieldsTitle = (
		GroupFieldsTitle: React.ComponentType<GroupFieldsTitleProps> | null
	) => {
		this.GroupFieldsTitle = GroupFieldsTitle;
	};

	XProfileUserAvatar: React.ComponentType<
		XProfileUserAvatarProps
	> | null = null;

	/**
	 * You can use this hook to customize the user avatar when viewing the XProfile Screen.
	 * @method
	 * @param {XProfileUserAvatarProps} XProfileUserAvatar
	 * @example
	 *
	 *  ...
	 *
	 * import AvatarImageUpload from "@src/components/AvatarImageUpload";
	 *
	 * export const applyCustomCode = async (externalCodeSetup) => {
	 *     externalCodeSetup.profileScreenHooksApi.setXProfileUserAvatar(
	 *         ({isOwnAccount, t, global, user, size, imagePickerProps}) => (
	 *             <AvatarImageUpload
	 *                 isOwnAccount={isOwnAccount}
	 *                 t={t}
	 *                 global={global}
	 *                 user={user}
	 *                 size={10}
	 *                 imagePickerProps={imagePickerProps}
	 *             />
	 *         )
	 *     );
	 * }
	 *
	 */
	setXProfileUserAvatar = (
		XProfileUserAvatar: React.ComponentType<XProfileUserAvatarProps> | null
	) => {
		this.XProfileUserAvatar = XProfileUserAvatar;
	};

	XProfileUserTypeComponent: React.ComponentType<
		XProfileUserTypeComponentProps
	> | null = null;

	/**
	 * You can use this hook to customize the component that displays the user type when viewing the XProfile Screen.
	 * @method
	 * @param {XProfileUserTypeComponentProps} XProfileUserTypeComponent
	 * @example
	 *
	 *  ...
	 *
	 * import AvatarImageUpload from "@src/components/AvatarImageUpload";
	 *
	 * export const applyCustomCode = async (externalCodeSetup) => {
	 *     externalCodeSetup.profileScreenHooksApi.setXProfileUserTypeComponent(
	 *         ({isOwnAccount, t, global, user, size, imagePickerProps}) => (
	 *             <AvatarImageUpload
	 *                 isOwnAccount={isOwnAccount}
	 *                 t={t}
	 *                 global={global}
	 *                 user={user}
	 *                 size={10}
	 *                 imagePickerProps={imagePickerProps}
	 *             />
	 *         )
	 *     );
	 * }
	 *
	 */
	setXProfileUserTypeComponent = (
		XProfileUserTypeComponent: React.ComponentType<
			XProfileUserTypeComponentProps
		> | null
	) => {
		this.XProfileUserTypeComponent = XProfileUserTypeComponent;
	};

	XProfileUserFullNameComponent: React.ComponentType<
		XProfileUserFullNameComponentProps
	> | null = null;

	/**
	 * You can use this hook to customize the component that displays the user's name in the header when viewing the XProfile Screen.
	 * @method
	 * @param {XProfileUserFullNameComponentProps} XProfileUserFullNameComponent
	 * @example
	 *
	 *  ...
	 *
	 * import Animated from "react-native-reanimated";
	 *
	 * export const applyCustomCode = async (externalCodeSetup) => {
	 *     externalCodeSetup.profileScreenHooksApi.setXProfileUserFullNameComponent(
	 *         ({styles, user}) => (
	 *             <Animated.View style={styles.container}>
	 *                 <Text style={styles.text}>{user.fullname}</Text>
	 *             </Animated.View>
	 *         )
	 *     );
	 * }
	 *
	 */
	setXProfileUserFullNameComponent = (
		XProfileUserFullNameComponent: React.ComponentType<
			XProfileUserFullNameComponentProps
		> | null
	) => {
		this.XProfileUserFullNameComponent = XProfileUserFullNameComponent;
	};

	XProfileUserHandleComponent: React.ComponentType<
		XProfileUserHandleComponentProps
	> | null = null;

	/**
	 * It is used to replace the default user handle component when viewing the XProfile screen.
	 * @method
	 * @param {React.ComponentType<XProfileUserHandleComponentProps>} XProfileUserHandleComponent
	 * @example
	 *
	 * ...
	 *
	 * import {TextDot} from "@src/components/TextDot";
	 *
	 * export const applyCustomCode = async (externalCodeSetup) => {
	 *     externalCodeSetup.profileScreenHooksApi.setXProfileUserHandleComponent(
	 *         ({hideHandle, styles, name, colors}) => {
	 *             return (
	 *                 <>
	 *                     {!hideHandle ? <Text style={styles.text}>{name}</Text> : null}
	 *                     <TextDot color={colors.descLightTextColor} style={styles.dot} />
	 *                 </>
	 *             );
	 *         }
	 *     );
	 * }
	 */
	setXProfileUserHandleComponent = (
		XProfileUserHandleComponent: React.ComponentType<
			XProfileUserHandleComponentProps
		> | null
	) => {
		this.XProfileUserHandleComponent = XProfileUserHandleComponent;
	};

	XProfileUserRegisteredComponent: React.ComponentType<
		XProfileUserRegisteredComponentProps
	> | null = null;

	/**
	 * It is used to replace user registration details component when viewing the XProfile screen.
	 * @method
	 * @param {React.ComponentType<XProfileUserRegisteredComponentProps>} XProfileUserRegisteredComponent
	 * @example
	 *
	 * externalCodeSetup.profileScreenHooksApi.setXProfileUserRegisteredComponent(
	 *         ({global, user, formatDateFunc, t}) => (
	 *             <Text style={global.xProfileJoined}>{`${t("members:joined")} ${formatDateFunc(user.registeredDate)}`}</Text>
	 *         )
	 * );
	 */
	setXProfileUserRegisteredComponent = (
		XProfileUserRegisteredComponent: React.ComponentType<
			XProfileUserRegisteredComponentProps
		> | null
	) => {
		this.XProfileUserRegisteredComponent = XProfileUserRegisteredComponent;
	};

	XProfileUserFollowingComponent: React.ComponentType<
		XProfileUserFollowingComponentProps
	> | null = null;

	/**
	 * You can use this to replace the user's following and/or follower details component when viewing the XProfile screen.
	 * @param {React.ComponentType<XProfileUserFollowingComponentProps>} XProfileUserFollowingComponent
	 * @method
	 * @example
	 *
	 * externalCodeSetup.profileScreenHooksApi.setXProfileUserFollowingComponent(
	 *   ({global, styles, user, t}) => {
	 *       return (
	 *           <View style={[global.row, {marginTop: 20}]}>
	 *               <View style={styles.metaBlock}>
	 *                   <Text style={[global.xProfileCount, styles.xProfileCount]}>
	 *                       {user.followers}
	 *                   </Text>
	 *                   <Text style={global.xProfileCountTitle}>
	 *                       {t(`members:followers`, {count: user.followers})}
	 *                   </Text>
	 *               </View>
	 *               <View style={styles.metaBlock}>
	 *                   <Text style={[global.xProfileCount, styles.xProfileCount]}>
	 *                       {user.following}
	 *                   </Text>
	 *                   <Text style={global.xProfileCountTitle}>
	 *                       {t("members:following")}
	 *                   </Text>
	 *               </View>
	 *           </View>
	 *       );
	 *   }
	 * );
	 */
	setXProfileUserFollowingComponent = (
		XProfileUserFollowingComponent: React.ComponentType<
			XProfileUserFollowingComponentProps
		> | null
	) => {
		this.XProfileUserFollowingComponent = XProfileUserFollowingComponent;
	};

	EditProfileInputComponent: React.ComponentType<
		EditProfileInputComponentProps
	> | null = null;

	/**
	 * You can use this hook to replace input components in the edit profile form.
	 * @param {React.ComponentType<EditProfileInputComponentProps>} EditProfileInputComponent
	 * @method
	 * @example
	 *
	 * //In custom_code/index.js...
	 *
	 * ...
	 *
	 * import MyEditInput from "./components/MyEditInput";
	 *
	 * export const applyCustomCode = (externalCodeSetup: any) => {
	 *     externalCodeSetup.profileScreenHooksApi.setEditProfileInputComponent(
	 *         props => {
	 *             const {formItem, DefaultComponent} = props;
	 *
	 *             if (formItem.name === "Nickname") {
	 *                 return <MyEditInput {...props} />;
	 *             }
	 *
	 *             return <DefaultComponent {...props} />;
	 *         }
	 *     );
	 * }
	 *
	 * //In custom_code/components/MyEditInput.js...
	 *
	 * import React, {useState} from "react";
	 * import {View, TextInput, ActivityIndicator, Text} from "react-native";
	 * import axios from "axios";
	 *
	 * const MyEditInput = props => {
	 *     const {values, formItem, handleInput} = props;
	 *
	 *     const [loading, setLoading] = useState(false);
	 *     const [data, setData] = useState(null);
	 *
	 *     const {value: oldValue} = values[formItem.id]
	 *         ? values[formItem.id]
	 *         : {};
	 *
	 *     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, formItem) => {
	 *         fetchData(text);
	 *         handleInput(formItem.id, formItem.required)(text);
	 *     };
	 *
	 *     return (
	 *         <View>
	 *             <TextInput
	 *                 onChangeText={(text) => handleTextChange(text, formItem)}
	 *                 placeholder="Type something..."
	 *                 value={oldValue}
	 *             />
	 *             {loading && <ActivityIndicator />}
	 *             {data && (
	 *                 <Text>
	 *                     Result: {data?.name} | {data?.abilities[0].ability.name}
	 *                 </Text>
	 *             )}
	 *         </View>
	 *     );
	 * };
	 *
	 * export default MyEditInput;
	 */
	setEditProfileInputComponent = (
		EditProfileInputComponent: React.ComponentType<
			EditProfileInputComponentProps
		> | null
	) => {
		this.EditProfileInputComponent = EditProfileInputComponent;
	};

	AchievementsComponent: React.ComponentType<
		AchievementsComponentProps
	> | null = null;

	/**
	 * You can use this hook to modify the achievements component when viewing the XProfile Screen.
	 * @method
	 * @param {AchievementsComponentProps} AchievementsComponent
	 * @example
	 *
	 *  ...
	 *
	 * import AppTouchableOpacity from "@src/components/AppTouchableOpacity";
	 *
	 * export const applyCustomCode = (externalCodeSetup) => {
	 *
	 *     externalCodeSetup.profileScreenHooksApi.setAchievementsComponent(({
	 *         styles,
	 *         ContainerComponent,
	 *         achievements,
	 *         navigateToAchievements
	 *     }) => {
	 *         return (
	 *             <View style={styles.containerStyle}>
	 *                 <ContainerComponent>
	 *                     {achievements.map(achievementType =>
	 *                         achievementType.achievement.map(item => (
	 *                             <AppTouchableOpacity onPress={navigateToAchievements}>
	 *                                 <View style={styles.badgeContainer}>
	 *                                     {(!!item.thumbnail_url || !!achievementType.image) && (
	 *                                         <Image
	 *                                             source={{
	 *                                                 uri: item.thumbnail_url || achievementType.image
	 *                                             }}
	 *                                             style={styles.image}
	 *                                         />
	 *                                     )}
	 *
	 *                                     <View style={styles.labelContainer} />
	 *                                     <Text numberOfLines={2} style={styles.label}>
	 *                                         {item.title}
	 *                                     </Text>
	 *                                 </View>
	 *                             </AppTouchableOpacity>
	 *                         ))
	 *                     )}
	 *                 </ContainerComponent>
	 *             </View>
	 *         );
	 *     });
	 * }
	 *
	 */
	setAchievementsComponent = (
		AchievementsComponent: React.ComponentType<
			AchievementsComponentProps
		> | null
	) => {
		this.AchievementsComponent = AchievementsComponent;
	};
}