Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Progress update fix #762

Merged
merged 2 commits into from
May 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 18 additions & 43 deletions packages/moocfi-quizzes/src/CourseStatusProvider/index.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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"
Expand Down Expand Up @@ -69,7 +67,6 @@ export const CourseStatusProvider: React.FunctionComponent<CourseStatusProviderP
const fetchProgressData = async () => {
try {
const progressData = await getUserCourseData(courseId, accessToken)

const data = transformData(progressData.currentUser, progressData.course)
setData(data)
setLoading(false)
Expand All @@ -85,6 +82,12 @@ export const CourseStatusProvider: React.FunctionComponent<CourseStatusProviderP
}
}

const progress: CourseProgressProviderInterface = {
error,
loading,
courseProgressData: data,
}

const logout = () => {
setLoading(true)
setError(false)
Expand All @@ -107,21 +110,15 @@ export const CourseStatusProvider: React.FunctionComponent<CourseStatusProviderP
setUpdateQuiz({ ...updateQuiz, [id]: false })
}

const progress: CourseProgressProviderInterface = {
error,
loading,
courseProgressData: data,
courseId,
accessToken,
}

const status: CourseStatusProviderInterface = {
updateQuiz,
quizUpdated,
}

return (
<CourseProgressProvider courseProgress={progress}>
<CourseProgressProviderContext.Provider
value={{ progress, fetchProgressData }}
>
<CourseStatusProviderContext.Provider value={status}>
<ToastContainer
enableMultiContainer
Expand All @@ -140,7 +137,7 @@ export const CourseStatusProvider: React.FunctionComponent<CourseStatusProviderP
/>
{children}
</CourseStatusProviderContext.Provider>
</CourseProgressProvider>
</CourseProgressProviderContext.Provider>
)
}

Expand All @@ -150,34 +147,18 @@ export const injectCourseProgress = <P extends CourseProgressProviderInterface>(
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<HTMLElement>()
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]) => {
Expand All @@ -193,7 +174,7 @@ export const injectCourseProgress = <P extends CourseProgressProviderInterface>(
1000

if (secondsOutOfView >= outOfViewThreshold) {
await refetchData()
await fetchProgressData()
}

// reset off view counter
Expand All @@ -220,13 +201,7 @@ export const injectCourseProgress = <P extends CourseProgressProviderInterface>(
}
}, [])

const isLoggedInAndLoading = accessToken && state.loading

return (
<div ref={ref}>
{!isLoggedInAndLoading && <Component {...props} {...injectProps} />}
</div>
)
return <div ref={ref}>{<Component {...props} {...injectProps} />}</div>
}

const transformData = (
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Exercise, "exercise_completions">[]
}

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<void>
}

export const CourseProgressProviderContext = createContext<
CourseProgressProviderContextInterface
>({ progress: {}, fetchProgressData: () => Promise.resolve() })
Loading