diff --git a/src/core/ExamStateProvider.jsx b/src/core/ExamStateProvider.jsx index ced1d07c..8519a822 100644 --- a/src/core/ExamStateProvider.jsx +++ b/src/core/ExamStateProvider.jsx @@ -13,6 +13,12 @@ import { IS_STARTED_STATUS } from '../constants'; // eslint-disable-next-line react/prop-types const StateProvider = ({ children, ...state }) => { + // This is bizzare and unecessary maybe, or maybe it wasn't documented as to why + // They took stuff out of redux and put it in a context, but they could have just used the redux store??? + // This was meant to only make "showTimer" change when the state would change. + // useMemo is used when computations are expensive + // But this isn't so why use it here? + // Idk why this is optimal const contextValue = useMemo(() => ({ ...state, showTimer: !!(state.activeAttempt && IS_STARTED_STATUS(state.activeAttempt.attempt_status)), diff --git a/src/core/OuterExamTimer.jsx b/src/core/OuterExamTimer.jsx index f5ef7fbf..0393b7f9 100644 --- a/src/core/OuterExamTimer.jsx +++ b/src/core/OuterExamTimer.jsx @@ -1,19 +1,32 @@ import React, { useEffect, useContext } from 'react'; +import { useSelector } from 'react-redux'; import PropTypes from 'prop-types'; import { AppContext } from '@edx/frontend-platform/react'; -import ExamStateContext from '../context'; + +// new imports +// import ExamStateContext from '../context'; +import { IS_STARTED_STATUS } from '../constants'; +import { stopExam, submitExam, expireExam, pollAttempt, pingAttempt, getLatestAttemptData } from '../data/thunks'; +import * as selectors from '../data/selectors'; + import { ExamTimerBlock } from '../timer'; import ExamAPIError from '../exam/ExamAPIError'; -import ExamStateProvider from './ExamStateProvider'; +// import ExamStateProvider from './ExamStateProvider'; const ExamTimer = ({ courseId }) => { - const state = useContext(ExamStateContext); + // const state = useContext(ExamStateContext); const { authenticatedUser } = useContext(AppContext); - const { - activeAttempt, showTimer, stopExam, submitExam, - expireExam, pollAttempt, apiErrorMsg, pingAttempt, - getLatestAttemptData, - } = state; + + // const { + // activeAttempt, showTimer, apiErrorMsg, + // } = state; + const activeAttempt = useSelector(selectors.activeAttempt); + // TODO: The logic surrounding this showTimer var is WEIRD. + //TODO:Move this boolean to some file to encapsulate. Probably best in constants also there's no hooks file. + const showTimer = !!(activeAttempt && IS_STARTED_STATUS(activeAttempt.attempt_status)); + // console.log(showTimer); + console.log(activeAttempt); + const apiErrorMsg = useSelector(selectors.apiErrorMsg); useEffect(() => { getLatestAttemptData(courseId); @@ -53,9 +66,9 @@ ExamTimer.propTypes = { * will be shown. */ const OuterExamTimer = ({ courseId }) => ( - - - + // + + // ); OuterExamTimer.propTypes = { diff --git a/src/data/selectors.js b/src/data/selectors.js new file mode 100644 index 00000000..a5499820 --- /dev/null +++ b/src/data/selectors.js @@ -0,0 +1,29 @@ +/* +Slice: activeAttempt, apiErrorMsg +In ExamStateProviders, needs to be moved to slice: showTimer, +Thunks, which need to be imported instead: stopExam, submitExam, expireExam, pollAttempt, pingAttempt, getLatestAttemptData, +*/ + +// TODO: Perhaps name the slice 'name:' var to 'currentExam', +// or change the one in frontend-app-exams-dashboard to 'examsList' or something. + +/* +I got this error: + + OuterExamTimer › is successfully rendered and shows timer if there is an exam in progress + + TypeError: Cannot read properties of undefined (reading 'activeAttempt') + + 7 | // TODO: Perhaps name the slice 'name:' var to 'currentExam', + 8 | // or change the one in frontend-app-exams-dashboard to 'examsList' or something. + > 9 | export const activeAttempt = state => state.exam.activeAttempt; + | ^ + 10 | export const apiErrorMsg = state => state.exam.apiErrorMsg; + 11 | export const showTimer = state => state.exam.showTimer; + +Why isn't the selector working? +*/ +export const activeAttempt = state => state.examState.activeAttempt; +export const apiErrorMsg = state => state.examState.apiErrorMsg; +export const showTimer = state => state.examState.showTimer; + diff --git a/src/data/slice.js b/src/data/slice.js index c049b536..7e1f890f 100644 --- a/src/data/slice.js +++ b/src/data/slice.js @@ -66,6 +66,7 @@ export const examSlice = createSlice({ exam_access_token: '', exam_access_token_expiration: '', }, + // showTimer: !!(state.activeAttempt && IS_STARTED_STATUS(state.activeAttempt.attempt_status)), }, reducers: { setAllowProctoringOptOut: (state, { payload }) => { diff --git a/src/exam/ExamWrapper.test.jsx b/src/exam/ExamWrapper.test.jsx index bee6ed74..e44d9f79 100644 --- a/src/exam/ExamWrapper.test.jsx +++ b/src/exam/ExamWrapper.test.jsx @@ -24,9 +24,19 @@ jest.mock('../data/thunks', () => { }; }); +// TODO: Make changes to what's mocked. should only be API functions and react/redux initialState stuff. + +// When we mock out the thunks below, we stamp out a lot of the non-async stuff +// Like loading states and other redux goodness. We can't ever change the loading state if +// The function it's in has been mocked away. +// The ONLY thing we should need to mock out in this app are API REST calls. getExamAttemptsData.mockReturnValue(jest.fn()); startTimedExam.mockReturnValue(jest.fn()); +// No idea what "subscribe" does but it probably shouldn't be mocked store.subscribe = jest.fn(); +// We should not mock this. This makes the dispatch function useless. It just needs the right setup to work. +// Things were mocked, then broke, then they mocked other things that didn't need to be mocked. +// This all can work out of the box with the right set up store.dispatch = jest.fn(); describe('SequenceExamWrapper', () => {