Source

externalCode/blogSingle.js

import * as React from "react";

/**
 * @typedef {Object} BeforeDetailsComponentProps
 * @property { BlogViewModel }  blog
 */

/**
 * @typedef {Object} BlogViewModel Blog model used by the app that came from Blog API. All fields from api response will also be available in BlogViewModel aside from the fields defined below. Refer to: {@link https://www.buddyboss.com/resources/api/#api-Blogs}
 * @property {Number} id Blog Id
 * @property {String} link Link to the blog post
 * @property {String} title Blog title
 * @property {Array} contentNative Blocks used in the blog
 * @property {String} content HTML content of the blog
 * @property {String} authorName Name of blog's author
 * @property {String} avatar Link to author's avatar
 * @property {String} date Date posted
 * @property {Number} commentCount Number of comments in the blog
 * @property {Boolean} allowComments Returns true if blog allows comments
 * @property {Array} replies Replies posted in the blog
 * @property {String|Number} featuredImage If a featured image is set, returns a string link to the featured image. Otherwise, will return an integer indicating that `require("../assets/img/default/default-blog-img.png")` is used for the featured image
 *
 */

/**
 * @typedef {Object} AfterDetailsComponentProps
 * @property {BlogViewModel}  blog
 */

/**
 * @typedef {Object} BlogDetailsComponentProps
 * @property {BlogViewModel}  blog
 * @property {Object}  global App global style
 * @property {Function}  titleOpacity Uses useMemo() hook and returns `titleOpacity`
 * @property {Object}  textStyle Returns a defined style object if blog is using a featured image
 */

/**
 * @typedef {Object} BlogHeaderAvatarProps
 * @property {BlogViewModel}  blog
 * @property {Object}  global App global style
 * @property {Object}  textStyle Returns defined style object if blog is using a featured image
 */

/**
 * @typedef {Function} TransformBlogButtonsCallback
 * @property {Array}  buttons Buttons for comment, facebook and twitter share
 * @property {BlogViewModel}  blog
 */

/**
 * @typedef {Object} AfterBlogProfileHeaderProps
 * @property {BlogViewModel}  blog
 */

/**
 * @typedef {Object} BeforeBlogSingleBodyProps
 * @property {BlogViewModel}  blog
 * @property {Object}  global App global style
 * @property {Object} colors App colors
 * @property {Function} linkClickHandler Helper function for deep linking
 * @property {TranslationFunction} t
 * @property {NavigationService} navigation
 */

/**
 * @typedef {Object} AfterBlogSingleBodyProps
 * @property {BlogViewModel}  blog
 * @property {Object}  global App global style
 * @property {Object} colors App colors
 * @property {Function} linkClickHandler Helper function for deep linking
 * @property {TranslationFunction} t
 * @property {NavigationService} navigation
 */

/**
 * @class
 * Blog Single Hooks.
 * Instance name: blogSingleApi
  
   You can utilize this to add details to your blog page and modify its structure.
 * @example
 * externalCodeSetup.blogSingleApi.METHOD_NAME
 */
export class BlogSingleApi {
	customHeaderBackground = null;

	/**
	 * You can add a custom cover image or banner for all your blogs to replace the default blog cover image.
	 * (If you want to change the image header background for a particular blog, then you would have to specify the blog in your function.)
	 * @param {String} customHeaderBackground Resource to replace a blog's cover image
	 * @method
	 * @example
	 *
	 * externalCodeSetup.blogSingleApi.setCustomHeaderBackground('https://link-to-image.png');
	 */
	setCustomHeaderBackground = customHeaderBackground => {
		this.customHeaderBackground = customHeaderBackground;
	};

	BeforeDetailsComponent = null;

	/**
	 * You can use this to add a component displaying information such as title and author before the blog details.
	 * @param {React.ComponentType<BeforeDetailsComponentProps>} BeforeDetailsComponent
	 * @method
	 * @example
	 * //In custom_code/components/BeforeDetails.js
	 *
	 * import React from 'react';
	 * import { View, Text } from 'react-native';
	 * import moment from 'moment';
	 * const BeforeDetails = (props) => {
	 *
	 *    const {blog} = props;
	 *
	 *    const lastModified = moment(blog.modified).startOf('day').fromNow();
	 *
	 *    return <View style={{padding: 20}}>
	 *        <Text> This blog was last modified {lastModified} </Text>
	 *    </View>
	 * }
	 *
	 * export default BeforeDetails;
	 *
	 * //In custom_code/index.js
	 *
	 * import BeforeDetails from "./components/BeforeDetails";
	 * export const applyCustomCode = externalCodeSetup => {
	 *   externalCodeSetup.blogSingleApi.setBeforeDetailsComponent((props) => <BeforeDetails {...props} />)
	 * }
	 *
	 */
	setBeforeDetailsComponent = BeforeDetailsComponent => {
		this.BeforeDetailsComponent = BeforeDetailsComponent;
	};

	AfterDetailsComponent = null;

	/**
	 * You can use this to add a component that displays details of the blogs such as author name and blog title within the header.
	 * @param {React.ComponentType<AfterDetailsComponentProps>} AfterDetailsComponent
	 * @method
	 * @example
	 * //In custom_code/components/AfterDetails.js
	 *
	 * import React from 'react';
	 * import { View, Text } from 'react-native';
	 * import moment from 'moment';
	 * const AfterDetails = (props) => {
	 *
	 *    const {blog} = props;
	 *
	 *    const lastModified = moment(blog.modified).startOf('day').fromNow();
	 *
	 *    return <View style={{padding: 20}}>
	 *        <Text> This blog was last modified {lastModified} </Text>
	 *    </View>
	 * }
	 *
	 * export default AfterDetails;
	 *
	 * //In custom_code/index.js
	 *
	 * ...
	 *
	 * import AfterDetails from "./components/AfterDetails";
	 * export const applyCustomCode = externalCodeSetup => {
	 *   externalCodeSetup.blogSingleApi.setAfterDetailsComponent((props) => <AfterDetails {...props} />)
	 * }
	 *
	 */
	setAfterDetailsComponent = AfterDetailsComponent => {
		this.AfterDetailsComponent = AfterDetailsComponent;
	};

	BlogDetailsComponent = null;

	/**
	 * It replaces the blog details component in the header.
	 * @param {React.ComponentType<BlogDetailsComponentProps>} BlogDetailsComponent
	 * @method
	 * @example
	 *
	 * ...
	 *
	 * import Animated from "react-native-reanimated";
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *  const BlogDetailsComponent = ({ blog, global, titleOpacity, textStyle }) => {
	 *   return (<Animated.Text
	 *    numberOfLines={4}
	 *    style={[
	 *      global.textHeaderTitle,
	 *      { alignSelf: "flex-start", opacity: titleOpacity },
	 *      textStyle
	 *    ]}
	 *   >
	 *    {blog.title} {(blog.commentCount > 0) && "- Check out all the fascinating comments!"}
	 *   </Animated.Text>);
	 *
	 *  }
	 *  externalCodeSetup.blogSingleApi.setBlogDetailsComponent(BlogDetailsComponent)
	 * }
	 */
	setBlogDetailsComponent = BlogDetailsComponent => {
		this.BlogDetailsComponent = BlogDetailsComponent;
	};

	BlogHeaderAvatar = null;

	/**
	 * You can replace the blog header avatar instead of using the default settings that includes displays of avatar, author name and publishing date.
	 * @param {React.ComponentType<BlogHeaderAvatarProps>} BlogHeaderAvatar
	 * @method
	 * @example <caption>User would like to add more info in the BlogHeaderAvatar component</caption>
	 *
	 * ...
	 *
	 * import AppAvatar from "@src/components/AppAvatar";
	 * import {FontWeights} from "@src/styles/global";
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *  const BlogHeaderAvatar = ({  blog, global, textStyle }) => {
	 *   return (
	 *     <View style={[global.row, { flex: 1 }]}>
	 *        <AppAvatar
	 *            size={35}
	 *            source={{ uri: blog.avatar }}
	 *            style={{ marginRight: 10 }}
	 *        />
	 *        <View>
	 *            <Text
	 *                style={[global.text, { fontWeight: FontWeights.semiBold }, textStyle]}>
	 *                {blog.authorName}
	 *            </Text>
	 *            <Text style={[global.text, { fontWeight: FontWeights.semiBold }, textStyle]}>{blog.link}</Text>
	 *            <Text style={[global.textMeta, textStyle]}>{blog.date}</Text>
	 *        </View>
	 *     </View>
	 *    );
	 *
	 *  }
	 *  externalCodeSetup.blogSingleApi.setBlogHeaderAvatar(BlogHeaderAvatar);
	 * }
	 */
	setBlogHeaderAvatar = BlogHeaderAvatar => {
		this.BlogHeaderAvatar = BlogHeaderAvatar;
	};

	transformBlogHeaderButtons = buttons => buttons;

	/**
	 * You can transform default blog buttons for commenting, Facebook and Twitter sharing and replace it with your preferred ones.
	 * @param {TransformBlogButtonsCallback} transformBlogHeaderButtons
	 * @method
	 * @example <caption>Add an email share button</caption>
	 *
	 * ...
	 *
	 * import Share from "react-native-share";
	 * import IconButton from "@src/components/IconButton";
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *
	 *    externalCodeSetup.blogSingleApi.setTransformBlogHeaderButtons((buttons, blog) => {
	 *
	 *        const social = Share.Social.EMAIL;
	 *
	 *        const hasCover = !!blog.featuredImage;
	 *        const iconBackgroundColor = hasCover ? "#fff" : "#A6ADB5";
	 *        const iconTintColor = hasCover ? "#000" : "#fff";
	 *
	 *        const Email = <IconButton
	 *            pressHandler={() =>
	 *                Share.shareSingle({
	 *                        message: blog.excerpt.rendered,
	 *                        url: blog.link,
	 *                        title: blog.title,
	 *                        subject: blog.title,
	 *                        email: "[email protected]",
	 *                        social: social
	 *                    })
	 *            }
	 *            icon={{uri: "https://link-to-image.png"}}
	 *            touchableStyle={{
	 *                backgroundColor: iconBackgroundColor,
	 *                alignItems: "center",
	 *                borderRadius: 18,
	 *                padding: 0,
	 *                marginRight: 8
	 *            }}
	 *            tintColor={iconTintColor}
	 *            style={{ height: 28, width: 28 }}
	 *            rtlStyleFix={"handled"}
	 *        />
	 *
	 *        return [...buttons, Email];
	 *
	 *    })
	 * }
	 */
	setTransformBlogHeaderButtons = transformBlogHeaderButtons => {
		this.transformBlogHeaderButtons = transformBlogHeaderButtons;
	};

	AfterProfileHeader = null;

	/**
	 * Used to add a component below the header if you want to include an abstract, image or any other information.
	 * @param {React.ComponentType<AfterBlogProfileHeaderProps>} AfterProfileHeader
	 * @method
	 * @example <caption>Add a banner image after blog profile header component</caption>
	 *
	 * ...
	 *
	 * import FastImage from "react-native-fast-image";
	 * export const applyCustomCode = externalCodeSetup => {
	 *
	 *    const AfterProfileHeader = ({ blog }) => {
	 *        return (<FastImage style={{width: "auto", height: 100}} source={{uri: "https://link-to-image.png"}} />)
	 *    }
	 *
	 *    externalCodeSetup.blogSingleApi.setAfterProfileHeader(AfterProfileHeader)
	 * }
	 */
	setAfterProfileHeader = AfterProfileHeader => {
		this.AfterProfileHeader = AfterProfileHeader;
	};

	BeforeBlogSingleBody = null;

	/**
	 * You can use this to add a component before the blog's body component.
	 * @param {React.ComponentType<BeforeBlogSingleBodyProps>} BeforeBlogSingleBody
	 * @method
	 * @example <caption> Add HTML with redirect function </caption>
	 *
	 * ...
	 *
	 * import HTML from "react-native-render-html";
	 *
	 * export const applyCustomCode = externalCodeSetup => {
	 *
	 *  externalCodeSetup.blogSingleApi.setBeforeBlogSingleBody((props) => {
	 *
	 *    const html = `Go to link: <a href="https://buddyboss.com">BuddyBoss</a>`;
	 *
	 *    return <View style={{ flex: 1, borderRadius: 10, borderWidth: 1, padding: 10, alignItems: "center" }}>
	 *      <HTML
	 *        containerStyle={{
	 *          paddingTop: 2,
	 *          paddingBottom: 1,
	 *          flex: 1
	 *        }}
	 *        html={html}
	 *        onLinkPress={props.linkClickHandler}
	 *      />
	 *    </View>
	 *
	 *  })
	 * }
	 *
	 */
	setBeforeBlogSingleBody = BeforeBlogSingleBody => {
		this.BeforeBlogSingleBody = BeforeBlogSingleBody;
	};

	AfterBlogSingleBody = null;

	/**
	 * You can use this to add a component after the blog's body component.
	 * @param {React.ComponentType<AfterBlogSingleBodyProps>} AfterBlogSingleBody
	 * @method
	 * @example <caption> Display other blogs from redux state </caption>
	 *
	 * ...
	 *
	 * import { useSelector } from "react-redux";
	 * export const applyCustomCode = externalCodeSetup => {
	 *
	 *  externalCodeSetup.blogSingleApi.setAfterBlogSingleBody((props) => {
	 *
	 *    const blogIds = useSelector(state => state.blog.all.ids);
	 *    const blogCache = useSelector(state => state.blogCache);
	 *
	 *    if (blogIds.size > 0) {
	 *
	 *      const goToBlog = (blog) => {
	 *        props.navigation.dispatch(
	 *          props.navigation.navigate({
	 *            routeName: "BlogSingleScreen",
	 *            params: {
	 *              blog
	 *            },
	 *	            key: `BlogSingleScreen-${blog.id.toString()}`
	 *          })
	 *        );
	 *      }
	 *
	 *      return <>
	 *        {blogIds.map((id) => {
	 *
	 *          if (props.blog.id == id) {
	 *            return null;
	 *          }
	 *
	 *          const details = blogCache.byId.get(id.toString());
	 *
	 *          return <TouchableOpacity style={{margin: 30}} onPress={() => goToBlog(details)}>
	 *            <View style={{flex: 1, borderRadius: 10, borderWidth: 1, padding: 10, alignItems: "center"}}>
	 *              <Text>
	 *                {details.title.rendered}
	 *              </Text>
	 *            </View>
	 *          </TouchableOpacity>
	 *
	 *        })}
	 *      </>
	 *
	 *    }
	 *
	 *    return null;
	 *
	 *  });
	 * }
	 */
	setAfterBlogSingleBody = AfterBlogSingleBody => {
		this.AfterBlogSingleBody = AfterBlogSingleBody;
	};
}