/**
* @typedef {"webview" | "htmlparser"} QuestionClosedRenderMode
*/
/**
* @typedef {Function} OverrideQuestionHTMLWrapperCallback
* @param {String} HTML Default HTML wrapper
* @param {String} inputHTML HTML that contains the input element
* @param {String} css CSS passed to webview by default
*/
/**
* @typedef {Object} QuizPrevNextComponentProps
* @property {Function} onQuizClick Function to execute if previous or next button is a quiz
* @property {Function} onLessonClick Function to execute if previous or next button is a lesson
* @property {Function} onTopicClick Function to execute if previous or next button is a topic
* @property {Object} global App global style
* @property {Object} colors App colors
* @property {TranslationFunction} t
* @property {?Object} prevObject Information about previous lesson/topic/quiz
* @property {?Object} nextObject Information about next lesson/topic/quiz
* @property {Number} courseId Course id of lesson/topic/quiz
* @property {Function} nextLockedAlert Shows an alert with information that next object is locked
*/
/**
* @typedef {Object} QuizHeaderProps
* @property {QuizViewModel} quiz
* @property {Number} currentSwiperPosition
* @property {Object} global App global style
* @property {Object} labels Learndash labels
* @property {Object<Array>} questions Current questions in the quiz
* @property {Number} quizOrder
* @property {Number} quizTotalCount
* @property {Object} colors App colors
* @property {Function} setHeaderHeight Helper function which can be called to set header height
* @property {Boolean | React.ComponentType} renderQuizTimer Returns `false` if quiz doesn't have a timer set. Will return a component if timer for the quiz is set.
* @property {TranslationFunction} t
* @property {NavigationService} navigation
* @property {Function} onQuizClick Function to execute if previous or next button is a quiz
* @property {Function} onLessonClick Function to execute if previous or next button is a lesson
* @property {Function} onTopicClick Function to execute if previous or next button is a topic
* @property {Number} courseId Course id of lesson/topic/quiz
* @property {?Object} nextObject Information about next lesson/topic/quiz
* @property {?Object} prevObject Information about previous lesson/topic/quiz
* @property {CourseViewModel} course
* @property {Function} nextLockedAlert Shows an alert with information that next object is locked
* @property {Boolean} isResultsVisible Returns `true` if screen is currently showing the results of the quiz
* @property {React.ComponentType} backToCourse Returns default back button component
* @property {Boolean} hidePrevNext Returns `true` if prev/next buttons should be hidden
* @property {Boolean | React.ComponentType} prevNext Returns `false` if hidePrevNext is `true`. Will return buttons which can navigate through previous and next screens if hidePrevNext is `false`
*/
/**
* @typedef {Object} QuizTitleComponentProps
* @property {QuizHeaderProps} QuizHeaderProps
*/
/**
* @typedef {Object} QuizScreenHeaderProps
* @property {QuizHeaderProps} QuizHeaderProps
* @property {Object} headerLeftStyle Default styling for left section of the header
* @property {Object} style Default styling of lesson header
*/
/**
* @class
* Quiz Hooks.
* Instance name: quizApi
You can use these hooks to customize the quiz questions for your app.
* @example
* externalCodeSetup.quizApi.METHOD_NAME
*/
export class QuizApi {
questionClosedRenderMode = "webview";
/**
* It overrides the Closed Question type render mode. The default mode is `webview`. If the question is not rendering correctly, you can try setting it to `htmlparser` mode.
* @method
* @param {QuestionClosedRenderMode} questionClosedRenderMode
* @example
* externalCodeSetup.quizApi.setQuestionClosedRenderMode("htmlparser")
*/
setQuestionClosedRenderMode = questionClosedRenderMode => {
this.questionClosedRenderMode = questionClosedRenderMode;
};
wrapQuestionHtmlFilter = (HTML, inputHTML, css) => HTML;
/**
* We use an HTML wrapper for "Fill in the blank" questions to accept input answers.
* You can use this to change the HTML output.
* @method
* @param {OverrideQuestionHTMLWrapperCallback} wrapQuestionHtmlFilter
* @example <caption>Add additional details below the input field</caption>
* externalCodeSetup.quizApi.setWrapQuestionHtmlFilter((HTMLWrapper, inputHtml, css) => {
*
* const disableZoom = true;
* const myNewHtml =
* `
* <!DOCTYPE html>
* <html>
* <head>
* <title>Topic Content</title>
* <meta http-equiv="content-type" content="text/html; charset=utf-8">
* <meta name="viewport" content="width=device-width, initial-scale=1 ${disableZoom ? "maximum-scale=1.0" : ""} ">
* <style type="text/css">
* ${css}
* .content {
* width: 100%;
* overflow: hidden;
* padding-bottom: 4px;
* }
* </style>
* </head>
* <body>
* <div class="content">
* ` +
* inputHtml +
* `
* <p> Please use UPPERCASE characters only</p>
* </div>
* </body>
* </html>
* `;
* return myNewHtml;
* })
*/
setWrapQuestionHtmlFilter = wrapQuestionHtmlFilter => {
this.wrapQuestionHtmlFilter = wrapQuestionHtmlFilter;
};
PrevNextComponent = null;
/**
* You can use this to replace the previous and next buttons on the quiz screen.
* @method
* @param {?React.ComponentType<QuizPrevNextComponentProps>} PrevNextComponent
* @example <caption> Change colors of the default previous and next buttons </caption>
*
* //In custom_code/components/PrevNext.js
*
* import React from "react";
* import { Text, View, StyleSheet } from "react-native";
* import AppTouchableOpacity from "@src/components/AppTouchableOpacity";
* import Icon from "@src/components/Icon";
* import { shadeColor } from "@src/utils";
*
* export const onObjectClick = (
* object,
* onQuizClick,
* onTopicClick,
* onLessonClick
* ) => {
* if (!!!object) {
* return false;
* }
* switch (object.type) {
* case "quiz":
* onQuizClick(object.parentType, object.parent)(object);
* break;
* case "topic":
* onTopicClick(object, object.parent);
* break;
* case "lesson":
* onLessonClick(object);
* break;
* }
* };
*
* const PrevNext = ({
* global,
* colors,
* t,
* prevObject,
* nextObject,
* courseId,
* onQuizClick,
* onLessonClick,
* onTopicClick,
* nextLockedAlert
* }) => {
*
* return (
* <View style={[global.row]}>
* <AppTouchableOpacity
* style={[
* global.wrappedButton,
* global.wrappedTextButton,
* { marginRight: 4, backgroundColor: "purple" }
* ]}
* onPress={() => {
* if (prevObject !== "disabled") {
* onObjectClick(prevObject, onQuizClick, onTopicClick, onLessonClick);
* }
* }}
* >
* <View style={global.row}>
* <View style={global.linkWithArrow}>
* <Text
* style={[
* global.wrappedTextButtonLabel,
* {
* color:
* !!!prevObject || prevObject === "disabled"
* ? shadeColor(colors.headerIconColor, 0.4)
* : "red"
* }
* ]}
* >
* {t("lesson:prevButtonText")}
* </Text>
* </View>
* </View>
* </AppTouchableOpacity>
*
* <AppTouchableOpacity
* style={[global.wrappedButton, global.wrappedTextButton, {backgroundColor: "purple"}]}
* onPress={() => {
* if (nextObject !== "disabled") {
* onObjectClick(nextObject, onQuizClick, onTopicClick, onLessonClick);
* } else if (typeof nextLockedAlert === "function") {
* nextLockedAlert();
* }
* }}
* >
* <View style={global.row}>
* <View style={global.linkWithArrow}>
* <Text
* style={[
* global.wrappedTextButtonLabel,
* {
* color:
* !!!nextObject || nextObject === "disabled"
* ? shadeColor(colors.headerIconColor, 0.4)
* : "blue"
* }
* ]}
* >
* {t("lesson:nextButtonText")}
* </Text>
* </View>
* </View>
* </AppTouchableOpacity>
* </View>
* );
* };
*
* export default PrevNext;
*
* //In custom_code/index.js...
*
* ...
*
* import PrevNextComponent from './components/PrevNext';
* export const applyCustomCode = externalCodeSetup => {
* externalCodeSetup.quizApi.setPrevNextComponent((props) => <PrevNextComponent {...props} />);
* }
*
*/
setPrevNextComponent = PrevNextComponent => {
this.PrevNextComponent = PrevNextComponent;
};
QuizTitleComponent = null;
/**
* You can use this to replace the quiz's title component.
* For example, you can use this to add more details to the quiz's title.
* @method
* @param {React.ComponentType<QuizTitleComponentProps>} QuizTitleComponent
* @example <caption> Add more quiz details to component </caption>
*
* //In custom_code/components/QuizTitle.js...
*
* import React from "react";
* import {View, Text} from "react-native";
* import Animated from "react-native-reanimated";
* const QuizTitle = ({
* quiz,
* global,
* colors,
* paddingTop = 14,
* setHeaderHeight,
* t,
* labels,
* questions,
* currentSwiperPosition,
* quizOrder,
* quizTotalCount
* }) => {
*
* const paddingBottom = 14;
*
* return (
* <Animated.View
* style={[
* {
* backgroundColor: colors.bodyFrontBg,
* width: "100%",
* shadowOffset: {width: 0, height: 1},
* shadowRadius: 1,
* shadowColor: "#000",
* shadowOpacity: 0.05
* }
* ]}
* >
* <View
* style={[
* global.row,
* {
* justifyContent: "space-between",
* alignItems: "flex-start",
* paddingTop,
* paddingBottom
* }
* ]}
* onLayout={event => {
* const {height} = event.nativeEvent.layout;
* typeof setHeaderHeight === "function" && setHeaderHeight(height);
* }}
* >
* <Animated.View
* style={{
* flex: 1,
* paddingHorizontal: 20
* }}
* >
* <Animated.Text
* style={[
* global.courseHeaderTitle,
* {marginBottom: 5}
* ]}
* >
* {quiz.title}
* </Animated.Text>
*
* {
* !quiz.hideQuestionPositionOverview &&
* !quiz.hideQuestionNumbering &&
* currentSwiperPosition > 0 ? (
* <Text style={global.courseHeaderSubTitle}>
* {t("quiz:questionCount", {
* question: labels.question,
* current: currentSwiperPosition,
* total: questions
* ? questions.size
* : ""
* })}
* </Text>
* ) : (
* <Text style={global.courseHeaderSubTitle}>
* {`Quiz ${quizOrder + 1} of ${quizTotalCount}`}
* </Text>
* )
* }
*
* <Text style={global.courseHeaderSubTitle}>
* Author: {quiz.author.name}
* </Text>
* <Text style={global.courseHeaderSubTitle}>
* Completed: {quiz.completed.toString()}
* </Text>
*
* </Animated.View>
* </View>
* </Animated.View>
* );
* };
*
* export default QuizTitle;
*
* //In custom_code/index.js...
*
* ...
* import QuizTitle from "./components/QuizTitle";
* export const applyCustomCode = externalCodeSetup => {
* externalCodeSetup.quizApi.setQuizTitleComponent(props => <QuizTitle {...props} />)
* }
*
*/
setQuizTitleComponent = QuizTitleComponent => {
this.QuizTitleComponent = QuizTitleComponent;
};
QuizScreenHeader = null;
/**
* You can use this hook to customize the header of the Quiz Single Screen which by default, contains the back-to-course button and the previous/next buttons.
* @method
* @param {React.ComponentType<QuizScreenHeaderProps>} QuizScreenHeader
* @example <caption> Add a timer on the quiz screen header </caption>
*
* //In custom_code/components/QuizScreenHeader.js...
*
* import React from "react";
* import {View, Text} from "react-native";
* import Animated from "react-native-reanimated";
* import {DEVICE_WIDTH} from "@src/styles/global";
* import AuthWrapper from "@src/components/AuthWrapper";
*
* const Header = ({
* headerLeftStyle,
* style,
* global,
* colors,
* backToCourse,
* headerRightAuthWrapperProps,
* prevNext,
* quiz,
* renderQuizTimer
* }) => {
* return (
* <Animated.View
* style={[
* global.row,
* global.fakeHeader,
* {
* backgroundColor: "transparent",
* paddingHorizontal: 10,
* overflow: "hidden"
* },
* {
* width: DEVICE_WIDTH
* },
* style
* ]}
* >
* <View
* style={[
* {
* alignItems: "center",
* justifyContent: "center",
* flexDirection: "row",
* flex: 1,
* height: "100%",
*
* backgroundColor: "gray",
* borderRadius: 20
* }
* ]}
* >
* <View style={[global.headerButtonLeft, headerLeftStyle]}>
* {backToCourse}
* </View>
*
* <View style={[global.headerCustomTitle]}>
* {renderQuizTimer(quiz, global, colors)}
* </View>
*
* <View style={[global.headerButtonRight]}>
* <AuthWrapper
* actionOnGuestLogin={"hide"}
* {...headerRightAuthWrapperProps}
* >
* {prevNext}
* </AuthWrapper>
* </View>
* </View>
* </Animated.View>
* );
* };
*
* export default Header;
*
* //In custom_code/index.js...
*
* import QuizScreenHeader from "./components/QuizScreenHeader";
* export const applyCustomCode = externalCodeSetup => {
* externalCodeSetup.quizApi.setQuizScreenHeader(props => <QuizScreenHeader {...props} />)
* }
*
*/
setQuizScreenHeader = QuizScreenHeader => {
this.QuizScreenHeader = QuizScreenHeader;
};
}
Source