Source

externalCode/quizResultScreen.js

/**
 * @typedef {Object} QuizVM
 * @property {Boolean} showCategoryScore Returns `true` if category score should be shown
 * @property {Boolean} hideResultQuizTime Returns `true` if result quiz time should be hidden
 * @property {Boolean} hideResultPoints Returns `true` if result points should be hidden
 * @property {Boolean} showAverageResult Returns `true` if average result should be shown
 * @property {Boolean} toplistActivated Returns `true` if top list is activated
 * @property {Boolean} hideResultCorrectQuestion Returns `true` if result correct question should be hidden
 * @property {Boolean} btnRestartQuizHidden Returns `true` if restart quiz button should be hidden
 * @property {Object} toplistData Contains top list data used for showing quiz leader board
 * @property {Boolean} resultGradeEnabled Returns `true` if result grade is enabled
 * @property {Object} resultText Contains result message which can also be displayed in Quiz Details screen
 */

/**
 * @typedef {Object} CircleProgressResultProps
 * @property {Number} progress Percentage of the score
 * @property {String} progressColor Filled color according to progress
 * @property {Number} width Line width of circle
 * @property {Number} size Size of circle
 * @property {String} unfilledColor Default color used for the unfilled portion of the circle
 * @property {React.ComponentType} children Component that contains the content of the circle
 */

/**
 * @typedef {Object} ArcContentProps
 * @property {Object} config App configuration object
 * @property {TranslationFunction} t
 * @property {Object} result Quiz result details
 * @property {QuizVM} quizVM
 * @property {Number} size Size of circle
 * @property {Number} progress Percentage of the score
 * @property {String} arcColor Default color depending on the value of the average score
 */

/**
 * @typedef {Object} ResultCorrectCountProps
 * @property {QuizVM} quizVM
 * @property {Object} global App global style
 * @property {Object} labels Learndash labels
 * @property {Number} correctAnswers Total count of correct answers
 * @property {Object} result Quiz result details
 */

/**
 * @typedef {Object} AverageResultProps
 * @property {QuizVM} quizVM
 * @property {Object} global App global style
 * @property {Object} styles
 * @property {String} progressColor
 * @property {TranslationFunction} t
 * @property {Object} result Quiz result details
 */

/**
 * @typedef {Object} ResultQuizTimeProps
 * @property {QuizVM} quizVM
 * @property {Object} global App global style
 * @property {Object} colors App colors
 * @property {TranslationFunction} t
 * @property {Object} result Quiz result details
 */

/**
 * @typedef {Object} RestartQuizButtonProps
 * @property {QuizVM} quizVM
 * @property {Object} global App global style
 * @property {Object} colors App colors
 * @property {Object} styles
 * @property {Function} onAgainClick Allows users to take the quiz again
 */

/**
 * @typedef {Object} ViewDetailsQuizButtonProps
 * @property {Object} global App global style
 * @property {Object} colors App colors
 * @property {Object} styles
 * @property {Function} onDetailsClick Allows users to redirect to Quiz Details screen
 */

/**
 * @typedef {Object} ViewCertificateButtonProps
 * @property {Object} result Quiz result details
 * @property {Object} styles
 * @property {TranslationFunction} t
 * @property {Function} setIsDownloadingState Sets the `downloading` state to `true`
 * @property {Function} setIsNotDownloadingState Sets the `downloading` state to `false`
 * @property {Boolean} downloading Returns `true` if certificate is currently downloading
 */

/**
 * @typedef {Object} QuizCompleteButtonProps
 * @property {Boolean} showContinue Returns `true` if the continue button should be shown
 * @property {Object} global App global style
 * @property {Object} colors App colors
 * @property {Function} onCompleteButtonClick Function to execute when the continue button is pressed
 * @property {TranslationFunction} t
 */

/**
 * @class
 * Quiz Result Screen Hooks.
 * Instance name: quizResultApi
  
   You can use these hooks to customize the quiz result information for your app such as customizing the average score display, buttons in the screen, and the result time.  
 * @example
 * externalCodeSetup.quizResultApi.METHOD_NAME
 */
export class QuizResultApi {
	CircleProgressResult = null;
	/**
	 * You can use this hook to customize the CircleProgressResult component.
	 * For example, you can change its color, animation, thickness, etc.
	 * @method
	 * @param {React.ComponentType<CircleProgressResultProps>} CircleProgressResult
	 * @example
	 *
	 * //In custom_code/components/CircleProgressResult.js...
	 *
	 * import React from "react";
	 * import {View} from "react-native";
	 * import ProgressCircle from "react-native-progress/Circle";
	 * const CircleProgressResult = ({
	 *     progress,
	 *     progressColor,
	 *     width,
	 *     size,
	 *     unfilledColor,
	 *     children
	 * }) => (
	 *     <>
	 *         <View>
	 *             <ProgressCircle
	 *                 size={size}
	 *                 progress={progress / 100}
	 *                 thickness={width}
	 *                 unfilledColor={unfilledColor}
	 *                 animated={true}
	 *                 borderWidth={0}
	 *                 color={progressColor}
	 *                 strokeCap="round"
	 *             />
	 *         </View>
	 *         <View
	 *             style={{
	 *                 width: "100%",
	 *                 height: "100%",
	 *                 position: "absolute",
	 *                 top: 0,
	 *                 left: 0
	 *             }}
	 *         >
	 *             {children}
	 *         </View>
	 *     </>
	 * );
	 * export default CircleProgressResult
	 *
	 * //In custom_code/index.js...
	 *
	 * ...
	 *
	 * import CircleProgressResult from "./components/CircleProgressResult";
	 *
	 * export const applyCustomCode = (externalCodeSetup) => {
	 *     externalCodeSetup.quizResultApi.setCircleProgressResult(props => <CircleProgressResult {...props} />)
	 * }
	 *
	 */
	setCircleProgressResult = CircleProgressResult => {
		this.CircleProgressResult = CircleProgressResult;
	};

	ArcContent = null;
	/**
	 * You can use this hook to customize the content inside the CircleProgressResult component.
	 * For example, you can change the result percentage or replace the CircleProgressResult component for the average score.
	 * @method
	 * @param {React.ComponentType<ArcContentProps>} ArcContent
	 * @example
	 *
	 * //In custom_code/components/ArcContent.js...
	 *
	 * import React from "react";
	 * import {View, Text} from "react-native";
	 * import {globalStyle, FontWeights} from "@src/styles/global";
	 * import CircleProgressResult from "./CircleProgressResult"; //See sample code from setCircleProgressResult hook
	 *
	 * const ArcContent = props => {
	 *   const {config, result, size, quizVM} = props;
	 *   const {global, colors, calcFontSize} = globalStyle(config.styles);
	 *
	 *   const percent = Math.round((result.result || 0) * 100) / 100;
	 *     return <>
	 *     <View
	 *       style={{
	 *         width: "100%",
	 *         height: "100%",
	 *         position: "absolute",
	 *         top: 0,
	 *         left: 0,
	 *         alignItems: "center",
	 *         justifyContent: "center"
	 *       }}
	 *     >
	 *       <Text>
	 *         <Text
	 *           key="progress"
	 *           style={{
	 *             ...global.text,
	 *             fontSize: calcFontSize(50),
	 *             fontWeight: FontWeights.semiBold,
	 *             color: colors.textColor
	 *           }}
	 *         >
	 *           {Math.floor(percent)}
	 *         </Text>
	 *         <Text
	 *           key="progress"
	 *           style={{
	 *             ...global.text,
	 *             fontSize: calcFontSize(24),
	 *             fontWeight: FontWeights.semiBold,
	 *             color: colors.textColor
	 *           }}
	 *         >
	 *           {(percent % 1).toString().substring(1, 4)}
	 *         </Text>
	 *         <Text
	 *           style={{
	 *             ...global.text,
	 *             fontSize: calcFontSize(24),
	 *             fontWeight: FontWeights.semiBold,
	 *             color: colors.textColor
	 *           }}
	 *         >
	 *           %
	 *         </Text>
	 *       </Text>
	 *     </View>
	 *     <View
	 *       style={{
	 *         opacity: 0.19,
	 *         width: size - 16,
	 *         height: size - 16,
	 *         position: "absolute",
	 *         top: 8,
	 *         left: 8,
	 *         backgroundColor: "transparent"
	 *       }}
	 *     >
	 *       {quizVM.showAverageResult && (
	 *         <View>
	 *           <CircleProgressResult
	 *             unfilledColor="#ffffff"
	 *             size={size - 16}
	 *             width={6}
	 *             progress={result.average_result}
	 *             progressColor={rgb(26, 193, 60)}
	 *           />
	 *         </View>
	 *       )}
	 *     </View>
	 *     </>
	 * };
	 *
	 * export default ArcContent;
	 *
	 * //In custom_code/index.js...
	 *
	 * ...
	 *
	 * import ArcContent from "./components/ArcContent";
	 * export const applyCustomCode = (externalCodeSetup) => {
	 *   externalCodeSetup.quizResultApi.setArcContent(props => <ArcContent {...props} />)
	 * }
	 *
	 */
	setArcContent = ArcContent => {
		this.ArcContent = ArcContent;
	};

	ResultCorrectCount = null;
	/**
	 * You can use this hook to customize the ResultCorrectCount component indicating how many questions were answered correctly.
	 * For example, you can add a message depending on the result of the quiz taken.
	 * @method
	 * @param {React.ComponentType<ResultCorrectCountProps>} ResultCorrectCount
	 * @example <caption> Add conditional message based on total score </caption>
	 *
	 * //In custom_code/components/ResultCorrectCount.js...
	 *
	 * import React from "react";
	 * import { Text } from "react-native"
	 *
	 * const ResultCorrectCount = ({ quizVM, global, labels, correctAnswers, result, t }) => {
	 *
	 *     const total = Object.keys(result.answers).length
	 *
	 *     return !quizVM.hideResultCorrectQuestion && (
	 *         <>
	 *             <Text
	 *                 style={{
	 *                     ...global.regularText,
	 *                     marginTop: 26,
	 *                     marginBottom: 30,
	 *                     width: 209,
	 *                     textAlign: "center"
	 *                 }}
	 *             >
	 *                 {t("quiz:correctCount", {
	 *                     questions: labels.questions.toLowerCase(),
	 *                     correct: correctAnswers,
	 *                     total
	 *                 })}
	 *             </Text>
	 *
	 *             {total == correctAnswers && <Text style={{ fontSize: 25 }}> Perfect Score!!!</Text>}
	 *         </>
	 *     )
	 * }
	 *
	 * export default ResultCorrectCount;
	 *
	 * //In custom_code/index.js...
	 *
	 * ...
	 *
	 * import ResultCorrectCount from "./components/ResultCorrectCount";
	 * export const applyCustomCode = (externalCodeSetup) => {
	 *     externalCodeSetup.quizResultApi.setResultCorrectCount(props => <ResultCorrectCount {...props} />)
	 * }
	 *
	 */
	setResultCorrectCount = ResultCorrectCount => {
		this.ResultCorrectCount = ResultCorrectCount;
	};

	AverageResult = null;
	/**
	 * You can use this hook to customize the component that displays "Your Score" and "Average Score" details.
	 * @method
	 * @param {React.ComponentType<AverageResultProps>} AverageResult
	 * @example
	 *
	 * //In custom_code/components/AverageResult.js...
	 *
	 * import React from "react";
	 * import {View, Text} from "react-native";
	 * import {FontWeights} from "@src/styles/global";
	 *
	 * const AverageResult = ({
	 *   quizVM,
	 *   global,
	 *   styles,
	 *   progressColor,
	 *   t,
	 *   result
	 * }) => quizVM.showAverageResult && (
	 *   <View
	 *     style={[
	 *       {
	 *         justifyContent: "center",
	 *         marginTop: 20,
	 *         marginBottom: 20,
	 *         width: 168
	 *       }
	 *     ]}
	 *   >
	 *     <View style={[global.row, styles.stats]}>
	 *       <View
	 *         style={{
	 *           width: 8,
	 *           height: 8,
	 *           borderRadius: 4,
	 *           marginRight: 6,
	 *           backgroundColor: progressColor
	 *         }}
	 *       />
	 *       <Text
	 *         style={[global.widgetItemDesc, {marginTop: 0, flex: 1}]}
	 *       >
	 *         {t("quiz:yourScore")}
	 *       </Text>
	 *       <Text
	 *         style={[
	 *           global.regularText,
	 *           {fontWeight: FontWeights.bold}
	 *         ]}
	 *       >
	 *         {Math.round(result.result * 10) / 10}%
	 *       </Text>
	 *     </View>
	 *
	 *     <View style={[global.row, styles.stats]}>
	 *       <View
	 *         style={{
	 *           opacity: 0.19,
	 *           width: 8,
	 *           height: 8,
	 *           borderRadius: 4,
	 *           marginRight: 6,
	 *           backgroundColor: rgb(26, 193, 60)
	 *         }}
	 *       />
	 *       <Text
	 *         style={[global.widgetItemDesc, {marginTop: 0, flex: 1}]}
	 *       >
	 *         {t("quiz:averageScore")}
	 *       </Text>
	 *       <Text
	 *         style={[
	 *           global.regularText,
	 *           {fontWeight: FontWeights.bold}
	 *         ]}
	 *       >
	 *         {Math.round(result.average_result * 10) / 10}%
	 *       </Text>
	 *     </View>
	 *   </View>
	 * )
	 * export default AverageResult;
	 *
	 * //In custom_code/index.js...
	 *
	 * ...
	 *
	 * import AverageResult from "./components/AverageResult"
	 * export const applyCustomCode = (externalCodeSetup) => {
	 *   externalCodeSetup.quizResultApi.setAverageResult(props => <AverageResult {...props} />)
	 * }
	 */
	setAverageResult = AverageResult => {
		this.AverageResult = AverageResult;
	};

	ResultQuizTime = null;
	/**
	 * You can use this hook to customize the displayed time indicating how long the user completed the quiz.
	 * For example, you can modify the time format.
	 * @method
	 * @param {React.ComponentType<ResultQuizTimeProps>} ResultQuizTime
	 * @example <caption> Change time format </caption>
	 *
	 * //In custom_code/components/ResultQuizTime.js...
	 *
	 * import React from "react";
	 * import {View,Text} from "react-native";
	 * import moment from "moment";
	 *
	 * import {FontWeights} from "@src/styles/global";
	 *
	 * const formatDateFunction = seconds => {
	 *   return moment.duration(seconds, "seconds").format("hh.mm.ss", {trim: false});
	 * };
	 *
	 * const ResultQuizTime = ({
	 *   quizVM,
	 *   global,
	 *   colors,
	 *   t,
	 *   result
	 * }) => !quizVM.hideResultQuizTime && (
	 *   <View
	 *     style={{
	 *       width: "100%",
	 *       borderTopWidth: 1,
	 *       borderTopColor: "rgb(231, 233, 236)",
	 *       paddingTop: 40,
	 *       paddingBottom: 7,
	 *       alignItems: "center"
	 *     }}
	 *   >
	 *     <Text
	 *       style={[
	 *         global.quizCheckTitle,
	 *         {
	 *           fontWeight: FontWeights.regular,
	 *           color: colors.descTextColor,
	 *           marginBottom: 8
	 *         }
	 *       ]}
	 *     >
	 *       {t("quiz:yourTime")}
	 *     </Text>
	 *     <Text
	 *       style={[
	 *         global.resultsSubTitle,
	 *         {
	 *           color: colors.textColor,
	 *           fontWeight: FontWeights.semiBold,
	 *           marginBottom: 10
	 *         }
	 *       ]}
	 *     >
	 *       {formatDateFunction(result.time)}
	 *     </Text>
	 *   </View>
	 * )
	 *
	 * export default ResultQuizTime;
	 *
	 * //In custom_code/index.js...
	 *
	 * ...
	 *
	 * import ResultQuizTime from "./components/ResultQuizTime";
	 * export const applyCustomCode = (externalCodeSetup) => {
	 *   externalCodeSetup.quizResultApi.setResultQuizTime(props => <ResultQuizTime {...props} />)
	 * }
	 */
	setResultQuizTime = ResultQuizTime => {
		this.ResultQuizTime = ResultQuizTime;
	};

	RestartQuizButton = null;
	/**
	 * You can use this hook to customize the RestartQuizButton component which allows the users to take the quiz again.
	 * @method
	 * @param {React.ComponentType<RestartQuizButtonProps>} RestartQuizButton
	 * @example <caption> Add a confirmation if user would like to take the test again </caption>
	 *
	 * //In custom_code/components/RestartQuizButton.js...
	 *
	 * import React from "react";
	 * import { Alert } from "react-native";
	 * import AppButton from "@src/components/AppButton";
	 *
	 * const RestartQuizButton = ({
	 *     quizVM,
	 *     styles,
	 *     global,
	 *     colors,
	 *     onAgainClick,
	 *     t
	 * }) => {
	 *
	 *     const onTakeAgain = () => {
	 *         Alert.alert(
	 *             "Please confirm",
	 *             "Would you like to take the quiz again?",
	 *             [
	 *                 {
	 *                     text: "Cancel",
	 *                     style: "cancel",
	 *                 },
	 *                 {
	 *                     text: "Ok",
	 *                     onPress: onAgainClick,
	 *                 },
	 *             ],
	 *         );
	 *     }
	 *
	 *     return !quizVM.btnRestartQuizHidden && (
	 *         <AppButton
	 *             key={"button"}
	 *             style={[
	 *                 styles.button,
	 *                 {
	 *                     backgroundColor: colors.primaryButtonBg,
	 *                     width: "100%"
	 *                 }
	 *             ]}
	 *             onPress={() => {
	 *                 onTakeAgain();
	 *             }}
	 *             labelStyle={{
	 *                 ...global.quizTakeAgainButtonLabel,
	 *                 color: colors.primaryButtonColor
	 *             }}
	 *             label={t("quiz:takeAgain")}
	 *             global={global}
	 *             loading={false}
	 *         />)
	 * }
	 *
	 * export default RestartQuizButton;
	 *
	 * //In custom_code/index.js...
	 *
	 * ...
	 *
	 * import RestartQuizButton from "./components/RestartQuizButton";
	 *
	 * export const applyCustomCode = (externalCodeSetup) => {
	 *   externalCodeSetup.quizResultApi.setRestartQuizButton(props => <RestartQuizButton {...props} />)
	 * }
	 */
	setRestartQuizButton = RestartQuizButton => {
		this.RestartQuizButton = RestartQuizButton;
	};

	ViewDetailsQuizButton = null;
	/**
	 * You can use this hook to customize the ViewDetailsQuizButton component which allows the users to view the Quiz Details.
	 * @method
	 * @param {React.ComponentType<ViewDetailsQuizButtonProps>} ViewDetailsQuizButton
	 * @example
	 *
	 * ...
	 *
	 * import AppButton from "@src/components/AppButton";
	 *
	 * export const applyCustomCode = (externalCodeSetup) => {
	 *     externalCodeSetup.quizResultApi.setViewDetailsQuizButton(({
	 *         global,
	 *         colors,
	 *         styles,
	 *         t,
	 *         onDetailsClick
	 *     }) => (
	 *         <AppButton
	 *             key={"button"}
	 *             style={[
	 *                 styles.button,
	 *                 {
	 *                     backgroundColor: colors.secondaryButtonBg,
	 *                     width: "100%"
	 *                 }
	 *             ]}
	 *             onPress={onDetailsClick}
	 *             labelStyle={{
	 *                 ...global.quizTakeAgainButtonLabel,
	 *                 color: colors.secondaryButtonColor
	 *             }}
	 *             label={t("quiz:viewDetails")}
	 *             global={global}
	 *             loading={false}
	 *         />
	 *     ))
	 * }
	 *
	 */
	setViewDetailsQuizButton = ViewDetailsQuizButton => {
		this.ViewDetailsQuizButton = ViewDetailsQuizButton;
	};

	ViewCertificateButton = null;
	/**
	 * You can use this hook to customize the ViewCertificateButton component that allows the users to view their certificate for the quiz.
	 * @method
	 * @param {React.ComponentType<ViewCertificateButtonProps>} ViewCertificateButton
	 * @example
	 *
	 * ...
	 *
	 * import CourseActionButton from "@src/components/Course/CourseActionButton";
	 * import {previewDocument} from "@src/utils/previewFiles";
	 * export const applyCustomCode = (externalCodeSetup) => {
	 *
	 *     const ViewCertificateButton = ({
	 *         result,
	 *         styles,
	 *         t,
	 *         setIsDownloadingState,
	 *         setIsNotDownloadingState,
	 *         downloading
	 *     }) => result.certificate &&
	 *         result.certificate.link && (
	 *             <CourseActionButton
	 *                 title={t("quiz:viewCertificate")}
	 *                 style={styles.viewButton}
	 *                 labelStyle={styles.labelStyle}
	 *                 onPress={() => {
	 *                     previewDocument({
	 *                         url: result.certificate.link,
	 *                         localName: result.certificate.filename,
	 *                         token,
	 *                         t,
	 *                         beginCallback: () => {
	 *                             setIsDownloadingState();
	 *                         },
	 *                         progressCallback: progress => {},
	 *                         doneCallback: () => {
	 *                             setIsNotDownloadingState();
	 *                         },
	 *                         failCallback: () => {
	 *                             setIsNotDownloadingState();
	 *                         }
	 *                     });
	 *                 }}
	 *                 loading={downloading}
	 *             />
	 *         )
	 *
	 *     externalCodeSetup.quizResultApi.setViewCertificateButton(props => <ViewCertificateButton {...props} />)
	 *
	 */
	setViewCertificateButton = ViewCertificateButton => {
		this.ViewCertificateButton = ViewCertificateButton;
	};

	QuizCompleteButton = null;
	/**
	 * You can use this hook to customize the QuizCompleteButton component or the Continue button that sends the users to the next lesson or topic.
	 * @method
	 * @param {React.ComponentType<QuizCompleteButtonProps>} QuizCompleteButton
	 * @example
	 *
	 * //In custom_code/components/QuizCompleteButton.js...
	 *
	 * import React from "react";
	 * import {View, Text} from "react-native";
	 * import AppTouchableOpacity from "@src/components/AppTouchableOpacity";
	 * import AuthWrapper from "@src/components/AuthWrapper";
	 * import {isColorDark} from "@src/utils/index";
	 *
	 * const QuizCompleteButton = ({
	 *   showContinue,
	 *   global,
	 *   colors,
	 *   onCompleteButtonClick,
	 *   t
	 * }) => (
	 *   <AuthWrapper actionOnGuestLogin={"hide"}>
	 *     {showContinue && (
	 *       <View
	 *         style={[
	 *           global.row,
	 *           {
	 *             backgroundColor: colors.bodyFrontBg,
	 *             borderTopColor: colors.borderColor
	 *           },
	 *           global.quizResultButtonContainer
	 *         ]}
	 *       >
	 *         <AppTouchableOpacity
	 *           style={[
	 *             {flex: 1},
	 *             {
	 *               opacity: 1,
	 *               backgroundColor: colors.primaryButtonBg
	 *             },
	 *             global.quizResultButton
	 *           ]}
	 *           onPress={onCompleteButtonClick}
	 *         >
	 *           <View style={global.row}>
	 *             <View style={global.linkWithArrow}>
	 *               <Text
	 *                 style={[
	 *                   {
	 *                     marginLeft: 10,
	 *                     color: !isColorDark(colors.bodyFrontBg) ? "white" : "black"
	 *                   },
	 *                   global.quizResultButtonLabel
	 *                 ]}
	 *               >
	 *                 {t("lesson:continueLessonOnQuizComplete")}
	 *               </Text>
	 *             </View>
	 *           </View>
	 *         </AppTouchableOpacity>
	 *       </View>
	 *     )}
	 *   </AuthWrapper>
	 * );
	 *
	 * export default QuizCompleteButton;
	 *
	 * //In custom_code/index.js...
	 *
	 * ...
	 *
	 * import QuizCompleteButton from "./components/QuizCompleteButton";
	 *
	 * export const applyCustomCode = (externalCodeSetup) => {
	 *   externalCodeSetup.quizResultApi.setQuizCompleteButton(props => <QuizCompleteButton {...props} />)
	 * }
	 *
	 */
	setQuizCompleteButton = QuizCompleteButton => {
		this.QuizCompleteButton = QuizCompleteButton;
	};
}