Source

externalCode/profileScreen.js

  1. import * as React from "react";
  2. /**
  3. * @typedef {Object} User Logged-in user's data
  4. */
  5. /**
  6. * @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
  7. */
  8. /**
  9. * @typedef { "just-me" | "friends" | "groups" | "favorites" | "mentions" | "following" } ActivitiesFilter
  10. */
  11. /**
  12. * @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
  13. */
  14. /**
  15. * Image resource (via request('...src'))
  16. * @typedef {number} LocalImageSource
  17. */
  18. /**
  19. * @typedef {Object} RemoteImageSource
  20. */
  21. /**
  22. * @typedef {LocalImageSource | RemoteImageSource} ImageSource
  23. */
  24. /**
  25. * @typedef {Object} SubscreenObject
  26. * @param {LocalImageSource} icon
  27. * @param {String} label
  28. * @param {?Number} count
  29. * @param {Function} onPress
  30. */
  31. /**
  32. * @typedef {Function} TransformProfileIgnoredSubscreensCallback
  33. * @param {Array<ProfileTab>} list Ignored subscreens list
  34. * @param {Boolean} isOwnAccount Returns `true` if profile displayed in screen is logged-in user's own account
  35. * @return {Array<string>} New list of ignored subscreens
  36. */
  37. /**
  38. * @typedef {Function} TransformProfileActivityFilters
  39. * @param {Array<ActivitiesFilter>} list
  40. * @return {Array<string>} - New list
  41. */
  42. /**
  43. * @typedef {Function} TransformProfileSubscreensListCallback
  44. * @param {Array<SubscreenObject>} list Subscreens list
  45. * @param {NavigationService} navigation
  46. * @param {User} user
  47. * @param {Boolean} isOwnAccount Returns `true` if profile displayed in screen is logged-in user's own account
  48. * @return {Array<SubscreenObject>} New subscreens list
  49. */
  50. /**
  51. * @typedef {Object} BeforeProfileDetailsComponentProps
  52. * @property { Object } colors App colors
  53. * @property { Object } global App global style
  54. * @property { MemberViewModel } user
  55. */
  56. /**
  57. * @typedef {Object} AfterProfileDetailsComponentProps
  58. * @property { MemberViewModel } user
  59. */
  60. /**
  61. * @typedef {Object} UserFullNameComponentProps
  62. * @property { MemberViewModel } user
  63. * @property { Object } global App global style
  64. * @property { Object } textStyle
  65. * @property { ?Object } topicLoading
  66. */
  67. /**
  68. * @typedef {Object} UserTypeComponentProps
  69. * @property { Boolean } isUserProfile
  70. * @property { MemberViewModel } user
  71. * @property { Object } global App global style
  72. * @property { Object } textStyle
  73. */
  74. /**
  75. * @typedef {Object} UserHandleComponentProps
  76. * @property { MemberViewModel } user
  77. * @property { Object } global App global style
  78. * @property { Object } textStyle
  79. * @property { Boolean } hideHandle Returns `true` if a setting to hide the handle was set
  80. */
  81. /**
  82. * @typedef {Object} UserRegisteredComponentProps
  83. * @property { MemberViewModel } user
  84. * @property { Object } global App global style
  85. * @property { TranslationFunction } t
  86. * @property {Function} formatDateFunc Helper function for parsing dates
  87. */
  88. /**
  89. * @typedef {Object} UserFollowingComponentProps
  90. * @property { MemberViewModel } user
  91. * @property { Object } global App global style
  92. * @property { Object } colors App colors
  93. * @property { TranslationFunction } t
  94. */
  95. /**
  96. * @typedef {Object} UserAvatarProps
  97. * @property { Boolean } isOwnAccount Returns `true` if the logged-in user is viewing their own account
  98. * @property { TranslationFunction } t
  99. * @property { Object } global
  100. * @property { MemberViewModel } user
  101. */
  102. /**
  103. * @typedef {Object} AfterProfileHeaderProps
  104. * @property { MemberViewModel } user
  105. */
  106. /**
  107. * @typedef {Object} ActionButtonProps
  108. * @property {Object} global App global style
  109. * @property {Object} button Default actions when button is pressed
  110. * @property {Object} color Color for icon
  111. * @property {Object} style
  112. * @property {TranslationFunction} t
  113. * @property {Boolean} withLabel Returns `true` if button has label
  114. * @property {Object} labelStyle
  115. * @property {Boolean} loading Returns `true` if component is still loading
  116. * @property {Boolean} first Returns `true` if button is first in the list
  117. * @property {Boolean} last Returns `true` if button is last in the list
  118. * @property {Boolean} highlight Returns `true` if button is highlighted
  119. */
  120. /**
  121. * @typedef {Function} OnChangeEventListenerCallback
  122. * @param {Number} id Field id
  123. * @param {Number} groupKey Group key
  124. * @param {Boolean} required Returns `true` if value for field is required
  125. * @param {Boolean} isDate Returns `true` if input is date
  126. * @param {String} newValue Returns value of input
  127. */
  128. /**
  129. * @typedef {Function} TransformEditProfileFieldSetsFilterCallback
  130. * @param {Array<Object>} list List of filters
  131. * @return {Array<Object>} New list of filters
  132. */
  133. /**
  134. * @typedef {Object} FieldSetObject
  135. * @property {Number} id id of field set
  136. * @property {String} name
  137. * @property {Object} description
  138. * @property {Number} group_order
  139. * @property {Boolean} can_delete Returns `true` if field set can be deleted
  140. * @property {Boolean} repeater_enabled Returns `true` if repeater is enabled
  141. * @property {Array<Object>} fields Fields in a field set
  142. */
  143. /**
  144. * @typedef {Function} TransformEditProfileFieldSetsCallback
  145. * @param {Array<FieldSetObject>} fieldSets List of field sets
  146. * @return {Array<FieldSetObject>} New list of field sets
  147. */
  148. /**
  149. * @typedef {Function} TransformViewProfileFieldSetsCallback
  150. * @param {Array<FieldSetObject>} fieldSets List of field sets
  151. * @return {Array<FieldSetObject>} New list of field sets
  152. */
  153. /**
  154. * @typedef {Function} EditProfileComponentDidUpdateCallback
  155. * @param {Object} prevProps
  156. * @param {Object} prevState
  157. * @param {Object} props
  158. * @param {Object} state
  159. * @param {Function} updateStoreValues Helper function which you can use to update the Edit Profile screen's field values
  160. */
  161. /**
  162. * @class
  163. * ProfileScreen Hooks.
  164. * Instance name: profileScreenHooksApi
  165. You can use this hook to personalize the profile screen by adding components before/after profile headers, blog details, custom header backgrounds and more.
  166. * @example
  167. * externalCodeSetup.profileScreenHooksApi.METHOD_NAME
  168. */
  169. export class ProfileScreenHooksApi {
  170. /**
  171. * @deprecated
  172. */
  173. activityFiltersSubFilter = list => list;
  174. setActivityFiltersSubFilter = activityFiltersSubFilter => {
  175. this.activityFiltersSubFilter = activityFiltersSubFilter;
  176. };
  177. /**
  178. * Sets a default cover
  179. * @private
  180. */
  181. baseCover;
  182. /**
  183. * @ignore
  184. * Reason for ignore: Function is not used anywhere in the app
  185. * Sets a default cover image. It will override Platform settings.
  186. * @param {LocalImageSource} baseCover
  187. */
  188. setBaseCover = baseCover => {
  189. this.baseCover = baseCover;
  190. };
  191. /**
  192. * @private
  193. */
  194. headerBgSource = null;
  195. /**
  196. * @ignore
  197. * Reason for ignore: Hook not being used to replace header background.
  198. * There are some instances where `headerBgSource` is used to check for a condition but it should be replaced by `customHeaderBackground`.
  199. * Set image for Profile Screen header background
  200. * @param {ImageSource}
  201. */
  202. setHeaderBgSource = source => {
  203. this.headerBgSource = source;
  204. };
  205. profileActivityFiltersFilter = list => list;
  206. /**
  207. * @ignore
  208. * Reason for ignore: Variable assignment is incorrect and filter is not being used in profile activities screen
  209. * Sets the function that can change filters array
  210. * @param {TransformProfileActivityFilters} activityFiltersFilter
  211. * @example
  212. * externalCodeSetup.profileScreenHooksApi.setProfileActivityFiltersFilter((
  213. * list,
  214. * ) => ([
  215. * ...list,
  216. * "followers",
  217. * ]))
  218. */
  219. setProfileActivityFiltersFilter = activityFiltersFilter => {
  220. this.activityFiltersFilter = activityFiltersFilter;
  221. };
  222. customHeaderBackground = null;
  223. /**
  224. * Replaces cover image for all profiles in the profile screen
  225. * @method
  226. * @param {String} customHeaderBackground Resource to replace cover image
  227. * @example
  228. * externalCodeSetup.profileScreenHooksApi.setCustomHeaderBackground('https://link-to-image.png')
  229. */
  230. setCustomHeaderBackground = customHeaderBackground => {
  231. this.customHeaderBackground = customHeaderBackground;
  232. };
  233. tabsList = list => list;
  234. /**
  235. * You can use this hook to add or modify existing tabs in the profile screen.
  236. * For example, you can add a new route in profile screen tab list
  237. * @method
  238. * @param {TransformProfileSubscreensListCallback} tabsList
  239. * @example <caption> Add a new route in profile screen tab list </caption>
  240. * ...
  241. * import { CommonActions } from "@react-navigation/native";
  242. * export const applyCustomCode = externalCodeSetup => {
  243. *
  244. * //Register a new route
  245. * externalCodeSetup.navigationApi.addNavigationRoute(
  246. * "book",
  247. * "BookScreen",
  248. * () => <Text>Custom Book Screen</Text>,
  249. * "All"
  250. * );
  251. *
  252. * //Add new menu item to profile screen tab list
  253. * externalCodeSetup.profileScreenHooksApi.setTabsList((
  254. * list,
  255. * navigation,
  256. * user,
  257. * isOwnAccount
  258. * ) => {
  259. * return [
  260. * ...list,
  261. * {
  262. * icon: require("@src/assets/img/about.png"), //Set icon
  263. * label: "Book", //Set label of menu
  264. * onPress: () => navigation.navigate(
  265. * CommonActions.navigate({
  266. * name: "book",
  267. * params: { //Pass params if needed
  268. * user,
  269. * isOwnAccount
  270. * },
  271. * key: user.id
  272. * })
  273. * )
  274. * }
  275. * ]
  276. * })
  277. * }
  278. */
  279. setTabsList = tabsList => {
  280. this.tabsList = tabsList;
  281. };
  282. /**
  283. * Hides user handle in profile header
  284. * @private
  285. */
  286. hideHandle = false;
  287. /**
  288. * Hides user handle in profile header by passing it as a `true` value.
  289. * @method
  290. * @param {Boolean} hideHandle
  291. * @example
  292. * externalCodeSetup.profileScreenHooksApi.setHideHandle(true)
  293. */
  294. setHideHandle = hideHandle => {
  295. this.hideHandle = hideHandle;
  296. };
  297. /**
  298. * filter ignored tabs on profile
  299. * @private
  300. */
  301. ignoreTabsFilter = (tabs, own) => tabs;
  302. /**
  303. * You can use this to set a function that is capable of hiding specific profile menu items.
  304. * @method
  305. * @param {TransformProfileIgnoredSubscreensCallback} ignoreTabsFilter
  306. * @example
  307. * externalCodeSetup.profileScreenHooksApi.setIgnoreTabsFilter((
  308. * list,
  309. * isOwnAccount
  310. * ) => [
  311. * ...list,
  312. * "activities",
  313. * "friends"
  314. * ])
  315. */
  316. setIgnoreTabsFilter = ignoreTabsFilter => {
  317. this.ignoreTabsFilter = ignoreTabsFilter;
  318. };
  319. BeforeDetailsComponent = null;
  320. /**
  321. * You can use it to add a component before the profile details.
  322. * @method
  323. * @param {React.ComponentType<BeforeProfileDetailsComponentProps>} BeforeDetailsComponent
  324. * @example
  325. * const BeforeDetailsComponent = ({user}) => (
  326. * <Text>User Type: {user.type}</Text>
  327. * )
  328. *
  329. * externalCodeSetup.profileScreenHooksApi.setBeforeDetailsComponent(BeforeDetailsComponent)
  330. */
  331. setBeforeDetailsComponent = BeforeDetailsComponent => {
  332. this.BeforeDetailsComponent = BeforeDetailsComponent;
  333. };
  334. AfterDetailsComponent = null;
  335. /**
  336. * 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.
  337. * @method
  338. * @param {React.ComponentType<AfterProfileDetailsComponentProps>} AfterDetailsComponent
  339. * @example
  340. * const AfterDetailsComponent = ({ user }) => (
  341. * <Text>Last activity: {user.lastActivity}</Text>
  342. * )
  343. *
  344. * externalCodeSetup.profileScreenHooksApi.setAfterDetailsComponent(AfterDetailsComponent)
  345. */
  346. setAfterDetailsComponent = AfterDetailsComponent => {
  347. this.AfterDetailsComponent = AfterDetailsComponent;
  348. };
  349. UserFullNameComponent = null;
  350. /**
  351. * You can use this to replace the user full name component and customize the name display according to your app preferences.
  352. * @method
  353. * @param {React.ComponentType<UserFullNameComponentProps>} UserFullNameComponent
  354. * @example
  355. * const UserFullNameComponent = (props) => (
  356. * <View>
  357. * <Text style={{fontSize: 20, color: "red"}}>
  358. * {props.user.fullname}
  359. * </Text>
  360. * </View>
  361. * )
  362. *
  363. * externalCodeSetup.profileScreenHooksApi.setUserFullNameComponent(UserFullNameComponent)
  364. */
  365. setUserFullNameComponent = UserFullNameComponent => {
  366. this.UserFullNameComponent = UserFullNameComponent;
  367. };
  368. UserTypeComponent = null;
  369. /**
  370. * You can use this to replace the user type component for the various app users.
  371. * @method
  372. * @param {React.ComponentType<UserTypeComponentProps>} UserTypeComponent
  373. * @example
  374. *
  375. * const UserTypeComponent = (props) => (
  376. * <View>
  377. * <Text>
  378. * User type: {props.user.type}
  379. * </Text>
  380. * </View>
  381. * )
  382. * externalCodeSetup.profileScreenHooksApi.setUserTypeComponent(UserTypeComponent);
  383. */
  384. setUserTypeComponent = UserTypeComponent => {
  385. this.UserTypeComponent = UserTypeComponent;
  386. };
  387. UserHandleComponent = null;
  388. /**
  389. * It is used to replace the default user handle component.
  390. * @method
  391. * @param {React.ComponentType<UserHandleComponentProps>} UserHandleComponent
  392. * @example
  393. *
  394. * const UserHandleComponent = (props) => (
  395. * <Text style={{color: "blue", fontSize: 30}}> @{props.user.nicename} </Text>
  396. * )
  397. *
  398. * externalCodeSetup.profileScreenHooksApi.setUserHandleComponent(UserHandleComponent)
  399. */
  400. setUserHandleComponent = UserHandleComponent => {
  401. this.UserHandleComponent = UserHandleComponent;
  402. };
  403. UserRegisteredComponent = null;
  404. /**
  405. * It is used to replace user registration details component.
  406. * @method
  407. * @param {React.ComponentType<UserRegisteredComponentProps>} UserRegisteredComponent
  408. * @example
  409. *
  410. * const UserRegisteredComponent = (props) => (
  411. * <View style={{ marginVertical: 5 }}>
  412. * <Text>Date registered: {props.formatDateFunc(props.user.registeredDate)}</Text>
  413. * </View>
  414. * )
  415. *
  416. * externalCodeSetup.profileScreenHooksApi.setUserRegisteredComponent(UserRegisteredComponent)
  417. */
  418. setUserRegisteredComponent = UserRegisteredComponent => {
  419. this.UserRegisteredComponent = UserRegisteredComponent;
  420. };
  421. UserFollowingComponent = null;
  422. /**
  423. * You can use this to replace the user's following and/or follower details component.
  424. * @param {React.ComponentType<UserFollowingComponentProps>} UserFollowingComponent
  425. * @example
  426. *
  427. * const UserFollowingComponent = (props) => (
  428. * <View style={{ flexDirection: "row" }}>
  429. * <Text> {props.user.followers} Followers </Text>
  430. * <Text> {props.user.following} Following </Text>
  431. * </View>
  432. * )
  433. *
  434. * externalCodeSetup.profileScreenHooksApi.setUserFollowingComponent(UserFollowingComponent)
  435. */
  436. setUserFollowingComponent = UserFollowingComponent => {
  437. this.UserFollowingComponent = UserFollowingComponent;
  438. };
  439. UserAvatar = null;
  440. /**
  441. * You can use it to replace the user avatar component.
  442. * @method
  443. * @param {React.ComponentType<UserAvatarProps>} UserAvatar
  444. * @example
  445. * ...
  446. * import AvatarImageUpload from "@src/components/AvatarImageUpload"
  447. * export const applyCustomCode = externalCodeSetup => {
  448. *
  449. * const UserAvatar = (props) => {
  450. * return (
  451. * <>
  452. * <Text>Tap avatar to edit</Text>
  453. * <AvatarImageUpload
  454. * isOwnAccount={props.isOwnAccount}
  455. * user={props.user}
  456. * size={100}
  457. * imagePickerProps={{ cropping: true, cropperCircleOverlay: true }}
  458. * />
  459. * </>
  460. * )
  461. * }
  462. *
  463. * externalCodeSetup.profileScreenHooksApi.setUserAvatar(UserAvatar)
  464. *
  465. */
  466. setUserAvatar = UserAvatar => {
  467. this.UserAvatar = UserAvatar;
  468. };
  469. AfterProfileHeader = null;
  470. /**
  471. * It adds a component after the profile header.
  472. * @method
  473. * @param {React.ComponentType<AfterProfileHeaderProps>} AfterProfileHeader
  474. * @example
  475. *
  476. * const AfterProfileHeader = (props) => (
  477. * <View style={{ marginLeft: 20 }}>
  478. * <Text> User Points: {props.user.points} </Text>
  479. * </View>
  480. * )
  481. *
  482. * externalCodeSetup.profileScreenHooksApi.setAfterProfileHeader(AfterProfileHeader)
  483. *
  484. */
  485. setAfterProfileHeader = AfterProfileHeader => {
  486. this.AfterProfileHeader = AfterProfileHeader;
  487. };
  488. HeaderRightComponent = null;
  489. /**
  490. * You can use it to add a component on the top right corner of the profile screen for users.
  491. * @method
  492. * @param {React.ComponentType<HeaderRightComponent>} HeaderRightComponent
  493. * @example
  494. *
  495. * //In custom_code/components/ProfileHeaderButton.js
  496. *
  497. * ...
  498. *
  499. * import { Alert } from "react-native";
  500. * import IconButton from "@src/components/IconButton";
  501. * import { globalStyle } from "@src/styles/global";
  502. * import { useSelector } from "react-redux"; //use useSelector to get state from redux
  503. *
  504. * export const ProfileHeaderButton = () => {
  505. *
  506. * const globalStyles = useSelector((state) => globalStyle(state.config.styles)) // Get style from redux
  507. * const user = useSelector((state) => state.user.userObject); // Get user from redux
  508. * const { colors } = globalStyles;
  509. * return (
  510. * <IconButton
  511. * icon={require("@src/assets/img/plus-circle.png")}
  512. * pressHandler={() => Alert.alert(`Hello ${user.nicename}!`) }// Do something here such as call api requests
  513. * tintColor={colors.headerIconColor}
  514. * style={{
  515. * height: 28
  516. * }}
  517. * />
  518. * )
  519. *
  520. * }
  521. *
  522. *
  523. * //In custom_code/index.js
  524. *
  525. * ...
  526. *
  527. * import { ProfileHeaderButton } from "./components/ProfileHeaderButton";
  528. *
  529. * export const applyCustomCode = externalCodeSetup => {
  530. * externalCodeSetup.profileScreenHooksApi.setHeaderRightComponent(() => <ProfileHeaderButton />)
  531. * };
  532. */
  533. setHeaderRightComponent = HeaderRightComponent => {
  534. this.HeaderRightComponent = HeaderRightComponent;
  535. };
  536. ActionButton = null;
  537. /**
  538. * You can use this hook to replace the action button in the profile header component.
  539. * @method
  540. * @param {React.ComponentType<ActionButtonProps>} ActionButton
  541. * @example
  542. * ...
  543. *
  544. * export const applyCustomCode = externalCodeSetup => {
  545. * const RoundIconButton = ({
  546. * global,
  547. * button,
  548. * color,
  549. * style,
  550. * t,
  551. * withLabel,
  552. * labelStyle,
  553. * loading,
  554. * first,
  555. * last,
  556. * highlight
  557. * }) => (
  558. * <>
  559. * <View
  560. * style={[
  561. * {justifyContent: "center", alignItems: "center"},
  562. * highlight
  563. * ? global.wrappedIconButtonHighlight
  564. * : styles.roundActionButton,
  565. * !first && !withLabel && {marginLeft: 10},
  566. * !last && !withLabel && {marginRight: 10},
  567. * style
  568. * ]}
  569. * activeOpacity={1}
  570. * >
  571. * {!loading &&
  572. * button.icon && (
  573. * <Icon
  574. * tintColor={highlight ? "#fff" : color || "#000"}
  575. * icon={button.icon}
  576. * styles={{height: 26, width: 26}}
  577. * />
  578. * )}
  579. * {loading && (
  580. * <ActivityIndicator
  581. * animating={true}
  582. * style={styles.indicator}
  583. * color={color}
  584. * size="small"
  585. * />
  586. * )}
  587. * </View>
  588. * {withLabel && (
  589. * <View style={{height: 40, paddingTop: 10}}>
  590. * <Text
  591. * ellipsizeMode="tail"
  592. * numberOfLines={2}
  593. * style={
  594. * [global.actionIconText, {textAlign: "center"}, labelStyle]
  595. * }
  596. * >
  597. * {t(button.label)}
  598. * </Text>
  599. * </View>
  600. * )}
  601. * </>
  602. * );
  603. *
  604. * const styles = StyleSheet.create({
  605. * indicator: {opacity: 0.45},
  606. * roundActionButton: {
  607. * backgroundColor: "#EDEEF2",
  608. * borderRadius: 24,
  609. * width: 48,
  610. * height: 48,
  611. * justifyContent: "center",
  612. * alignItems: "center",
  613. * marginHorizontal: 5
  614. * }
  615. * });
  616. *
  617. * externalCodeSetup.profileScreenHooksApi.setActionButton((props) => <RoundIconButton {...props} />)
  618. * }
  619. */
  620. setActionButton = ActionButton => {
  621. this.ActionButton = ActionButton;
  622. };
  623. editProfileFieldSetsFilter = list => list;
  624. /**
  625. * Sets the filters available on the Edit Profile screen as the filter tabs.
  626. * 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.
  627. * @method
  628. * @param {TransformEditProfileFieldSetsFilterCallback} editProfileFieldSetsFilter
  629. * @example <caption> Change name of filter with id 1 </caption>
  630. *
  631. * export const applyCustomCode = externalCodeSetup => {
  632. *
  633. * externalCodeSetup.profileScreenHooksApi.setEditProfileFieldSetsFilter(props => {
  634. * if (Array.isArray(props.filters)){
  635. *
  636. * const modifiedFilters = props.filters.map(obj => {
  637. *
  638. * if (obj.id === 1){
  639. * return {
  640. * ...obj,
  641. * name: "New name"
  642. * }
  643. * }
  644. *
  645. * return obj;
  646. * })
  647. *
  648. * return {
  649. * ...props,
  650. * filters: modifiedFilters
  651. * };
  652. *
  653. * }
  654. *
  655. * return props;
  656. *
  657. * });
  658. * }
  659. */
  660. setEditProfileFieldSetsFilter = editProfileFieldSetsFilter => {
  661. this.editProfileFieldSetsFilter = editProfileFieldSetsFilter;
  662. };
  663. handleInputFieldCallback = null;
  664. /**
  665. * You can use this hook to add a callback to a function when an input field on the profile Edit screen is changed.
  666. * For example, you can add a function to display a prompt whenever the word 'Bar' is typed in a specific profile field.
  667. * @method
  668. * @param {OnChangeEventListenerCallback} onHandledInput
  669. * @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>
  670. *
  671. * export const applyCustomCode = externalCodeSetup => {
  672. *
  673. * externalCodeSetup.profileScreenHooksApi.addHandleInputFieldCallback(props => {
  674. *
  675. * const { id, newValue } = props;
  676. *
  677. * if (id === 24) {
  678. *
  679. * if (newValue.includes("Food")) {
  680. *
  681. * //If "Food" is selected, display the first field set only
  682. * externalCodeSetup.profileScreenHooksApi.setEditProfileFieldSets(fieldSets => {
  683. * return [...fieldSets].splice(0,1);
  684. * });
  685. *
  686. * } else {
  687. *
  688. * //Reset field sets
  689. * externalCodeSetup.profileScreenHooksApi.setEditProfileFieldSets(fieldSets => fieldSets);
  690. * }
  691. *
  692. * }
  693. * })
  694. * }
  695. */
  696. addHandleInputFieldCallback = onHandledInput => {
  697. this.handleInputFieldCallback = onHandledInput;
  698. };
  699. editProfileFieldSets = fieldSets => fieldSets;
  700. /**
  701. * Sets the field sets and the filters available on the Edit Profile screen.
  702. * @method
  703. * @param {TransformEditProfileFieldSetsCallback} editProfileFieldSets
  704. * @example <caption>Delete a field from a field set</caption>
  705. *
  706. * export const applyCustomCode = externalCodeSetup => {
  707. *
  708. * externalCodeSetup.profileScreenHooksApi.setEditProfileFieldSets(fieldSets => {
  709. *
  710. * const modifiedFieldSets = fieldSets.map(obj => {
  711. *
  712. * if (obj.id === 1){
  713. *
  714. * //Delete a field from the field set
  715. * const fields = obj.fields.slice(1);
  716. *
  717. * return {
  718. * ...obj,
  719. * fields: fields
  720. * }
  721. * }
  722. * return obj;
  723. * });
  724. *
  725. * return modifiedFieldSets;
  726. * });
  727. * }
  728. */
  729. setEditProfileFieldSets = editProfileFieldSets => {
  730. this.editProfileFieldSets = editProfileFieldSets;
  731. };
  732. viewProfileFieldSets = fieldSets => fieldSets;
  733. /**
  734. * Used to set and modify the field sets in the View Profile screen.
  735. * You can show/hide the field sets in the View Profile screen.
  736. * @method
  737. * @param {TransformViewProfileFieldSetsCallback} viewProfileFieldSets
  738. * @example
  739. *
  740. * export const applyCustomCode = externalCodeSetup => {
  741. *
  742. * externalCodeSetup.profileScreenHooksApi.setViewProfileFieldSets(fieldSets => {
  743. *
  744. * if (Array.isArray(fieldSets)){
  745. * const modifiedFieldSets = [...fieldSets].slice(1);
  746. *
  747. * return modifiedFieldSets;
  748. * }
  749. *
  750. * return fieldSets;
  751. *
  752. * })
  753. * }
  754. */
  755. setViewProfileFieldSets = viewProfileFieldSets => {
  756. this.viewProfileFieldSets = viewProfileFieldSets;
  757. };
  758. editProfileComponentDidUpdateCallback = null;
  759. /**
  760. * You can use this hook to set a callback function in the Edit Profile screen's componentDidUpdate function.
  761. * For example, you can use this to make a network request (or call an API) if a state's value is changed.
  762. * @method
  763. * @param {EditProfileComponentDidUpdateCallback} onComponentDidUpdate
  764. * @example <caption>Change edit profile field values based on WebView's message</caption>
  765. *
  766. * //In custom_code/MyCustomScreen.js...
  767. *
  768. * import React, { useState } from 'react';
  769. * import { View, Text, StyleSheet, Button, Modal, TouchableOpacity } from "react-native";
  770. * import isEqual from 'lodash.isequal';
  771. * import { WebView } from 'react-native-webview';
  772. * import EditScreen from "@src/containers/Custom/Profile/Xprofile/Edit";
  773. * import { backButton } from "@src/utils";
  774. * import { getExternalCodeSetup } from '../../src/externalCode/externalRepo';
  775. *
  776. * const MyHTML = require('./../html/sample.html');
  777. *
  778. * const MyModal = ({ modalVisible, setModalVisible, updateStoreValues }) => {
  779. *
  780. * //You can change this according to the message sent by the webview's source
  781. * const matchedMessage = "Clicked!";
  782. *
  783. * return <View style={styles.centeredView}>
  784. * <Modal
  785. * animationType="slide"
  786. * transparent={true}
  787. * visible={modalVisible}
  788. * onRequestClose={() => {
  789. * setModalVisible(!modalVisible);
  790. * }}
  791. * style={{
  792. * margin: 15
  793. * }}
  794. * >
  795. * <WebView
  796. * source={MyHTML}
  797. * onMessage={event => {
  798. *
  799. * const message = event.nativeEvent.data;
  800. *
  801. * if (message == matchedMessage) {
  802. * updateStoreValues()
  803. * }
  804. * }}
  805. *
  806. * />
  807. * <TouchableOpacity
  808. * style={[styles.button, styles.buttonClose]}
  809. * onPress={() => setModalVisible(!modalVisible)}
  810. * >
  811. * <Text style={styles.textStyle}>Hide Modal</Text>
  812. * </TouchableOpacity>
  813. * </Modal>
  814. * </View>
  815. * }
  816. *
  817. * const MyCustomScreen = (props) => {
  818. *
  819. * const matchedValue = "gmap";
  820. *
  821. * const [modalVisible, setModalVisible] = useState(false);
  822. * const [callbackUpdateStoreValues, setCallbackUpdateStoreValues] = useState(null);
  823. *
  824. *
  825. * //Set condition when to open modal. If the field matches the value of matchedValue, then it will open the modal.
  826. * getExternalCodeSetup().profileScreenHooksApi.addHandleInputFieldCallback(props => {
  827. * const { id, newValue, groupKey } = props;
  828. * if (groupKey === 1 && id === 3) {
  829. * if (newValue === matchedValue) {
  830. * setModalVisible(true)
  831. * }
  832. * }
  833. * })
  834. *
  835. * getExternalCodeSetup().profileScreenHooksApi.setEditProfileComponentDidUpdateCallback(paramProps => {
  836. *
  837. * const {
  838. * prevProps,
  839. * prevState,
  840. * props,
  841. * state,
  842. * updateStoreValues } = paramProps;
  843. *
  844. * //Update callbackUpdateStoreValues only when there are updates in the EditProfile component
  845. * if (!isEqual(state.updates, prevState.updates) && Object.keys(prevState.updates).length !== 0) {
  846. *
  847. * const updateValues = () => {
  848. *
  849. * //You can get the appropriate groupKey and id by doing a console.log for state and/or prevState.
  850. * //In this case, we're updating the store values of groupKey = 1 and id = 1 with a the webViewInput value.
  851. * const groupKey = 1,
  852. * id = 1,
  853. * property = "value",
  854. * webViewInput = "Updated from web view";
  855. *
  856. * updateStoreValues(groupKey, id, property, webViewInput);
  857. *
  858. * }
  859. *
  860. * setCallbackUpdateStoreValues(() => () => updateValues());
  861. * }
  862. *
  863. * });
  864. *
  865. * return <>
  866. * <EditScreen />
  867. * <MyModal
  868. * modalVisible={modalVisible}
  869. * setModalVisible={setModalVisible}
  870. * updateStoreValues={callbackUpdateStoreValues}
  871. * />
  872. * </>
  873. * }
  874. *
  875. * MyCustomScreen.navigationOptions = ({ navigation, screenProps }) => {
  876. * const { t, colors, calcFontSize, global } = screenProps;
  877. * const { borderColor } = colors;
  878. * const { params = {} } = navigation.state;
  879. *
  880. * const hideBackButton = true;
  881. *
  882. * let headerLeft = params.renderHeaderLeft
  883. * ? params.renderHeaderLeft()
  884. * : backButton({
  885. * navigation,
  886. * headerColor: colors.headerIconColor,
  887. * text: t("common:back"),
  888. * textStyle: global.headerText,
  889. * colors
  890. * });
  891. *
  892. * if (hideBackButton) {
  893. * headerLeft = null;
  894. * }
  895. *
  896. * return {
  897. * headerTitle: (
  898. * <Text
  899. * ellipsizeMode="tail"
  900. * numberOfLines={1}
  901. * style={global.appHeaderTitle}
  902. * >
  903. * {t("profile:editXprofile")}
  904. * </Text>
  905. * ),
  906. * tabBarVisible: false,
  907. * headerLeft: headerLeft,
  908. * headerRight: params.renderHeaderRight ? params.renderHeaderRight() : null,
  909. * headerStyle: {
  910. * ...StyleSheet.flatten(global.header),
  911. * borderBottomColor: borderColor,
  912. * borderBottomWidth: StyleSheet.hairlineWidth
  913. * }
  914. * };
  915. * };
  916. *
  917. * export default MyCustomScreen;
  918. *
  919. * const styles = StyleSheet.create({
  920. * centeredView: {
  921. * justifyContent: "center",
  922. * alignItems: "center",
  923. * },
  924. * button: {
  925. * position: "absolute",
  926. * bottom: 80,
  927. * width: "50%",
  928. * borderRadius: 20,
  929. * padding: 10,
  930. * elevation: 2,
  931. * alignSelf: "center"
  932. * },
  933. * buttonClose: {
  934. * backgroundColor: "#2196F3",
  935. * },
  936. * textStyle: {
  937. * color: "white",
  938. * fontWeight: "bold",
  939. * textAlign: "center"
  940. * }
  941. * });
  942. *
  943. * //In custom_code/html/sample.html...
  944. * <!DOCTYPE html>
  945. * <html>
  946. * <meta name="viewport" content="width=device-width, initial-scale=1">
  947. *
  948. * <head>
  949. * <style>
  950. * body {
  951. * background-color: #18222d;
  952. * height: 100vh;
  953. * display: flex;
  954. * justify-content: center;
  955. * align-items: center;
  956. * }
  957. * </style>
  958. * </head>
  959. * <body>
  960. * <div>
  961. * <button type="button" onclick="clicked()">Click Me!</button>
  962. * </div>
  963. * <script>
  964. * const OS = "ios";
  965. * const sendMessageToRN = (message) => {
  966. * if (OS === "ios") {
  967. * if (window.ReactNativeWebView.postMessage.length !== 1) {
  968. * setTimeout(sendMessageToRN(message), 200);
  969. * } else {
  970. * window.ReactNativeWebView.postMessage(message);
  971. * }
  972. * } else {
  973. * window.ReactNativeWebView.postMessage(message);
  974. * setTimeout(() => window.ReactNativeWebView.postMessage(message), 50);
  975. * }
  976. * }
  977. * const clicked = () => {
  978. * sendMessageToRN("Clicked!")
  979. * }
  980. * </script>
  981. * </body>
  982. *
  983. * </html>
  984. *
  985. * //In custom_code/index.js...
  986. *
  987. * ...
  988. *
  989. * import MyCustomScreen from "./components/MyCustomScreen";
  990. * export const applyCustomCode = externalCodeSetup => {
  991. * externalCodeSetup.navigationApi.replaceScreenComponent("EditXprofile", MyCustomScreen);
  992. * }
  993. *
  994. *
  995. */
  996. setEditProfileComponentDidUpdateCallback = onComponentDidUpdate => {
  997. this.editProfileComponentDidUpdateCallback = onComponentDidUpdate;
  998. };
  999. }