Class

TopicsApi

TopicsApi()

Topic/Discussion Hooks. Instance name: topicsApi

You can use this to customize the default Topic/Discussion option such as rendering a custom topic item in the list and other options.

Constructor

# new TopicsApi()

Example
externalCodeSetup.topicsApi.METHOD_NAME

Methods

# setFetchParamsFilter(fetchParamsFilter)

It overrides the parameters that are used to fetch topics in the Topics screen so that you can make it as customizable as possible when calling its API.

Parameters:
Name Type Description
fetchParamsFilter TransformTopicsParams
Example

Create a custom filter in topics screen

//In custom_code/components/TopicsFiltersCustom.js...

import React, { useState } from "react";
import { TextInput, View, Button, Text, Switch } from 'react-native'
import { useDispatch } from "react-redux";
import { topicsLoadRequest } from "@src/actions/topics";
import { getExternalCodeSetup } from "@src/externalCode/externalRepo";
import withGlobalStyles from "@src/components/hocs/withGlobalStyles";

const hook = getExternalCodeSetup().topicsApi;
const screenName = "topics";

getExternalCodeSetup().indexScreenApiHooks.setHeaderHeight((defaultHeaderHeight, filterType, navigation) => {

   if (filterType === "topics"){
       return 300
   }

   return defaultHeaderHeight;

});

const filter = "all";
const subfilters = {
   orderby: "activity", //"meta_value", "date", "ID", "author", "title", "modified", "parent", "rand", "popular", "activity"
   order: "desc"
}

const refresh = true; //Set to true to refresh list
const searchTerm = ""

const TopicsFiltersCustom = (props) => {

   const { navigation, colors } = props;

   const dispatch = useDispatch();

   //If showing the matched screen, show custom filter before displaying list component
   if (navigation?.state?.params?.item?.object === screenName) {

       const [tag, setTag] = useState(false);

       const handleSubmit = () => {

           //Set custom parameters before fetching
           hook.setFetchParamsFilter((props) => {

               //You can add more parameters such as "subject", "keyword" etc...
               return {
                   ...props,
                   tag
               }
           })

           //Dispatch redux action to call api using customized filters
           dispatch(topicsLoadRequest(filter, subfilters, refresh, searchTerm));

       }

       return <View style={{ backgroundColor: colors.whiteColor, alignItems: "center", justifyContent: "center" }}>

           <TextInput
               style={{paddingHorizontal: 20, marginTop: 10, fontSize: 20}}
               autoFocus
               value={tag}
               onChangeText={tag => setTag(tag)}
               placeholder="Search for tag..."
           />
           <Button
               onPress={() => handleSubmit()}
               title="Filter"
           />
       </View>
   }

   return null;

}

export default withGlobalStyles(TopicsFiltersCustom);

//In custom_code/index.js...

...

import TopicsFiltersCustom from "./components/TopicsFiltersCustom";
export const applyCustomCode = externalCodeSetup => {
   externalCodeSetup.filterScreenApiHooks.setAfterFilterComponent(TopicsFiltersCustom);
}

# setReplyItemAvatar()

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.

Deprecated:
  • Please use the equivalent hook: topicSingleApi.setReplyItemAvatar()
Example

Add a "Verified" text below the avatar

...

import AppAvatar from "@src/components/AppAvatar";
import AppTouchableOpacity from "@src/components/AppTouchableOpacity";

export const applyCustomCode = externalCodeSetup => {

  externalCodeSetup.topicsApi.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>
  })
}

# setReplyItemContent()

You can use this hook to customize the reply item content.

Deprecated:
  • Please use the equivalent hook: topicSingleApi.setReplyItemContent()
Example

Implement default BB component

//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";

import {
  documentToViewModel
} from "@src/utils";

const ReplyItemContent = ({
  content,
  readMoreContentSize,
  attachmentMarginTop,
  formatTextForDisplay,
  filterContentCss,
  reply,
  colors,
  t,
  global,
  tagsStyles,
  imagesInitialDimensions,
  computedWidth,
  referer,
  alterChildrenHTML,
  attemptDeepLink,
  aTagRenderer,
  iframeRender
}) => (
  <View style={{flex: 1, marginTop: 6}}>
    <ReadMore
      content={content}
      size={readMoreContentSize}
      colors={colors}
      t={t}
      global={global}
    >
      {content => (
        <HTML
          tagsStyles={{
            ...tagsStyles,
            div: {
              ...tagsStyles.p,
            },
            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: attachmentMarginTop}}
        colors={colors}
        global={global}
        t={t}
        toUserBasedOnSettings={() => reply.navigateToProfile()}
        showActionButtons={false}
      />
    )}
    {!!reply.videos && (
      <ImageCollection
        item={reply}
        containerStyle={{marginTop: attachmentMarginTop}}
        colors={colors}
        global={global}
        t={t}
        toUserBasedOnSettings={() => reply.navigateToProfile()}
        showActionButtons={false}
      />
    )}
    {!!reply.gif?.preview_url ? (
      <View style={{marginTop: attachmentMarginTop}}>
        <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 = 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.topicsApi.setReplyItemContent(props => <ReplyItemContent {...props} />)
}

# setReplyItemHeader()

You can use this hook to customize the reply item header which by default contains the author of the reply and its date.

Deprecated:
  • Please use the equivalent hook: topicSingleApi.setReplyItemHeader()
Example

Change display date format

...
export const applyCustomCode = externalCodeSetup => {
  externalCodeSetup.topicsApi.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>

  })
}

# setSubFiltersFilter(subFiltersFilter)

You can use this to set the subfilter function to rearrange the order of the filters in the Discussion screen. For example, you can rearrange the labels such as "Date created", "Last Active" and more on the discussions screen under the search bar.

Parameters:
Name Type Description
subFiltersFilter TransformTopicSubFiltersFilterCallback
Example

User would like to use "Date Created" filter only

externalCodeSetup.topicsApi.setSubFiltersFilter((filters) => {
  return ["date"]; //available filters include "activity", "date", "title", "popular"
})

# setTopicItemComponent(TopicItemComponentnullable)

It renders a custom topic item component in the topic list.

Parameters:
Name Type Attributes Description
TopicItemComponent React.ComponentType.<TopicItemProps> <nullable>
Example

Add more details to Discussion/Topic item

//In custom_code/components/TopicItem.js

import React from 'react';
import { View, Text, StyleSheet, Animated } from 'react-native';
import AppTouchableOpacity from "@src/components/AppTouchableOpacity";
import {getAvatar} from "@src/utils";
import AppAvatar from "@src/components/AppAvatar";
import AuthWrapper from "@src/components/AuthWrapper";
import ActionSheetButton from "@src/components/ActionButtons/ActionSheetButton";
import {GUTTER} from "@src/styles/global";

const TopicItem = (props) => {

   const {topic, styles, actionButtons, formatDateFunc, t} = props;

   const global = styles.global;
   const colors = styles.colors;

   let rootStyle;

   if (topic.actionStates.sticky) rootStyle = [global.itemSticky];

   if (!topic.actionStates.open) rootStyle = [global.itemClosed];


   const Item = <AppTouchableOpacity onPress={topic.toSingle} style={[rootStyle]}>
       <Animated.View
           style={{
               ...StyleSheet.absoluteFillObject
           }}
       />
       <View
           style={{
               ...global.row,
               flex: 1,
               marginHorizontal: GUTTER
           }}
       >
           <AppAvatar
               size={42}
               name={topic.author.name}
               source={{
                   uri: getAvatar(topic.author.avatar, 96)
               }}
               style={{ marginTop: 15, alignSelf: "flex-start" }}
           />
           <View
               style={{
                   ...global.bottomBorder,
               ...global.row,
               flex: 1,
               marginLeft: 10
           }}
       >
           <View
               style={[
                   {
                       flex: 1,
                       paddingTop: 15,
                       paddingBottom: 14,
                       paddingLeft: 0,
                       paddingRight: 0
                   }
               ]}
           >
               <Text
                   style={{
                       ...global.itemTitle,
                       paddingRight: 40,
                       marginBottom: 3
                   }}
                   numberOfLines={2}
                   ellipsizeMode={"tail"}
               >
                   {topic.title}
               </Text>
               <Text numberOfLines={1} ellipsizeMode={"tail"}>{topic.shortContent}</Text>
               <View style={{ ...global.row, marginBottom: 5 }}>
                   <Text style={global.itemMeta}>{topic.voiceCount}</Text>
                   <View style={global.dotSep} />
                   <Text style={global.itemMeta}>{topic.replyCount}</Text>
              </View>
               <Text style={{ ...global.textAlt, color: colors.descTextColor }}>
                   {t("topics:lastActive", {
                       date: formatDateFunc(topic.lastActive)
                   })}
               </Text>
           </View>
           <AuthWrapper actionOnGuestLogin={"hide"}>
               <ActionSheetButton
                   color={colors.textIconColor}
                   object={topic}
                   colors={colors}
                   actionButtons={actionButtons}
                   headerProps={{
                       onClick: topic.toSingle,
                       title: topic.title,
                       description: t("topics:lastActive", {
                           date: formatDateFunc(topic.lastActive)
                       }),
                       avatarSource: {
                           uri: getAvatar(topic.author.avatar, 96)
                       }
                   }}
                   global={global}
                   t={t}
               />
           </AuthWrapper>
       </View>
     </View>
   </AppTouchableOpacity>

   return Item;
}

export default TopicItem;

//In custom_code/index.js...

...

import TopicItem from "./components/TopicItem";
export const applyCustomCode = externalCodeSetup => {
  externalCodeSetup.topicsApi.setTopicItemComponent(props => {
    return <TopicItem {...props} />;
  })
}

# setTopicItemHeader()

You can use this to customize the author name and date in the topic single screen.

Deprecated:
  • Please use the equivalent hook: topicSingleApi.setTopicItemHeader()
Example

Add a "Verified" text beside the author's name

//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.topicsApi.setTopicItemHeader( (props) => <TopicItemHeader {...props} />);
}

# setTopicToViewModelFilter(topicToViewModelFilter)

You can use it to set the callback function that can change an existing topic's view model object.

Parameters:
Name Type Description
topicToViewModelFilter TransformTopicViewModelCallback
Example
externalCodeSetup.topicsApi.setTopicToViewModelFilter((props, topic) => {
 return {
  ...props,
  topicUpvotes: 30
 };
})