Source

externalCode/blogScreen.js

import * as React from "react";

export const API_NAME = "blogApi";

/**
 * @typedef { Object } BlogSubFilterProps
 * @property { Array<Object> }  categories
 * @property { Array<Object> }  year Year values in the sub filter
 */

/**
 * @typedef {Function} TransformBlogSubFiltersFilterCallback
 * @property { BlogSubFilterProps }
 * @return {Object}
 */

/**
 * @typedef {Object} BlogItemProps
 * @property {Object} global App global style
 * @property {Object} colors App colors
 * @property {BlogViewModel} item
 * @property {Number} index
 * @property {TranslationFunction} t
 * @property {Number} currentUserId
 * @property {Boolean} allowBookmark Returns `true` if item can be bookmarked
 * @property {Function} bookmarkActions Toogles the bookmark action
 * @property {Boolean} isSavedItems Returns `true` if currently viewing saved items only
 */

/**
 * @typedef {Function} TransformBlogParams
 * @param {FetchBlogParams}
 * @return {Object}
 */

/**
 * @typedef {Object} FetchBlogParams
 * @see {@link https://www.buddyboss.com/resources/api/#api-Blogs-GetBBBlogs}
 * @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
 * @property {String} orderby
 * @property {Number} author ID of the user whose blogs user can post to.
 */

/**
 * @typedef {Object} BookmarkComponentProps
 * @property {Object} global App global style
 * @property {Object} styles Default styles
 * @property {Function} onPress Toggles bookmark function
 * @property {Object} hitSlop Default hit slop assigned to the button
 * @property {Object} animatedStyle Bookmark animation
 * @property {Boolean} fixAlignRight Returns `true` if fix for alignRight should be added
 * @property {Boolean} bookmarked Returns `true` item is bookmarked
 * @property {String} fontIconName Default icon name
 * @property {String} fontIconVariant Default icon variant
 * @property {String} tintColor Tint color of bookmark
 * @property {Boolean} withUnderlay Returns `true` if underlay style should be used
 * @property {String} underlayTheme Returns underlay theme to be used
 * @property {NavigationService} navigation
 */

/**
 * @class
 * Blog Screen Hooks.
 * Instance name: blogApi
 * @example
 * externalCodeSetup.blogApi.METHOD_NAME
 */
export class BlogApi {
	showSearch = true;

	/**
	 * You can use this to remove the search component from the blog and forums screen.
	 * For example, the search bar that is by default at the top of the blog screen would be hidden.
	 * @method
	 * @example
	 * externalCodeSetup.blogApi.hideSearch();
	 */
	hideSearch = () => {
		this.showSearch = false;
	};

	subFilterProps = subFilterProps => subFilterProps;

	/**
	 * Sets the available sub filter function for blogs.
	 * For example, you can define the items in the year sub filter.
	 * @method
	 * @param {TransformBlogSubFiltersFilterCallback} subFiltersFilter
	 * @example <caption>User would like to specify a year subfilter</caption>
	 * externalCodeSetup.blogApi.setSubFiltersFilter((filters) => {
	 * return {
	 *   categories: [
	 *     {
	 *       value: "",
	 *       translatedLabel: "subFilter:all_blog_categories"
	 *     }
	 *   ],
	 *   year: [
	 *      {
	 *       value: "",
	 *       translatedLabel: "subFilter:all_years"
	 *      },
	 *      {
	 *       label: "2021",
	 *       value: "2021"
	 *      },
	 *      {
	 *       label: "2000",
	 *       value: "2000"
	 *      }
	 *    ]
	 *  }
	 * });
	 *
	 * @example <caption>User would like to remove the sub filters</caption>
	 * externalCodeSetup.blogApi.setSubFiltersFilter((filters) => {});
	 */
	setSubFiltersFilter = subFilterProps => {
		this.subFilterProps = subFilterProps;
	};

	BlogItemComponent = null;

	/**
	 * Replaces the blog item component in the blog list.
	 * @method
	 * @param {?React.ComponentType<BlogItemProps>} BlogItemComponent
	 * @example
	 * //In custom_code/components/BlogItem.js...
	 * import React, {useState} from "react";
	 * import {View, StyleSheet, Text} from "react-native";
	 * import AppTouchableOpacity from "@src/components/AppTouchableOpacity";
	 * import {ItemTitle} from "@src/components/TextComponents";
	 * import {GUTTER} from "@src/styles/global";
	 * import IconButton from "@src/components/IconButton";
	 * import BookmarkButton from "@src/components/Blog/BookmarkButton";
	 *
	 * import AppImage from "@src/components/AppImage";
	 * import {AccessAuthWrapper} from "@src/components/AccessControl/AccessWrapper";
	 * import Icon from "@src/components/Icon";
	 * import * as Animatable from "react-native-animatable";
	 *
	 * const BlogItem = props => {
	 *     const {
	 *         item,
	 *         global,
	 *         colors,
	 *         allowBookmark,
	 *         bookmarkActions,
	 *         index,
	 *         isSavedItems,
	 *         navigation
	 *     } = props;
	 *
	 *     const itemHeight = global.blogItemImageStyle.height + styles.item.marginTop;
	 *     const [startItemRemoveAnimation, setStartItemRemoveAnimation] = useState(
	 *         false
	 *     );
	 *
	 *     const animation = {
	 *         from: {
	 *             opacity: 1,
	 *             height: itemHeight,
	 *             transform: [{scaleY: 1}]
	 *         },
	 *         to: {
	 *             opacity: 0,
	 *             height: 0,
	 *             transform: [{scaleY: 0.6}]
	 *         }
	 *     };
	 *
	 *     return (
	 *         <Animatable.View
	 *             duration={500}
	 *             onAnimationEnd={() => {
	 *                 bookmarkActions && bookmarkActions(item);
	 *             }}
	 *             style={[styles.container]}
	 *             {...startItemRemoveAnimation && {animation}}
	 *         >
	 *             <AccessAuthWrapper item={item}>
	 *                 <AppTouchableOpacity
	 *                     onPress={item.onClick}
	 *                     style={[styles.item, index === 0 ? {paddingTop: 0} : {}]}
	 *                 >
	 *                     <View style={[global.row, styles.viewContainer]}>
	 *                         <View style={[styles.imageAccessAuthWrapper]}>
	 *                             {item.featuredImage.fontIconName ? (
	 *                                 <Icon
	 *                                     icon={item.featuredImage}
	 *                                     styles={global.blogItemImageStyle}
	 *                                 />
	 *                             ) : (
	 *                                 <AppImage
	 *                                     style={[global.blogItemImageStyle]}
	 *                                     resizeMode="cover"
	 *                                     source={item.featuredImage}
	 *                                 />
	 *                             )}
	 *                         </View>
	 *
	 *                         <View style={[global.bottomBorder, styles.infoContainer]}>
	 *                             <ItemTitle
	 *                                 global={global}
	 *                                 numberOfLines={2}
	 *                                 style={[global.blogItemTitle, {color: colors.headingsColor}]}
	 *                             >
	 *                                 {item.title}
	 *                             </ItemTitle>
	 *
	 *                             <Text style={[global.blogItemAuthorText]}>{item.authorName}</Text>
	 *
	 *                             <View style={{flex: 1}} />
	 *
	 *                             <View
	 *                                 style={[global.row, {alignItems: "center", paddingTop: 10}]}
	 *                             >
	 *                                 <Text style={global.textMeta}>{item.date}</Text>
	 *
	 *                                 {item.allowComments && <View style={global.dotSep} />}
	 *
	 *                                 {item.allowComments && (
	 *                                     <IconButton
	 *                                         icon={{fontIconName: "comment-square-dots", weight: 400}}
	 *                                         tintColor={colors.textIconColor}
	 *                                         size={20}
	 *                                         renderText={() => (
	 *                                             <Text style={[styles.iconText, global.activityCount]}>
	 *                                                 {item.commentCount}
	 *                                             </Text>
	 *                                         )}
	 *                                     />
	 *                                 )}
	 *                                 {allowBookmark && (
	 *                                     <BookmarkButton
	 *                                         global={global}
	 *                                         colors={colors}
	 *                                         item={item}
	 *                                         navigation={navigation}
	 *                                         is_bookmarked={item.bb_bookmark.is_bookmarked}
	 *                                         disableBouncingAnimation={isSavedItems}
	 *                                         onPress={() => {
	 *                                             if (isSavedItems) setStartItemRemoveAnimation(true);
	 *                                             else bookmarkActions && bookmarkActions(item);
	 *                                         }}
	 *                                     />
	 *                                 )}
	 *                             </View>
	 *                         </View>
	 *                     </View>
	 *                 </AppTouchableOpacity>
	 *             </AccessAuthWrapper>
	 *         </Animatable.View>
	 *     );
	 * };
	 *
	 * const styles = StyleSheet.create({
	 *     container: {
	 *         flex: 1
	 *     },
	 *     item: {
	 *         flex: 1,
	 *         marginTop: 16,
	 *         paddingHorizontal: GUTTER
	 *     },
	 *     infoContainer: {
	 *         marginLeft: 16,
	 *         paddingRight: 16,
	 *         paddingBottom: 10,
	 *         flex: 1
	 *     },
	 *     viewContainer: {
	 *         justifyContent: "space-between",
	 *         flex: 1,
	 *         alignItems: "flex-start"
	 *     },
	 *     iconText: {marginLeft: 6, alignSelf: "center"}
	 * });
	 *
	 * export default BlogItem;
	 *
	 * //In custom_code/index.js
	 *
	 * ...
	 *
	 * import BlogItem from "./components/BlogItem";
	 * export const applyCustomCode = externalCodeSetup => {
	 *   externalCodeSetup.blogApi.setBlogItemComponent(props => <BlogItem {...props} />)
	 * }
	 */
	setBlogItemComponent = BlogItemComponent => {
		this.BlogItemComponent = BlogItemComponent;
	};

	fetchParamsFilter = params => params;

	/**
	 * It overrides the parameters that are used to fetch blog posts in the Blog screen so that you can make it as customizable as possible when calling its API.
	 * @method
	 * @param {TransformBlogParams} fetchParamsFilter
	 *
	 * @example <caption> Create a custom filter in blog screen </caption>
	 *
	 * //In components/BlogFiltersCustom.js...
	 *
	 * import React, { useState } from "react";
	 * import { TextInput, View, Button } from 'react-native'
	 * import { useDispatch } from "react-redux";
	 * import { blogRequested } from "@src/actions/blog/blog";
	 * import { getExternalCodeSetup } from "@src/externalCode/externalRepo";
	 * import withGlobalStyles from "@src/components/hocs/withGlobalStyles";
	 * import DatePicker from 'react-native-datepicker'
	 *
	 * const hook = getExternalCodeSetup().blogApi;
	 * hook.hideSearch();
	 *
	 * const screenName = "blog";
	 *
	 * const filter = "all";
	 * const subfilters = ""
	 *
	 * const refresh = true; //Set to true to refresh list
	 * const searchTerm = ""
	 * const userId = 1; //Define author id
	 *
	 *
	 * const BlogFiltersCustom = (props) => {
	 *
	 *    const { navigation, route, colors } = props;
	 *
	 *    const dispatch = useDispatch();
	 *
	 *    //If showing the matched screen, show custom filter before displaying list component
	 *    if (route?.params?.item?.object === screenName) {
	 *
	 *        const [date, setDate] = useState(new Date())
	 *
	 *        const handleSubmit = () => {
	 *
	 *            //Set custom parameters before fetching
	 *            hook.setFetchParamsFilter((props) => {
	 *
	 *                //You can add more parameters such as "subject", "keyword" etc...
	 *                return {
	 *                    ...props,
	 *                    date
	 *                }
	 *            })
	 *
	 *            //Dispatch redux action to call api using customized filters
	 *            dispatch(blogRequested(filter, subfilters, refresh, searchTerm, userId));
	 *
	 *        }
	 *
	 *        return <View style={{ backgroundColor: colors.whiteColor, flexDirection: "row", alignItems: "center", justifyContent: "center" }}>
	 *            <DatePicker
	 *                style={{ width: 200 }}
	 *                date={date}
	 *                mode="date"
	 *                format="YYYY-MM-DD"
	 *                confirmBtnText="Confirm"
	 *                cancelBtnText="Cancel"
	 *                customStyles={{
	 *                    dateIcon: {
	 *                        position: 'absolute',
	 *                        left: 0,
	 *                        top: 4,
	 *                        marginLeft: 0
	 *                    },
	 *                    dateInput: {
	 *                        marginLeft: 36
	 *                    }
	 *                }}
	 *                onDateChange={(date) => { setDate(date) }}
	 *            />
	 *            <Button
	 *                onPress={() => handleSubmit()}
	 *                title="Filter"
	 *            />
	 *        </View>
	 *    }
	 *
	 *    return null;
	 *
	 * }
	 *
	 * export default withGlobalStyles(BlogFiltersCustom);
	 *
	 * //In custom_code/index.js...
	 *
	 * ...
	 *
	 * import BlogFiltersCustom from "./components/BlogFiltersCustom";
	 * export const applyCustomCode = externalCodeSetup => {
	 *    externalCodeSetup.filterScreenApiHooks.setAfterFilterComponent(BlogFiltersCustom);
	 * }
	 */
	setFetchParamsFilter = fetchParamsFilter => {
		this.fetchParamsFilter = fetchParamsFilter;
	};

	BookmarkComponent = null;

	/**
	 * You can use this hook to modify the bookmark component.
	 * @method
	 * @param {?React.ComponentType<BookmarkComponentProps>} BookmarkComponent
	 * @example <caption> Change background color of bookmarks </caption>
	 *
	 *
	 * import React from "react";
	 * import {View, TouchableOpacity, Animated} from "react-native";
	 * import IconButton from "@src/components/IconButton";
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *    externalCodeSetup.blogApi.setBookmarkComponent(props => {
	 *        const {
	 *            global,
	 *            styles,
	 *            onPress,
	 *            hitSlop,
	 *            animatedStyle,
	 *            fixAlignRight,
	 *            bookmarked,
	 *            fontIconName,
	 *            fontIconVariant,
	 *            tintColor,
	 *            withUnderlay,
	 *            underlayTheme
	 *        } = props;
	 *        return (
	 *            <View style={[global.column, styles.bookmarkContainer]}>
	 *                <TouchableOpacity
	 *                    onPress={onPress}
	 *                    activeOpacity={0.5}
	 *                    hitSlop={hitSlop}
	 *                >
	 *                    <Animated.View style={animatedStyle}>
	 *                        <IconButton
	 *                            fixAlignRight={fixAlignRight}
	 *                            //Commented out both props to make IconButton use touchableStyle props...
	 *                            // withUnderlay={withUnderlay}
	 *                            // underlayTheme={underlayTheme}
	 *                            icon={{
	 *                                fontIconName: fontIconName,
	 *                                fontIconVariant: fontIconVariant
	 *                            }}
	 *                            tintColor={tintColor}
	 *                            size={20}
	 *                            touchableStyle={{
	 *                                backgroundColor: "darkgray",
	 *                                width: 32,
	 *                                height: 32,
	 *                                borderRadius: 16,
	 *                                alignItems: "center",
	 *                                justifyContent: "center"
	 *                            }}
	 *                        />
	 *                    </Animated.View>
	 *                </TouchableOpacity>
	 *            </View>
	 *        );
	 *    });
	 * }
	 */
	setBookmarkComponent = BookmarkComponent => {
		this.BookmarkComponent = BookmarkComponent;
	};
}