import * as React from "react";
import {TCourseViewModel, TTranslationFunction, TQuizViewModel} from "./types";
import {NavigationService} from "./types";
import {NavigationProp} from "@react-navigation/native";
export const API_NAME = "courseSingleApi";
/**
* CoursePropsFilterCallback
*/
type CoursePropsFilterCallback = {
/**
* Course props
*/
oldProps: Record<any, any>
};
/**
* CourseStatusHiddenCallback
*/
type CourseStatusHiddenCallback = {
course: TCourseViewModel,
/**
* Returns `true` if course progress has started
*/
hasStarted: boolean,
/**
* Course structure
*/
courseTree: Record<any, any>
};
/**
* CourseDescriptionHiddenCallback
*/
type CourseDescriptionHiddenCallback = {
course: TCourseViewModel
};
/**
* CategoryTagsHiddenCallback
*/
type CategoryTagsHiddenCallback = {
course: TCourseViewModel
};
/**
* TransformCourseActionButtonsCallback
*/
type TransformCourseActionButtonsCallback = {
CourseActionBtn: JSX.Element,
course: TCourseViewModel,
t: Function,
/**
* App global style
*/
global: Record<any, any>,
/**
* App colors
*/
colors: Record<any, any>,
/**
* Products associated to the course
*/
products: Record<any, any>[],
navigation: NavigationService,
/**
* Starts the course when called
*/
startCourse: Function,
/**
* Continues the course when called for a course in progress
*/
continueCourse: Function,
/**
* Renders the price component when called
*/
priceComponentRender: Function
};
/**
* CourseMaterialsProps
*/
type CourseMaterialsProps = {
/**
* App global style
*/
global: Record<any, any>,
/**
* App colors
*/
colors: Record<any, any>,
t: TTranslationFunction,
/**
* Contents of course materials field
*/
materials: string,
/**
* Default styles for different tags
*/
tagsStyles: Record<any, any>,
/**
* Default styles for course materials
*/
materialsStyles: Record<any, any>,
/**
* Function to set a font style
*/
baseFontStyle: Function,
navigation: NavigationProp<any, any>,
/**
* Helper function that handles link press for the HTML component
*/
onLinkPress: Function
};
/**
* HeaderAuthorComponentProps
*/
type HeaderAuthorComponentProps = {
/**
* Author details
*/
user: Record<any, any>,
/**
* Returns `true` if course is using cover image
*/
lightStyle: boolean,
/**
* App global style
*/
global: Record<any, any>,
/**
* User id of course author
*/
userId: number,
onUserProfilePress: Function,
loadingInProgress: Function,
t: TTranslationFunction
};
/**
* CourseTitleProps
*/
type CourseTitleProps = {
course: TCourseViewModel,
/**
* App global style
*/
_globalStyles: Record<any, any>,
/**
* App colors
*/
colors: Record<any, any>,
/**
* Returns `true` if course is using cover image
*/
lightStyle: boolean,
/**
* Default title styles
*/
styles: Record<any, any>,
/**
* Returns an object generated by React Native Animated
*/
animatedOpacity: Record<any, any>
};
/**
* CourseHeaderDetailsProps
*/
type CourseHeaderDetailsProps = {
courseVM: TCourseViewModel,
/**
* App global style
*/
global: Record<any, any>,
/**
* App colors
*/
colors: Record<any, any>,
labels: Record<any, any>,
t: TTranslationFunction,
/**
* Lessons count
*/
lCount: number,
/**
* Topics count
*/
tCount: number,
/**
* Quizzes count
*/
qCount: number,
/**
* Can be used to determine if participants should be shown in the component
*/
learndashCourseParticipants: number,
navigation: NavigationProp<any, any>,
/**
* Returns `true` if participants should be shown
*/
shouldShowParticipants: Boolean,
/**
* Returns `true` if course image should be shown
*/
shouldShowImageBackground: Boolean,
/**
* Returns `true` if course video should be shown
*/
shouldShowVideo: Boolean,
/**
* Returns `true` if "Course Includes" label should be shown
*/
shouldShowCourseIncludesTitle: Boolean,
/**
* Returns `true` if lessons label should be shown
*/
shouldShowLessonsText: Boolean,
/**
* Returns `true` if topics label should be shown
*/
shouldShowTopicsText: Boolean,
/**
* Returns `true` if quizzes label should be shown
*/
shouldShowQuizzesText: Boolean,
/**
* Returns `true` if course certificate label should be shown
*/
shouldShowCertificatesText: Boolean,
/**
* Returns the lessons label
*/
lessonsText: String,
/**
* Returns the topics label
*/
topicsText: String,
/**
* Returns the quizzes label
*/
quizzesText: String,
/**
* Returns the enrolled label
*/
enrolledText: String,
/**
* Default styles
*/
styles: Record<any, any>
};
/**
* DownloadProgressComponentProps
*/
type DownloadProgressComponentProps = {
/**
* Cancels the download progress
*/
onPress: Function,
/**
* App colors
*/
colors: Record<any, any>,
/**
* Current progress of download
*/
progress: number,
/**
* Size of progress circle
*/
size: number,
/**
* Returns `true` if component should use lightStyle
*/
lightStyle: boolean,
/**
* Default hitSlop for component
*/
hitSlop: Record<string, number>,
/**
* Default container style
*/
containerStyle: Record<any, any>
};
/**
* CourseHeaderRightProps
*/
type CourseHeaderRightProps = {
/**
* App global style
*/
global: Record<any, any>,
/**
* App colors
*/
colors: Record<any, any>,
t: TTranslationFunction,
courseVM: TCourseViewModel,
/**
* Returns `true` if course should be open for guest user
*/
isShowingOpenCourseForGuestUser: boolean,
/**
* Can be used to indicate what DownloadStatusIndicator component style should use
*/
contentStyle: Record<any, any>
};
/*
*
* DownloadIconProps
*/
type DownloadIconProps = {
/**
* Returns the status of the course being downloaded. Status includes `Complete`, `Pending`, `Failed`, etc.
*/
status: string,
/**
* Starts the download process
*/
start: Function,
/**
* Cancels the download process
*/
cancel: Function,
/**
* Opens a modal that contains actions available for the downloaded
*/
openOptions: Function,
/**
* Icon size
*/
size: number,
/**
* Returns `true` if component should use lightStyle
*/
lightStyle: boolean,
/**
* App colors
*/
colors: Record<any, any>,
/**
* Default color of icon
*/
idleIconColor: string,
/**
* Default hitSlop for component
*/
hitSlop: Record<string, number>
};
/**
* CourseStartedHeaderDetailsProps
*/
type CourseStartedHeaderDetailsProps = {
/**
* App global style
*/
global: Record<any, any>,
/**
* App colors
*/
colors: Record<any, any>,
t: TTranslationFunction,
courseVM: TCourseViewModel,
/**
* Default styles for the header component
*/
styles: Record<any, any>,
/**
* Helper function for parsing dates
*/
formatDateFunc: Function
};
/**
* CourseContentTitleProps
*/
type CourseContentTitleProps = {
/**
* App global style
*/
global: Record<any, any>,
t: TTranslationFunction,
labels: Record<any, any>,
/**
* Returns `true` if course content is collapsable
*/
isCollapsable: boolean,
/**
* Returns `true` if collapsable items are already expanded
*/
expanded: boolean,
/**
* Hides all the collapsable items
*/
hideAll: Function,
/**
* Expands all the collapsable items
*/
expand: Function
};
/**
* CourseSectionItemProps
*/
type CourseSectionItemProps = {
/**
* Data about item to be rendered
*/
item: Record<any, any>,
/**
* Returns progression of the item
*/
progression: number,
/**
* App global style
*/
global: Record<any, any>,
/**
* App colors
*/
colors: Record<any, any>,
/**
* Returns `true` if navigation to the item is allowed
*/
allowNavigation: boolean,
/**
* Navigates to the lesson; topic or quiz
*/
onItemPress: Function,
/**
* Returns number of topics and quizzes for the top-level item.
* This can be used when determining if the lesson should be expandable or not.
*/
count: number,
/**
* Result of using require() when fetching the appropriate icon for each item
*/
icon: number,
/**
* Toggle or expand the item. This can be called when creating a custom IconComponent
*/
toggleExpand: Function,
/**
* Returs the type of section item
*/
blockType: string | undefined,
/**
* Calls an alert component which shows an error message
*/
showAlertMessage: Function,
/**
* Default component for displaying the steps label in the item
*/
StepsComponent: JSX.Element,
/**
* Default component for displaying the left-most icon in the item
*/
IconComponent: JSX.Element
};
/**
* QuizSectionTitleProps
*/
type QuizSectionTitleProps = {
/**
* App global style
*/
global: Record<any, any>,
t: TTranslationFunction
};
/**
* CourseDescriptionSectionTitleProps
*/
type CourseDescriptionSectionTitleProps = {
/**
* Returns `true` if there are blocks to be rendered natively
*/
shouldRenderBlocks: boolean,
courseVM: TCourseViewModel,
/**
* App global style
*/
global: Record<any, any>,
t: TTranslationFunction,
labels: Record<any, any>
};
/**
* CourseMaterialsSectionTitleProps
*/
type CourseMaterialsSectionTitleProps = {
/**
* App global style
*/
global: Record<any, any>,
t: TTranslationFunction,
labels: Record<any, any>
};
/**
* CourseDescriptionReadMoreComponentProps
*/
type CourseDescriptionReadMoreComponentProps = {
courseVM: TCourseViewModel,
/**
* Opens the desription modal
*/
openDescriptionModal: Function,
/**
* App global style
*/
global: Record<any, any>,
t: TTranslationFunction
};
/**
* WebViewOfflineComponentProps
*/
type WebViewOfflineComponentProps = {
/**
* App global style
*/
global: Record<any, any>,
t: TTranslationFunction,
/**
* Default container style
*/
containerStyle: Record<any, any>,
/**
* Default style of EmptyList component
*/
emptyListStyle: Record<any, any>
};
/**
* WebViewDescriptionComponentProps
*/
type WebViewDescriptionComponentProps = {
/**
* Returns `true` if device is connected to an internet network
*/
online: boolean,
t: TTranslationFunction,
/**
* The default function used for determining how to handle webview requests.
*/
onShouldStartLoadWithRequest: Function,
/**
* Default height limit
*/
heightLimit: number,
/**
* Contains data of the web site to be loaded in the webview
*/
source: Record<any, any>,
/**
* App global style
*/
global: Record<any, any>,
/**
* App colors
*/
colors: Record<any, any>,
/**
* Modal which shows up when the "Read More" component is pressed
*/
ModalHeaderComponent: React.FC
};
/**
* CourseQuizItemProps
*/
type CourseQuizItemProps = {
item: TQuizViewModel,
style: Record<any, any>,
/**
* App global style
*/
global: Record<any, any>,
/**
* App colors
*/
colors: Record<any, any>,
/**
* Can be used to determine if actual progress should be shown or if item should be indicated as completed
*/
skipProgress: boolean,
/**
* Navigates to the quiz
*/
onPress: Function,
/**
* Returns `true` if navigation to the item is allowed
*/
allowNavigation: boolean,
/**
* Default component for displaying the left-most icon in the item
*/
IconComponent: JSX.Element
};
/**
* LessonProgressStepsProps
*/
type LessonProgressStepsProps = {
t: TTranslationFunction,
/**
* Item details
*/
item: Record<any, any>,
/**
* App global style
*/
global: Record<any, any>,
/**
* App colors
*/
colors: Record<any, any>,
/**
* Number of items inside the item
*/
count: number,
course: TCourseViewModel,
/**
* Describes the type of item being generated
*/
blockType: string,
/**
* Number of completed steps inside the lesson
*/
completedSteps: number
};
/**
* @class
* Single Course Screen Hooks.
* Instance name: courseSingleApi
Hooks to modify the individual courses’ pages.
* @example
* externalCodeSetup.courseSingleApi.METHOD_NAME
*/
export class CourseSingleApi {
/**
* @deprecated
*/
// show number of lessons on course single screen beside the lesson title
setShowLessonsCount = () => (this.showLessonsCount = true);
/**
* @deprecated
*/
// show number of quizzes on course single screen beside the quizzes title
setShowQuizzesCount = () => (this.showQuizzesCount = true);
/**
* @deprecated
* @private
* @property {boolean} showLessonsCount
*/
showLessonsCount = true;
/**
* @deprecated
* @private
* @property {boolean} showQuizzesCount
*/
showQuizzesCount = true;
headerAuthorRenderer: React.ComponentType<
HeaderAuthorComponentProps
> | null = null;
/**
* Sets component for overriding the default course author component.
* @method
* @param {React.ComponentType<HeaderAuthorComponentProps>} renderer
* @example <caption>Add more details besides the author name</caption>
*
* //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} />)
* }
*/
setHeaderAuthorRenderer = (
renderer: React.ComponentType<HeaderAuthorComponentProps> | null
) => {
this.headerAuthorRenderer = renderer;
};
filterIncomingCourseProps = (props: Record<any, any>) => props;
/**
* Sets the callback function `filterIncomingCourseProps` used for filtering props that are coming into Courses Single Screen component
* @param {CoursePropsFilterCallback} filterIncomingCourseProps
* @method
* @example <caption>Add a date object to incoming course props</caption>
* externalCodeSetup.courseSingleApi.setFilterIncomingCourseProps((props) => {
* return {
* ...props,
* date: new Date()
* }
* });
*/
setFilterIncomingCourseProps = (
filterIncomingCourseProps: (
filterIncomingCourseProps: Record<any, any>
) => Record<any, any>
) => {
this.filterIncomingCourseProps = filterIncomingCourseProps;
};
isCategoryTagsHidden = (course: TCourseViewModel) => false;
/**
* You can use this to show or hide the category tags in the course single screen.
* @param {CategoryTagsHiddenCallback} CategoryTagsHiddenCallback
* @method
* @example
*
* externalCodeSetup.courseSingleApi.setIsCategoryTagsHidden((course) => {
* return false;
* })
*/
setIsCategoryTagsHidden = (
isCategoryTagsHidden: (course: TCourseViewModel) => boolean
) => {
this.isCategoryTagsHidden = isCategoryTagsHidden;
};
CourseMaterialsComponent: React.ComponentType<
CourseMaterialsProps
> | null = null;
/**
* Sets a component that overrides the content added in course materials field found in BuddyBoss site > Learndash LMS > Courses > [Selected Course] > Settings.
* @method
* @param {React.ComponentType<CourseMaterialsProps>} CourseMaterialsComponent
* @example <caption>Use a different component depending on the logged-in user</caption>
*
* ...
*
* const DEVICE_WIDTH = Dimensions.get("window").width;
* import { useSelector } from "react-redux";
* import WebView from "react-native-webview";
* const ent = require("ent");
*
* export const applyCustomCode = (externalCodeSetup) => {
*
* externalCodeSetup.courseSingleApi.setCourseMaterialsComponent(({
* tagsStyles,
* materialsStyles,
* baseFontStyle,
* materials,
* onLinkPress,
* global
* }) => {
*
* const CourseMaterial = () => {
* const user = useSelector((state) => state.user.userObject);
*
* return user.id === 1 ?
* <>
* <Text style={{ marginBottom: 20 }}> Welcome back {user.name}! We prepared the materials for you. </Text>
* <WebView source={{ uri: 'https://buddyboss.com' }} style={{ width: "auto", height: 300 }} />
* </>
* :
* <View style={global.courseRoundBox}>
* {typeof materials === "string" && (
* <View style={{ paddingHorizontal: 15 }}>
* <HTML
* tagsStyles={{ ...tagsStyles, ...materialsStyles }}
* baseFontStyle={baseFontStyle(15)}
* html={ent.decode(materials)}
* imagesMaxWidth={DEVICE_WIDTH - 32}
* onLinkPress={onLinkPress}
* />
* </View>
* )}
* </View>
*
* }
*
* return <CourseMaterial />
*
* })
* }
*/
setCourseMaterialsComponent = (
CourseMaterialsComponent: React.ComponentType<CourseMaterialsProps> | null
) => {
this.CourseMaterialsComponent = CourseMaterialsComponent;
};
transformCourseActionButtons: (
CourseActionBtn: JSX.Element,
course: TCourseViewModel,
t: Function,
colors: Record<any, any>,
global: Record<any, any>,
products: Record<any, any>[],
navigation: NavigationService,
startCourse: Function,
continueCourse: Function,
priceComponentRender: Function
) => JSX.Element = CourseActionBtn => CourseActionBtn;
/**
* You can transform the default course action buttons which are starting, buying or continuing a course by replacing it with your preferred action buttons.
* @param {TransformCourseActionButtonsCallback} transformCourseActionButtons
* @method
* @example <caption> Add more components for course action </caption>
*
* import CourseActionButton from "@src/components/Course/CourseActionButton";
* export const applyCustomCode = (externalCodeSetup) => {
* externalCodeSetup.courseSingleApi.setTransformCourseActionButtons((
* CourseActionBtn,
* course,
* t,
* colors,
* global,
* products,
* navigation,
* startCourse,
* continueCourse,
* priceComponentRender) => {
*
* return <>
* <View style={{ paddingHorizontal: 20, paddingVertical: 10 }}>
* <Text>To continue the course, tap the button below++</Text>
* </View>
*
* <View style={{
* paddingHorizontal: 20,
* paddingVertical: 16,
* flexDirection: "row",
* alignItems: "center",
* justifyContent: "space-between"
* }}>
* {CourseActionBtn}
* <CourseActionButton
* title={"Go to Courses screen"}
* onPress={() => navigation.navigate("CoursesScreen")}
* style={{ backgroundColor: "cyan" }}
* />
* </View>
* </>
* })
*
* }
*/
setTransformCourseActionButtons = (
transformCourseActionButtons: (
CourseActionBtn: JSX.Element,
course: TCourseViewModel,
t: Function,
colors: Record<any, any>,
global: Record<any, any>,
products: Record<any, any>[],
navigation: NavigationService,
startCourse: Function,
continueCourse: Function,
priceComponentRender: Function
) => JSX.Element
) => {
this.transformCourseActionButtons = transformCourseActionButtons;
};
isCourseStatusHidden = (
course: TCourseViewModel,
hasStarted: boolean,
courseTree: Record<any, any>
) => false;
/**
* You can use this hook to show or hide the course status component.
* @param {CourseStatusHiddenCallback} CourseStatusHiddenCallback
* @method
* @example <caption> Hide the course status component depending on the course title </caption>
* externalCodeSetup.courseSingleApi.setIsCourseStatusHidden((course, hasStarted, courseTree) => {
* if (course.title == "React Native"){
* return true;
* }
* return false;
* })
*/
setIsCourseStatusHidden = (
isCourseStatusHidden: (
course: TCourseViewModel,
hasStarted: boolean,
courseTree: Record<any, any>
) => boolean
) => {
this.isCourseStatusHidden = isCourseStatusHidden;
};
isCourseDescriptionHidden = (course: TCourseViewModel) => false;
/**
* You can use this hook to show or hide the course description component.
* @param {CourseDescriptionHiddenCallback} CourseDescriptionHiddenCallback
* @method
* @example <caption> Hide the course description depending on the course title </caption>
* externalCodeSetup.courseSingleApi.setIsCourseDescriptionHidden((course) => {
* if (course.title == "React Native"){
* return true;
* }
* return false;
* })
*/
setIsCourseDescriptionHidden = (
isCourseDescriptionHidden: (course: TCourseViewModel) => boolean
) => {
this.isCourseDescriptionHidden = isCourseDescriptionHidden;
};
CourseTitleComponent: React.ComponentType<CourseTitleProps> | null = null;
/**
* You can use this to replace the course title component.
* For example, you can use this to change the title's font size.
* @method
* @param {React.ComponentType<CourseTitleProps>} CourseTitleComponent
* @example
*
* import React from "react";
* import Animated from "react-native-reanimated";
* import {View} from "react-native";
*
* export const applyCustomCode = externalCodeSetup => {
* externalCodeSetup.courseSingleApi.setCourseTitleComponent(props => {
* const {
* course,
* _globalStyles,
* colors,
* styles,
* lightStyle,
* animatedOpacity
* } = props;
*
* return (
* <View>
* <Animated.Text
* style={[
* _globalStyles.iosStyleScreenTitle,
* styles.title,
* lightStyle ? {color: "white"} : {color: colors.textColor},
* animatedOpacity
* ]}
* >
* {course.title}
* </Animated.Text>
* </View>
* );
* });
* };
*/
setCourseTitleComponent = (
CourseTitleComponent: React.ComponentType<CourseTitleProps> | null
) => {
this.CourseTitleComponent = CourseTitleComponent;
};
CourseHeaderDetails: React.ComponentType<
CourseHeaderDetailsProps
> | null = null;
/**
* 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.
* @method
* @param {React.ComponentType<CourseHeaderDetailsProps>} CourseHeaderDetails
* @example
*
* import Icon from "@src/components/Icon";
* import AppImageBackground from "@src/components/AppImageBackground";
* import {CourseVideo} from "@src/components/Course/CourseStatus";
* import AppAvatar from "@src/components/AppAvatar";
*
* export const applyCustomCode = (externalCodeSetup) => {
* externalCodeSetup.courseSingleApi.setCourseHeaderDetails(props => {
* const {
* courseVM,
* global,
* labels,
* colors,
* t,
* navigation,
* shouldShowParticipants,
* shouldShowImageBackground,
* shouldShowVideo,
* shouldShowCourseIncludesTitle,
* shouldShowLessonsText,
* shouldShowTopicsText,
* shouldShowQuizzesText,
* shouldShowCertificatesText,
* lessonsText,
* topicsText,
* quizzesText,
* enrolledText,
* styles
* } = props;
*
* const size = 26;
*
* return (
* <>
* <View style={styles.mediaContainer}>
* {shouldShowImageBackground && (
* <AppImageBackground
* source={{uri: courseVM.featuredUrl}}
* style={{width: "100%", height: 200}}
* />
* )}
* {shouldShowVideo && (
* <CourseVideo
* url={courseVM.videoUrl}
* feature={courseVM.featuredUrl}
* global={global}
* colors={colors}
* navigation={navigation}
* />
* )}
* </View>
*
* <View style={styles.container}>
* {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}>{enrolledText}</Text>
* </View>
* )}
* {shouldShowCourseIncludesTitle && (
* <Text style={styles.courseIncludesTitle}>
* {t("course:includesTitle", {label: labels.course})}
* </Text>
* )}
* {shouldShowLessonsText && (
* <View style={{flexDirection: "row", marginBottom: 5}}>
* <Icon
* size={30}
* webIcon={"IconAndroidGroup"}
* tintColor={colors.descLightTextColor}
* icon={{fontIconName: "book", weight: 400}}
* />
* <Text style={styles.courseIncludesText}>{lessonsText}</Text>
* </View>
* )}
* {shouldShowTopicsText && (
* <View style={{flexDirection: "row", marginBottom: 5, marginLeft: 5}}>
* <Icon
* size={24}
* webIcon={"IconAndroidGroup"}
* tintColor={colors.descLightTextColor}
* icon={{fontIconName: "text", weight: 500}}
* />
* <Text style={styles.courseIncludesText}>{topicsText}</Text>
* </View>
* )}
* {shouldShowQuizzesText && (
* <View style={{flexDirection: "row", marginLeft: 5, marginBottom: 5}}>
* <Icon
* size={24}
* webIcon={"IconAndroidGroup"}
* tintColor={colors.descLightTextColor}
* icon={{fontIconName: "question", weight: 100}}
* />
* <Text style={styles.courseIncludesText}>{quizzesText}</Text>
* </View>
* )}
* {shouldShowCertificatesText && (
* <View style={{flexDirection: "row", marginLeft: 5}}>
* <Icon
* size={26}
* webIcon={"IconAndroidGroup"}
* tintColor={colors.descLightTextColor}
* icon={{fontIconName: "certificate", weight: 400}}
* />
* <Text style={styles.courseIncludesText}>
* {t("course:certificate", {label: labels.course})} asdfasdf
* </Text>
* </View>
* )}
* </View>
* </>
* );
* });
* }
*
*/
setCourseHeaderDetails = (
CourseHeaderDetails: React.ComponentType<CourseHeaderDetailsProps> | null
) => {
this.CourseHeaderDetails = CourseHeaderDetails;
};
HeaderRightComponent: React.ComponentType<
CourseHeaderRightProps
> | null = null;
/**
* You can use this hook to customize the component on the top right corner of the CourseSingleScreen which the download icon usually occupies.
* @method
* @param {React.ComponentType<CourseHeaderRightProps>} HeaderRightComponent
* @example
* ...
*
* import AuthWrapper from "@src/components/AuthWrapper";
* import DownloadStatusIndicator from "@src/components/Downloader/DownloadStatusIndicator";
* import StatusBarWrapper from "@src/utils/StatusBarWrapper";
* import { DownloadTypes } from "@src/services/enums/downloads";
*
* export const applyCustomCode = (externalCodeSetup: any) => {
*
* externalCodeSetup.courseSingleApi.setHeaderRightComponent(({
* global,
* colors,
* t,
* courseVM,
* isShowingOpenCourseForGuestUser,
* contentStyle
* }) => (
* courseVM.hasAccess &&
* !courseVM.offlineDisabled && (
* <AuthWrapper
* actionOnGuestLogin={
* isShowingOpenCourseForGuestUser
* ? null
* : "showAuthScreen"
* }
* >
* <DownloadStatusIndicator
* lightStyle={
* contentStyle === StatusBarWrapper.lightStyle
* }
* postId={courseVM.id}
* postType={DownloadTypes.course}
* colors={colors}
* global={global}
* size={30}
* t={t}
* iconColor={colors.linkColor}
* containerStyle={{
* marginLeft: 15
* }}
* />
* </AuthWrapper>
* )
*
* ))
* }
*/
setHeaderRightComponent = (
HeaderRightComponent: React.ComponentType<CourseHeaderRightProps> | null
) => {
this.HeaderRightComponent = HeaderRightComponent;
};
DownloadProgressComponent: React.ComponentType<
DownloadProgressComponentProps
> | null = null;
/**
* You can use this hook to customize the DownloadProgressComponent which shows up when the course is currently downloading.
* @param {React.ComponentType<DownloadProgressComponentProps>} DownloadProgressComponent
* @method
* @example
*
* ...
*
* import ProgressCircle from "react-native-progress/Circle";
* import {TouchableWithoutFeedback} from "react-native";
* import Icon from "@src/components/Icon";
* export const applyCustomCode = (externalCodeSetup) => {
*
* externalCodeSetup.courseSingleApi.setDownloadProgressComponent(({
* size,
* lightStyle,
* colors,
* onPress,
* hitSlop,
* containerStyle,
* progress
* }) => {
* const iconSize = size - 10;
* const color = lightStyle ? "#fff" : colors.highlightColor;
* const unfilledColor = lightStyle
* ? "rgba(255, 255, 255, 0.24)"
* : "rgba(0, 0, 0, 0.24)";
* return (
* <TouchableWithoutFeedback onPress={onPress} hitSlop={hitSlop}>
* <View style={containerStyle}>
* <ProgressCircle
* size={size}
* progress={progress}
* thickness={2}
* unfilledColor={unfilledColor}
* animated={true}
* borderWidth={0}
* color={color}
* />
* <Icon
* icon={{fontIconName: "stop", weight: 300}}
* tintColor={color}
* styles={{height: iconSize, width: iconSize, position: "absolute"}}
* />
* </View>
* </TouchableWithoutFeedback>
* );
* })
* }
*/
setDownloadProgressComponent = (
DownloadProgressComponent: React.ComponentType<
DownloadProgressComponentProps
> | null
) => {
this.DownloadProgressComponent = DownloadProgressComponent;
};
DownloadIcon: React.ComponentType<DownloadIconProps> | null = null;
/**
* You can use this hook to customize the DownloadIcon which can be used to download a course, delete a course, or indicate if a course has already been downloaded.
*
* @param {React.ComponentType<DownloadIconProps>} DownloadIcon
* @method
* @example
*
* import React from "react";
* import Animated from "react-native-reanimated";
* import AppTouchableWithoutFeedback from "@src/components/AppTouchableWithoutFeedback";
* import Icon from "@src/components/Icon";
* export const applyCustomCode = externalCodeSetup => {
* externalCodeSetup.courseSingleApi.setDownloadIcon(
* ({colors, idleIconColor, lightStyle, start, size, hitSlop}) => {
* const iconColor =
* idleIconColor ?? (lightStyle ? "#fff" : colors.linkColor);
* const action = start;
* return (
* <AppTouchableWithoutFeedback
* onPress={action}
* debounce={500}
* hitSlop={hitSlop}
* >
*
* <Animated.View>
* <Icon
* icon={{fontIconName: "cloud-download", weight: 400}}
* styles={{height: size, width: size}}
* tintColor={iconColor}
* />
* </Animated.View>
* </AppTouchableWithoutFeedback>
* );
* }
* );
* };
*
*/
setDownloadIcon = (
DownloadIcon: React.ComponentType<DownloadIconProps> | null
) => {
this.DownloadIcon = DownloadIcon;
};
CourseStartedHeaderDetails: React.ComponentType<
CourseStartedHeaderDetailsProps
> | null = null;
/**
* You can use this hook to customize the Course Status component of the LearnDash course details if a course is in progress or is already complete.
* @param {React.ComponentType<CourseStartedHeaderDetailsProps>} CourseStartedHeaderDetails
* @method
* @example
*
* ...
*
* import Icon from "@src/components/Icon";
* import Progress from "@src/components/Progress";
* export const applyCustomCode = (externalCodeSetup) => {
*
* externalCodeSetup.courseSingleApi.setCourseStartedHeaderDetails(({
* global,
* colors,
* t,
* courseVM,
* styles,
* formatDateFunc
* }) => <View
* style={[
* global.row,
* {
* paddingHorizontal: 20,
* paddingVertical: 16,
* flex: 1
* }
* ]}
* >
* <View style={{flex: 1, marginRight: 8}}>
* {courseVM.hasAccess ? (
* <Text style={[global.title, styles.title]}>
* {!!!courseVM.completed
* ? t("course:inProgress")
* : t("course:completed")}
* </Text>
* ) : (
* <Text style={[global.title, styles.title]}>
* {t("course:enrolled")}
* </Text>
* )}
* {courseVM.modifiedDate && (
* <Text style={[global.courseDate, {marginTop: 5}]}>
* {`${t("course:lastActivity")} ${formatDateFunc(
* courseVM.modifiedDate
* )}`}
* </Text>
* )}
* </View>
* <Text style={[global.progressLargeText, styles.progressText]}>
* {courseVM.progression + "%"}
* </Text>
* <Progress
* size={32}
* checkIcon={
* <Icon
* styles={{width: 36, height: 36}}
* icon={{fontIconName: "check", weight: 200}}
* tintColor={colors.successColor}
* />
* }
* isCompleted={!!courseVM.completed}
* progress={courseVM.progression}
* colors={{
* highlightColor: colors.highlightColor,
* bodyBg: "#e7e9ec"
* }}
* />
* </View>
* );
* }
*
*
*/
setCourseStartedHeaderDetails = (
CourseStartedHeaderDetails: React.ComponentType<
CourseStartedHeaderDetailsProps
> | null
) => {
this.CourseStartedHeaderDetails = CourseStartedHeaderDetails;
};
CourseContentTitle: React.ComponentType<
CourseContentTitleProps
> | null = null;
/**
* You can use this hook to customize the "Course Content" title and the "Expand All" function.
* @param {React.ComponentType<CourseContentTitleProps>} CourseContentTitle
* @method
* @example
*
* ...
*
* export const applyCustomCode = (externalCodeSetup) => {
* externalCodeSetup.courseSingleApi.setCourseContentTitle(({
* global,
* labels,
* isCollapsable,
* expanded,
* hideAll,
* expand,
* t
* }) => (
* <View
* style={[
* global.row,
* {flex: 1, justifyContent: "space-between", marginBottom: 9}
* ]}
* >
* <Text style={[global.courseRoundBoxTitleAbove, {marginBottom: 0}]}>
* {t("course:contentTitle", {label: labels.course})}
* </Text>
* {isCollapsable && (
* <TouchableWithoutFeedback
* hitSlop={{top: 20, right: 20, bottom: 20, left: 20}}
* onPress={() => {
* if (expanded) {
* hideAll();
* } else {
* expand();
* }
* }}
* >
* <Text style={[global.link, {fontSize: 17}]}>
* {expanded ? t("courses:collapseAll") : t("courses:expandAll")}
* </Text>
* </TouchableWithoutFeedback>
* )}
* </View>
* ));
* }
*
*/
setCourseContentTitle = (
CourseContentTitle: React.ComponentType<CourseContentTitleProps> | null
) => {
this.CourseContentTitle = CourseContentTitle;
};
CourseSectionItem: React.ComponentType<CourseSectionItemProps> | null = null;
/**
* You can use this hook to customize the Item components that are displayed as course content.
* @param {React.ComponentType<CourseSectionItemProps>} CourseSectionItem
* @method
* @example <caption> Use your own icon component instead of the default IconComponent of lessons with children</caption>
*
* ...
*
* import Icon from "@src/components/Icon";
* import LearnItem from "@src/components/Course/LearnItem";
* export const applyCustomCode = (externalCodeSetup) => {
*
* externalCodeSetup.courseSingleApi.setCourseSectionItem(props => {
* const {
* item,
* progression,
* global,
* colors,
* allowNavigation,
* onItemPress,
* StepsComponent,
* IconComponent,
* blockType,
* toggleExpand,
* count,
* showAlertMessage
* } = props;
*
* let CustomIcon = IconComponent;
* if (blockType === "lesson" && count > 0) {
* CustomIcon = (
* <TouchableOpacity onPress={toggleExpand}>
* <Icon
* tintColor={colors.descLightTextColor}
* icon={{fontIconName: "heart", weight: 400}}
* styles={{
* opacity: !allowNavigation ? 0.45 : 1,
* marginRight: 10,
* width: 25,
* height: 25
* }}
* />
* </TouchableOpacity>
* );
* }
*
* return (
* <LearnItem
* key={item.id}
* style={{
* paddingLeft: 0
* }}
* item={{
* id: item.id,
* completed: item.completed,
* title: item.title,
* progression
* }}
* global={global}
* colors={colors}
* onPress={() => {
* if (allowNavigation) {
* showAlertMessage();
* return false;
* }
* onItemPress();
* }}
* allowNavigation={allowNavigation}
* beforeProgress={StepsComponent}
* beforeLabel={CustomIcon}
* />
* );
* });
* }
*/
setCourseSectionItem = (
CourseSectionItem: React.ComponentType<CourseSectionItemProps> | null
) => {
this.CourseSectionItem = CourseSectionItem;
};
QuizSectionTitle: React.ComponentType<QuizSectionTitleProps> | null = null;
/**
* You can use this hook to customize the component that displays the "Final Quizzes" text.
* @method
* @param {React.ComponentType<QuizSectionTitleProps>} QuizSectionTitle
* @example
*
* ...
*
* externalCodeSetup.courseSingleApi.setQuizSectionTitle(({global, t}) => (
* <Text style={global.courseRoundBoxSectionTitle}>
* {t("course:finalQuizzes")}
* </Text>
* ));
*/
setQuizSectionTitle = (
QuizSectionTitle: React.ComponentType<QuizSectionTitleProps> | null
) => {
this.QuizSectionTitle = QuizSectionTitle;
};
CourseDescriptionSectionTitle: React.ComponentType<
CourseDescriptionSectionTitleProps
> | null = null;
/**
* You can use this hook to customize the component that displays the "Course Description" text.
* @method
* @param {React.ComponentType<CourseDescriptionSectionTitleProps>} CourseDescriptionSectionTitle
* @example
*
* ...
*
* export const applyCustomCode = (externalCodeSetup) => {
* externalCodeSetup.courseSingleApi.setCourseDescriptionSectionTitle(
* ({shouldRenderBlocks, courseVM, global, labels, t}) => {
* if (shouldRenderBlocks || courseVM.content) {
* return (
* <Text style={[global.courseRoundBoxTitleAbove]}>
* {t("course:descriptionTitle", {
* label: labels.course
* })}
* </Text>
* );
* }
*
* return null;
* }
* );
* }
*/
setCourseDescriptionSectionTitle = (
CourseDescriptionSectionTitle: React.ComponentType<
CourseDescriptionSectionTitleProps
> | null
) => {
this.CourseDescriptionSectionTitle = CourseDescriptionSectionTitle;
};
CourseMaterialsSectionTitle: React.ComponentType<
CourseMaterialsSectionTitleProps
> | null = null;
/**
* You can use this hook to customize the component that displays the "Course Materials" text.
* @method
* @param {React.ComponentType<CourseMaterialsSectionTitleProps>} CourseMaterialsSectionTitle
* @example
*
* ...
*
* export const applyCustomCode = (externalCodeSetup) => {
* externalCodeSetup.courseSingleApi.setCourseMaterialsSectionTitle(
* ({global, t, labels}) => (
* <Text style={[global.courseRoundBoxTitleAbove, {marginBottom: 20}]}>
* {t("course:courseMaterials", {label: labels.course})}
* </Text>
* )
* );
* }
*/
setCourseMaterialsSectionTitle = (
CourseMaterialsSectionTitle: React.ComponentType<
CourseMaterialsSectionTitleProps
> | null
) => {
this.CourseMaterialsSectionTitle = CourseMaterialsSectionTitle;
};
CourseDescriptionReadMoreComponent: React.ComponentType<
CourseDescriptionReadMoreComponentProps
> | null = null;
/**
* You can use this hook to customize the "Read More" text that shows the course description in a modal when pressed.
* @method
* @param {React.ComponentType<CourseDescriptionReadMoreComponentProps>} CourseDescriptionReadMoreComponent
* @example
*
* ...
* import AppTouchableWithoutFeedback from "@src/components/AppTouchableWithoutFeedback";
* export const applyCustomCode = (externalCodeSetup) => {
* externalCodeSetup.courseSingleApi.setCourseDescriptionReadMoreComponent(({
* courseVM,
* openDescriptionModal,
* global,
* t
* }) => {
* if (courseVM.contentNative.length > 1) {
* return <View style={{alignItems: "center"}}>
* <AppTouchableWithoutFeedback
* onPress={openDescriptionModal}
* >
* <Text
* style={[
* global.linkRegular,
* {
* paddingHorizontal: 20,
* paddingVertical: 10
* }
* ]}
* >
* {t("courses:readMore")}
* </Text>
* </AppTouchableWithoutFeedback>
* </View>
* }
* return null
* })
* }
*/
setCourseDescriptionReadMoreComponent = (
CourseDescriptionReadMoreComponent: React.ComponentType<
CourseDescriptionReadMoreComponentProps
> | null
) => {
this.CourseDescriptionReadMoreComponent = CourseDescriptionReadMoreComponent;
};
WebViewOfflineComponent: React.ComponentType<
WebViewOfflineComponentProps
> | null = null;
/**
* You can use this hook to customize the component that displays a "This content is not available offline" message if a webview is used for rendering the course description while the device is offline.
* @method
* @param {React.ComponentType<WebViewOfflineComponentProps>} WebViewOfflineComponent
* @example
*
* ...
*
* import EmptyList from "@src/components/EmptyList";
*
* export const applyCustomCode = (externalCodeSetup) => {
* externalCodeSetup.courseSingleApi.setWebViewOfflineComponent(
* ({containerStyle, t, global, emptyListStyle}) => (
* <View style={containerStyle}>
* <Text>Please connect to an internet network</Text>
* <EmptyList
* emptyText={{
* title: t("common:contentOfflineMessage"),
* icon: {fontIconName: "wifi-slash", weight: 400}
* }}
* global={global}
* style={emptyListStyle}
* />
* </View>
* )
* );
* }
*/
setWebViewOfflineComponent = (
WebViewOfflineComponent: React.ComponentType<
WebViewOfflineComponentProps
> | null
) => {
this.WebViewOfflineComponent = WebViewOfflineComponent;
};
WebViewDescriptionComponent: React.ComponentType<
WebViewDescriptionComponentProps
> | null = null;
/**
* You can use this hook to replace the webview being used in the course description.
* For example, you can choose to replace it with the default react-native webview.
* @method
* @param {React.ComponentType<WebViewDescriptionComponentProps>} WebViewDescriptionComponent
* @example
*
* ...
*
* import WebViewWithMore from "@src/components/WebViewWithMore";
* export const applyCustomCode = (externalCodeSetup) => {
* externalCodeSetup.courseSingleApi.setWebViewDescriptionComponent(
* ({
* online,
* t,
* onShouldStartLoadWithRequest,
* heightLimit,
* source,
* global,
* colors,
* ModalHeaderComponent
* }) => (
* <WebViewWithMore
* online={online}
* t={t}
* onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
* disableLoadingPadding={true}
* height={heightLimit}
* source={source}
* global={global}
* colors={colors}
* ModalHeaderComponent={ModalHeaderComponent}
* />
* )
* );
* }
*/
setWebViewDescriptionComponent = (
WebViewDescriptionComponent: React.ComponentType<
WebViewDescriptionComponentProps
> | null
) => {
this.WebViewDescriptionComponent = WebViewDescriptionComponent;
};
CourseQuizItem: React.ComponentType<CourseQuizItemProps> | null = null;
/**
* You can use this hook to customize the Item components that are displayed as final quizzes.
* @param {React.ComponentType<CourseQuizItemProps>} CourseQuizItem
* @method
* @example
*
* ...
*
* import LearnItem from "@src/components/Course/LearnItem";
*
* export const applyCustomCode = (externalCodeSetup) => {
* externalCodeSetup.courseSingleApi.setCourseQuizItem(
* ({
* item,
* style,
* global,
* colors,
* skipProgress,
* onPress,
* allowNavigation,
* IconComponent
* }) => (
* <LearnItem
* key={item.id}
* style={style}
* item={item}
* global={global}
* colors={colors}
* skipProgress={skipProgress}
* onPress={onPress}
* allowNavigation={allowNavigation}
* beforeLabel={IconComponent}
* />
* )
* );
* }
*
*/
setCourseQuizItem = (
CourseQuizItem: React.ComponentType<CourseQuizItemProps> | null
) => {
this.CourseQuizItem = CourseQuizItem;
};
LessonProgressStepsComponent: React.ComponentType<
LessonProgressStepsProps
> | null = null;
/**
* You can use this to replace the course lesson progress step component.
* @method
* @param {React.ComponentType<LessonProgressStepsProps>} LessonProgressStepsComponent
* @example <caption>Replace the course lesson progress steps component</caption>
*
* export const applyCustomCode = (externalCodeSetup) => {
* externalCodeSetup.courseSingleApi.setLessonProgressStepsComponent(props => {
* const {count, completedSteps, blockType, global, t} = props;
*
* return (
* !!count &&
* count > 0 &&
* blockType === "lesson" ? (
* <Text style={[global.caption]}>
* {`${t("courses:stepsOutOf", {completedSteps, count})}`}
* </Text>
* ) : <></>
* );
* });
* }
*/
setLessonProgressStepsComponent = (
LessonProgressStepsComponent: React.ComponentType<
LessonProgressStepsProps
> | null
) => {
this.LessonProgressStepsComponent = LessonProgressStepsComponent;
};
}
Source