Source

externalCode/membersList.js

  1. import * as React from "react";
  2. /**
  3. * @typedef {Object} Member
  4. */
  5. /**
  6. * @typedef {Object} FeatureValues
  7. */
  8. /**
  9. * @typedef {Function} TransformMemberViewModelCallback
  10. * @param {MemberViewModel} viewModel
  11. * @param {Object} item Member data from API
  12. * @param {ToUserBasedOnSettings} toUserBasedOnSettings
  13. * @param {Boolean} isOwnAccount Returns `true` if item is logged in user's own account
  14. * @param {Boolean} isProfile
  15. * @return {Object}
  16. */
  17. /**
  18. * @typedef {Function} TransformMemberActionsCallback
  19. * @param {Array<ButtonConfig>}
  20. * @return {Array<ButtonConfig>}
  21. */
  22. /**
  23. * @typedef {Object} ButtonConfig
  24. * @property {?String} permissionField
  25. * @property {String} statusField
  26. * @property {Array<ButtonFlow>} flow
  27. */
  28. /**
  29. * @typedef {Function} CheckProperty
  30. * @param {Object} object View model
  31. * @return {Boolean}
  32. */
  33. /**
  34. * Redux action to dispatch or simple function to execute
  35. * @typedef {Function} DoFunction
  36. * @param {Object} item - View model
  37. * @param {?String} value - Value from prompt popup input
  38. * @return {Function}
  39. * @example
  40. * doFunction: (item, value) => () => { navigateToReport(item.id) } // `navigateToReport` is function that we dont want to dispatch on redux
  41. * doFunction: (item, value) =>
  42. documentUpdateRequest(item, {
  43. title: value,
  44. group_id: item.groupId,
  45. folder_id: item.folderId
  46. }) // `documentUpdateRequest` is redux action to dispatch
  47. */
  48. /**
  49. * Get error from redux state
  50. * @typedef {Function} GetError
  51. * @param {Object} store - Whole redux store
  52. * @return {any}
  53. * @example
  54. * state => state.forums.unsubscribe.error
  55. */
  56. /**
  57. * @typedef {Object} ActionButton
  58. * @property {?Number} icon - Image loaded via "require"
  59. * @property {String} label - Translatable string
  60. * @property {DoFunction} doFunction - Redux action to dispatch or simple function to execute
  61. * @property {?GetError} getError - Redux action to dispatch or simple function to execute
  62. * @property {?Boolean} isNavigation - If true button will not show spinner when changing state
  63. * @property {?String} promptMessage - If set prompt popup with input will show on action executed
  64. * @property {Function} promptDefaultValue
  65. */
  66. /**
  67. * @typedef {Object} ButtonFlow
  68. * @property {CheckProperty} check Checks for a view model property and checks if it should show the button
  69. * @property {Array} buttons Buttons to show if check passes
  70. */
  71. /**
  72. * @typedef {Function} ToUserBasedOnSettings
  73. * @param {?String} selectedTabId - Profile subscreen idenitifier
  74. * @param {User} User - User to navigate to
  75. * @param {Object} params - Params to send through route
  76. */
  77. /**
  78. * @typedef {Object} MemberItemComponentProps
  79. * @property { ?Number } groupId Group Id
  80. * @property { ?Boolean } groupMember Returns `true` if user is a member of the group
  81. * @property { Object } colors App colors
  82. * @property { Object } global App global style
  83. * @property { MemberViewModel } item
  84. * @property { Number } index Index of the item in the list
  85. * @property { TranslationFunction } t
  86. * @property { Array } actions Member actions
  87. * @property { Object } settings App settings
  88. * @property { Boolean } lastItem Returns `true` if it is the last element in the list
  89. * @property { String } locale
  90. * @property { Object } rawData Member data from API
  91. */
  92. /**
  93. * @typedef {Function} TransformMembersParams
  94. * @param {FetchMembersParams}
  95. * @return {Object}
  96. */
  97. /**
  98. * @typedef {Object} FetchMembersParams
  99. * @see {@link https://www.buddyboss.com/resources/api/#api-Members-GetBBMembers}
  100. * @property {Number} per_page Maximum number of items to be returned in result set.
  101. * @property {Number} page Current page of the collection.
  102. * @property {String} scope Limit result set to items with a specific scope.
  103. * @property {String} search Limit results to those matching a string.
  104. * @property {String} type Shorthand for certain orderby/order combinations.
  105. */
  106. /**
  107. * @typedef {Function} TransformMembersSubFiltersFilterCallback
  108. * @param {Object}
  109. * @return {Object}
  110. */
  111. /**
  112. * @class
  113. * Members Screen Hooks.
  114. * Instance name: membersListHooksApi
  115. You can customize how the members list screen such as replacing item components, modifying call to action buttons and more.
  116. * @example
  117. * externalCodeSetup.membersListHooksApi.METHOD_NAME
  118. */
  119. export class MembersListHooksApi {
  120. // filters and changes screen navigationOptions
  121. memberViewModelFilter = (
  122. viewModel,
  123. item,
  124. toUserBasedOnSettings,
  125. currentUserId,
  126. isProfile
  127. ) => viewModel;
  128. /**
  129. * Sets the callback function that can change an existing member view model object.
  130. * @method
  131. * @param {TransformMemberViewModelCallback} memberViewModelFilter
  132. * @example
  133. * externalCodeSetup.membersListHooksApi.setMemberViewModelFilter((props) => {
  134. * return {...props, date: new Date()}
  135. * })
  136. */
  137. setMemberViewModelFilter = memberViewModelFilter => {
  138. this.memberViewModelFilter = memberViewModelFilter;
  139. };
  140. // filters and changes action buttons list
  141. actionsFilter = membersActions => membersActions;
  142. /**
  143. * It sets the filter function to modify the member action buttons array such as removing an action button from the list.
  144. * @method
  145. * @param {TransformMemberActionsCallback} actionsFilter
  146. * @example <caption>The following example shows how to remove a button from the members list.</caption>
  147. * externalCodeSetup.membersListHooksApi.setActionsFilter((buttonConfig) => {
  148. * return buttonConfig.splice(0, 1);
  149. * })
  150. */
  151. setActionsFilter = actionsFilter => {
  152. this.actionsFilter = actionsFilter;
  153. };
  154. /**
  155. * @deprecated
  156. * hides the BuddyPressListFilters bellow header in MembersList
  157. */
  158. shouldHideFilterComponent = false;
  159. hideFilterComponent = () => {
  160. this.shouldHideFilterComponent = true;
  161. };
  162. MemberItemComponent = null;
  163. /**
  164. * Replaces a member item component in the members list.
  165. * For example, you can add more information about the members as shown in the example below.
  166. * @method
  167. * @param {?React.ComponentType<MemberItemComponentProps>} MemberItemComponent
  168. * @example <caption> Use default member item structure and add more information about the member </caption>
  169. *
  170. * //In custom_code/components/MemberItem.js
  171. *
  172. * import React, { useMemo } from "react";
  173. * import { View, StyleSheet, Text, ActivityIndicator } from "react-native";
  174. *
  175. * //Load BuddyBoss components and helper functions
  176. * import AppAvatar from "@src/components/AppAvatar";
  177. * import { ItemTitle } from "@src/components//TextComponents";
  178. * import Icon from "@src/components/Icon";
  179. * import AppTouchableOpacity from "@src/components/AppTouchableOpacity";
  180. * import { Settings } from "@src/reducers/config";
  181. * import AuthWrapper from "@src/components/AuthWrapper";
  182. * import { withSettings } from "@src/components/hocs/withSettings";
  183. * import ActionSheetButton from "@src/components/ActionButtons/ActionSheetButton";
  184. * import { formatDate, displayUserName } from "@src/utils";
  185. * import { GUTTER } from "@src/styles/global";
  186. *
  187. * const MemberItem = props => {
  188. * const {
  189. * item,
  190. * global,
  191. * actions,
  192. * index,
  193. * colors,
  194. * settings,
  195. * lastItem,
  196. * groupId,
  197. * locale,
  198. * t,
  199. * showLoader = false,
  200. * rawData
  201. * } = props;
  202. *
  203. * const userMeta = useMemo(() => {
  204. * if (!!groupId && item.groupJoiningDate) {
  205. * return `${t("members:joined")} ${formatDate(locale)(
  206. * item.groupJoiningDate
  207. * )}`;
  208. * } else if (settings[Settings.ENABLE_MEMBER_TYPE_DISPLAY] && item.type) {
  209. * return item.type;
  210. * } else if (item.nicename) {
  211. * return displayUserName(item.nicename);
  212. * }
  213. * });
  214. *
  215. * return (
  216. * <AppTouchableOpacity
  217. * onPress={item.onClick}
  218. * style={[styles.item, index === 0 ? { paddingTop: 0 } : {}]}
  219. * >
  220. * <View style={[global.row, styles.itemInner]}>
  221. * <AppAvatar
  222. * size={64}
  223. * name={item.fullname}
  224. * source={{
  225. * uri: item.avatarUrl
  226. * }}
  227. * />
  228. * <View
  229. * style={[global.row, !lastItem && global.bottomBorder, styles.text]}
  230. * >
  231. * <View style={{ flex: 1 }}>
  232. * <ItemTitle global={global} style={{ marginBottom: 3 }}>
  233. * {item.fullname}
  234. * </ItemTitle>
  235. * {!!userMeta && <Text style={global.itemMeta}>{userMeta}</Text>}
  236. *
  237. * //Add more data for member...
  238. * <Text>Link to user profile: {rawData?.link}</Text>
  239. * <Text>Last Activity: {rawData?.last_activity}</Text>
  240. * <Text>Friendship status: {rawData?.friendship_status}</Text>
  241. * </View>
  242. * {showLoader ? (
  243. * <ActivityIndicator size={"small"} color={colors.highlightColor} />
  244. * ) : (
  245. * <ActionSheetButton
  246. * headerProps={{
  247. * id: item.id,
  248. * title: item.fullname,
  249. * avatarSource: { uri: item.avatarUrl },
  250. * onClick: item.onClick
  251. * }}
  252. * object={item}
  253. * actionButtons={actions}
  254. * global={global}
  255. * colors={colors}
  256. * t={t}
  257. * renderButton={() => (
  258. * <AuthWrapper actionOnGuestLogin={"hide"}>
  259. * <Icon
  260. * icon={{fontIconName: "ellipsis-h", weight: 300}}
  261. * tintColor={colors.textIconColor}
  262. * styles={{
  263. * margin: 5,
  264. * height: 16
  265. * }}
  266. * />
  267. * </AuthWrapper>
  268. * )}
  269. * />
  270. * )}
  271. * </View>
  272. * </View>
  273. * </AppTouchableOpacity>
  274. * );
  275. * };
  276. *
  277. * const styles = StyleSheet.create({
  278. * item: {
  279. * flex: 1,
  280. * paddingHorizontal: GUTTER
  281. * },
  282. * itemInner: {
  283. * flex: 1,
  284. * justifyContent: "space-between"
  285. * },
  286. * text: {
  287. * paddingVertical: 24,
  288. * marginLeft: 14,
  289. * justifyContent: "space-between",
  290. * flex: 1
  291. * },
  292. * buttonsWrap: {}
  293. * });
  294. *
  295. * export default withSettings(MemberItem);
  296. *
  297. * //In custom_code/index.js
  298. *
  299. * ...
  300. *
  301. * import MemberItem from "./components/MemberItem";
  302. * export const applyCustomCode = externalCodeSetup => {
  303. * externalCodeSetup.membersListHooksApi.setMemberItemComponent(props => {
  304. * return <MemberItem {...props} />
  305. * })
  306. * }
  307. */
  308. setMemberItemComponent = MemberItemComponent => {
  309. this.MemberItemComponent = MemberItemComponent;
  310. };
  311. fetchParamsFilter = params => params;
  312. /**
  313. * It overrides the parameters that are used to fetch members in the Members screen so that you can make it as customizable as possible when calling its API.
  314. * @method
  315. * @param {TransformMembersParams} fetchParamsFilter
  316. *
  317. * @example <caption> Create a custom filter in members screen </caption>
  318. *
  319. * //In components/MembersBeforeList.js...
  320. *
  321. * import React, { useState } from "react";
  322. * import { TextInput, View, Button } from 'react-native'
  323. * import { useDispatch } from "react-redux";
  324. * import { membersRequested } from "@src/actions/members";
  325. * import { getExternalCodeSetup } from "@src/externalCode/externalRepo";
  326. * import withGlobalStyles from "@src/components/hocs/withGlobalStyles";
  327. *
  328. * const hook = getExternalCodeSetup().membersListHooksApi;
  329. *
  330. * const screenName = "book";
  331. *
  332. * const filter = "all"; //"all", "friends", "following", "followers", "requests"
  333. * const subfilters = {
  334. * type: "active" // "active", "newest", "alphabetical", "random", "online", "popular"
  335. * };
  336. *
  337. * const refresh = true; //Set to true to refresh list
  338. * const searchTerm = ""
  339. *
  340. * const MembersBeforeList = (props) => {
  341. *
  342. * const { navigation, route, colors } = props;
  343. *
  344. * const dispatch = useDispatch();
  345. *
  346. * //If showing the matched screen, show custom filter before displaying list component
  347. * if (route?.params?.item?.object === screenName) {
  348. *
  349. * const [experience, setExperience] = useState('');
  350. * const [coursesCompleted, setCoursesCompleted] = useState('')
  351. *
  352. * const handleSubmit = () => {
  353. *
  354. * //Set custom parameters before fetching documents
  355. * hook.setFetchParamsFilter((props) => {
  356. *
  357. * //You can add more parameters such as "subject", "keyword" etc...
  358. * return {
  359. * ...props,
  360. * experience,
  361. * coursesCompleted
  362. * }
  363. * })
  364. *
  365. * //Dispatch redux action to call api using customized filters
  366. * dispatch(membersRequested(filter, subfilters, refresh, searchTerm));
  367. *
  368. * }
  369. *
  370. * return <View style={{ backgroundColor: colors.whiteColor}}>
  371. *
  372. * <TextInput
  373. * style={{paddingHorizontal: 20, marginTop: 10, fontSize: 20}}
  374. * autoFocus
  375. * keyboardType="number-pad"
  376. * value={experience}
  377. * onChangeText={experience => setExperience(experience)}
  378. * placeholder="Experience (Cumulative Years)"
  379. * />
  380. *
  381. * <TextInput
  382. * style={{paddingHorizontal: 20, marginTop: 10, fontSize: 20}}
  383. * value={coursesCompleted}
  384. * onChangeText={coursesCompleted => setCoursesCompleted(coursesCompleted)}
  385. * placeholder="Courses completed (Enter keyword...)"
  386. * />
  387. *
  388. * <Button
  389. * onPress={() => handleSubmit()}
  390. * title="Filter"
  391. * />
  392. * </View>
  393. * }
  394. *
  395. * return null;
  396. *
  397. * }
  398. *
  399. * export default withGlobalStyles(MembersBeforeList);
  400. *
  401. * //In components/MyCustomScreen.js...
  402. * import React from 'react';
  403. * import MembersScreen from "@src/containers/Custom/MembersScreen";
  404. *
  405. * const MyCustomScreen = props => (<MembersScreen {...props} showSearch={false} hideFilters={true} headerHeight={250} />)
  406. *
  407. *
  408. * export default MyCustomScreen;
  409. *
  410. * //In custom_code/index.js...
  411. *
  412. * ...
  413. *
  414. * import MembersBeforeList from "./components/MembersBeforeList";
  415. * export const applyCustomCode = externalCodeSetup => {
  416. *
  417. * externalCodeSetup.filterScreenApiHooks.setAfterFilterComponent(MembersBeforeList);
  418. *
  419. * externalCodeSetup.navigationApi.addNavigationRoute(
  420. * "book",
  421. * "BookScreen",
  422. * MyCustomScreen,
  423. * "All"
  424. * );
  425. * externalCodeSetup.navigationApi.addNavigationRoute(
  426. * "book",
  427. * "BookScreen",
  428. * MyCustomScreen,
  429. * "Main"
  430. * );
  431. * }
  432. */
  433. setFetchParamsFilter = fetchParamsFilter => {
  434. this.fetchParamsFilter = fetchParamsFilter;
  435. };
  436. subFiltersFilter = filters => filters;
  437. /**
  438. * You can use this to set the sub filter function to rearrange the order of the filters in the Members screen.
  439. * For example, you can rearrange the labels to set "Alphabetical" as the default filter on the members list screen under the search bar.
  440. * @method
  441. * @param {TransformMembersSubFiltersFilterCallback} subFiltersFilter
  442. * @example <caption>User would like to set "Alphabetical" as the first filter</caption>
  443. * externalCodeSetup.membersListHooksApi.setSubFiltersFilter(filter => {
  444. * return {
  445. * type: [
  446. * {
  447. * value: "alphabetical",
  448. * label: "Alphabetical"
  449. * },
  450. * {
  451. * value: "active",
  452. * label: "Recently Active"
  453. * },
  454. * {
  455. * value: "newest",
  456. * label: "Newest Members"
  457. * }
  458. * ],
  459. * member_type: filter.member_type
  460. * };
  461. * });
  462. */
  463. setSubFiltersFilter = subFiltersFilter => {
  464. this.subFiltersFilter = subFiltersFilter;
  465. };
  466. }