import * as React from "react";
/**
* @typedef {Object} MemberViewModel
* @property {Number} id Id of member
* @property {Boolean} someoneelse Returns true if member is not the user logged in
* @property {String} fullname Full name of user
* @property {String} avatarUrl Url to user's avatar
* @property {String} avatarFull Url to user's full avatar image
* @property {String} friendStatus Returns friend status
* @property {String} nicename Returns a nice name format of user's name
* @property {?Number} friendshipId Returns the friendship id between logged in user and user loaded in the model
* @property {String} lastActivity Returns last activity of user
* @property {Function} onClick Can be used to redirect to user's profile
* @property {String} coverSrc Url to user's cover image
* @property {Number} points Points associated to user
* @property {Array} userPoints User points information
* @property {String} type User type
* @property {String} registeredDate Date when the user registered
* @property {String} groupJoiningDate Date when the user joined the group
* @property {Number} followers Number of followers
* @property {Number} following Number of persons that the user is following
* @property {Boolean} can_report Returns `true` if user can be reported
* @property {Boolean} reported Returns `true` if user has already been reported
* @property {MemberViewModel} inviter Inviter member details
*/
/**
* @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"} ActivitySubFilter
*/
/**
* @typedef {Function} TransformGroupActivityCallback
* @param {Array<ActivitySubFilter>} list
* @return {Array<string>}
*/
/**
* @ignore
* @typedef {Function} TransformGroupsParamsCallback
* @param {Object} filterProps - params
* @return {Object}
*/
/**
* @typedef {Object} GroupViewModel
* @property {Number} parentId
* @property {Number} id Group id
* @property {String} avatar Group avatar
* @property {Boolean} coverImage Returns `true` if group has cover image
* @property {Boolean} isMember Returns `true` if logged in user is a member
* @property {Boolean} isAdmin Returns `true` if logged in user is an admin
* @property {Boolean} isAuthor Returns `true` if logged in user is the creator of the group
* @property {Boolean} isMod Returns `true` if logged in user is assigned as a moderator
* @property {Boolean} isSoloOrganizer Returns `true` if logged in user is assigned as an organizer
* @property {Boolean} notAdmin Returns `true` if logged in user is not an admin
* @property {Boolean|Number} inviteId Returns the invite id if logged in user has been invited to join. Otherwise, will return `false`
* @property {Boolean} can_report Returns `true` if group can be reported
* @property {Boolean} reported Returns `true` if group has already been reported
* @property {Boolean} requestId Returns request id if logged in user is trying to join the group. Otherwise, will return `false`
* @property {String} title Group title
* @property {String} content Group description
* @property {String} contentRendered Group description rendered
* @property {String} shortContent Group short description
* @property {Boolean} hasForum Returns `true` if group has associated forum
* @property {Number} membersCount Number of members in the group
* @property {String} status Group visibility. Ex: `public`, `hidden`
* @property {Array} admins
* @property {String} role Role of logged in user
* @property {String} roleLabel Role label of logged in user
* @property {String} type Group type
* @property {Array} subgroupIds
* @property {Function} navigateToWeb Function to navigate to group link in web
* @property {Function} getGroup Function to get all fields given by api. Ex: `const group = getGroup(g => g)`.
* @property {Boolean} canPost Returns `true` if logged in user can post
* @property {Boolean} canJoin Returns `true` if logged in user can join
* @property {Boolean} canCreateMedia Returns `true` if logged in user can create media
* @property {Boolean} canCreateDocument Returns `true` if logged in user can create document
* @property {Function} onClick Function to navigate to group item
*/
/**
* @typedef {Object} GroupItemComponentProps
* @property {Object} global App global style
* @property {Object} colors App colors
* @property {GroupViewModel} item
* @property {Number} index Item index in a list
* @property {TranslationFunction} t
* @property {Array} actions Group actions such as functions to join or leave a group
* @property {Number} currentUserId Logged in user ID
* @return {?React.ComponentType}
*/
/**
* @typedef {Object} GroupPendingMemberItemComponentProps
* @property { Object } colors App colors
* @property { Object } global App global style
* @property { MemberViewModel } item
* @property { Number } index Index of the item in the list
* @property { TranslationFunction } t
* @property { Object } groupInvite Group inviter details
*/
/**
* @typedef {Function} TransformGroupActionsCallback
* @param {Array<ButtonConfig>}
* @return {Array<ButtonConfig>}
*/
/**
* @typedef {Object} ButtonConfig
* @property {?string} permissionField
* @property {string} statusField
* @property {Array<ButtonFlow>} flow
*/
/**
* @typedef {Object} ButtonFlow
* @property {CheckProperty} check - Checks for a view model property, and based on the check it decides if it should show button
* @property {Array<Button>} buttons - Buttons to show if check passes
*/
/**
* @typedef {Function} CheckProperty
* @param {Object} object - View model
* @return {boolean}
*/
/**
* @typedef {Object} TransformGroupSubFiltersFilterCallback
* @property {Object} type Filter types such as `active`, `popular`, `newest`, `alphabetical`
* @property {Object} group_type Group types
*/
/**
* @typedef {Function} TransformGroupsParams
* @param {FetchGroupsParams}
* @return {Object}
*/
/**
* @typedef {Object} FetchGroupsParams
* @see {@link https://www.buddyboss.com/resources/api/#api-Groups-GetBBGroups}
* @property {Number} per_page Maximum number of items to be returned in result set.
* @property {Number} page Current page of the collection.
* @property {String} search Limit results to those matching a string.
* @property {String} order Order sort attribute ascending or descending. Default value: `desc`. Allowed values: `asc`, `desc`
* @property {String} orderby Order Groups by which attribute. Default value: `date_created`. Allowed values: `date_created`, `last_activity`, `total_member_count`, `name`, `random`
* @property {String} type Shorthand for certain orderby/order combinations. Default value: `active`. Allowed values: `active`, `newest`, `alphabetical`, `random`, `popular`
* @property {String} scope Limit result set to items with a specific scope. Default value: `all`. Allowed values: `all`, `personal`
*
*/
/**
* @class
* Groups Index Hooks.
* Instance name: groupsListHooksApi
You can customize Group lists such as replacing list components, adding sub filters to the groups list and more with this hook.
* @example
* externalCodeSetup.groupsListHooksApi.METHOD_NAME
*/
export class GroupsListHooksApi {
groupActivitySubFilter = list => list;
/**
* @ignore Not being used in app
* Sets the filter function capable of modifying array for groups Activity
* @method
* @param {TransformGroupActivityCallback} groupActivitySubFilter
*/
setGroupActivitySubFilter = groupActivitySubFilter => {
this.groupActivitySubFilter = groupActivitySubFilter;
};
filterProps = filterProps => filterProps;
/**
* @ignore Not being used in app
* Sets the filter function capable of modifying fetch params that are used in the request
* @method
* @param {TransformGroupsParamsCallback} filterProps
*/
setFilterProps = filterProps => {
this.filterProps = filterProps;
};
subFilterProps = subFilterProps => subFilterProps;
/**
*
* Sets the available sub filter function.
* As shown in the example below, it's one of the ways you can modify the existing subfilter and customize it according to your preferences.
* @method
* @param {TransformGroupSubFiltersFilterCallback} subFiltersFilter
* @example <caption>You can add a "Recently Active" subfilter; remove the "All Group Types" subfilter; and change the labels of the group types sub filters.</caption>
* externalCodeSetup.groupsListHooksApi.setSubFiltersFilter((filters) => {
* return {
* type: [{ value: "active", label: "Recently Active Groups" }],
* group_type: [
* { value: "beagle", label: "Beagle Dogs" },
* { value: "japanese-spitz", label: "Japanese Spitz Dogs" }
* ]
* }
*
* });
*/
setSubFiltersFilter = subFilterProps => {
this.subFilterProps = subFilterProps;
};
GroupItemComponent = null;
/**
* It used to replace the default group item component in the groups list.
* For example, you can add a short description of the group or disable avatar to display in the list.
* @method
* @param {?React.ComponentType<GroupItemComponentProps>} GroupItemComponent
* @example <caption>Add a short group description and remove avatar display.</caption>
* //In custom_code/components/GroupItem.js...
*
* import React from "react";
* import {
* View,
* StyleSheet,
* Text,
* Image,
* TouchableOpacity,
* } from "react-native";
*
* //Import BuddyBoss components and helper functions...
* import AppTouchableOpacity from "@src/components/AppTouchableOpacity";
* import { ItemTitle } from "@src/components/TextComponents";
* import Icon from "@src/components/Icon";
* import AuthWrapper from "@src/components/AuthWrapper";
* import {
* groupInviteDescription,
* groupMembersCountTranslation,
* groupStatusTranslation,
* } from "@src/utils";
* import ActionButtonList from "@src/components/ActionButtons/ActionButtonList";
* import GroupActionSheetWrapper from "@src/components/Group/GroupActionSheetWrapper";
* import { GUTTER } from "@src/styles/global";
* import FontManager from "@src/FontManager";
* import { Typographies } from "@src/services/enums/branding";
*
* const GroupItem = props => {
* const { item, global, colors, actions, userId, index, t, isInvite } = props;
*
* const styles = getStyles(colors);
*
* return (
* <AppTouchableOpacity
* onPress={item.onClick}
* style={[styles.item, index === 0 ? { paddingTop: 0 } : {}]}
* >
* <View
* style={[
* global.row,
* { justifyContent: "space-between", flex: 1, alignItems: "flex-start" }
* ]}
* >
*
* <View style={[global.bottomBorder, {
* paddingBottom: 15, paddingBottom: 14,
* marginLeft: 14,
* flex: 1
* }]}>
* //Group title
* <ItemTitle global={global} style={{ marginBottom: 4 }}>
* {item.title}
* </ItemTitle>
*
* //Show component depending on if user received invitation to join the group
* {isInvite ? (
* <View>
* {groupInviteDescription(item, t, global, { marginBottom: 9 })}
* {!!item.inviteMessage && <Text>{item.inviteMessage}</Text>}
* </View>
* ) : (
* <View style={[global.row, {
* flex: 1,
* flexWrap: "wrap"
* }]}>
* <Text style={global.itemMeta}>
* {groupStatusTranslation(t, item)}
* </Text>
* <View style={global.dotSep} />
* <Text style={global.itemMeta}>
* {groupMembersCountTranslation(t, item)}
* </Text>
* </View>
* )}
*
* <View style={{
* marginTop: 16,
* position: "relative"
* }}>
* <View style={{ alignItems: "flex-start" }}>
*
* // Add short content
* {item.shortContent != "" && <Text style={{ color: colors.labelTextColor, marginBottom: 10 }}> {item.shortContent} </Text> }
*
* //Show a component depending on if user has already joined the group
* {item.isMember && !isInvite ? (
* <GroupActionSheetWrapper
* group={item}
* actionButtons={actions}
* {...{
* global,
* colors,
* t
* }}
* >
* <View
* style={[
* global.wrappedButton,
* global.wrappedTextButton,
* global.row,
* { backgroundColor: colors.labelBgColor }
* ]}
* >
* <Icon
* icon={require("@src/assets/img/checkmark.png")}
* tintColor={colors.labelTextColor}
* styles={{
* width: 11,
* height: 11,
* marginLeft: -2,
* marginRight: 4
* }}
* rtlStyleFix={"handled"}
* />
* <Text
* style={[
* global.wrappedTextButtonLabel,
* { color: colors.labelTextColor }
* ]}
* >
* {item.role}
* </Text>
* </View>
* </GroupActionSheetWrapper>
* ) : (
* <AuthWrapper>
* <ActionButtonList
* hideIcons={true}
* actionButtons={actions}
* object={item}
* t={t}
* color={colors.labelTextColor}
* buttonStyle={({ label }) => ({
* ...(label.match(/acceptInvite/)
* ? styles.inviteAcceptButton
* : label.match(/cancelInvite/)
* ? styles.inviteRejectButton
* : { marginRight: 10 }),
* backgroundColor: colors.labelBgColor
* })}
* textStyle={({ label }, color) =>
* label.match(/acceptInvite/)
* ? {
* ...global.boldText,
* color
* }
* : label.match(/cancelInvite/)
* ? { ...global.boldText, color: colors.warningColor }
* : { ...global.boldText, color }
* }
* />
* </AuthWrapper>
* )}
* </View>
* </View>
* </View>
* </View>
* </AppTouchableOpacity>
* );
* };
*
* const getStyles = colors =>
* StyleSheet.create({
* item: {
* flex: 1,
* marginTop: 15,
* paddingHorizontal: GUTTER
* },
* inviteAcceptButton: {
* flex: 0.65,
* marginRight: 4,
* backgroundColor: colors.highlightColor,
* height: FontManager.applyFontHeightAdjustment(36, Typographies.bodyText),
* justifyContent: "center",
* borderRadius: 8
* },
* inviteRejectButton: {
* flex: 1,
* paddingVertical: 5,
* marginLeft: 4,
* height: FontManager.applyFontHeightAdjustment(36, Typographies.bodyText),
* justifyContent: "center",
* borderRadius: 8,
* borderWidth: StyleSheet.hairlineWidth,
* borderColor: colors.borderColor
* }
* });
*
* export default GroupItem;
*
* //In custom_code/index.js...
*
* ...
*
* import GroupItem from "./components/GroupItem";
* export const applyCustomCode = externalCodeSetup => {
* externalCodeSetup.groupsListHooksApi.setGroupItemComponent(props => <GroupItem {...props} />)
* }
*/
setGroupItemComponent = GroupItemComponent => {
this.GroupItemComponent = GroupItemComponent;
};
GroupPendingMemberItemComponent = null;
/**
* Replaces group item component in the pending invites group list.
* @method
* @param {?React.ComponentType<GroupPendingMemberItemComponentProps>} GroupPendingMemberItemComponent
* @example <caption>You can add a component to redirect users in the pending invites list.</caption>
* //In custom_code/components/PendingMemberItem.js
*
* import React, {useEffect, useState} from "react";
* import {
* View,
* StyleSheet,
* Text,
* ActivityIndicator,
* TouchableOpacity
* } from "react-native";
*
* //Load BuddyBoss components...
* import AppAvatar from "@src/components/AppAvatar";
* import IconButton from "@src/components/IconButton";
* import {GUTTER, FontWeights} from "@src/styles/global";
* import {groupInviteModify} from "@src/actions/groupInvites";
* import {connect} from "react-redux"; // Will be used to connect to redux for dispatching an action
* import {usePrevious} from "@src/components/hooks";
*
* const MemberItem = props => {
* const {
* item,
* global,
* groupInvite,
* loading,
* groupInviteModify,
* index,
* colors,
* settings,
* t
* } = props;
*
* const [active, setActive] = useState(false);
* const prevloading = usePrevious(loading);
*
* useEffect(
* () => {
* if (prevloading === true && loading === false) {
* setActive(false);
* }
* },
* [loading]
* );
*
* return (
* <View
* style={[
* global.row,
* styles.itemInner,
* styles.item,
* index === 0 ? {paddingTop: 0} : {}
* ]}
* >
* <AppAvatar
* size={50}
* name={item.fullname}
* source={{
* uri: item.avatarUrl
* }}
* />
* <View style={[global.row, global.bottomBorder, styles.text]}>
* <View>
* <Text
* style={[
* global.text,
* {marginBottom: 5, fontWeight: FontWeights.medium}
* ]}
* >
* {item.fullname}
* </Text>
* </View>
*
* //Add component to redirect to user profile
* <TouchableOpacity onPress={() => item.onClick()}>
* <Text>Go to user profile</Text>
* </TouchableOpacity>
*
* {loading && active ? (
* <ActivityIndicator />
* ) : (
* <IconButton
* pressHandler={() => {
* setActive(true);
* groupInviteModify(groupInvite);
* }}
* icon={require("@src/assets/img/x.png")}
* tintColor="#C6C6C8"
* style={{
* width: 11,
* height: 11
* }}
* />
* )}
* </View>
* </View>
* );
* };
*
* const styles = StyleSheet.create({
* item: {
* paddingHorizontal: GUTTER
* },
* itemInner: {
* flex: 1,
* justifyContent: "space-between"
* },
* text: {
* paddingVertical: 20,
* marginLeft: 14,
* justifyContent: "space-between",
* flex: 1
* }
* });
*
* const mapStateToProps = (state, ownProps) => {
* const groupId = ownProps.groupId;
* return {
* loading: state.groupInvites.modify.cancelLoading
* };
* };
*
* const mapDispatchToProps = (dispatch, ownProps) => {
* return {
* //Dispatch `cancel` action to reject the invitation
* groupInviteModify: groupInvite =>
* dispatch(
* groupInviteModify("cancel", groupInvite.group_id, groupInvite.id)
* )
* };
* };
*
* export default connect(
* mapStateToProps,
* mapDispatchToProps
* )(MemberItem);
*
* //In custom_code/index.js
*
* ...
*
* import PendingMemberItem from "./components/PendingMemberItem";
* export const applyCustomCode = externalCodeSetup => {
*
* externalCodeSetup.groupsListHooksApi.setGroupPendingMemberItemComponent(props => <PendingMemberItem {...props} />)
* }
*
*/
setGroupPendingMemberItemComponent = GroupPendingMemberItemComponent => {
this.GroupPendingMemberItemComponent = GroupPendingMemberItemComponent;
};
// filters and changes action buttons list
actionsFilter = groupActions => groupActions;
/**
* Sets the filter function for modifing group action buttons array
* @param {TransformGroupActionsCallback} actionsFilter
*/
setActionsFilter = actionsFilter => {
this.actionsFilter = actionsFilter;
};
fetchParamsFilter = params => params;
/**
* It overrides the parameters that are used to fetch groups in the Group screen so that you can make it as customizable as possible when calling its API.
* @method
* @param {TransformGroupsParams} fetchParamsFilter
*
* @example <caption> Create a custom filter in groups screen </caption>
*
* //In components/GroupFiltersCustom.js...
*
* import React, { useState } from "react";
* import { TextInput, View, Button, Text, Switch } from 'react-native'
* import { useDispatch } from "react-redux";
* import { groupsRequested } from "@src/actions/socialGroups";
* import { getExternalCodeSetup } from "@src/externalCode/externalRepo";
* import withGlobalStyles from "@src/components/hocs/withGlobalStyles";
*
* const hook = getExternalCodeSetup().groupsListHooksApi;
*
* getExternalCodeSetup().indexScreenApiHooks.setHeaderHeight((defaultHeaderHeight, filterType, navigation) => {
*
* if (filterType === "groups")
* return 300;
*
* return defaultHeaderHeight;
* });
*
* const screenName = "HomeGroupsScreen";
*
* const filter = "all"; //"all", "personal", "my-groups", "invites"
* const subfilters = {type: "active"}; // "active", "newest", "alphabetical", "popular";
*
* const refresh = true; //Set to true to refresh list
* const searchTerm = ""
*
*
* const GroupFiltersCustom = (props) => {
*
* const { navigation, route, colors } = props;
*
* const dispatch = useDispatch();
*
* //If showing the matched screen, show custom filter before displaying list component
* if (navigation?.state?.routeName === screenName) {
*
* const [isEnabled, setIsEnabled] = useState(false);
*
* const toggleSwitch = () => setIsEnabled(previousState => !previousState)
*
* const handleSubmit = () => {
*
* //Set custom parameters before fetching
* hook.setFetchParamsFilter((props) => {
*
* //You can add more parameters such as "subject", "keyword" etc...
* return {
* ...props,
* show_hidden: isEnabled
* }
* })
*
* //Dispatch redux action to call api using customized filters
* dispatch(groupsRequested(filter, subfilters, refresh, searchTerm));
*
* }
*
* return <View style={{ backgroundColor: colors.whiteColor, flexDirection: "row", alignItems: "center", justifyContent: "center" }}>
*
* <Text>Show hidden groups?</Text>
* <Switch
* trackColor={{ false: "#767577", true: "#81b0ff" }}
* thumbColor={isEnabled ? "#f5dd4b" : "#f4f3f4"}
* ios_backgroundColor="#3e3e3e"
* onValueChange={toggleSwitch}
* value={isEnabled}
* />
* <Button
* onPress={() => handleSubmit()}
* title="Filter"
* />
* </View>
* }
*
* return null;
*
* }
*
* export default withGlobalStyles(GroupFiltersCustom);
*
* //In custom_code/index.js...
*
* ...
*
* import GroupFiltersCustom from "./components/GroupFiltersCustom";
* export const applyCustomCode = externalCodeSetup => {
* externalCodeSetup.filterScreenApiHooks.setAfterFilterComponent(GroupFiltersCustom);
* }
*/
setFetchParamsFilter = fetchParamsFilter => {
this.fetchParamsFilter = fetchParamsFilter;
};
}
Source