import * as React from "react";
import {TGroupViewModel, TTranslationFunction, TMemberViewModel} from "./types";
/**
* GroupItemComponentProps
*/
type GroupItemComponentProps = {
/**
* App global style
*/
global: Record<any, any>,
/**
* App colors
*/
colors: Record<any, any>,
item: TGroupViewModel,
index: number,
t: TTranslationFunction,
actions: Record<any, any>[],
currentUserId: number
};
/**
* GroupPendingMemberItemComponentProps
*/
type GroupPendingMemberItemComponentProps = {
/** App colors */
colors: Record<any, any>,
/** App global style */
global: Record<any, any>,
/** */
item: TMemberViewModel,
/** Index of the item in the list */
index: number,
/** */
t: TTranslationFunction,
/** Group inviter details */
groupInvite: Record<any, any>
};
/**
* TransformGroupActionsCallback
*/
type TransformGroupActionsCallback = {
TransformGroupActionsCallback: ButtonConfig[]
};
/**
* ButtonConfig
*/
type ButtonConfig = {
permissionField: string,
statusField: string,
flow: ButtonFlow[]
};
/**
* ButtonFlow
*/
type ButtonFlow = {
/**
* Checks for a view model property, and based on the check it decides if it should show button
*/
check: CheckProperty,
/**
* Buttons to show if check passes
*/
buttons: Record<any, any>[]
};
/**
* CheckProperty
*/
type CheckProperty = {
object: Record<any, any>
};
/**
* TransformGroupSubFiltersFilterCallback
*/
type TransformGroupSubFiltersFilterCallback = {
/**
* Filter types such as `active`, `popular`, `newest`, `alphabetical`
*/
type: Record<any, any>,
/**
* Group types
*/
group_type: Record<any, any>
};
/**
* TransformGroupsParams
*/
type TransformGroupsParams = {
group: FetchGroupsParams
};
/**
* FetchGroupsParams
* @see {@link https://www.buddyboss.com/resources/api/#api-Groups-GetBBGroups}
*/
type FetchGroupsParams = {
/** Maximum number of items to be returned in result set. */
per_page: number,
/** Current page of the collection. */
page: number,
/** Limit results to those matching a string. */
search: string,
/** Order sort attribute ascending or descending. Default value: `desc`. Allowed values: `asc`, `desc` */
order: "asc" | "desc",
/** Order Groups by which attribute. Default value: `date_created`. Allowed values: `date_created`, `last_activity`, `total_member_count`, `name`, `random` */
orderby:
| "date_created"
| "last_activity"
| "total_member_count"
| "name"
| "random",
/** Shorthand for certain orderby/order combinations. Default value: `active`. Allowed values: `active`, `newest`, `alphabetical`, `random`, `popular` */
type: "active" | "newest" | "alphabetical" | "random" | "popular",
/** Limit result set to items with a specific scope. Default value: `all`. Allowed values: `all`, `personal` */
scope: "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: Record<any, any>) => 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} subFilterProps
* @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: (
viewModel: TransformGroupSubFiltersFilterCallback | Record<any, any>
) => TransformGroupSubFiltersFilterCallback | Record<any, any>
) => {
this.subFilterProps = subFilterProps;
};
GroupItemComponent: React.ComponentType<
GroupItemComponentProps
> | null = 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={{fontIconName: "check", weight: 400}}
* tintColor={colors.labelTextColor}
* styles={{
* width: 16,
* height: 16,
* 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: React.ComponentType<GroupItemComponentProps> | null
) => {
this.GroupItemComponent = GroupItemComponent;
};
GroupPendingMemberItemComponent: React.ComponentType<
GroupPendingMemberItemComponentProps
> | null = 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={{fontIconName: "times", weight: 300}}
* tintColor="#C6C6C8"
* />
* )}
* </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: React.ComponentType<
GroupPendingMemberItemComponentProps
> | null
) => {
this.GroupPendingMemberItemComponent = GroupPendingMemberItemComponent;
};
actionsFilter = (
groupActions: TransformGroupActionsCallback | Record<any, any>
) => groupActions;
/**
* Sets the filter function for modifing group action buttons array
* @method
* @param {TransformGroupActionsCallback} actionsFilter
* @example <caption> Remove the "Leave" button </caption>
*
* externalCodeSetup.groupsListHooksApi.setActionsFilter(props => {
*
* let newButtons = props[1];
* delete newButtons.flow[1];
*
* return [
* ...props,
* newButtons
* ];
*
* })
*/
setActionsFilter = (
actionsFilter: (
viewModel: TransformGroupActionsCallback | Record<any, any>
) => TransformGroupActionsCallback | Record<any, any>
) => {
this.actionsFilter = actionsFilter;
};
fetchParamsFilter = (params: TransformGroupsParams | Record<any, any>) =>
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: (
fetchParamsFilter: TransformGroupsParams | Record<any, any>
) => TransformGroupsParams | Record<any, any>
) => {
this.fetchParamsFilter = fetchParamsFilter;
};
}
Source