Source

externalCode/navigation.js

import * as React from "react";
import {NavigationService} from "./types";

/**
 * @typedef {"Auth" | "noAuth" | "Main" | "All"} NavigatorNames
 */

/**
 * @typedef {"LoginScreen" | "SignupScreen" | "CodeVerificationScreen" | "LinkAccountScreen" | "SocialLoginDataInputScreen" | "ForgotScreen" | "SocialLoginScreen"} AuthStackScreenNames
 */

/**
 * @typedef {"SettingsAboutScreen" | "ProfileGroupSingleScreen" | "SurveyScreen" | "ActivitiesCreatePostScreen" | "MessagesCreatePostScreen" | "QuizSingleScreen" | "QuizResults" | "QuizDetails" | "QuizReview" | "CourseSectionScreen" | "CourseQuizzesScreen" | "CourseMaterialsScreen" | "BBMediaFullView" | "ActivityMedia" | "MergeTopicScreen" | "LessonSingleScreen" | "LearnTopicSingleScreen" | "SubForumsSingleScreen" | "ForumsSingleScreen" | "CoursesSingleScreen" | "CoursesScreen" | "CoursesCategorySingleScreen" | "TopicsTagScreen" | "TopicsSingleScreen" | "AddTopicScreen" | "SelectScreen" | "MoveReplyScreen" | "SplitReplyScreen" | "NewReplyScreen" | "CreateNewAlbum" | "AddNewProfilePhoto" | "AddNewGroupPhoto" | "SettingsPushScreen" | "SettingsPrivacyScreen" | "GroupsSingleScreen" | "PageScreen" | "BlockScreen" | "ProfileScreen" | "PrivacySettingsScreen" | "GroupInviteSettingsScreen" | "LoginInfoScreen" | "AlbumSingleScreen" | "CreateNewPhotosScreen" | "AlbumAddPhotosScreen" | "HomeNotificationsScreen" | "HomeForumsScreen" | "HomeCourseCategoriesScreen" | "HomeTopicsScreen" | "HomeGroupsScreen" | "HomeMemberScreen" | "HomeActivityScreen" | "HomeMembersScreen" | "ReportFormScreen" | "ManageGroupDetails" | "EditNavigation" | "EditXprofile" | "ViewXprofile" | "PhotoXprofile" | "CoverXprofile" | "ProfileCourses" | "ProfilePhotos" | "GlobalPhotos" | "ProfileGamipress_achievements" | "ProfileGamipress_points" | "ProfileGamipress_ranks" | "ProfileBadgeos_achievements" | "ProfileBadgeos_points" | "ProfileBadgeos_ranks" | "ProfileActivities" | "ProfileCertificates" | "ProfileResults" | "ProfileBlog" | "ProfileXprofile" | "ProfileInvites" | "ProfileFriends" | "ProfileForums" | "ProfileGroups" | "GroupMessages" | "GroupMessageCreatePostScreen" | "GroupActivity" | "GroupPhotos" | "GroupAdmin" | "GroupManageSettings" | "GroupManageSettingsForm" | "GroupManageCoverPhoto" | "GroupManageMembers" | "GroupManagePhoto" | "GroupManageDelete" | "GroupManageDetails" | "GroupMembers" | "GroupSubgroups" | "GroupInvite" | "SelectGroupMembers" | "PendingInvites" | "SubmitInvite" | "GroupRequests" | "GroupCourses" | "ProductSingleScreen" | "ActivitySingleScreen" | "BlogSingleScreen" | "BlogSingleDeeplink" | "BlogReplyScreen" | "MyLibraryScreen" | "DownloadedCoursesScreen" | "BlogScreen" | "SettingsScreen" | "GroupsScreen" | "NotificationsScreen" | "TopicsScreen" | "CourseCategoriesScreen" | "ActivitiesScreen" | "MessagesScreen" | "MembersScreen" | "ForumsScreen" | "ProductsScreen" | "EmailInvitesSendScreen" | "EmailInvitesSentScreen" | "EmailInviteMessageScreen" | "SendFeedbackScreen" | "ReportBugScreen"} MainStackScreenNames
 */

/**
 * @typedef {"my_library" | "blog" | "settings" | "groups" | "notifications" | "topics" | "courses" | "courses_category" | "courses_all" | "activity" | "documents" | "messages" | "members" | "forums" | "profile" | "iap_products" | "photos" | "videos" | "gamipress_achievements" | "badgeos_achievements" | "course_certificates" } MainMenuScreenNames
 */

/**
 * @typedef {AuthStackScreenNames | MainStackScreenNames | MainMenuScreenNames} ScreenName
 */

/**
 * @typedef {Object} Navigator
 * @property {NavigationService} navigation Navigation object
 * @property {Function} dispatch Dispatch function
 */

/**
 * @typedef {Object} RoutesObject
 * @example
 * {
 *     AppLockScreen: {screen: AppLockNavigator},
 *     Main: {screen: MainScreenNavigator, navigationOptions: {header: null}},
 * 	   ...
 * }
 */

/**
 * @typedef {Function} AfterAuthRoutesFilter
 * @param {RoutesObject} routes Current routes
 * @return {RoutesObject} New routes
 */

/**
 * @typedef {Function} ShouldUpdateCallback
 * @param {Object} thisProps Current props
 * @param {Object} nextProps Next props
 * @param {Object} thisState Current state
 * @param {Object} nextState Next state
 * @return {Boolean} Return `true` if component should update
 */

/**
 * @typedef {Function} BottomTabsSettingsFilterCallback
 * @param {Object} tabsOptions `tabsOptions` include `tabBarOptions`, `initialRouteName` `tabBarComponent` etc..
 * @param {Object} colors App colors
 * @return {Object} New tab settings
 */

/**
 * @typedef {Function} BottomTabsRoutesFilterCallback
 * @param {Object} routes Current bottom tab routes
 * @param {Object} metaMoreScreen Meta data of the More Screen
 * @param {Function} constructTabbarAllScreens Returns the appropriate screens depending on value of "Tab Bar Visibility"
 * @param {Boolean} showTabBarOnAllScreens Returns `true` if "Tab Bar Visibility" is set to "Show on All Screens"
 * @return {Object} New routes
 */

/**
 * @typedef {Function} InitialAfterAuthRouteCallback
 * @param {Object} props AppNavigator component props
 * @return {String} Route
 */

/**
 * @typedef {Function} NavigatorEventListenerCallback
 * @param {Navigator} navigator Root navigator object
 */

/**
 * @typedef {Function} HOCsFilter
 * @param {Array<Function>} hocs Initial HOCs array
 * @return {Array<Function>} New HOCs array
 */

/**
 * @typedef {Function} MapStateToProps
 * @param {Object} state - Redux state
 * @return {Object} - Props
 */

/**
 * @typedef {Object} animatedSwitchNavigatorProps
 * @property {Object} routes Default navigation routes
 * @property {Object} options Default navigation options
 * @property {Object} routeProps Contains information which can be utilized when creating custom navigators
 * @return {animatedSwitchNavigatorReturn}
 */

/**
 * @typedef {Object} animatedSwitchNavigatorReturn
 * @property {Object} routes New navigation routes
 * @property {Object} options New navigation options
 */

/**
 * @typedef {Object} IconProps
 * @property {Object} item Route information
 * @property {Boolean} focused Returns `true` if icon is currently active
 * @property {String} tintColor Icon color
 * @property {Function} calcFontSize Used to calculate font size based on the fixed value you assign and percent set on branding options
 * @property {Object} colors App colors
 */

/**
 * @typedef {Object} LabelProps
 * @property {Object} props Route information
 * @property {Array<Object>} textStyle Default style for the text component
 */

/**
 * @typedef {Object} BottomTabBarProps
 * @property {Object} state Route state information
 * @property {Object} descriptors Bottom tab bar menu items
 * @property {NavigationService} navigation
 */

/**
 * @class
 * Navigation Hooks.
 * Instance name: navigationApi
  
   You can use this hook to customize app navigation aspects such as adding a new route to the app react-navigation tree, adding new HOC (Higher-Order Components) and more.
 * @example
 * externalCodeSetup.navigationApi.METHOD_NAME
 */
export class NavigationApi {
	/**
	 * @ignore
	 * The app shows the SocialLoginScreen when this property is set to `true`
	 * Note that social login needs to be enabled and configured at the backend
	 * beforehand.
	 * @private
	 * @type {Boolean}
	 */
	socialLoginEnabled = false;
	/**
	 * @ignore Social login support removed
	 * Enables social login (if social login is enabled and configured at the backend beforehand)
	 * @method
	 */
	enableSocialLogin = () => {
		this.socialLoginEnabled = true;
	};

	/**
	 * @deprecated
	 */
	filterMultisiteDrawerSettings = (switchSite, options) => options;
	setFilterMultisiteDrawerSettings = filterMultisiteDrawerSettings => {
		this.filterMultisiteDrawerSettings = filterMultisiteDrawerSettings;
	};

	/**
	 * @deprecated
	 */
	filterInitialAfterAuthRoute = (props, initialAfterAuthRoute) =>
		initialAfterAuthRoute;
	setFilterInitialAfterAuthRoute = filterInitialAfterAuthRoute => {
		this.filterInitialAfterAuthRoute = filterInitialAfterAuthRoute;
	};

	/**
	 * @deprecated
	 * We don't use default icons anymore. All icons are provided by plugin.
	 */
	customIcons = {};
	addCustomIcons = iconsMap => {
		Object.assign(this.customIcons, iconsMap);
	};

	/**
	 * @deprecated
	 */
	setShowIosBottomTabsLabels = shouldShow => {
		console.warn(
			"This hook is deprecated. " +
				"Use globalSettings.app_menu.ios.show_labels and globalSettings.app_menu.android.show_labels instead"
		);
	};

	/**
	 * @deprecated
	 */
	composeHooks = newHooks => newHooks;
	/**
	 * @deprecated
	 */
	AddComposeHooks = composeHooks => {
		console.warn("This hook is deprecated. Use addComposeHocs");
		this.composeHooks = composeHooks;
	};

	/**
	 * @deprecated
	 * We use a custom TabBar component now.
	 */
	iosBottomTabsIconRenderer = null;
	setIosBottomTabsIcon = renderFunction => {
		console.warn("This hook is depreciated");
		this.iosBottomTabsIconRenderer = renderFunction;
	};

	/**
	 * Used to provide additional component update logic
	 * @private
	 */
	shouldComponentUpdate = (thisProps, nextProps, thisState, nextState) =>
		undefined;
	/**
	 * Used to provide custom update logic to AppNavigator.
	 * This function is called in shouldComponentUpdate.
	 * If this hook returns a boolean, the component will use this to determine if it should update.
	 * For more info about shouldComponentUpdate function, refer to {@link https://reactjs.org/docs/react-component.html#shouldcomponentupdate}
	 * @method
	 * @param {ShouldUpdateCallback} updateFunc
	 * @example
	 * externalCodeSetup.navigationApi.setShouldComponentUpdate((thisProps, nextProps, thisState, nextState) => {
	 *  //If condition...
	 *  return true;
	 * })
	 */
	setShouldComponentUpdate = updateFunc => {
		this.shouldComponentUpdate = updateFunc;
	};

	/**
	 * Used to provide a custom initial after auth route
	 * Takes AppNavigator props and returns a route name
	 * Return null to use default initial route logic
	 * @private
	 */
	initialAfterAuthRoute = props => null;
	/**
	 * By default we navigate to the "Main" stack after navigation.
	 * This hook is used to provide a different route after login.
	 * It uses the callback function `func` that will receive AppNavigator props and return a route name as a string.
	 * More examples at: {@link https://www.buddyboss.com/resources/dev-docs/app-development/extending-the-buddyboss-app/creating-new-screens/}
	 * @method
	 * @param {InitialAfterAuthRouteCallback} func
	 * @return {ScreenName} Return string screen name
	 * @example <caption> Show products screen after user authentication </caption>
	 * externalCodeSetup.navigationApi.setInitialAfterAuthRoute( props => {
	 *   if (props.auth.isLoggedIn){
	 *     return "ProductsScreen";
	 *   }
	 * })
	 */
	setInitialAfterAuthRoute = func => {
		this.initialAfterAuthRoute = func;
	};

	/**
	 * Used to provide custom state props
	 * Takes redux state as an argument and returns an object mapping state to props
	 * @private
	 */
	mapStateToProps = state => ({});

	/**
	 * Used to provide AppNavigator component custom redux props.
	 * It uses the `mapStateFunc` callback function that should receive redux state as an argument and return state props as an object.
	 * @method
	 * @param {MapStateToProps} mapStateFunc
	 * @example <caption> Add `blog` redux state and a localDate field to AppNavigator component props </caption>
	 * externalCodeSetup.navigationApi.setMapStateToProps( props => {
	 *   return {...props, blog: props.blog, localDate: new Date()}
	 * })
	 *
	 * //These new props can now be used in AppNavigator functions. For example:
	 * externalCodeSetup.navigationApi.setInitialAfterAuthRoute( props => {
	 *    if (props.localDate < foo)
	 *      return "CustomScreen"
	 * })
	 *
	 */
	setMapStateToProps = mapStateFunc => {
		this.mapStateToProps = mapStateFunc;
	};
	/**
	 * @ignore
	 * @private
	 */
	defaultStackNavigatorConfig = {};
	/**
	 * @ignore
	 * Reason for ignoring: A lot of stackNavigator options can't be used in the app such as setting a header style
	 * Each screen in the TabBarNavigator is wrapped in a StackNavigator.
	 * This hook is used to provide a custom stackConfigs all of these StackNavigators.
	 * For more info on how setting config for defaultStackNavigator, see {@link https://reactnavigation.org/docs/4.x/stack-navigator}
	 * @method
	 * @param {Object} config Configuration object
	 */
	setDefaultStackNavigatorConfig = config => {
		this.defaultStackNavigatorConfig = config;
	};

	/**
	 * @private
	 */
	newNavigationRoutes = [];
	/**
	 * Add a new route to react-navigation tree.
	 * NOTE: The route will be added to the root navigator if the parentNavigator value is missing.
	 * More information can be found at: {@link https://www.buddyboss.com/resources/dev-docs/app-development/extending-the-buddyboss-app/creating-new-screens/}
	 * @method
	 * @param {String} id Key of route in route map
	 * @param {String} routeName Route name in react-navigation tree
	 * @param {React.ComponentType<any>} component Route component
	 * @param {NavigatorNames} parentNavigator Parent navigator name; can be one of the NavigatorNames values.
	 * @example <caption> Register a new route and navigate to that route </caption>
	 *
	 * //After adding BookScreen as a new navigation route, you can now navigate to this screen using NavigationService
	 * externalCodeSetup.navigationApi.addNavigationRoute(
	 *  "book",
	 *  "BookScreen",
	 *  () => <Text>Custom Book Screen</Text>,
	 *  "All"
	 * );
	 *
	 * //Example on how to navigate to BookScreen
	 * //Since "book" is now registered, app can now navigate to that route
	 * const MyCustomLoginScreen = (props) => (
	 *  <View>
	 *    <Text> This is a custom login screen</Text>
	 *    <Button title="Go To Book Screen" onPress={() => props.navigation.navigate("book")}/>
	 *  </View>
	 * )
	 * externalCodeSetup.navigationApi.replaceScreenComponent(
	 *  "LoginScreen",
	 *  MyCustomLoginScreen
	 * );
	 */
	addNavigationRoute = (id, routeName, component, parentNavigator) => {
		this.newNavigationRoutes.push({id, routeName, component, parentNavigator});
	};

	/**
	 * @private
	 */
	screenReplacements = {};
	/**
	 * Replaces a certain screen in the app.
	 * More information at {@link https://www.buddyboss.com/resources/dev-docs/app-development/extending-the-buddyboss-app/creating-new-screens/}
	 * @method
	 * @param {ScreenName} componentName The name of the screen registered in the core BuddyBoss App navigation.
	 * @param {React.ComponentType<any>} replaceWith  Your custom screen component.
	 * @example <caption> Replace LoginScreen with MyScreen </caption>
	 *
	 * const MyScreen = (props) => (
	 *		<View style={{flex: 1}}>
	 *			<Text>This is my screen</Text>
	 *		</View>
	 *	);
	 *
	 *
	 *  externalCodeSetup.navigationApi.replaceScreenComponent("LoginScreen", MyScreen);
	 */
	replaceScreenComponent = (componentName, replaceWith) => {
		this.screenReplacements[componentName] = replaceWith;
	};

	/**
	 * @private
	 * @property {Array<NavigatorEventListenerCallback>} navigatorCreatedCallbacks
	 */
	navigatorCreatedCallbacks = [];
	/**
	 * Use to subscribe to root navigator created event.
	 * This will return the navigator object.
	 * Useful for dispatching navigation events outside of navigation tree components or getting state of navigator
	 * @method
	 * @param {NavigatorEventListenerCallback} onNavigatorCreated
	 * @returns {UnsubscribeFunction} Unsubscribe function
	 * @example
	 *  externalCodeSetup.navigationApi.addNavigatorCreatedCallback(props => {
	 *    Alert.alert("Navigator created!")
	 *  })
	 */
	addNavigatorCreatedCallback = onNavigatorCreated => {
		this.navigatorCreatedCallbacks.push(onNavigatorCreated);
		return () => {
			const index = this.navigatorCreatedCallbacks.indexOf(onNavigatorCreated);
			if (index !== -1) {
				this.navigatorCreatedCallbacks.splice(index, 1);
			}
		};
	};

	/**
	 *
	 * @param {Object} tabsOptions
	 * @param {Object} colors
	 * @private
	 */
	filterBottomTabsSettings = (tabsOptions, colors) => tabsOptions;
	/**
	 * You can use this hook to configure `createBottomTabNavigator` from react-navigation.
	 * `tabsOptions` and `colors` objects are provided which you can use to return a new settings object.
	 * @method
	 * @param {BottomTabsSettingsFilterCallback} filterBottomTabsSettings
	 * @example <caption> Change bottom tab bar's active tint color </caption>
	 *   externalCodeSetup.navigationApi.setFilterBottomTabsSettings((tabsOptions, colors) => {
	 *     return {
	 *      ...tabsOptions,
	 *      tabBarOptions: {
	 *        ...tabsOptions.tabBarOptions,
	 *        tabBarActiveTintColor: colors.headerBg
	 *       }
	 *     }
	 *   })
	 */
	setFilterBottomTabsSettings = filterBottomTabsSettings => {
		this.filterBottomTabsSettings = filterBottomTabsSettings;
	};

	/**
	 *
	 * @param {Object} routes
	 * @private
	 */
	filterBottomTabsRoutes = routes => routes;
	/**
	 * You can use this hook to configure `createBottomTabNavigator` from react-navigation.
	 * `routes` object is provided which you can use to return a new routes object.
	 * @method
	 * @param {BottomTabsRoutesFilterCallback} filterBottomTabsRoutes
	 * @example <caption> Create a custom More Screen </caption>
	 *
	 * import React from "react";
	 * import {Platform} from "react-native";
	 * import {add} from "react-native-reanimated";
	 * import MoreScreen from "@src/containers/Custom/MoreScreen";
	 * import FontManager from "@src/FontManager";
	 * import {NAV_HEIGHT, BARHEIGHT} from "@src/styles/global";
	 * import {SEARCH_HEIGHT} from "@src/components/Search";
	 * import {useSearchTransition} from "@src/components/listUtils";
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *     const MyCustomScreen = props => {
	 *         const showSearch = true;
	 *         const SPACING_TOP = FontManager.applyFontHeightAdjustment(NAV_HEIGHT + 17);
	 *         const INITIAL_SCROLL = -SPACING_TOP + SEARCH_HEIGHT;
	 *
	 *         const {listTopMargin} = useSearchTransition(
	 *             showSearch,
	 *             false,
	 *             Platform.select({
	 *                 ios: NAV_HEIGHT - 16 - BARHEIGHT,
	 *                 android: NAV_HEIGHT - 26 - BARHEIGHT
	 *             })
	 *         );
	 *
	 *         const searchContainerPaddingTop = add(listTopMargin, NAV_HEIGHT + 17);
	 *
	 *         return (
	 *             <MoreScreen
	 *                 screenTitle="More Menus"
	 *                 containerPaddingTop={NAV_HEIGHT}
	 *                 contentInsetTop={SPACING_TOP}
	 *                 contentOffsetY={INITIAL_SCROLL}
	 *                 searchContainerPaddingTop={searchContainerPaddingTop}
	 *                 showSearch={showSearch}
	 *             />
	 *         );
	 *     };
	 *
	 *
	 *     externalCodeSetup.navigationApi.setFilterBottomTabsRoutes(routes => {
	 *         return {
	 *             ...routes,
	 *             MoreScreen: {
	 *                 ...routes.MoreScreen.options,
	 *                 screen: {
	 *                     ...routes.MoreScreen.screen,
	 *                     component: MyCustomScreen
	 *                 }
	 *             }
	 *         };
	 *     });
	 * }
	 *
	 */
	setFilterBottomTabsRoutes = filterBottomTabsRoutes => {
		this.filterBottomTabsRoutes = filterBottomTabsRoutes;
	};

	filterAfterAuthRoutes = routes => routes;
	/**
	 * You can use this hook to filter possible changes after authentication routes.
	 * `filterAfterAuthRoutes` takes current routes object and returns a new one.
	 * For more info, see {@link https://www.buddyboss.com/resources/dev-docs/app-development/extending-the-buddyboss-app/creating-new-screens/}
	 * @method
	 * @param {AfterAuthRoutesFilter} filterAfterAuthRoutes
	 * @example <caption> Add custom screen to auth route then navigate to custom screen</caption>
	 *
	 * ...
	 *
	 * import {withNavigation} from "@src/components/hocs/withNavigation";
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *     const MyWelcomeScreen = withNavigation(props => {
	 *         return (
	 *             <View style={{flex: 1, justifyContent: "center", alignSelf: "center"}}>
	 *                 <Text> Welcome to our App! New adventure awaits!</Text>
	 *                 <Button
	 *                     title="Go to App"
	 *                     onPress={() => props.navigation.navigate("Main")}
	 *                 />
	 *             </View>
	 *         );
	 *     });
	 *
	 *
	 *     externalCodeSetup.navigationApi.setFilterAfterAuthRoutes(afterAuthRoutes => {
	 *         return {
	 *             ...afterAuthRoutes,
	 *             MyWelcomeScreen: {
	 *                 screen: MyWelcomeScreen
	 *             }
	 *         };
	 *     });
	 *
	 *     externalCodeSetup.navigationApi.setInitialAfterAuthRoute(props => {
	 *         return "MyWelcomeScreen";
	 *     });
	 * };
	 */
	setFilterAfterAuthRoutes = filterAfterAuthRoutes => {
		this.filterAfterAuthRoutes = filterAfterAuthRoutes;
	};

	composeHocs = hocs => hocs;
	/**
	 * Use this hook to register new HOCs (Higher-Order Components) which will be applied to the AppNavigator component.
	 * For more info on how to use HOCs, refer to: {@link https://reactjs.org/docs/higher-order-components.html}
	 * @method
	 * @param {HOCsFilter} composeHocs
	 * @example <caption> Add new HOC </caption>
	 *  externalCodeSetup.navigationApi.addComposeHocs(hocs => {
	 *   return [
	 *    ...hocs,
	 *    withMyCustomHoc
	 *   ];
	 * })
	 */
	addComposeHocs = composeHocs => {
		this.composeHocs = composeHocs;
	};

	bottomTabBarIcon = (icon, props) => icon;

	/**
	 * You can use this to replace the bottom tab bar icons to display your preferred icons.
	 * @method
	 * @param {React.ComponentType<Icon>} Icon Tab icon component
	 * @param {IconProps} props
	 * @example
	 * import React from "react";
	 * import Icon from "@src/components/Icon";
	 * import BadgeIcon from "@src/components/BadgeIcon";
	 * import {getIcon} from "@src/navigators/util";
	 * import {glyphMap as bbIconGlyphMap} from "@src/components/BBIcon";
	 * export const applyCustomCode = (externalCodeSetup) => {
	 *     externalCodeSetup.navigationApi.setBottomTabBarIcon(
	 *         (icon, props) => {
	 *             const {tintColor, focused, calcFontSize, colors, item} = props;
	 *
	 *             if (!item) return null;
	 *
	 *             const {color, menuIcon} = getIcon(
	 *                 item,
	 *                 bbIconGlyphMap,
	 *                 focused,
	 *                 tintColor
	 *             );
	 *
	 *             if (item.object === "notifications" || item.object === "messages") {
	 *                 return (
	 *                     <BadgeIcon
	 *                         calcFontSize={calcFontSize}
	 *                         tintColor={color}
	 *                         bottomTabsBg={colors.bottomTabsBg}
	 *                         warningColor={colors.warningColor}
	 *                         foregroundColor={item.icon.fg_color}
	 *                         platform="ios"
	 *                         inMore={false}
	 *                         app={"learnerapp"}
	 *                         type={item.object}
	 *                         icon={menuIcon}
	 *                         styles={{height: 25, width: 25}}
	 *                     />
	 *                 );
	 *             }
	 *
	 *             return (
	 *                 <Icon
	 *                     icon={menuIcon}
	 *                     foregroundColor={item.icon.fg_color}
	 *                     tintColor={color}
	 *                     styles={{height: 25, width: 25}}
	 *                 />
	 *             );
	 *         }
	 *     );
	 * }
	 */
	setBottomTabBarIcon = bottomTabBarIcon => {
		this.bottomTabBarIcon = bottomTabBarIcon;
	};

	bottomTabBarLabel = (label, labelProps) => label;

	/**
	 * You can use this to replace the bottom tab bar label to display your preferred labels.
	 * @method
	 * @param {React.ComponentType<Text>} Label Tab label component
	 * @param {LabelProps} labelProps
	 *
	 * externalCodeSetup.navigationApi.setBottomTabBarLabel((label, labelProps) => (
	 *     <Text
	 *         style={labelProps.textStyle}
	 *         numberOfLines={1}
	 *         ellipsizeMode={"tail"}
	 *         allowFontScaling={false}
	 *     >
	 *         {labelProps.props.item.label}
	 *     </Text>
	 * ));
	 */
	setBottomTabBarLabel = bottomTabBarLabel => {
		this.bottomTabBarLabel = bottomTabBarLabel;
	};

	bottomTabBar = null;

	/**
	 * You can use this to replace the bottom tab bar component according to your preference.
	 * For more information regarding the BottomTabBar, you can use the following link: {@link https://reactnavigation.org}
	 * @method
	 * @param {React.ComponentType<BottomTabBarProps>} BottomTabBarProps
	 * @example <caption> Create your own bottom tab bar </caption>
	 *
	 * import React from "react";
	 * import {Text, View, TouchableOpacity} from "react-native";
	 *
	 * import {isTabletOrIPad} from "@src/utils";
	 * import {
	 *     FontWeights,
	 *     textRTLStyleFix,
	 *     correctBottomSafeArea
	 * } from "@src/styles/global";
	 * import {useSafeAreaInsets} from "react-native-safe-area-context";
	 *
	 * import Icon from "@src/components/Icon";
	 * import BadgeIcon from "@src/components/BadgeIcon";
	 * import {getMenuFontIconVariant} from "@src/navigators/util";
	 * import {glyphMap as bbIconGlyphMap} from "@src/components/BBIcon";
	 * import withGlobalStyles from "@src/components/hocs/withGlobalStyles";
	 * import {useScreenProps} from "@src/navigators/v5/ScreenPropsProvider";
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *     const renderIcon = props => {
	 *         const {calcFontSize} = useScreenProps(["calcFontSize"]);
	 *
	 *         const {focused, item, tabBarOptions, colors} = props;
	 *         const {tabBarActiveTintColor} = tabBarOptions;
	 *
	 *         if (!item) return null;
	 *
	 *         let color;
	 *         let menuIcon;
	 *         if (item.icon.type === "buddyboss") {
	 *             if (bbIconGlyphMap[item.icon.id]) {
	 *                 menuIcon = {
	 *                     fontIconName: item.icon.id,
	 *                     fontIconVariant: getMenuFontIconVariant(item.icon, focused)
	 *                 };
	 *             } else {
	 *                 menuIcon = {
	 *                     uri: focused ? item.icon.uri_active || item.icon.uri : item.icon.uri
	 *                 };
	 *             }
	 *             color = focused ? tabBarActiveTintColor : item.icon.color;
	 *         } else {
	 *             menuIcon = {
	 *                 uri: focused ? item.icon.uri_active || item.icon.uri : item.icon.uri
	 *             };
	 *             if (item.icon.type !== "custom") {
	 *                 color = focused ? tabBarActiveTintColor : item.icon.color;
	 *             } else if (item.icon.fill_color) {
	 *                 color = focused ? tabBarActiveTintColor : item.icon.color;
	 *             }
	 *         }
	 *
	 *         if (item.object === "notifications" || item.object === "messages") {
	 *             return (
	 *                 <BadgeIcon
	 *                     calcFontSize={calcFontSize}
	 *                     tintColor={color}
	 *                     bottomTabsBg={colors.bottomTabsBg}
	 *                     warningColor={colors.warningColor}
	 *                     foregroundColor={item.icon.fg_color}
	 *                     platform="ios"
	 *                     inMore={false}
	 *                     app={"learnerapp"}
	 *                     type={item.object}
	 *                     icon={menuIcon}
	 *                     styles={{height: 25, width: 25}}
	 *                 />
	 *             );
	 *         }
	 *
	 *         return (
	 *             <Icon
	 *                 icon={menuIcon}
	 *                 foregroundColor={item.icon.fg_color}
	 *                 tintColor={color}
	 *                 styles={{height: 25, width: 25}}
	 *             />
	 *         );
	 *     };
	 *
	 *     const MyTabBar = withGlobalStyles(props => {
	 *         const {state, descriptors, navigation, global, colors} = props;
	 *
	 *         const insets = useSafeAreaInsets();
	 *         const bottomSafeArea = correctBottomSafeArea(insets.bottom);
	 *
	 *         const textStyle = [
	 *             global.menuLabelStyle,
	 *             {
	 *                 marginHorizontal: 5,
	 *                 marginTop: isTabletOrIPad() ? -(bottomSafeArea / 5) : 2,
	 *                 fontWeight: FontWeights["medium"],
	 *                 ...textRTLStyleFix(),
	 *                 opacity: 1,
	 *                 textAlign: "center"
	 *             }
	 *         ];
	 *
	 *         return (
	 *             <View style={{flexDirection: "row"}}>
	 *                 {state.routes.map((route, index) => {
	 *                     const {options} = descriptors[route.key];
	 *
	 *                     const item = route.params.item;
	 *
	 *                     const isFocused = state.index === index;
	 *
	 *                     const onPress = () => {
	 *                         const event = navigation.emit({
	 *                             type: "tabPress",
	 *                             target: route.key,
	 *                             canPreventDefault: true
	 *                         });
	 *
	 *                         if (!isFocused && !event.defaultPrevented) {
	 *                             navigation.navigate({name: route.name, merge: true});
	 *                         }
	 *                     };
	 *
	 *                     return (
	 *                         <TouchableOpacity
	 *                             onPress={onPress}
	 *                             style={{
	 *                                 flex: 1,
	 *                                 height: 40,
	 *                                 alignItems: "center",
	 *                                 alignSelf: "center",
	 *                                 marginBottom: 33
	 *                             }}
	 *                             activeOpacity={1}
	 *                         >
	 *                             <View
	 *                                 style={{
	 *                                     height: 30,
	 *                                     alignItems: "center",
	 *                                     justifyContent: "center"
	 *                                 }}
	 *                             >
	 *                                 {renderIcon({
	 *                                     focused: isFocused,
	 *                                     item,
	 *                                     colors,
	 *                                     tabBarOptions: options
	 *                                 })}
	 *                             </View>
	 *
	 *                             <Text
	 *                                 style={[
	 *                                     textStyle,
	 *                                     {color: isFocused ? colors.linkColor : colors.textColor}
	 *                                 ]}
	 *                                 numberOfLines={1}
	 *                                 ellipsizeMode="tail"
	 *                                 allowFontScaling={false}
	 *                             >
	 *                                 {item.label}
	 *                             </Text>
	 *                         </TouchableOpacity>
	 *                     );
	 *                 })}
	 *             </View>
	 *         );
	 *     });
	 *
	 *     externalCodeSetup.navigationApi.setBottomTabBar(props => (
	 *         <MyTabBar {...props} />
	 *     ));
	 * }
	 */
	setBottomTabBar = bottomTabBar => {
		this.bottomTabBar = bottomTabBar;
	};

	animatedSwitchNavigator = (routes, options) => ({
		routes: routes,
		options: options
	});

	/**
	 * This hook allows you to create your own animated switch navigator.
	 * For example, you can add a new route and use it as your initial switch route instead of the default "Auth" route which will show you the login screen.
	 * @method
	 * @param {animatedSwitchNavigatorProps} animatedSwitchNavigator
	 * @returns {animatedSwitchNavigatorReturn}
	 *
	 * @example <caption> Add a custom screen to be used as initial switch route </caption>
	 *
	 * //In custom_code/MyCustomScreen.js...
	 *
	 * import React from 'react';
	 * import { View, Text, Button } from 'react-native';
	 *
	 * const MyCustomScreen = props => {
	 *
	 *  return <View style={{ flex: 1, justifyContent: "center", alignSelf: "center" }}>
	 *    <Text> Welcome to our App! New adventure awaits!</Text>
	 *    <Button title="Go to App" onPress={() => props.navigation.navigate("Auth")} />
	 *  </View>
	 * }
	 *
	 * export default MyCustomScreen;
	 *
	 *
	 * //In custom_code/index.js...
	 *
	 * ...
	 *
	 * import MyCustomScreen from "./components/MyCustomScreen";
	 * export const applyCustomCode = externalCodeSetup => {
	 *
	 *  externalCodeSetup.navigationApi.setAnimatedSwitchNavigator((routes, options, routeProps) => {
	 *
	 *    const feature = routeProps.settings.features.multisite_network;
	 *    const hasMultiSite = Platform.select({
	 *      ios: feature.is_enabled_ios,
	 *      android: feature.is_enabled_android
	 *    })
	 *
	 *     // Get the initial switch route based on state data.
	 *     // getInitialSwitchRoute() is based on BB App's AppNavigator.js
	 *     // Feel free to copy and paste this to your own code
	 *
	 *    const getInitialSwitchRoute = () => {
	 *
	 *      const defaultInitialRoute = "Auth";
	 *
	 *      const myCustomRoute = "MyCustomScreen"
	 *
	 *      if (!routeProps.hasValidSigning) {
	 *        return "InvalidSigningScreen";
	 *      }
	 *
	 *      if (routeProps.shouldEnforceVersionControl) {
	 *        return "VersionControlScreen";
	 *      } else if (routeProps.isLoggedIn) {
	 *        if (
	 *          routeProps.isFeatureEnabled(hasMultiSite) &&
	 *          routeProps.sites.selectedSite === null
	 *        ) {
	 *          return "AuthSiteSelectionScreen";
	 *        } else {
	 *          return routeProps.shouldLockApp ? "AppLockScreen" : "noAuth";
	 *        }
	 *      }
	 *      else {
	 *        return myCustomRoute; //Use my own custom route instead of the default "Auth" route
	 *        // return defaultInitialRoute;
	 *      }
	 *
	 *    };
	 *
	 *    const newRoutes = {
	 *     ...routes,
	 *     MyCustomScreen: {
	 *      screen: MyCustomScreen
	 *     }
	 *    }
	 *
	 *    const newOptions = {
	 *     ...options,
	 *     initialRouteName: getInitialSwitchRoute()
	 *    }
	 *
	 *    return {
	 *     routes: newRoutes,
	 *     options: newOptions,
	 *    }
	 *
	 *  })
	 * }
	 */
	setAnimatedSwitchNavigator = animatedSwitchNavigator => {
		this.animatedSwitchNavigator = animatedSwitchNavigator;
	};

	screensWithoutTabBar = [];

	/**
	 * This hook allows you to customise which screens will not have a tab bar even when the "Show on All Screens" option is selected.
	 * Therefore you can use this hook to specifically select the pages where you don’t want to display tab bars.
	 * @method
	 * @param {Array} screensWithoutTabBar Screen names: {@link https://www.buddyboss.com/resources/app-codex/global.html#MainStackScreenNames}
	 * @example
	 *
	 * externalCodeSetup.navigationApi.setScreensWithoutTabBar(["BlogSingleScreen", "CoursesSingleScreen"])
	 */
	setScreensWithoutTabBar = screensWithoutTabBar => {
		this.screensWithoutTabBar = screensWithoutTabBar;
	};
}