/**
* @typedef {Object} TopicProps
* @property { Object } global App global style
* @property { Object } colors App colors
* @property { TranslationFunction } t
* @property {TopicViewModel} topic
*/
/**
* @typedef {Object} TopicTitleComponentProps
* @property {TopicProps} topicProps
*/
/**
* @typedef {Object} TopicContentComponentProps
* @property {TopicProps} topicProps
* @property {Object} tagsStyles Styles used for different HTML tags
* @property {String} content Topic content
* @property {Function} attemptDeepLink Helper function to attempt deep link
* @property {Number} computedWidth Default computed width of component to render
*/
/**
* @typedef {Object} TopicReplyButtonProps
* @property {TopicProps} topicProps
* @property {Boolean} topicCloseForUser Returns `true` if topic is closed for the logged-in user
* @property {Function} openClosedDiscussionModal Helper function that shows a modal if the topic has been closed to new replies
*/
/**
* @typedef {Object} TopicMetadataComponentProps
* @property {TopicProps} topicProps
*/
/**
* @typedef {Object} TopicItemHeaderProps
* @property {TopicViewModel} item
* @property {FormatDateFunction} formatDateFunc Function to help format date
* @property {Object} global App global style
* @property {String} textColor Default text color
* @property {String} linkColor Default link color
* @property {Boolean} light Returns `true` if text should be light colored
* @property {String} alignItems Default alignment of items
* @property {Number} avatarSize Default size of avatar
* @property {Object} titleStyle Default styling for title
* @property {Object} actionButtons Topic functions from redux
*/
/**
* @typedef {Object} ReplyItemAvatarProps
* @property {Object} reply Reply details
* @property {Object} global App global style
* @property {Boolean} isNested Returns `true` if the reply is nested inside a reply
*/
/**
* @typedef {Object} ReplyItemHeaderProps
* @property {Object} global App global style
* @property {Object} headerTitleStyle Default styling applied to the component
* @property {Object} reply Reply details
* @property {Function} formatDateFunc Helper function which can be used to format dates
*/
/**
* @typedef {Object} ReplyItemContentProps
* @property {Function} formatTextForDisplay Helper function which formats a text
* @property {Function} filterContentCss Helper function which filters css to a safe format
* @property {Object} reply Reply details
* @property {Object} colors App colors
* @property {TranslationFunction} t
* @property {Object} global App global style
* @property {Object} tagsStyles Default styling for HTML tags
* @property {Object} imagesInitialDimensions
* @property {Number} computedWidth
* @property {String} referer Used by iframeRender to add `referer` to a webview
* @property {Function} alterChildrenHTML Helper function which cleans up HTML
* @property {Function} attemptDeepLink Helper function to attempt deep link
* @property {Function} aTagRenderer Helper function for rendering anchor tags
* @property {Function} iframeRender Helper function for rendering iFrame in an HTML
*
*/
/**
* @class
* Topic/Discussion Single Hooks.
* Instance name: topicSingleApi
You can use this to customize the default topic/discussion single screen components such as the ReplyItemAvatar, ReplyItemContent, and so on.
* @example
* externalCodeSetup.topicSingleApi.METHOD_NAME
*/
export class TopicSingleApi {
TopicItemHeader = null;
/**
* You can use this to customize the author name and date in the topic single screen.
* @method
* @param {React.ComponentType<TopicItemHeaderProps>} TopicItemHeader
* @example <caption> Add a "Verified" text beside the author's name </caption>
*
* //In custom_code/components/TopicItemHeader.js...
*
* import React from "react";
* import {View, Text, TouchableOpacity} from "react-native";
* import {getAvatar} from "@src/utils";
* import AppTouchableOpacity from "@src/components/AppTouchableOpacity";
* import AppAvatar from "@src/components/AppAvatar";
*
* const renderVerified = (author) => {
* if(author.id === 1){
* return <Text style={{fontSize: 10}}>Verified</Text>
* }
*
* return null;
* }
*
* const ItemHeader = ({
* item,
* global,
* formatDateFunc,
* textColor,
* linkColor,
* light,
* alignItems,
* avatarSize,
* titleStyle,
* actionButtons
* }) => {
*
* let lightStyle = {};
* if (light) lightStyle = {color: "#ffffff"};
*
* let alignStyle = {};
* if (alignItems) alignStyle = {alignItems: alignItems};
* return (
* <View style={[global.itemHeader, alignStyle]}>
* <View style={[global.itemLeft, {alignItems: "center"}]}>
* <AppTouchableOpacity
* onPress={item.navigateToProfile ? item.navigateToProfile : () => {}}
* style={global.avatarWrap}
* >
* <AppAvatar
* size={avatarSize}
* name={item.author.name}
* source={{
* uri: getAvatar(item.author.avatar, 96)
* }}
* />
* </AppTouchableOpacity>
* {!!item.author.name && (
* <View style={{flex: 1}}>
* <Text
* style={[
* global.itemName,
* lightStyle,
* titleStyle
* ]}
* >
* {item.author.name} {renderVerified(item.author)}
* </Text>
* <View style={{flexDirection: "row", flexWrap: "wrap"}}>
* <Text style={[global.itemMeta, lightStyle]}>
* {formatDateFunc(item.lastActive)}
* </Text>
* </View>
* </View>
* )}
* </View>
* </View>
* );
* };
*
* export default ItemHeader;
*
* //In custom_code/index.js...
*
* ...
*
* import TopicItemHeader from "./components/TopicItemHeader";
*
* export const applyCustomCode = externalCodeSetup => {
* externalCodeSetup.topicSingleApi.setTopicItemHeader( (props) => <TopicItemHeader {...props} />);
* }
*/
setTopicItemHeader = TopicItemHeader => {
this.TopicItemHeader = TopicItemHeader;
};
ReplyItemAvatar = null;
/**
* You can use this hook to customize the reply item avatar in the topic single screen.
* For example, you can add a "verified" icon or text beside the avatar.
* @method
* @param {React.ComponentType<ReplyItemAvatarProps>} ReplyItemAvatar
* @example <caption> Add a "Verified" text below the avatar </caption>
*
* ...
*
* import AppAvatar from "@src/components/AppAvatar";
* import AppTouchableOpacity from "@src/components/AppTouchableOpacity";
*
* export const applyCustomCode = externalCodeSetup => {
*
* externalCodeSetup.topicSingleApi.setReplyItemAvatar(props => {
*
* const { reply, global, isNested } = props
*
* return <AppTouchableOpacity
* onPress={reply.navigateToProfile ? reply.navigateToProfile : () => { }}
* style={global.avatarWrap}
* >
* <AppAvatar
* size={isNested ? 30 : 40}
* name={reply.author.fullname}
* source={{
* uri: reply.author.avatarUrl
* }}
* />
* <Text>Verified</Text>
* </AppTouchableOpacity>
* })
* }
*/
setReplyItemAvatar = ReplyItemAvatar => {
this.ReplyItemAvatar = ReplyItemAvatar;
};
ReplyItemHeader = null;
/**
* You can use this hook to customize the reply item header which by default contains the author of the reply and its date.
* @method
* @param {React.ComponentType<ReplyItemHeaderProps>} ReplyItemHeader
* @example <caption> Change display date format </caption>
*
* ...
* export const applyCustomCode = externalCodeSetup => {
* externalCodeSetup.topicSingleApi.setReplyItemHeader(props => {
* const { global, headerTitleStyle, reply, formatDateFunc } = props;
* return <View style={global.row}>
* <Text style={[global.itemName, headerTitleStyle, { marginBottom: 0 }]}>
* {reply.author.fullname}
* </Text>
* {!reply.author.reported && (
* <Text style={[global.itemLightMeta, { marginLeft: 8 }]}>
* //{formatDateFunc(reply.date)}
* {reply.date}
* </Text>
* )}
* </View>
*
* })
* }
*/
setReplyItemHeader = ReplyItemHeader => {
this.ReplyItemHeader = ReplyItemHeader;
};
ReplyItemContent = null;
/**
* You can use this hook to customize the reply item content.
* @method
* @param {React.ComponentType<ReplyItemContentProps>} ReplyItemContent
* @example <caption> Implement default BB component </caption>
*
* //In custom_code/components/ReplyItemContent.js...
*
* import React from "react";
* import {View} from "react-native";
* import HTML from "react-native-render-html";
* import ReadMore from "@src/components/ReadMore";
* import AutoSizeImage from "@src/components/AutoSizeImage";
* import ImageCollection from "@src/components/ImageCollection";
* import {GifVideoPlayer} from "@src/components/Gif";
* import EmbeddedDocumentItem from "@src/components/Documents/EmbeddedDocumentItem";
*
* const ReplyItemContent = ({
* formatTextForDisplay,
* filterContentCss,
* reply,
* colors,
* t,
* global,
* tagsStyles,
* imagesInitialDimensions,
* computedWidth,
* referer,
* alterChildrenHTML,
* attemptDeepLink,
* aTagRenderer,
* iframeRender
* }) => (
* <View style={{flex: 1, marginTop: 6}}>
* <ReadMore
* content={formatTextForDisplay(filterContentCss(reply.content))}
* size={300}
* colors={colors}
* t={t}
* global={global}
* >
* {content => (
* <HTML
* tagsStyles={{
* ...tagsStyles,
* p: {
* ...tagsStyles.p,
* marginBottom: 0
* },
* iframe: {
* marginTop: 10
* }
* }}
* baseFontStyle={global.textHtml}
* html={content}
* imagesInitialDimensions={imagesInitialDimensions}
* staticContentMaxWidth={computedWidth}
* alterChildren={alterChildrenHTML(computedWidth)}
* onLinkPress={attemptDeepLink}
* renderers={{
* a: aTagRenderer(computedWidth),
* iframe: iframeRender(referer),
* img: (htmlAttribs, children, convertedCSSStyles, passProps) => {
* return (
* <AutoSizeImage
* url={htmlAttribs.src}
* wrapperStyle={{
* marginTop: convertedCSSStyles.marginTop,
* marginBottom: convertedCSSStyles.marginBottom
* }}
* style={{
* ...convertedCSSStyles,
* paddingVertical: 100
* }}
* />
* );
* }
* }}
* />
* )}
* </ReadMore>
* {!!reply.media && (
* <ImageCollection
* item={reply}
* containerStyle={{marginTop: 16}}
* colors={colors}
* global={global}
* t={t}
* toUserBasedOnSettings={() => reply.navigateToProfile()}
* showActionButtons={false}
* />
* )}
* {!!reply.videos && (
* <ImageCollection
* item={reply}
* containerStyle={{marginTop: 16}}
* colors={colors}
* global={global}
* t={t}
* toUserBasedOnSettings={() => reply.navigateToProfile()}
* showActionButtons={false}
* />
* )}
* {!!reply.gif?.preview_url ? (
* <View style={{marginTop: 16}}>
* <GifVideoPlayer
* url={reply.gif?.video_url}
* poster={reply.gif?.preview_url}
* width={computedWidth - 30}
* containerStyle={{backgroundColor: "#F9F9F9"}}
* />
* </View>
* ) : null}
*
* {reply?.documents?.length > 0 &&
* reply.documents.map(item => {
* const viewModel: DocumentViewModel = documentToViewModel(item);
*
* return (
* <EmbeddedDocumentItem
* {...{
* t,
* colors,
* global,
* token,
* viewModel,
* navigation
* }}
* />
* );
* })}
* </View>
* );
*
* export default ReplyItemContent
*
* //In custom_code/index.js...
*
* import ReplyItemContent from "./components/ReplyItemContent"
* export const applyCustomCode = externalCodeSetup => {
* externalCodeSetup.topicSingleApi.setReplyItemContent(props => <ReplyItemContent {...props} />)
* }
*
*/
setReplyItemContent = ReplyItemContent => {
this.ReplyItemContent = ReplyItemContent;
};
TopicTitleComponent = null;
/**
* You can use this hook to customize the title of the topic/discussion in the TopicSingleScreen.
* @method
* @param {TopicTitleComponentProps} TopicTitleComponent
* @example
* externalCodeSetup.topicSingleApi.setTopicTitleComponent(({
* global,
* topic
* }) => <Text style={[global.topicSingleTitle, { marginBottom: 20, color: "red" }]}>
* {topic.title}
* </Text>
* )
*/
setTopicTitleComponent = TopicTitleComponent => {
this.TopicTitleComponent = TopicTitleComponent;
};
TopicContentComponent = null;
/**
* You can use this hook to customize the content of the topic/discussion in the TopicSingleScreen.
* @method
* @param {TopicContentComponentProps} TopicContentComponent
* @example
*
* ...
*
* import HTML from "react-native-render-html";
* import ReadMore from "@src/components/ReadMore";
* import {
* alterChildrenHTML
* } from "@src/utils";
* import { aTagRenderer } from "@src/utils/htmlRender";
* export const applyCustomCode = (externalCodeSetup: any) => {
*
* externalCodeSetup.topicSingleApi.setTopicContentComponent(({
* colors,
* content,
* global,
* t,
* tagsStyles,
* attemptDeepLink,
* computedWidth,
* topic
* }) => {
*
* const newContent = `<a href="https://google.com/search?q=${topic.title}"> Press this to google the topic </a>`
*
* return <View style={{ marginTop: -4 }}>
* <ReadMore
* colors={colors}
* content={content}
* size={400}
* t={t}
* global={global}
* style={{ marginBottom: 20 }}
* >
* {content => (
* <HTML
* html={content + newContent}
* tagsStyles={{
* ...tagsStyles,
* iframe: {
* marginTop: 10,
* marginBottom: 10
* }
* }}
* baseFontStyle={global.textHtml}
* onLinkPress={attemptDeepLink}
* staticContentMaxWidth={computedWidth}
* alterChildren={alterChildrenHTML(computedWidth)}
* renderers={{
* a: aTagRenderer(computedWidth)
* }}
* />
* )}
* </ReadMore>
* </View>
* })
* }
*/
setTopicContentComponent = TopicContentComponent => {
this.TopicContentComponent = TopicContentComponent;
};
TopicReplyButton = null;
/**
* You can use this hook to customize the reply button which allows the users to reply to the main content of the topic/discussion.
* @method
* @param {TopicReplyButtonProps} TopicReplyButton
* @example
* ...
*
* import Icon from "@src/components/Icon";
* import AppTouchableOpacity from "@src/components/AppTouchableOpacity";
* import AuthWrapper from "@src/components/AuthWrapper";
* export const applyCustomCode = (externalCodeSetup: any) => {
* externalCodeSetup.topicSingleApi.setTopicReplyButton(({
* t,
* colors,
* global,
* topic,
* topicCloseForUser,
* openClosedDiscussionModal
* }) =>
* <AuthWrapper>
* <AppTouchableOpacity
* activeOpacity={topicCloseForUser ? 0.5 : 1}
* style={[global.itemFooter, { opacity: topicCloseForUser ? 0.5 : 1 }]}
* onPress={
* topicCloseForUser ? openClosedDiscussionModal : topic.newReply
* }
* hitSlop={{ top: 10, right: 20, bottom: 20, left: 20 }}
* >
* <Icon
* icon={{fontIconName: "reply", weight: 400}}
* webIcon={"IconReply"}
* tintColor={colors.descLightTextColor}
* style={{
* width: 14,
* height: 12
* }}
* />
* <Text
* style={[global.itemMeta, { marginLeft: 6, color: colors.textColor }]}
* >
* {t("topic:reply")}
* </Text>
* </AppTouchableOpacity>
* </AuthWrapper>
* )
* }
*/
setTopicReplyButton = TopicReplyButton => {
this.TopicReplyButton = TopicReplyButton;
};
TopicMetadataComponent = null;
/**
* You can use this hook to customize the metadeta component in the TopicSingleScreen which by default, displays the number of members and replies in the discussion.
* @method
* @param {TopicMetadataComponentProps} TopicMetadataComponent
* @example
*
* externalCodeSetup.topicSingleApi.setTopicMetadataComponent(({
* global,
* topic
* }) =>
* <View style={[global.itemFooterMeta, {marginLeft: "auto"}]}>
* <Text style={global.itemMeta}>
* {topic.voiceCount} • {topic.replyCount}
* </Text>
* </View>
* )
*/
setTopicMetadataComponent = TopicMetadataComponent => {
this.TopicMetadataComponent = TopicMetadataComponent;
};
}
Source