diff --git a/packages/moocfi-quizzes/src/CourseStatusProvider/index.tsx b/packages/moocfi-quizzes/src/CourseStatusProvider/index.tsx index 7b4c14c9..a16ae8a2 100644 --- a/packages/moocfi-quizzes/src/CourseStatusProvider/index.tsx +++ b/packages/moocfi-quizzes/src/CourseStatusProvider/index.tsx @@ -1,5 +1,4 @@ -import * as React from "react" -import { useEffect, useRef, useState } from "react" +import React, { useContext, useEffect, useRef, useState } from "react" import { CourseProgressProviderInterface, ProgressData, @@ -9,9 +8,8 @@ import { ProgressResponse, ExerciseCompletionsBySection, CourseResponse, - CourseProgressProvider, - useCourseProgressState, } from "../contexes/courseProgressProviderContext" +import { CourseProgressProviderContext } from "../contexes/courseProgressProviderContext" import { PointsByGroup } from "../modelTypes" import { languageOptions } from "../utils/languages" import { ToastContainer, toast, TypeOptions } from "react-toastify" @@ -69,7 +67,6 @@ export const CourseStatusProvider: React.FunctionComponent { try { const progressData = await getUserCourseData(courseId, accessToken) - const data = transformData(progressData.currentUser, progressData.course) setData(data) setLoading(false) @@ -85,6 +82,12 @@ export const CourseStatusProvider: React.FunctionComponent { setLoading(true) setError(false) @@ -107,21 +110,15 @@ export const CourseStatusProvider: React.FunctionComponent + {children} - + ) } @@ -150,34 +147,18 @@ export const injectCourseProgress =

( props: P, ) => { // initial course progress - const { state } = useCourseProgressState() - // course progress with updates - const { courseId, accessToken, ...rest } = state - const [injectProps, setInjectProps] = useState(rest) + const { progress: injectProps, fetchProgressData } = useContext( + CourseProgressProviderContext, + ) // Ref for the wrapped element const ref: any = useRef() const rootMargin = "0px" - const outOfViewThreshold = 10 - - const refetchData = async () => { - if (courseId && accessToken) { - // fetch data - setInjectProps({ ...injectProps, loading: true }) - const progressData = await getUserCourseData(courseId, accessToken) - const data = transformData(progressData.currentUser, progressData.course) - setInjectProps({ - ...injectProps, - courseProgressData: data, - loading: false, - }) - } - } + const outOfViewThreshold = 4 /** * Triggers a refetch of progress after given time interval * */ - useEffect(() => { const observer = new IntersectionObserver( async ([entry]) => { @@ -193,7 +174,7 @@ export const injectCourseProgress =

( 1000 if (secondsOutOfView >= outOfViewThreshold) { - await refetchData() + await fetchProgressData() } // reset off view counter @@ -220,13 +201,7 @@ export const injectCourseProgress =

( } }, []) - const isLoggedInAndLoading = accessToken && state.loading - - return ( -

- {!isLoggedInAndLoading && } -
- ) + return
{}
} const transformData = ( diff --git a/packages/moocfi-quizzes/src/contexes/courseProgressProviderContext.ts b/packages/moocfi-quizzes/src/contexes/courseProgressProviderContext.ts new file mode 100644 index 00000000..7a30af57 --- /dev/null +++ b/packages/moocfi-quizzes/src/contexes/courseProgressProviderContext.ts @@ -0,0 +1,98 @@ +import React, { createContext, useContext, useReducer } from "react" + +import { ProviderBaseInterface } from "./courseStatusProviderContext" +import { PointsByGroup } from "../modelTypes" + +export interface ProgressResponse { + user_course_progressess: UserCourseProgress + completions: Completion[] +} + +export interface CourseResponse { + exercises: Omit[] +} + +export interface UserCourseProgress { + max_points: number + n_points: number + progress: PointsByGroup[] + course: Course +} + +export interface Completion { + id: string +} + +export interface Course { + points_needed: number + exercises: Exercise[] +} + +export type Exercise = { + id: string + quizzes_id: string + name: string + part: number + section: number + max_points: number + exercise_completions: ExerciseCompletion[] +} + +export type ExerciseCompletion = { + exercise_id: string + exercise_quizzes_id: string + part: number + section: number + n_points: number + completed: boolean + exercise_completion_required_actions: RequiredActionObject[] +} + +export interface ExerciseCompletionsBySection { + part: number + section: number + exercises_total: number + exercises_completed: number + required_actions: RequiredAction[] +} + +export interface RequiredActionObject { + value: RequiredAction +} + +export enum RequiredAction { + REJECTED = "REJECTED", + GIVE_PEER_REVIEW = "GIVE_PEER_REVIEW", + PENDING_PEER_REVIEW = "PENDING_PEER_REVIEW", +} + +export interface ProgressData { + completed: boolean + points_to_pass: number + n_points: number + max_points: number + exercise_completions: number + total_exercises: number + required_actions: RequiredAction[] + progress: PointsByGroup[] + exercises: Exercise[] + answers: ExerciseCompletion[] + exercise_completions_by_section: ExerciseCompletionsBySection[] +} + +export interface CourseProgressProviderInterface extends ProviderBaseInterface { + error?: boolean + loading?: boolean + courseProgressData?: ProgressData + courseId?: string + accessToken?: string +} + +interface CourseProgressProviderContextInterface { + progress: CourseProgressProviderInterface + fetchProgressData: () => Promise +} + +export const CourseProgressProviderContext = createContext< + CourseProgressProviderContextInterface +>({ progress: {}, fetchProgressData: () => Promise.resolve() }) diff --git a/packages/moocfi-quizzes/src/contexes/courseProgressProviderContext.tsx b/packages/moocfi-quizzes/src/contexes/courseProgressProviderContext.tsx deleted file mode 100644 index d51da0d8..00000000 --- a/packages/moocfi-quizzes/src/contexes/courseProgressProviderContext.tsx +++ /dev/null @@ -1,177 +0,0 @@ -import React, { createContext, useContext, useReducer } from "react" - -import { ProviderBaseInterface } from "./courseStatusProviderContext" -import { PointsByGroup } from "../modelTypes" - -export interface ProgressResponse { - user_course_progressess: UserCourseProgress - completions: Completion[] -} - -export interface CourseResponse { - exercises: Omit[] -} - -export interface UserCourseProgress { - max_points: number - n_points: number - progress: PointsByGroup[] - course: Course -} - -export interface Completion { - id: string -} - -export interface Course { - points_needed: number - exercises: Exercise[] -} - -export type Exercise = { - id: string - quizzes_id: string - name: string - part: number - section: number - max_points: number - exercise_completions: ExerciseCompletion[] -} - -export type ExerciseCompletion = { - exercise_id: string - exercise_quizzes_id: string - part: number - section: number - n_points: number - completed: boolean - exercise_completion_required_actions: RequiredActionObject[] -} - -export interface ExerciseCompletionsBySection { - part: number - section: number - exercises_total: number - exercises_completed: number - required_actions: RequiredAction[] -} - -export interface RequiredActionObject { - value: RequiredAction -} - -export enum RequiredAction { - REJECTED = "REJECTED", - GIVE_PEER_REVIEW = "GIVE_PEER_REVIEW", - PENDING_PEER_REVIEW = "PENDING_PEER_REVIEW", -} - -export interface ProgressData { - completed: boolean - points_to_pass: number - n_points: number - max_points: number - exercise_completions: number - total_exercises: number - required_actions: RequiredAction[] - progress: PointsByGroup[] - exercises: Exercise[] - answers: ExerciseCompletion[] - exercise_completions_by_section: ExerciseCompletionsBySection[] -} - -export interface CourseProgressProviderInterface extends ProviderBaseInterface { - error?: boolean - loading?: boolean - courseProgressData?: ProgressData - courseId?: string - accessToken?: string -} - -type CourseProgressProviderActionTypes = - | { type: "SET_LOADING"; payload: boolean } - | { type: "SET_COURSE_PROGRESS"; payload: ProgressData } - | { type: "SET_ERROR"; payload: boolean } - | { - type: "SET_ALL" - payload: CourseProgressProviderInterface - } - -// const initialState: CourseProgressProviderInterface = { -// error: false, -// loading: false, -// } - -export const courseProgressReducer = ( - state: CourseProgressProviderInterface, - action: CourseProgressProviderActionTypes, -): CourseProgressProviderInterface => { - switch (action.type) { - case "SET_LOADING": - return { ...state, loading: action.payload } - case "SET_COURSE_PROGRESS": - return { ...state, courseProgressData: { ...action.payload } } - case "SET_ERROR": - return { ...state, error: action.payload } - case "SET_ALL": - return { - ...state, - loading: action.payload.loading, - error: action.payload.error, - courseProgressData: action.payload.courseProgressData, - } - default: - throw new Error(`Unhandled action type.}`) - } -} - -const CourseProgressContext = createContext< - | { - state: CourseProgressProviderInterface - dispatch: React.Dispatch - } - | undefined ->(undefined) - -export const setLoading = ( - value: boolean, -): CourseProgressProviderActionTypes => { - return { - type: "SET_LOADING", - payload: value, - } -} - -export const setAll = ( - value: CourseProgressProviderInterface, -): CourseProgressProviderActionTypes => { - return { - type: "SET_ALL", - payload: value, - } -} - -export const CourseProgressProvider = ({ - children, - courseProgress, -}: { - children: React.ReactElement - courseProgress: CourseProgressProviderInterface -}): JSX.Element => { - const [_, dispatch] = useReducer(courseProgressReducer, courseProgress) - return ( - - {children} - - ) -} - -export const useCourseProgressState = () => { - const context = useContext(CourseProgressContext) - if (context === undefined) { - throw new Error( - "useCourserProgressState must be used within a CourseProgressProvider", - ) - } - return context -}