Members
Methods
# setCourseHeaderDetails(CourseHeaderDetails)
You can use this hook to customize the Course Status component of the LearnDash course details if a course is not yet in progress. For example, you can use this to change the course's preview video, featured image or the "Course Includes" details.
Parameters:
Name | Type | Description |
---|---|---|
CourseHeaderDetails |
React.ComponentType.<CourseHeaderDetailsProps> |
Example
Implement default BB component
...
import Icon from "@src/components/Icon";
import AppImageBackground from "@src/components/AppImageBackground";
import { CourseVideo } from "@src/components/Course/CourseStatus";
export const applyCustomCode = externalCodeSetup => {
externalCodeSetup.courseSingleApi.setCourseHeaderDetails(props => {
const {
courseVM,
global,
labels,
colors,
t,
lCount,
tCount,
qCount,
navigation,
learndashCourseParticipants
} = props;
const size = 26;
const shouldShowParticipants = !!+learndashCourseParticipants;
const showIncludesTitle = lCount !== 0 ||
tCount !== 0 ||
qCount !== 0 ||
courseVM.certificate?.available
const styles = StyleSheet.create({
videoContainer: {
borderTopLeftRadius: 10,
borderTopRightRadius: 10,
overflow: "hidden",
...(!shouldShowParticipants && {
borderBottomLeftRadius: 10,
borderBottomRightRadius: 10
})
},
avatar: {
width: size + 4,
height: size + 4,
borderRadius: size + 4 / 2,
borderColor: colors.bodyFrontBg,
borderWidth: 2
},
enrolledText: {
...global.textAlt,
left: -8,
fontSize: 13
},
courseDetailsText: {
...global.itemText,
marginBottom: 10,
marginLeft: 14
}
});
return (
<>
<View
style={styles.videoContainer}
>
{!!!courseVM.videoUrl &&
!!courseVM.featuredUrl && (
<AppImageBackground
source={{ uri: courseVM.featuredUrl }}
style={{ width: "100%", height: 200 }}
/>
)}
{typeof courseVM.videoUrl === "string" &&
courseVM.videoUrl !== "" && (
<CourseVideo
url={courseVM.videoUrl}
feature={courseVM.featuredUrl}
global={global}
colors={colors}
navigation={navigation}
/>
)}
</View>
<View
style={{
paddingHorizontal: 16,
...(shouldShowParticipants && { paddingVertical: 12 })
}}
>
{courseVM?.members?.length > 0 &&
shouldShowParticipants && (
<View style={{ ...global.row }}>
{courseVM.members
.map(x => x.member_rest?.avatar?.thumb || "")
.map((url, index) => (
<AppAvatar
key={url}
style={[styles.avatar, { left: index === 0 ? 0 : -10 * index, zIndex: index }]}
borderRadius={size / 2}
size={size}
source={{
uri: url
}}
/>
))}
<Text style={styles.enrolledText}>
{courseVM.enrolledMembers > 2
? `+${courseVM.enrolledMembers - 2} ${t("course:enrolled")}`
: ""}
</Text>
</View>
)}
{showIncludesTitle && (
<Text
style={[
global.courseIncludesTitle,
{
marginBottom: 15,
marginTop: courseVM?.members?.length > 0 ? 20 : 0
}
]}
>
{t("course:includesTitle", { label: labels.course })}
</Text>
)}
{lCount !== 0 && (
<View style={{ flexDirection: "row" }}>
<Icon
webIcon={"IconAndroidGroup"}
tintColor={colors.descLightTextColor}
icon={require("@src/assets/img/book.png")}
/>
<Text
style={styles.courseDetailsText}
>
{lCount === ""
? " " + labels.lessons
: t("course:itemCount", {
count: lCount,
slabel: labels.lesson,
plabel: labels.lessons
})}
</Text>
</View>
)}
{tCount !== 0 && (
<View style={{ flexDirection: "row" }}>
<Icon
webIcon={"IconAndroidGroup"}
tintColor={colors.descLightTextColor}
icon={require("@src/assets/img/topic.png")}
/>
<Text
style={styles.courseDetailsText}
>
{tCount === ""
? " " + labels.topics
: t("course:itemCount", {
count: tCount,
slabel: labels.topic,
plabel: labels.topics
})}
</Text>
</View>
)}
{qCount !== 0 && (
<View style={{ flexDirection: "row" }}>
<Icon
webIcon={"IconAndroidGroup"}
tintColor={colors.descLightTextColor}
icon={require("@src/assets/img/quiz.png")}
/>
<Text
style={styles.courseDetailsText}
>
{qCount === ""
? " " + labels.quiz
: t("course:itemCount", {
count: qCount,
slabel: labels.quiz,
plabel: labels.quizzes
})}
</Text>
</View>
)}
{courseVM.certificate?.available && (
<View style={{ flexDirection: "row" }}>
<Icon
webIcon={"IconAndroidGroup"}
tintColor={colors.descLightTextColor}
icon={require("@src/assets/img/small-certificate.png")}
/>
<Text
style={styles.courseDetailsText}
>
{t("course:certificate", { label: labels.course })}
</Text>
</View>
)}
</View>
</>
);
})
}
# setCourseMaterialsComponent(CourseMaterialsComponent)
Sets a component that overrides the content added in course materials field found in BuddyBoss site > Learndash LMS > Courses > [Selected Course] > Settings.
Parameters:
Name | Type | Description |
---|---|---|
CourseMaterialsComponent |
React.ComponentType.<CourseMaterialsProps> |
Example
Use a different component depending on the logged-in user
//In custom_code/components/CourseMaterial.js
import React, { useState } from 'react';
import { View, Text, Alert, Dimensions } from 'react-native';
import { useSelector } from "react-redux";
import HTML from "react-native-render-html";
import { WebView } from 'react-native-webview';
import {
documentFilename,
isInExtensionList,
htmlHandleClicks
} from "@src/utils";
import { previewDocument } from "@src/utils/previewFiles";
const ent = require("ent");
const DEVICE_WIDTH = Dimensions.get("window").width;
const CourseMaterial = (props) => {
const { tagsStyles, materialsStyles, baseFontStyle, materials, navigation, t } = props;
const user = useSelector((state) => state.user.userObject);
const online = useSelector((state) => state.network.online);
const [opening, setOpening] = useState(false);
const [progress, setProgress] = useState(0);
const onLinkPress = async (evt, url) => {
//Get auth from available screen props
const { auth } = navigation.getScreenProps();
//Checks if extension added in url is supported for preview
if (isInExtensionList(url)) {
return await previewDocument({
url,
localName: documentFilename(url),
token: auth.token,
t,
beginCallback: () => {
setProgress(0);
setOpening(true);
},
progressCallback: setProgress,
doneCallback: localFile => {
setTimeout(() => {
setOpening(false);
}, 1000);
},
failCallback: () => {
setTimeout(() => {
setOpening(false);
}, 1000);
}
});
}
if (online) {
//If online, handle urls using app's helper function
return htmlHandleClicks(navigation, false)(evt, url);
}
Alert.alert(t("course:materialRequiresConnection"));
};
return user.id === 1 ?
//Use a new component for SpecialUserComponent
<>
<Text style={{marginBottom: 20}}> Welcome back {user.name}! We prepared the materials for you. </Text>
<WebView source={{ uri: 'https://link-to-best-materials.com' }} style={{ width: "auto", height: 300 }} />
</>
:
//Use app's default component for RegularUserComponent
<HTML
tagsStyles={{ ...tagsStyles, ...materialsStyles }}
baseFontStyle={baseFontStyle(15)}
html={ent.decode(materials)}
imagesMaxWidth={DEVICE_WIDTH - 32}
onLinkPress={onLinkPress}
/>
}
export default CourseMaterial;
//In custom_code/index.js
...
import CourseMaterial from "./components/CourseMaterial";
export const applyCustomCode = externalCodeSetup => {
externalCodeSetup.courseSingleApi.setCourseMaterialsComponent((props) => <CourseMaterial {...props} /> )
}
# setCourseTitleComponent(CourseTitleComponent)
You can use this to replace the course title component. For example, you can use this to change the title's font size.
Parameters:
Name | Type | Description |
---|---|---|
CourseTitleComponent |
React.ComponentType.<CourseTitleProps> |
Example
Change the title's font size depending on the course title
import Animated from "react-native-reanimated";
export const applyCustomCode = externalCodeSetup => {
externalCodeSetup.courseSingleApi.setCourseTitleComponent((props) => {
const { course, global, colors, styles, lightStyle, opacity } = props;
let fontSize = 34;
if (course.title === "React Native") {
fontSize = 20;
}
return <View>
<Animated.Text
style={[
global.iosStyleScreenTitle,
styles.title,
lightStyle ? { color: "white" } : { color: colors.textColor },
{ opacity, fontSize: fontSize }
]}
>
{course.title}
</Animated.Text>
</View>
});
}
# setFilterIncomingCourseProps(filterIncomingCourseProps)
Sets the callback function filterIncomingCourseProps
used for filtering props that are coming into Courses Single Screen component
Parameters:
Name | Type | Description |
---|---|---|
filterIncomingCourseProps |
CoursePropsFilterCallback |
Example
Add a date object to incoming course props
externalCodeSetup.courseSingleApi.setFilterIncomingCourseProps((props) => {
return {
...props,
date: new Date()
}
});
# setHeaderAuthorRenderer(renderer)
Sets component for overriding the default course author component.
Parameters:
Name | Type | Description |
---|---|---|
renderer |
React.ComponentType.<HeaderAuthorComponentProps> |
Example
Add more details besides the author name
//In custom_code/components/CourseAuthor.js
import React from 'react';
import { View, Text } from 'react-native';
import AppAvatar from "@src/components/AppAvatar";
import AppTouchableOpacity from "@src/components/AppTouchableOpacity";
import { getBestImageVariant } from "@src/utils/CCDataUtil";
import { withUserClickHandler } from "@src/components/hocs/withUserClickHandler"; //Use BuddyBoss HOC for easier navigation
const CourseAuthor = (props) => {
const { user, global, lightStyle, toUserBasedOnSettings } = props;
//Able navigate to profile of `user` because of wrapping component with HOC
const boundCallback = React.useMemo(
() => toUserBasedOnSettings.bind(null, null, user),
[user]
);
//Use BuddyBoss getBestImageVariant helper function to get best image size for the component
const userAvatarUrl = React.useMemo(
() =>
!!user
? user.avatar_urls
? getBestImageVariant(user.avatar_urls, 96)
: user?.avatar?.thumb
: "",
[user]
);
return <View style={[global.row]}>
<AppTouchableOpacity onPress={boundCallback}>
<View>
{user && (
<AppAvatar
size={26}
name={user.name}
source={{
uri: userAvatarUrl
}}
/>
)}
</View>
</AppTouchableOpacity>
<View style={{ marginLeft: 8 }}>
{user && (
<>
<Text
style={[
global.itemAuthorName,
{ marginBottom: 1 },
lightStyle ? { color: "white" } : {}
]}
>
{user?.name}
</Text>
<Text> {user?.member_rest.user_email} </Text>
</>
)}
</View>
</View>
}
export default withUserClickHandler(CourseAuthor);
//In custom_code/index.js
...
import CourseAuthor from "./components/CourseAuthor";
export const applyCustomCode = externalCodeSetup => {
externalCodeSetup.courseSingleApi.setHeaderAuthorRenderer((props) => <CourseAuthor {...props} />)
}
# setIsCategoryTagsHidden(isCategoryTagsHidden)
You can use this to show or hide the category tags in the course single screen.
Parameters:
Name | Type | Description |
---|---|---|
isCategoryTagsHidden |
IsCategoryTagsHiddenCallback |
Example
externalCodeSetup.courseSingleApi.setIsCategoryTagsHidden((course) => {
return false;
})
# setIsCourseDescriptionHidden(isCourseDescriptionHidden)
You can use this hook to show or hide the course description component.
Parameters:
Name | Type | Description |
---|---|---|
isCourseDescriptionHidden |
IsCourseDescriptionHiddenCallback |
Example
Hide the course description depending on the course title
externalCodeSetup.courseSingleApi.setIsCourseDescriptionHidden((course) => {
if (course.title == "React Native"){
return true;
}
return false;
})
# setIsCourseStatusHidden(isCourseStatusHidden)
You can use this hook to show or hide the course status component.
Parameters:
Name | Type | Description |
---|---|---|
isCourseStatusHidden |
IsCourseStatusHiddenCallback |
Example
Hide the course status component depending on the course title
externalCodeSetup.courseSingleApi.setIsCourseStatusHidden((course, hasStarted, courseTree) => {
if (course.title == "React Native"){
return true;
}
return false;
})
# setTransformCourseActionButtons(transformCourseActionButtons)
You can transform the default course action buttons which are starting, buying or continuing a course by replacing it with your preferred action buttons.
Parameters:
Name | Type | Description |
---|---|---|
transformCourseActionButtons |
TransformCourseActionButtonsCallback |
Example
Add more components for course action
import CourseActionButton from "@src/components/Course/CourseActionButton";
export const applyCustomCode = externalCodeSetup => {
externalCodeSetup.courseSingleApi.setTransformCourseActionButtons((
CourseActionBtn,
courseVM,
t,
colors,
global,
products,
navigation,
startCourse,
continueCourse,
priceComponentRender) => {
const Info =
<View style={{ paddingHorizontal: 20, paddingVertical: 10 }}>
<Text>To continue the course, tap the button below</Text>
</View>
const Redirect =
<View style={{
paddingHorizontal: 20,
paddingVertical: 16,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between"
}}>
<CourseActionButton
title={"Go to Courses screen"}
onPress={() => navigation.navigate("CoursesScreen")}
style={{backgroundColor: "cyan"}}
/>
</View>
return [Info, CourseActionBtn, Redirect];
})
}