Class

QuizApi

QuizApi()

Quiz Hooks. Instance name: quizApi

You can use these hooks to customize the quiz questions for your app.

Constructor

# new QuizApi()

Example
externalCodeSetup.quizApi.METHOD_NAME

Methods

# setMaterialsComponent(MaterialsComponent)

You can use this to customize the component that displays the materials of the lesson.

Parameters:
Name Type Description
MaterialsComponent React.ComponentType.<QuizMaterialsComponentProps>
Example
...

import {
  Dimensions,
} from "react-native";
import HTML from "react-native-render-html";
import {RenderListPrefix} from "@src/components/Course/CourseMaterials"
const ent = require("ent");
const DEVICE_WIDTH = Dimensions.get("window").width;

export const applyCustomCode = (externalCodeSetup) => {

  externalCodeSetup.quizApi.setMaterialsComponent(props => {
      const {
          tagsStyles,
          materialsStyles,
          baseFontStyle,
          materials,
          onLinkPress,
          global,
          colors
      } = props;

      return (
          <HTML
              tagsStyles={{...tagsStyles, ...materialsStyles}}
              baseFontStyle={baseFontStyle(15)}
              html={ent.decode(materials)}
              imagesMaxWidth={DEVICE_WIDTH - 32}
              onLinkPress={onLinkPress}
              listsPrefixesRenderers={{
                  ul: (attrib, children, styles, passProps) => (
                      <RenderListPrefix
                          parent={"ul"}
                          colors={colors}
                          global={global}
                          passProps={passProps}
                      />
                  ),
                  ol: (attrib, children, styles, passProps) => (
                      <RenderListPrefix
                          parent={"ol"}
                          colors={colors}
                          global={global}
                          passProps={passProps}
                      />
                  )
              }}
          />
      );
  });
}

# setOfflineComponent(OfflineComponent)

You can use this hook to customize the component that displays a "Come back later. Quizzes are not available offline" message. The component is visible when attempting to start a quiz while the device is offline.

Parameters:
Name Type Description
OfflineComponent React.ComponentType.<QuizOfflineComponentProps>
Example
...

import Icon from "@src/components/Icon";

export const applyCustomCode = (externalCodeSetup) => {
    externalCodeSetup.quizApi.setOfflineComponent(
        ({global, t, wrapperCompsStyle, renderNav, styles}) => (
            <View style={[wrapperCompsStyle, styles.wrapper]} key={"intro"}>
                {renderNav()}
                <View style={styles.container}>
                    <Icon
                        tintColor={"#DCDDDF"}
                        icon={{fontIconName: "wifi-slash", weight: 400}}
                        styles={global.emptyIcon}
                    />
                    <Text style={[global.emptyTitle, styles.title]}>
                        {t("quiz:offlineAlertTitle")}
                    </Text>
                    <Text style={[global.emptyDesc, styles.message]}>
                        {t("quiz:offlineAlertMessage")}
                    </Text>
                </View>
            </View>
        )
    );
}

# setPrevNextComponent(PrevNextComponent)

You can use this to replace the previous and next buttons on the quiz screen.

Parameters:
Name Type Description
PrevNextComponent React.ComponentType.<QuizPrevNextComponentProps>
Example

Change colors of the default previous and next buttons

//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} />);
}

# setQuestionClosedRenderMode(questionClosedRenderMode)

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.

Parameters:
Name Type Description
questionClosedRenderMode QuestionClosedRenderMode

Available values: webview | htmlparser

Example
externalCodeSetup.quizApi.setQuestionClosedRenderMode("htmlparser")

# setQuestionOverviewButtons(QuestionOverviewButtons)

You can use this hook to customize the "Review Question" and "Quiz summary" buttons in the QuizSingleScreen.

Parameters:
Name Type Description
QuestionOverviewButtons QuestionOverviewButtonsProps
Example
...

import QuestionOverviewButtons from "@src/components/QuestionOverview/QuestionOverviewButtons";

export const applyCustomCode = (externalCodeSetup) => {
    externalCodeSetup.questionApiHooks.setQuestionOverviewButtons(
        ({
            style,
            global,
            t,
            labels,
            onReviewPress,
            onSummaryPress,
            disableSummaryButton
        }) => (
            <QuestionOverviewButtons
                style={style}
                global={global}
                reviewQuestionLabel={t("quiz:reviewQuestionLabel", {
                    question: labels.question
                })}
                summaryQuestionLabel={t("quiz:quizSummaryLabel", {
                    quiz: labels.quiz
                })}
                onReviewPress={onReviewPress}
                onSummaryPress={onSummaryPress}
                disableSummaryButton={disableSummaryButton}
            />
        )
    );
}

# setQuizScreenHeader(QuizScreenHeader)

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.

Parameters:
Name Type Description
QuizScreenHeader React.ComponentType.<QuizScreenHeaderProps>
Example

Add a timer on the quiz screen header

//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} />)
}

# setQuizStartButton(QuizStartButton)

You can use this to customize the "Start Quiz" button that appears when opening the QuizSingleScreen.

Parameters:
Name Type Description
QuizStartButton React.ComponentType.<QuizStartButtonProps>
Example
import AppButton from "@src/components/AppButton";

export const applyCustomCode = (externalCodeSetup) => {
    externalCodeSetup.quizApi.setQuizStartButton(props => {
        const {
            showStart,
            styles,
            global,
            quiz,
            checkForm,
            startQuiz,
            quizStartLoading,
            isResumeQuiz,
            t,
            labels
        } = props;

        return showStart ? (
            <View
                style={[
                    styles.buttonWrap,
                    {paddingBottom: styles.containerPadding},
                    global.quizStartButtonContainer
                ]}
            >
                <AppButton
                    style={[
                        styles.button,
                        {
                            backgroundColor: styles.buttonBackground
                        },
                        global.quizStartButton
                    ]}
                    onPress={() => {
                        if (quiz.formActivated && !checkForm(quiz)) {
                            return false;
                        } else if (!quizStartLoading) {
                            startQuiz(quiz);
                        }
                    }}
                    label={t(isResumeQuiz ? "quiz:continue" : "quiz:start", {
                        quiz: labels.quiz
                    })}
                    labelStyle={global.quizStartButtonLabel}
                    global={global}
                    loading={quizStartLoading}
                    loadingStyle={global.quizStartButtonLoading}
                />
            </View>
        ) : (
            <></>
        );

    });
}

# setQuizTimer(QuizTimer)

You can use this hook to customize the QuizTimer component.

Parameters:
Name Type Description
QuizTimer React.ComponentType.<QuizTimerProps>
Example
...

import Icon from "@src/components/Icon";
import TimeCountDown from "@src/components/TimerCountDown";

export const applyCustomCode = (externalCodeSetup: ExternalCodeSetup) => {
    externalCodeSetup.quizApi.setQuizTimer(
        ({
            showTimer,
            item,
            global,
            colors,
            styles,
            timerRef,
            onTimePassed,
            onTimeOver
        }) =>
            showTimer ? (
                <View style={styles.container}>
                    <Icon
                        icon={{fontIconName: "stopwatch", weight: 400}}
                        webIcon={"IconArrowBack"}
                        tintColor={colors.textIconColor}
                        styles={styles.icon}
                    />
                    <TimeCountDown
                        ref={timerRef}
                        paused={false}
                        onTimePassed={onTimePassed}
                        onTimeOver={onTimeOver}
                        secondsLimit={item.timeLimit}
                        textProps={{style: global.timer}}
                    />
                    <Text>The timer</Text>
                </View>
            ) : (
                <></>
            )
    );
}

# setQuizTitleComponent(QuizTitleComponent)

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.

Parameters:
Name Type Description
QuizTitleComponent React.ComponentType.<QuizTitleComponentProps>
Example

Add more quiz details to component

//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} />)
}

# setWebViewContentComponent(WebViewContentComponent)

You can use this hook to replace the webview being used in the quiz content. For example, you can choose to replace it with the default react-native webview.

Parameters:
Name Type Description
WebViewContentComponent React.ComponentType.<QWebViewContentComponentProps>
Example
...

import WebViewWithMore from "@src/components/WebViewWithMore";
export const applyCustomCode = (externalCodeSetup) => {
    externalCodeSetup.quizApi.setWebViewContentComponent(
        ({
            online,
            t,
            onShouldStartLoadWithRequest,
            height,
            source,
            global,
            colors,
            ModalHeaderComponent
        }) => (
            <WebViewWithMore
                online={online}
                t={t}
                onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
                height={height}
                source={source}
                global={global}
                colors={colors}
                ModalHeaderComponent={ModalHeaderComponent}
            />
        )
    );
}

# setWrapQuestionHtmlFilter(wrapQuestionHtmlFilter)

We use an HTML wrapper for "Fill in the blank" questions to accept input answers. You can use this to change the HTML output.

Parameters:
Name Type Description
wrapQuestionHtmlFilter OverrideQuestionHTMLWrapperCallback
Example

Add additional details below the input field

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;
 })