@coorpacademy/app-review 0.46.14-react18.80 → 0.46.14-react18.83
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/actions/api/fetch-correction.js +18 -0
- package/es/actions/api/fetch-rank.js +34 -0
- package/es/actions/api/fetch-skill.js +16 -0
- package/es/actions/api/fetch-slide.js +31 -0
- package/es/actions/api/fetch-slides-to-review-by-skill-ref.js +32 -0
- package/es/actions/api/fetch-video-props.js +34 -0
- package/es/actions/api/post-answer.js +36 -0
- package/es/actions/api/post-progression.js +25 -0
- package/es/actions/data/token.js +5 -0
- package/es/actions/ui/answers.js +54 -0
- package/es/actions/ui/navigation.js +26 -0
- package/es/actions/ui/next-slide.js +33 -0
- package/es/actions/ui/quit-popin.js +8 -0
- package/es/actions/ui/show-button-revising.js +6 -0
- package/es/actions/ui/slides.js +10 -0
- package/es/common/index.js +10 -0
- package/es/configure-store.js +26 -0
- package/es/helpers/css-register.js +2 -0
- package/es/index.js +48 -0
- package/es/reducers/data/corrections.js +25 -0
- package/es/reducers/data/current-skill.js +13 -0
- package/es/reducers/data/index.js +17 -0
- package/es/reducers/data/progression.js +20 -0
- package/es/reducers/data/rank.js +27 -0
- package/es/reducers/data/slides.js +20 -0
- package/es/reducers/data/token.js +11 -0
- package/es/reducers/data/videos.js +33 -0
- package/es/reducers/index.js +7 -0
- package/es/reducers/ui/answers.js +28 -0
- package/es/reducers/ui/current-slide-ref.js +21 -0
- package/es/reducers/ui/index.js +19 -0
- package/es/reducers/ui/navigation.js +18 -0
- package/es/reducers/ui/positions.js +30 -0
- package/es/reducers/ui/quit-popin.js +16 -0
- package/es/reducers/ui/show-button-revising.js +17 -0
- package/es/reducers/ui/show-congrats.js +20 -0
- package/es/reducers/ui/slide.js +52 -0
- package/es/types/common.js +1 -0
- package/es/types/slides.js +1 -0
- package/es/views/slides/index.js +387 -0
- package/es/views/slides/map-api-slide-to-ui.js +177 -0
- package/lib/actions/api/fetch-correction.js +25 -0
- package/lib/actions/api/fetch-rank.js +43 -0
- package/lib/actions/api/fetch-skill.js +23 -0
- package/lib/actions/api/fetch-slide.js +38 -0
- package/lib/actions/api/fetch-slides-to-review-by-skill-ref.js +39 -0
- package/lib/actions/api/fetch-video-props.js +43 -0
- package/lib/actions/api/post-answer.js +43 -0
- package/lib/actions/api/post-progression.js +32 -0
- package/lib/actions/data/token.js +9 -0
- package/lib/actions/ui/answers.js +61 -0
- package/lib/actions/ui/navigation.js +31 -0
- package/lib/actions/ui/next-slide.js +40 -0
- package/lib/actions/ui/quit-popin.js +11 -0
- package/lib/actions/ui/show-button-revising.js +10 -0
- package/lib/actions/ui/slides.js +14 -0
- package/lib/common/index.js +17 -0
- package/lib/configure-store.js +32 -0
- package/lib/helpers/css-register.js +7 -0
- package/lib/index.js +76 -0
- package/lib/reducers/data/corrections.js +30 -0
- package/lib/reducers/data/current-skill.js +15 -0
- package/lib/reducers/data/index.js +22 -0
- package/lib/reducers/data/progression.js +22 -0
- package/lib/reducers/data/rank.js +32 -0
- package/lib/reducers/data/slides.js +26 -0
- package/lib/reducers/data/token.js +13 -0
- package/lib/reducers/data/videos.js +36 -0
- package/lib/reducers/index.js +12 -0
- package/lib/reducers/ui/answers.js +31 -0
- package/lib/reducers/ui/current-slide-ref.js +23 -0
- package/lib/reducers/ui/index.js +24 -0
- package/lib/reducers/ui/navigation.js +20 -0
- package/lib/reducers/ui/positions.js +35 -0
- package/lib/reducers/ui/quit-popin.js +18 -0
- package/lib/reducers/ui/show-button-revising.js +19 -0
- package/lib/reducers/ui/show-congrats.js +22 -0
- package/lib/reducers/ui/slide.js +58 -0
- package/lib/types/common.js +2 -0
- package/lib/types/slides.js +2 -0
- package/lib/views/slides/index.js +395 -0
- package/lib/views/slides/map-api-slide-to-ui.js +182 -0
- package/package.json +4 -4
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import buildTask from '@coorpacademy/redux-task';
|
|
2
|
+
import get from 'lodash/fp/get';
|
|
3
|
+
export const CORRECTION_FETCH_REQUEST = '@@correction/FETCH_REQUEST';
|
|
4
|
+
export const CORRECTION_FETCH_SUCCESS = '@@correction/FETCH_SUCCESS';
|
|
5
|
+
export const CORRECTION_FETCH_FAILURE = '@@correction/FETCH_FAILURE';
|
|
6
|
+
export const fetchCorrection = (dispatch, getState, { services }) => {
|
|
7
|
+
const state = getState();
|
|
8
|
+
const slideRef = get(['ui', 'currentSlideRef'], state);
|
|
9
|
+
const token = get(['data', 'token'], state);
|
|
10
|
+
const progressionId = get(['data', 'progression', '_id'], state);
|
|
11
|
+
const answer = get(['ui', 'answers', slideRef], state);
|
|
12
|
+
const action = buildTask({
|
|
13
|
+
types: [CORRECTION_FETCH_REQUEST, CORRECTION_FETCH_SUCCESS, CORRECTION_FETCH_FAILURE],
|
|
14
|
+
meta: { slideRef },
|
|
15
|
+
task: () => services.fetchCorrection(slideRef, token, progressionId, answer)
|
|
16
|
+
});
|
|
17
|
+
return dispatch(action);
|
|
18
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import buildTask from '@coorpacademy/redux-task';
|
|
2
|
+
import get from 'lodash/fp/get';
|
|
3
|
+
export const RANK_FETCH_START_REQUEST = '@@rank/FETCH_START_REQUEST';
|
|
4
|
+
export const RANK_FETCH_START_SUCCESS = '@@rank/FETCH_START_SUCCESS';
|
|
5
|
+
export const RANK_FETCH_START_FAILURE = '@@rank/FETCH_START_FAILURE';
|
|
6
|
+
export const RANK_FETCH_END_REQUEST = '@@rank/FETCH_END_REQUEST';
|
|
7
|
+
export const RANK_FETCH_END_SUCCESS = '@@rank/FETCH_END_SUCCESS';
|
|
8
|
+
export const RANK_FETCH_END_FAILURE = '@@rank/FETCH_END_FAILURE';
|
|
9
|
+
const bailout = (path) => (state) => {
|
|
10
|
+
const value = get(path, state);
|
|
11
|
+
return !Number.isNaN(value);
|
|
12
|
+
};
|
|
13
|
+
export const fetchRank = (dispatch, getState, services, types, path) => {
|
|
14
|
+
const action = buildTask({
|
|
15
|
+
types,
|
|
16
|
+
task: () => {
|
|
17
|
+
const state = getState();
|
|
18
|
+
const token = get(['data', 'token'], state);
|
|
19
|
+
return services.fetchRank(token);
|
|
20
|
+
},
|
|
21
|
+
bailout: path ? bailout(path) : undefined
|
|
22
|
+
});
|
|
23
|
+
return dispatch(action);
|
|
24
|
+
};
|
|
25
|
+
export const fetchStartRank = (dispatch, getState, { services }) => {
|
|
26
|
+
return fetchRank(dispatch, getState, services, [RANK_FETCH_START_REQUEST, RANK_FETCH_START_SUCCESS, RANK_FETCH_START_FAILURE], 'data.rank.start');
|
|
27
|
+
};
|
|
28
|
+
export const fetchEndRank = (dispatch, getState, { services }) => {
|
|
29
|
+
return fetchRank(dispatch, getState, services, [
|
|
30
|
+
RANK_FETCH_END_REQUEST,
|
|
31
|
+
RANK_FETCH_END_SUCCESS,
|
|
32
|
+
RANK_FETCH_END_FAILURE
|
|
33
|
+
]);
|
|
34
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import get from 'lodash/fp/get';
|
|
2
|
+
import buildTask from '@coorpacademy/redux-task';
|
|
3
|
+
export const SKILL_FETCH_REQUEST = '@@skill/FETCH_REQUEST';
|
|
4
|
+
export const SKILL_FETCH_SUCCESS = '@@skill/FETCH_SUCCESS';
|
|
5
|
+
export const SKILL_FETCH_FAILURE = '@@skill/FETCH_FAILURE';
|
|
6
|
+
export const fetchSkill = (skillRef) => (dispatch, getState, { services }) => {
|
|
7
|
+
const action = buildTask({
|
|
8
|
+
types: [SKILL_FETCH_REQUEST, SKILL_FETCH_SUCCESS, SKILL_FETCH_FAILURE],
|
|
9
|
+
task: () => {
|
|
10
|
+
const state = getState();
|
|
11
|
+
const token = get(['data', 'token'], state);
|
|
12
|
+
return services.fetchSkill(skillRef, token);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
return dispatch(action);
|
|
16
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import buildTask from '@coorpacademy/redux-task';
|
|
2
|
+
import get from 'lodash/fp/get';
|
|
3
|
+
import has from 'lodash/fp/has';
|
|
4
|
+
import isEmpty from 'lodash/fp/isEmpty';
|
|
5
|
+
import { setCurrentSlide } from '../ui/slides';
|
|
6
|
+
export const SLIDE_FETCH_REQUEST = '@@slides/FETCH_REQUEST';
|
|
7
|
+
export const SLIDE_FETCH_SUCCESS = '@@slides/FETCH_SUCCESS';
|
|
8
|
+
export const SLIDE_FETCH_FAILURE = '@@slides/FETCH_FAILURE';
|
|
9
|
+
export const fetchSlide = (slideRef) => async (dispatch, getState, { services }) => {
|
|
10
|
+
const action = buildTask({
|
|
11
|
+
types: [SLIDE_FETCH_REQUEST, SLIDE_FETCH_SUCCESS, SLIDE_FETCH_FAILURE],
|
|
12
|
+
bailout: (state) => {
|
|
13
|
+
return has(`data.slides.${slideRef}`, state);
|
|
14
|
+
},
|
|
15
|
+
task: () => {
|
|
16
|
+
const state = getState();
|
|
17
|
+
const token = get(['data', 'token'], state);
|
|
18
|
+
return services.fetchSlide(slideRef, token);
|
|
19
|
+
},
|
|
20
|
+
meta: { slideRef }
|
|
21
|
+
});
|
|
22
|
+
const response = await dispatch(action);
|
|
23
|
+
if (response.type === SLIDE_FETCH_SUCCESS) {
|
|
24
|
+
const slideFromAPI = response.payload;
|
|
25
|
+
const state = getState();
|
|
26
|
+
const slides = get('data.progression.state.slides', state);
|
|
27
|
+
if (isEmpty(slides)) {
|
|
28
|
+
dispatch(setCurrentSlide(slideFromAPI));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import buildTask from '@coorpacademy/redux-task';
|
|
2
|
+
import difference from 'lodash/fp/difference';
|
|
3
|
+
import map from 'lodash/fp/map';
|
|
4
|
+
import get from 'lodash/fp/get';
|
|
5
|
+
import { showButtonRevising } from '../ui/show-button-revising';
|
|
6
|
+
export const SLIDES_TO_REVIEW_FETCH_REQUEST = '@@slidesToReview/FETCH_REQUEST';
|
|
7
|
+
export const SLIDES_TO_REVIEW_FETCH_SUCCESS = '@@slidesToReview/FETCH_SUCCESS';
|
|
8
|
+
export const SLIDES_TO_REVIEW_FETCH_FAILURE = '@@slidesToReview/FETCH_FAILURE';
|
|
9
|
+
export const fetchSlidesToReviewBySkillRef = async (dispatch, getState, { services }) => {
|
|
10
|
+
const state = getState();
|
|
11
|
+
const action = buildTask({
|
|
12
|
+
types: [
|
|
13
|
+
SLIDES_TO_REVIEW_FETCH_REQUEST,
|
|
14
|
+
SLIDES_TO_REVIEW_FETCH_SUCCESS,
|
|
15
|
+
SLIDES_TO_REVIEW_FETCH_FAILURE
|
|
16
|
+
],
|
|
17
|
+
task: () => {
|
|
18
|
+
const token = state.data.token;
|
|
19
|
+
const progression = state.data.progression;
|
|
20
|
+
const skillRef = progression.content.ref;
|
|
21
|
+
return services.fetchSlidesToReviewBySkillRef(token, skillRef, 10);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
const response = await dispatch(action);
|
|
25
|
+
if (response.type === SLIDES_TO_REVIEW_FETCH_SUCCESS) {
|
|
26
|
+
const answeredSlidesRef = get(['data', 'progression', 'state', 'slides'], state);
|
|
27
|
+
const slidesToReview = map(({ slideId }) => slideId, response.payload);
|
|
28
|
+
if (difference(slidesToReview, answeredSlidesRef).length >= 5) {
|
|
29
|
+
dispatch(showButtonRevising);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import get from 'lodash/fp/get';
|
|
2
|
+
export const SET_VIDEO_PROPS = '@@slide/SET_VIDEO_PROPS';
|
|
3
|
+
export const SHOW_VIDEO = '@@slide/SHOW_VIDEO';
|
|
4
|
+
export const setVideoProps = (payload) => ({
|
|
5
|
+
type: SET_VIDEO_PROPS,
|
|
6
|
+
payload
|
|
7
|
+
});
|
|
8
|
+
export const showVideo = (slideId) => ({
|
|
9
|
+
type: SHOW_VIDEO,
|
|
10
|
+
payload: { slideId }
|
|
11
|
+
});
|
|
12
|
+
export const fetchPropsVideo = (slideId) => async (dispatch, getState, { appendVideoOptions }) => {
|
|
13
|
+
const state = getState();
|
|
14
|
+
const slideFromAPI = get(['data', 'slides', slideId], state);
|
|
15
|
+
if (!slideFromAPI) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const slideMedia = get(['question', 'medias', '0'], slideFromAPI);
|
|
19
|
+
if (slideMedia && slideMedia.type === 'video') {
|
|
20
|
+
const videoProps = get(['data', 'videos', slideId], state);
|
|
21
|
+
if (videoProps) {
|
|
22
|
+
dispatch(showVideo(slideId));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const props = await appendVideoOptions(slideMedia);
|
|
26
|
+
props.src[0].loading = false; // set to false to not show it until the next slide is unstack
|
|
27
|
+
props.src[0].type = 'video';
|
|
28
|
+
dispatch(setVideoProps({
|
|
29
|
+
slideId,
|
|
30
|
+
props
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
return;
|
|
34
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import buildTask from '@coorpacademy/redux-task';
|
|
2
|
+
import get from 'lodash/fp/get';
|
|
3
|
+
import { fetchCorrection } from './fetch-correction';
|
|
4
|
+
import { fetchSlide } from './fetch-slide';
|
|
5
|
+
import { fetchStartRank } from './fetch-rank';
|
|
6
|
+
export const POST_ANSWER_REQUEST = '@@answer/POST_REQUEST';
|
|
7
|
+
export const POST_ANSWER_SUCCESS = '@@answer/POST_SUCCESS';
|
|
8
|
+
export const POST_ANSWER_FAILURE = '@@answer/POST_FAILURE';
|
|
9
|
+
export const postAnswer = async (dispatch, getState, { services, onEndProgression }) => {
|
|
10
|
+
const state = getState();
|
|
11
|
+
const currentSlideRef = get(['ui', 'currentSlideRef'], state);
|
|
12
|
+
const token = get(['data', 'token'], state);
|
|
13
|
+
const answer = get(['ui', 'answers', currentSlideRef], state);
|
|
14
|
+
const progression = get(['data', 'progression'], state);
|
|
15
|
+
if (!progression)
|
|
16
|
+
throw new Error('Cannot answer a question of an inexistent progression');
|
|
17
|
+
const action = buildTask({
|
|
18
|
+
types: [POST_ANSWER_REQUEST, POST_ANSWER_SUCCESS, POST_ANSWER_FAILURE],
|
|
19
|
+
meta: { slideRef: currentSlideRef },
|
|
20
|
+
task: () => services.postAnswer(progression, token, answer)
|
|
21
|
+
});
|
|
22
|
+
const response = await dispatch(action);
|
|
23
|
+
if (response.type === POST_ANSWER_SUCCESS) {
|
|
24
|
+
const updatedProgression = response.payload;
|
|
25
|
+
const slideRef = updatedProgression.state.nextContent.ref;
|
|
26
|
+
if (slideRef !== 'successExitNode') {
|
|
27
|
+
await dispatch(fetchSlide(slideRef));
|
|
28
|
+
await dispatch(fetchCorrection);
|
|
29
|
+
await dispatch(fetchStartRank);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
onEndProgression(updatedProgression);
|
|
33
|
+
await dispatch(fetchCorrection);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import buildTask from '@coorpacademy/redux-task';
|
|
2
|
+
import get from 'lodash/fp/get';
|
|
3
|
+
import { fetchSlide } from './fetch-slide';
|
|
4
|
+
import { fetchSkill } from './fetch-skill';
|
|
5
|
+
export const POST_PROGRESSION_REQUEST = '@@progression/POST_REQUEST';
|
|
6
|
+
export const POST_PROGRESSION_SUCCESS = '@@progression/POST_SUCCESS';
|
|
7
|
+
export const POST_PROGRESSION_FAILURE = '@@progression/POST_FAILURE';
|
|
8
|
+
export const postProgression = (skillRef, testingSlideRef) => async (dispatch, getState, { services, onStartProgression }) => {
|
|
9
|
+
const state = getState();
|
|
10
|
+
const token = get(['data', 'token'], state);
|
|
11
|
+
const action = buildTask({
|
|
12
|
+
types: [POST_PROGRESSION_REQUEST, POST_PROGRESSION_SUCCESS, POST_PROGRESSION_FAILURE],
|
|
13
|
+
task: () => testingSlideRef
|
|
14
|
+
? services.postSandboxProgression(testingSlideRef, skillRef, token)
|
|
15
|
+
: services.postProgression(skillRef, token)
|
|
16
|
+
});
|
|
17
|
+
const response = await dispatch(action);
|
|
18
|
+
if (response.type === POST_PROGRESSION_SUCCESS) {
|
|
19
|
+
const progression = response.payload;
|
|
20
|
+
const slideRef = progression.state.nextContent.ref;
|
|
21
|
+
onStartProgression(progression);
|
|
22
|
+
await dispatch(fetchSlide(slideRef));
|
|
23
|
+
await dispatch(fetchSkill(skillRef));
|
|
24
|
+
}
|
|
25
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import flatten from 'lodash/fp/flatten';
|
|
2
|
+
import get from 'lodash/fp/get';
|
|
3
|
+
import getOr from 'lodash/fp/getOr';
|
|
4
|
+
import includes from 'lodash/fp/includes';
|
|
5
|
+
import pull from 'lodash/fp/pull';
|
|
6
|
+
export const EDIT_QCM = '@@answer/EDIT_QCM';
|
|
7
|
+
export const EDIT_QCM_GRAPHIC = '@@answer/EDIT_QCM_GRAPHIC';
|
|
8
|
+
export const EDIT_QCM_DRAG = '@@answer/EDIT_QCM_DRAG';
|
|
9
|
+
export const EDIT_TEMPLATE = '@@answer/EDIT_TEMPLATE';
|
|
10
|
+
export const EDIT_BASIC = '@@answer/EDIT_BASIC';
|
|
11
|
+
export const EDIT_SLIDER = '@@answer/EDIT_SLIDER';
|
|
12
|
+
export const ANSWER_EDIT = {
|
|
13
|
+
qcm: EDIT_QCM,
|
|
14
|
+
qcmGraphic: EDIT_QCM_GRAPHIC,
|
|
15
|
+
qcmDrag: EDIT_QCM_DRAG,
|
|
16
|
+
template: EDIT_TEMPLATE,
|
|
17
|
+
basic: EDIT_BASIC,
|
|
18
|
+
slider: EDIT_SLIDER
|
|
19
|
+
};
|
|
20
|
+
const buildAnswer = (userAnswers, questionType, newValue) => {
|
|
21
|
+
switch (questionType) {
|
|
22
|
+
case 'qcm':
|
|
23
|
+
case 'qcmGraphic':
|
|
24
|
+
case 'qcmDrag': {
|
|
25
|
+
const firstValue = newValue[0];
|
|
26
|
+
return includes(firstValue, userAnswers)
|
|
27
|
+
? pull(firstValue, userAnswers)
|
|
28
|
+
: flatten([...userAnswers, firstValue]);
|
|
29
|
+
}
|
|
30
|
+
case 'basic':
|
|
31
|
+
case 'slider':
|
|
32
|
+
case 'template':
|
|
33
|
+
return newValue;
|
|
34
|
+
/* istanbul ignore next */ default:
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
export const editAnswer = (answer) => (dispatch, getState) => {
|
|
39
|
+
const state = getState();
|
|
40
|
+
const currentSlideRef = get(['ui', 'currentSlideRef'], state);
|
|
41
|
+
const userAnswers = getOr([], ['ui', 'answers', currentSlideRef], state);
|
|
42
|
+
const slide = get(['data', 'slides', currentSlideRef], state);
|
|
43
|
+
if (!slide)
|
|
44
|
+
throw new Error('No slide was found');
|
|
45
|
+
const questionType = get(['question', 'type'], slide);
|
|
46
|
+
const type = get(questionType, ANSWER_EDIT);
|
|
47
|
+
if (!type)
|
|
48
|
+
throw new Error(`Question type ${questionType} is not supported`);
|
|
49
|
+
return dispatch({
|
|
50
|
+
type,
|
|
51
|
+
meta: { slideRef: currentSlideRef },
|
|
52
|
+
payload: buildAnswer(userAnswers, questionType, answer)
|
|
53
|
+
});
|
|
54
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const NAVIGATE_TO = '@@navigation/NAVIGATE_TO';
|
|
2
|
+
export const NAVIGATE_BACK = '@@navigation/NAVIGATE_BACK';
|
|
3
|
+
export const START_APP = '@@navigation/START_APP';
|
|
4
|
+
export const navigateTo = (newViewName) => async (dispatch, getState, { callbackOnViewChanged }) => {
|
|
5
|
+
const action = {
|
|
6
|
+
type: NAVIGATE_TO,
|
|
7
|
+
payload: newViewName
|
|
8
|
+
};
|
|
9
|
+
const res = await dispatch(action);
|
|
10
|
+
if (callbackOnViewChanged) {
|
|
11
|
+
callbackOnViewChanged(newViewName);
|
|
12
|
+
}
|
|
13
|
+
return res;
|
|
14
|
+
};
|
|
15
|
+
export const navigateBack = async (dispatch, getState, { callbackOnViewChanged }) => {
|
|
16
|
+
const action = {
|
|
17
|
+
type: NAVIGATE_BACK
|
|
18
|
+
};
|
|
19
|
+
const res = await dispatch(action);
|
|
20
|
+
if (callbackOnViewChanged) {
|
|
21
|
+
const storeState = getState();
|
|
22
|
+
const viewName = storeState.ui.navigation[storeState.ui.navigation.length - 1];
|
|
23
|
+
callbackOnViewChanged(viewName);
|
|
24
|
+
}
|
|
25
|
+
return res;
|
|
26
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import filter from 'lodash/fp/filter';
|
|
2
|
+
import get from 'lodash/fp/get';
|
|
3
|
+
import { fetchEndRank } from '../api/fetch-rank';
|
|
4
|
+
import { fetchSlidesToReviewBySkillRef } from '../api/fetch-slides-to-review-by-skill-ref';
|
|
5
|
+
import { fetchPropsVideo } from '../api/fetch-video-props';
|
|
6
|
+
export const NEXT_SLIDE = '@@slide/NEXT_SLIDE';
|
|
7
|
+
export const nextSlide = async (dispatch, getState) => {
|
|
8
|
+
const state = getState();
|
|
9
|
+
const progression = state.data.progression;
|
|
10
|
+
const { isCorrect, allAnswers, slides } = progression.state;
|
|
11
|
+
const correctAnswers = filter((answer) => answer.isCorrect, allAnswers);
|
|
12
|
+
const nextSlideRef = get(['state', 'nextContent', 'ref'], state.data.progression);
|
|
13
|
+
const payload = {
|
|
14
|
+
currentSlideRef: get(['ui', 'currentSlideRef'], state),
|
|
15
|
+
nextSlideRef,
|
|
16
|
+
animationType: isCorrect ? 'unstack' : 'restack',
|
|
17
|
+
totalCorrectAnswers: correctAnswers.length,
|
|
18
|
+
answeredSlides: slides
|
|
19
|
+
};
|
|
20
|
+
const action = {
|
|
21
|
+
type: NEXT_SLIDE,
|
|
22
|
+
payload
|
|
23
|
+
};
|
|
24
|
+
if (nextSlideRef === 'successExitNode') {
|
|
25
|
+
await dispatch(fetchEndRank);
|
|
26
|
+
await dispatch(fetchSlidesToReviewBySkillRef);
|
|
27
|
+
dispatch(action);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
dispatch(action);
|
|
31
|
+
await dispatch(fetchPropsVideo(payload.nextSlideRef));
|
|
32
|
+
}
|
|
33
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { fetchPropsVideo } from '../api/fetch-video-props';
|
|
2
|
+
export const SET_CURRENT_SLIDE = '@@slide/SET_CURRENT_SLIDE';
|
|
3
|
+
export const setCurrentSlide = (slideFromAPI) => async (dispatch) => {
|
|
4
|
+
dispatch({
|
|
5
|
+
type: SET_CURRENT_SLIDE,
|
|
6
|
+
payload: slideFromAPI
|
|
7
|
+
});
|
|
8
|
+
await dispatch(fetchPropsVideo(slideFromAPI._id));
|
|
9
|
+
return;
|
|
10
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import concat from 'lodash/fp/concat';
|
|
2
|
+
import slice from 'lodash/fp/slice';
|
|
3
|
+
export const slideIndexes = ['0', '1', '2', '3', '4'];
|
|
4
|
+
export const getProgressionSlidesRefs = (progression) => {
|
|
5
|
+
if (progression.state.step.current <= 5) {
|
|
6
|
+
const slideRef = progression.state.nextContent.ref;
|
|
7
|
+
return concat(progression.state.slides, [slideRef]);
|
|
8
|
+
}
|
|
9
|
+
return slice(0, 5, progression.state.slides);
|
|
10
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { applyMiddleware, compose, createStore } from 'redux';
|
|
2
|
+
import thunk from 'redux-thunk';
|
|
3
|
+
import { getServices } from '@coorpacademy/review-services';
|
|
4
|
+
import { identity } from 'lodash/fp';
|
|
5
|
+
import rootReducer from './reducers';
|
|
6
|
+
export default function configureStore(options) {
|
|
7
|
+
const _compose = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
|
|
8
|
+
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
|
|
9
|
+
name: 'app-review',
|
|
10
|
+
trace: true,
|
|
11
|
+
traceLimit: 25
|
|
12
|
+
})
|
|
13
|
+
: compose;
|
|
14
|
+
const locale = options.locale;
|
|
15
|
+
const thunkOptions = {
|
|
16
|
+
services: options.services || getServices(locale),
|
|
17
|
+
callbackOnViewChanged: options.callbackOnViewChanged,
|
|
18
|
+
appendVideoOptions: options.appendVideoOptions,
|
|
19
|
+
onStartProgression: options.onStartProgression || identity,
|
|
20
|
+
onEndProgression: options.onEndProgression || identity
|
|
21
|
+
};
|
|
22
|
+
const thunkMiddleware = thunk.withExtraArgument(thunkOptions);
|
|
23
|
+
const enhancer = _compose(applyMiddleware(thunkMiddleware));
|
|
24
|
+
const store = createStore(rootReducer, undefined, enhancer);
|
|
25
|
+
return store;
|
|
26
|
+
}
|
package/es/index.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { useSelector, useDispatch, Provider } from 'react-redux';
|
|
3
|
+
import AppReviewTemplate from '@coorpacademy/components/es/template/app-review';
|
|
4
|
+
import isEmpty from 'lodash/fp/isEmpty';
|
|
5
|
+
import get from 'lodash/fp/get';
|
|
6
|
+
import configureStore from './configure-store';
|
|
7
|
+
import { navigateBack, navigateTo } from './actions/ui/navigation';
|
|
8
|
+
import { storeToken } from './actions/data/token';
|
|
9
|
+
import { postProgression } from './actions/api/post-progression';
|
|
10
|
+
import { mapStateToSlidesProps } from './views/slides';
|
|
11
|
+
const ConnectedApp = (options) => {
|
|
12
|
+
const dispatch = useDispatch();
|
|
13
|
+
const props = {
|
|
14
|
+
viewName: useSelector((state) => state.ui.navigation[state.ui.navigation.length - 1]),
|
|
15
|
+
slides: useSelector((state) => mapStateToSlidesProps(state, dispatch, options)),
|
|
16
|
+
navigateBack: () => dispatch(navigateBack)
|
|
17
|
+
};
|
|
18
|
+
return React.createElement(AppReviewTemplate, { ...props });
|
|
19
|
+
};
|
|
20
|
+
const storeTokenAndCreateProgression = async (store, options) => {
|
|
21
|
+
const token = get('token', options);
|
|
22
|
+
if (store === null || isEmpty(token))
|
|
23
|
+
return;
|
|
24
|
+
store.dispatch(storeToken(token));
|
|
25
|
+
const skillRef = get('skillRef', options);
|
|
26
|
+
const testingSlideRef = get('testingSlideRef', options);
|
|
27
|
+
if (skillRef) {
|
|
28
|
+
store.dispatch(navigateTo('loader'));
|
|
29
|
+
await store.dispatch(postProgression(skillRef, testingSlideRef));
|
|
30
|
+
store.dispatch(navigateTo('slides'));
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const AppReview = ({ options }) => {
|
|
34
|
+
const [store, setStore] = useState(null);
|
|
35
|
+
const { translate, onQuitClick, onStartProgression, onEndProgression, skin, backgroundImage } = options;
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const newStore = configureStore(options);
|
|
38
|
+
setStore(newStore);
|
|
39
|
+
storeTokenAndCreateProgression(newStore, options);
|
|
40
|
+
// should create store, store token and create progression only once on mount
|
|
41
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
42
|
+
}, []);
|
|
43
|
+
if (!store)
|
|
44
|
+
return null;
|
|
45
|
+
return (React.createElement(Provider, { store: store },
|
|
46
|
+
React.createElement(ConnectedApp, { onQuitClick: onQuitClick, onStartProgression: onStartProgression, onEndProgression: onEndProgression, translate: translate, skin: skin, backgroundImage: backgroundImage })));
|
|
47
|
+
};
|
|
48
|
+
export default AppReview;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import set from 'lodash/fp/set';
|
|
2
|
+
import { CORRECTION_FETCH_SUCCESS, CORRECTION_FETCH_REQUEST } from '../../actions/api/fetch-correction';
|
|
3
|
+
import { POST_PROGRESSION_REQUEST } from '../../actions/api/post-progression';
|
|
4
|
+
const initialState = {};
|
|
5
|
+
const reducer = (
|
|
6
|
+
// eslint-disable-next-line default-param-last
|
|
7
|
+
state = initialState, action) => {
|
|
8
|
+
switch (action.type) {
|
|
9
|
+
case CORRECTION_FETCH_REQUEST: {
|
|
10
|
+
const { meta } = action;
|
|
11
|
+
return set([meta.slideRef], null, state);
|
|
12
|
+
}
|
|
13
|
+
case CORRECTION_FETCH_SUCCESS: {
|
|
14
|
+
const { meta } = action;
|
|
15
|
+
const correction = action.payload;
|
|
16
|
+
return set([meta.slideRef], correction, state);
|
|
17
|
+
}
|
|
18
|
+
case POST_PROGRESSION_REQUEST: {
|
|
19
|
+
return initialState;
|
|
20
|
+
}
|
|
21
|
+
default:
|
|
22
|
+
return state;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
export default reducer;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SKILL_FETCH_SUCCESS } from '../../actions/api/fetch-skill';
|
|
2
|
+
const initialState = null;
|
|
3
|
+
const reducer = (
|
|
4
|
+
// eslint-disable-next-line default-param-last
|
|
5
|
+
state = initialState, action) => {
|
|
6
|
+
switch (action.type) {
|
|
7
|
+
case SKILL_FETCH_SUCCESS:
|
|
8
|
+
return action.payload;
|
|
9
|
+
default:
|
|
10
|
+
return state;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
export default reducer;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { combineReducers } from 'redux';
|
|
2
|
+
import corrections from './corrections';
|
|
3
|
+
import progression from './progression';
|
|
4
|
+
import slides from './slides';
|
|
5
|
+
import token from './token';
|
|
6
|
+
import rank from './rank';
|
|
7
|
+
import currentSkill from './current-skill';
|
|
8
|
+
import videos from './videos';
|
|
9
|
+
export default combineReducers({
|
|
10
|
+
corrections,
|
|
11
|
+
progression,
|
|
12
|
+
slides,
|
|
13
|
+
token,
|
|
14
|
+
rank,
|
|
15
|
+
currentSkill,
|
|
16
|
+
videos
|
|
17
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { POST_ANSWER_SUCCESS } from '../../actions/api/post-answer';
|
|
2
|
+
import { POST_PROGRESSION_REQUEST, POST_PROGRESSION_SUCCESS } from '../../actions/api/post-progression';
|
|
3
|
+
const initialState = null;
|
|
4
|
+
const reducer = (
|
|
5
|
+
// eslint-disable-next-line default-param-last
|
|
6
|
+
state = initialState, action) => {
|
|
7
|
+
switch (action.type) {
|
|
8
|
+
case POST_ANSWER_SUCCESS:
|
|
9
|
+
case POST_PROGRESSION_SUCCESS: {
|
|
10
|
+
const progression = action.payload;
|
|
11
|
+
return progression;
|
|
12
|
+
}
|
|
13
|
+
case POST_PROGRESSION_REQUEST: {
|
|
14
|
+
return initialState;
|
|
15
|
+
}
|
|
16
|
+
default:
|
|
17
|
+
return state;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
export default reducer;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import set from 'lodash/fp/set';
|
|
2
|
+
import { POST_PROGRESSION_REQUEST } from '../../actions/api/post-progression';
|
|
3
|
+
import { RANK_FETCH_START_SUCCESS, RANK_FETCH_END_SUCCESS } from '../../actions/api/fetch-rank';
|
|
4
|
+
const initialState = {
|
|
5
|
+
start: Number.NaN,
|
|
6
|
+
end: Number.NaN
|
|
7
|
+
};
|
|
8
|
+
const reducer = (
|
|
9
|
+
// eslint-disable-next-line default-param-last
|
|
10
|
+
state = initialState, action) => {
|
|
11
|
+
switch (action.type) {
|
|
12
|
+
case POST_PROGRESSION_REQUEST: {
|
|
13
|
+
return initialState;
|
|
14
|
+
}
|
|
15
|
+
case RANK_FETCH_START_SUCCESS: {
|
|
16
|
+
const { payload } = action;
|
|
17
|
+
return set('start', payload.rank, state);
|
|
18
|
+
}
|
|
19
|
+
case RANK_FETCH_END_SUCCESS: {
|
|
20
|
+
const { payload } = action;
|
|
21
|
+
return set('end', payload.rank, state);
|
|
22
|
+
}
|
|
23
|
+
default:
|
|
24
|
+
return state;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
export default reducer;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import set from 'lodash/fp/set';
|
|
2
|
+
import { SLIDE_FETCH_SUCCESS } from '../../actions/api/fetch-slide';
|
|
3
|
+
import { POST_PROGRESSION_REQUEST } from '../../actions/api/post-progression';
|
|
4
|
+
export const initialState = {};
|
|
5
|
+
const reducer = (
|
|
6
|
+
// eslint-disable-next-line default-param-last
|
|
7
|
+
state = initialState, action) => {
|
|
8
|
+
switch (action.type) {
|
|
9
|
+
case SLIDE_FETCH_SUCCESS: {
|
|
10
|
+
const slide = action.payload;
|
|
11
|
+
return set([slide._id], slide, state);
|
|
12
|
+
}
|
|
13
|
+
case POST_PROGRESSION_REQUEST: {
|
|
14
|
+
return initialState;
|
|
15
|
+
}
|
|
16
|
+
default:
|
|
17
|
+
return state;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
export default reducer;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { STORE_TOKEN } from '../../actions/data/token';
|
|
2
|
+
// eslint-disable-next-line default-param-last
|
|
3
|
+
const reducer = (state = '', action) => {
|
|
4
|
+
switch (action.type) {
|
|
5
|
+
case STORE_TOKEN:
|
|
6
|
+
return action.payload;
|
|
7
|
+
default:
|
|
8
|
+
return state;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
export default reducer;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { get, set } from 'lodash/fp';
|
|
2
|
+
import { SET_VIDEO_PROPS, SHOW_VIDEO } from '../../actions/api/fetch-video-props';
|
|
3
|
+
import { NEXT_SLIDE } from '../../actions/ui/next-slide';
|
|
4
|
+
export const initialState = {};
|
|
5
|
+
const reducer = (
|
|
6
|
+
// eslint-disable-next-line default-param-last
|
|
7
|
+
state = initialState, action) => {
|
|
8
|
+
switch (action.type) {
|
|
9
|
+
case SET_VIDEO_PROPS: {
|
|
10
|
+
const { slideId, props } = action.payload;
|
|
11
|
+
return set(slideId, props, state);
|
|
12
|
+
}
|
|
13
|
+
case NEXT_SLIDE: {
|
|
14
|
+
const { currentSlideRef } = action.payload;
|
|
15
|
+
const isMediaVideo = get(currentSlideRef, state);
|
|
16
|
+
if (isMediaVideo) {
|
|
17
|
+
return set([currentSlideRef, 'src', '0', 'loading'], true, state);
|
|
18
|
+
}
|
|
19
|
+
return state;
|
|
20
|
+
}
|
|
21
|
+
case SHOW_VIDEO: {
|
|
22
|
+
const { slideId } = action.payload;
|
|
23
|
+
const isMediaVideo = get(slideId, state);
|
|
24
|
+
if (isMediaVideo) {
|
|
25
|
+
return set([slideId, 'src', '0', 'loading'], false, state);
|
|
26
|
+
}
|
|
27
|
+
return state;
|
|
28
|
+
}
|
|
29
|
+
default:
|
|
30
|
+
return state;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
export default reducer;
|