@coorpacademy/app-review 0.0.21-alpha.4 → 0.1.1
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/courses.d.ts +2 -3
- package/es/actions/data/is-fetching.d.ts +5 -0
- package/es/actions/data/is-fetching.js +4 -0
- package/es/actions/data/validate-slide.d.ts +5 -0
- package/es/actions/data/validate-slide.js +6 -0
- package/es/actions/finished-slides.d.ts +7 -0
- package/es/actions/finished-slides.js +7 -0
- package/es/actions/index.d.ts +2 -5
- package/es/actions/index.js +1 -3
- package/es/actions/navigation.d.ts +5 -6
- package/es/actions/review-status.d.ts +7 -0
- package/es/actions/review-status.js +7 -0
- package/es/actions/slides.d.ts +20 -0
- package/es/actions/slides.js +17 -0
- package/es/actions/step-items.d.ts +14 -0
- package/es/actions/step-items.js +12 -0
- package/es/actions/token.d.ts +0 -1
- package/es/common/index.d.ts +8 -0
- package/es/common/index.js +8 -0
- package/es/configure-store.d.ts +3 -12
- package/es/configure-store.js +5 -7
- package/es/fixtures/qcm-slide.d.ts +2 -0
- package/es/fixtures/qcm-slide.js +29 -0
- package/es/fixtures/temp-fixture.d.ts +3 -0
- package/es/fixtures/temp-fixture.js +84 -0
- package/es/helpers/css-register.d.ts +1 -0
- package/es/helpers/css-register.js +2 -0
- package/es/index.d.ts +5 -12
- package/es/index.js +62 -14
- package/es/index.test.d.ts +1 -0
- package/es/index.test.js +52 -0
- package/es/middlewares/on-start-app.d.ts +1 -2
- package/es/middlewares/on-start-app.js +20 -9
- package/es/reducers/courses.d.ts +2 -3
- package/es/reducers/courses.js +2 -1
- package/es/reducers/data.d.ts +7 -0
- package/es/reducers/data.js +76 -0
- package/es/reducers/finished-slides.d.ts +6 -0
- package/es/reducers/finished-slides.js +32 -0
- package/es/reducers/index.d.ts +11 -6
- package/es/reducers/index.js +14 -4
- package/es/reducers/navigation.d.ts +4 -3
- package/es/reducers/navigation.js +4 -1
- package/es/reducers/review-status.d.ts +5 -0
- package/es/reducers/review-status.js +15 -0
- package/es/reducers/slides.d.ts +6 -0
- package/es/reducers/slides.js +112 -0
- package/es/reducers/step-items.d.ts +6 -0
- package/es/reducers/step-items.js +75 -0
- package/es/reducers/token.d.ts +5 -7
- package/es/reducers/token.js +4 -12
- package/es/services/fetch-demo.d.ts +0 -1
- package/es/services/fetch-demo.test.d.ts +0 -1
- package/es/services/fetch-responses.d.ts +6 -7
- package/es/services/fetch-responses.js +2 -2
- package/es/services/fetch.mocks.d.ts +3 -4
- package/es/services/fetch.mocks.js +3 -3
- package/es/types/common.d.ts +7 -0
- package/es/types/common.js +1 -0
- package/es/types/data/index.d.ts +14 -0
- package/es/types/data/index.js +1 -0
- package/es/types/dispatchers.d.ts +17 -0
- package/es/types/dispatchers.js +1 -0
- package/es/types/finished-slides.d.ts +9 -0
- package/es/types/finished-slides.js +1 -0
- package/es/types/review-status.d.ts +1 -0
- package/es/types/review-status.js +1 -0
- package/es/types/slides.d.ts +87 -0
- package/es/types/slides.js +1 -0
- package/es/types/states.d.ts +6 -0
- package/es/types/states.js +1 -0
- package/es/types/step-items.d.ts +26 -0
- package/es/types/step-items.js +5 -0
- package/es/types/store-state.d.ts +12 -0
- package/es/types/store-state.js +1 -0
- package/es/types/utils.d.ts +3 -0
- package/es/types/utils.js +1 -0
- package/es/types/views.d.ts +64 -0
- package/es/types/views.js +1 -0
- package/lib/actions/courses.d.ts +2 -3
- package/lib/actions/data/is-fetching.d.ts +5 -0
- package/lib/actions/data/is-fetching.js +4 -0
- package/lib/actions/data/validate-slide.d.ts +5 -0
- package/lib/actions/data/validate-slide.js +6 -0
- package/lib/actions/finished-slides.d.ts +7 -0
- package/lib/actions/finished-slides.js +7 -0
- package/lib/actions/index.d.ts +2 -5
- package/lib/actions/index.js +1 -3
- package/lib/actions/navigation.d.ts +5 -6
- package/lib/actions/review-status.d.ts +7 -0
- package/lib/actions/review-status.js +7 -0
- package/lib/actions/slides.d.ts +20 -0
- package/lib/actions/slides.js +17 -0
- package/lib/actions/step-items.d.ts +14 -0
- package/lib/actions/step-items.js +12 -0
- package/lib/actions/token.d.ts +0 -1
- package/lib/common/index.d.ts +8 -0
- package/lib/common/index.js +8 -0
- package/lib/configure-store.d.ts +3 -12
- package/lib/configure-store.js +10 -59
- package/lib/fixtures/qcm-slide.d.ts +2 -0
- package/lib/fixtures/qcm-slide.js +29 -0
- package/lib/fixtures/temp-fixture.d.ts +3 -0
- package/lib/fixtures/temp-fixture.js +84 -0
- package/lib/helpers/css-register.d.ts +1 -0
- package/lib/helpers/css-register.js +2 -0
- package/lib/index.d.ts +5 -12
- package/lib/index.js +71 -34
- package/lib/index.test.d.ts +1 -0
- package/lib/index.test.js +160 -0
- package/lib/middlewares/on-start-app.d.ts +1 -2
- package/lib/middlewares/on-start-app.js +29 -16
- package/lib/reducers/courses.d.ts +2 -3
- package/lib/reducers/courses.js +2 -1
- package/lib/reducers/data.d.ts +7 -0
- package/lib/reducers/data.js +77 -0
- package/lib/reducers/finished-slides.d.ts +6 -0
- package/lib/reducers/finished-slides.js +44 -0
- package/lib/reducers/index.d.ts +11 -6
- package/lib/reducers/index.js +14 -4
- package/lib/reducers/navigation.d.ts +4 -3
- package/lib/reducers/navigation.js +6 -1
- package/lib/reducers/review-status.d.ts +5 -0
- package/lib/reducers/review-status.js +16 -0
- package/lib/reducers/slides.d.ts +6 -0
- package/lib/reducers/slides.js +119 -0
- package/lib/reducers/step-items.d.ts +6 -0
- package/lib/reducers/step-items.js +87 -0
- package/lib/reducers/token.d.ts +5 -7
- package/lib/reducers/token.js +4 -13
- package/lib/services/fetch-demo.d.ts +0 -1
- package/lib/services/fetch-demo.js +25 -23
- package/lib/services/fetch-demo.test.d.ts +0 -1
- package/lib/services/fetch-responses.d.ts +6 -7
- package/lib/services/fetch-responses.js +2 -2
- package/lib/services/fetch.mocks.d.ts +3 -4
- package/lib/services/fetch.mocks.js +28 -16
- package/lib/types/common.d.ts +7 -0
- package/lib/types/common.js +1 -0
- package/lib/types/data/index.d.ts +14 -0
- package/lib/types/data/index.js +1 -0
- package/lib/types/dispatchers.d.ts +17 -0
- package/lib/types/dispatchers.js +1 -0
- package/lib/types/finished-slides.d.ts +9 -0
- package/lib/types/finished-slides.js +1 -0
- package/lib/types/review-status.d.ts +1 -0
- package/lib/types/review-status.js +1 -0
- package/lib/types/slides.d.ts +87 -0
- package/lib/types/slides.js +1 -0
- package/lib/types/states.d.ts +6 -0
- package/lib/types/states.js +1 -0
- package/lib/types/step-items.d.ts +26 -0
- package/lib/types/step-items.js +5 -0
- package/lib/types/store-state.d.ts +12 -0
- package/lib/types/store-state.js +1 -0
- package/lib/types/utils.d.ts +3 -0
- package/lib/types/utils.js +1 -0
- package/lib/types/views.d.ts +64 -0
- package/lib/types/views.js +1 -0
- package/package.json +21 -7
- package/src/actions/courses.ts +2 -2
- package/src/actions/data/is-fetching.ts +12 -0
- package/src/actions/data/validate-slide.ts +15 -0
- package/src/actions/finished-slides.ts +21 -0
- package/src/actions/index.ts +8 -5
- package/src/actions/navigation.ts +6 -5
- package/src/actions/review-status.ts +19 -0
- package/src/actions/slides.ts +45 -0
- package/src/actions/step-items.ts +39 -0
- package/src/common/index.ts +14 -0
- package/src/configure-store.ts +14 -22
- package/src/fixtures/qcm-slide.ts +31 -0
- package/src/fixtures/temp-fixture.ts +94 -0
- package/src/helpers/css-modules-require-hook.d.ts +4 -0
- package/src/helpers/css-register.ts +3 -0
- package/src/index.test.tsx +82 -0
- package/src/index.tsx +75 -22
- package/src/middlewares/on-start-app.ts +35 -20
- package/src/reducers/courses.ts +3 -2
- package/src/reducers/data.ts +88 -0
- package/src/reducers/finished-slides.ts +48 -0
- package/src/reducers/index.ts +14 -4
- package/src/reducers/navigation.ts +7 -3
- package/src/reducers/review-status.ts +23 -0
- package/src/reducers/slides.ts +136 -0
- package/src/reducers/step-items.ts +112 -0
- package/src/reducers/token.ts +6 -15
- package/src/services/fetch-demo.ts +17 -15
- package/src/services/fetch-responses.ts +7 -7
- package/src/services/fetch.mocks.ts +29 -14
- package/src/types/common.ts +8 -0
- package/src/types/data/index.ts +16 -0
- package/src/types/dispatchers.ts +18 -0
- package/src/types/finished-slides.ts +9 -0
- package/src/types/globals.d.ts +6 -0
- package/src/types/review-status.ts +1 -0
- package/src/types/slides.ts +107 -0
- package/src/types/states.ts +6 -0
- package/src/types/step-items.ts +31 -0
- package/src/types/store-state.ts +20 -0
- package/src/types/utils.ts +4 -0
- package/src/types/views.tsx +69 -0
- package/es/actions/courses.d.ts.map +0 -1
- package/es/actions/index.d.ts.map +0 -1
- package/es/actions/navigation.d.ts.map +0 -1
- package/es/actions/token.d.ts.map +0 -1
- package/es/components/__tmp-backup-vues-mobile/revision-home.d.ts +0 -10
- package/es/components/__tmp-backup-vues-mobile/revision-home.d.ts.map +0 -1
- package/es/components/__tmp-backup-vues-mobile/revision-home.js +0 -83
- package/es/components/__tmp-backup-vues-mobile/revision-home.stories.d.ts +0 -1
- package/es/components/__tmp-backup-vues-mobile/revision-home.stories.d.ts.map +0 -1
- package/es/components/__tmp-backup-vues-mobile/revision-home.stories.js +0 -14
- package/es/components/__tmp-backup-vues-mobile/revision-onboarding.d.ts +0 -4
- package/es/components/__tmp-backup-vues-mobile/revision-onboarding.d.ts.map +0 -1
- package/es/components/__tmp-backup-vues-mobile/revision-onboarding.js +0 -57
- package/es/components/__tmp-backup-vues-mobile/revision-quizzer.d.ts +0 -19
- package/es/components/__tmp-backup-vues-mobile/revision-quizzer.d.ts.map +0 -1
- package/es/components/__tmp-backup-vues-mobile/revision-quizzer.js +0 -120
- package/es/configure-store.d.ts.map +0 -1
- package/es/index.d.ts.map +0 -1
- package/es/middlewares/on-start-app.d.ts.map +0 -1
- package/es/reducers/courses.d.ts.map +0 -1
- package/es/reducers/index.d.ts.map +0 -1
- package/es/reducers/navigation.d.ts.map +0 -1
- package/es/reducers/token.d.ts.map +0 -1
- package/es/services/fetch-demo.d.ts.map +0 -1
- package/es/services/fetch-demo.test.d.ts.map +0 -1
- package/es/services/fetch-responses.d.ts.map +0 -1
- package/es/services/fetch.mocks.d.ts.map +0 -1
- package/lib/actions/courses.d.ts.map +0 -1
- package/lib/actions/index.d.ts.map +0 -1
- package/lib/actions/navigation.d.ts.map +0 -1
- package/lib/actions/token.d.ts.map +0 -1
- package/lib/components/__tmp-backup-vues-mobile/revision-home.d.ts +0 -10
- package/lib/components/__tmp-backup-vues-mobile/revision-home.d.ts.map +0 -1
- package/lib/components/__tmp-backup-vues-mobile/revision-home.js +0 -98
- package/lib/components/__tmp-backup-vues-mobile/revision-home.stories.d.ts +0 -1
- package/lib/components/__tmp-backup-vues-mobile/revision-home.stories.d.ts.map +0 -1
- package/lib/components/__tmp-backup-vues-mobile/revision-home.stories.js +0 -14
- package/lib/components/__tmp-backup-vues-mobile/revision-onboarding.d.ts +0 -4
- package/lib/components/__tmp-backup-vues-mobile/revision-onboarding.d.ts.map +0 -1
- package/lib/components/__tmp-backup-vues-mobile/revision-onboarding.js +0 -60
- package/lib/components/__tmp-backup-vues-mobile/revision-quizzer.d.ts +0 -19
- package/lib/components/__tmp-backup-vues-mobile/revision-quizzer.d.ts.map +0 -1
- package/lib/components/__tmp-backup-vues-mobile/revision-quizzer.js +0 -128
- package/lib/configure-store.d.ts.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/middlewares/on-start-app.d.ts.map +0 -1
- package/lib/reducers/courses.d.ts.map +0 -1
- package/lib/reducers/index.d.ts.map +0 -1
- package/lib/reducers/navigation.d.ts.map +0 -1
- package/lib/reducers/token.d.ts.map +0 -1
- package/lib/services/fetch-demo.d.ts.map +0 -1
- package/lib/services/fetch-demo.test.d.ts.map +0 -1
- package/lib/services/fetch-responses.d.ts.map +0 -1
- package/lib/services/fetch.mocks.d.ts.map +0 -1
- package/src/components/__tmp-backup-vues-mobile/revision-home.stories.tsx +0 -16
- package/src/components/__tmp-backup-vues-mobile/revision-home.tsx +0 -125
- package/src/components/__tmp-backup-vues-mobile/revision-onboarding.tsx +0 -84
- package/src/components/__tmp-backup-vues-mobile/revision-quizzer.tsx +0 -176
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import map from 'lodash/fp/map';
|
|
2
|
+
import {
|
|
3
|
+
SlidesAction,
|
|
4
|
+
STORE_FIRST_SLIDE,
|
|
5
|
+
UPDATE_SLIDES_ON_NEXT,
|
|
6
|
+
UPDATE_SLIDES_ON_VALIDATION
|
|
7
|
+
} from '../actions/slides';
|
|
8
|
+
import {HIGHEST_INDEX, slideNumbers, TOTAL_SLIDES_STACK} from '../common';
|
|
9
|
+
import {
|
|
10
|
+
Slide,
|
|
11
|
+
Slides,
|
|
12
|
+
UpdateSlidesOnNextPayload,
|
|
13
|
+
UpdateSlidesOnValidationPayload
|
|
14
|
+
} from '../types/slides';
|
|
15
|
+
|
|
16
|
+
// -----------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
export type State = Slides;
|
|
19
|
+
|
|
20
|
+
const getInitialState = (): State => {
|
|
21
|
+
const state: State = {
|
|
22
|
+
slideNumbers
|
|
23
|
+
};
|
|
24
|
+
// eslint-disable-next-line fp/no-loops
|
|
25
|
+
for (let index = 0; index < TOTAL_SLIDES_STACK; index++) {
|
|
26
|
+
state[index] = {hidden: false, position: index};
|
|
27
|
+
}
|
|
28
|
+
return state;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const initialState: State = getInitialState();
|
|
32
|
+
|
|
33
|
+
// -----------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
const setFirstSlide = (state, slide: Slide): State => {
|
|
36
|
+
const _state: State = {
|
|
37
|
+
slideNumbers
|
|
38
|
+
};
|
|
39
|
+
// eslint-disable-next-line lodash-fp/no-unused-result
|
|
40
|
+
map(index => {
|
|
41
|
+
const previousValue = state[index];
|
|
42
|
+
// Only update the 1st one
|
|
43
|
+
const content = index === 0 ? {...previousValue, ...slide} : {...previousValue};
|
|
44
|
+
_state[index] = {...content};
|
|
45
|
+
}, state.slideNumbers);
|
|
46
|
+
|
|
47
|
+
return _state;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// ||-------> Handles the updates of a given slide on validation,
|
|
51
|
+
// & then updates de remaining slides if this given change should impact their content
|
|
52
|
+
const stateUpdateOnValidation = (state: State, payload: UpdateSlidesOnValidationPayload): State => {
|
|
53
|
+
const {slideNumber, newSlideContent, numberOfFinishedSlides, nextSlide} = payload;
|
|
54
|
+
const _state: State = {
|
|
55
|
+
slideNumbers
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// eslint-disable-next-line lodash-fp/no-unused-result
|
|
59
|
+
map(index => {
|
|
60
|
+
const previousValue = state[index];
|
|
61
|
+
// the current slide
|
|
62
|
+
if (index === slideNumber) {
|
|
63
|
+
// only one slide remaining, presets the next value
|
|
64
|
+
if (numberOfFinishedSlides === HIGHEST_INDEX) {
|
|
65
|
+
_state[index] = {
|
|
66
|
+
...previousValue,
|
|
67
|
+
nextSlide,
|
|
68
|
+
position: newSlideContent.position,
|
|
69
|
+
validationResult: newSlideContent.validationResult
|
|
70
|
+
};
|
|
71
|
+
} else _state[slideNumber] = {...previousValue, ...newSlideContent};
|
|
72
|
+
} else {
|
|
73
|
+
// updates the rest of the slides here
|
|
74
|
+
const {hidden, position} = previousValue;
|
|
75
|
+
_state[index] = {...nextSlide, endReview: newSlideContent.endReview, hidden, position};
|
|
76
|
+
}
|
|
77
|
+
}, state.slideNumbers);
|
|
78
|
+
|
|
79
|
+
return _state;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// ||-------> Handles the updates of a given slide on correction popin's next click,
|
|
83
|
+
// & then updates de remaining slides (updates position to trigger animations)
|
|
84
|
+
const stateUpdateOnNext = (state: State, payload: UpdateSlidesOnNextPayload): State => {
|
|
85
|
+
const {slideNumber, newSlideContent, numberOfFinishedSlides} = payload;
|
|
86
|
+
const _state: State = {
|
|
87
|
+
slideNumbers
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// eslint-disable-next-line lodash-fp/no-unused-result
|
|
91
|
+
map(index => {
|
|
92
|
+
const previousValue = state[index];
|
|
93
|
+
// the current slide
|
|
94
|
+
if (index === slideNumber) {
|
|
95
|
+
// when there is only one slide remaining, updates the slide value
|
|
96
|
+
if (numberOfFinishedSlides === HIGHEST_INDEX && previousValue.nextSlide) {
|
|
97
|
+
_state[index] = {...previousValue.nextSlide, position: previousValue.position};
|
|
98
|
+
} else _state[slideNumber] = newSlideContent;
|
|
99
|
+
} else {
|
|
100
|
+
// updates the rest of the slides here
|
|
101
|
+
const {hidden, position, answerUI, questionText} = previousValue;
|
|
102
|
+
_state[index] = {
|
|
103
|
+
hidden,
|
|
104
|
+
position: position - 1, // position shift
|
|
105
|
+
answerUI,
|
|
106
|
+
questionText,
|
|
107
|
+
endReview: newSlideContent.endReview
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}, state.slideNumbers);
|
|
111
|
+
|
|
112
|
+
return _state;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// -----------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
// eslint-disable-next-line default-param-last
|
|
118
|
+
const reducer = (state: State = initialState, action: SlidesAction): State => {
|
|
119
|
+
switch (action.type) {
|
|
120
|
+
case STORE_FIRST_SLIDE: {
|
|
121
|
+
return setFirstSlide(state, action.payload);
|
|
122
|
+
}
|
|
123
|
+
case UPDATE_SLIDES_ON_VALIDATION: {
|
|
124
|
+
return stateUpdateOnValidation(state, action.payload);
|
|
125
|
+
}
|
|
126
|
+
case UPDATE_SLIDES_ON_NEXT: {
|
|
127
|
+
return stateUpdateOnNext(state, action.payload);
|
|
128
|
+
}
|
|
129
|
+
default:
|
|
130
|
+
return state;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// -----------------------------------------------------------------------------
|
|
135
|
+
|
|
136
|
+
export default reducer;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import isNil from 'lodash/fp/isNil';
|
|
2
|
+
import get from 'lodash/fp/get';
|
|
3
|
+
import has from 'lodash/fp/has';
|
|
4
|
+
import map from 'lodash/fp/map';
|
|
5
|
+
import {
|
|
6
|
+
StepItemsAction,
|
|
7
|
+
UPDATE_STEP_ITEMS_ON_NEXT,
|
|
8
|
+
UPDATE_STEP_ITEMS_ON_VALIDATION
|
|
9
|
+
} from '../actions/step-items';
|
|
10
|
+
import {HIGHEST_INDEX, slideNumbers, TOTAL_SLIDES_STACK} from '../common';
|
|
11
|
+
import {SlideNumber} from '../types/slides';
|
|
12
|
+
import {FinishedSlides} from '../types/finished-slides';
|
|
13
|
+
import {StepItem, StepItems} from '../types/step-items';
|
|
14
|
+
|
|
15
|
+
// -----------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
export type State = StepItems;
|
|
18
|
+
|
|
19
|
+
const getInitialState = (): State => {
|
|
20
|
+
const state: StepItems = {
|
|
21
|
+
slideNumbers
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// eslint-disable-next-line fp/no-loops
|
|
25
|
+
for (let index = 0; index < TOTAL_SLIDES_STACK; index++) {
|
|
26
|
+
const current = index === 0;
|
|
27
|
+
state[index] = {current, value: `${index + 1}`, icon: null};
|
|
28
|
+
}
|
|
29
|
+
return state;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const initialState: State = getInitialState();
|
|
33
|
+
|
|
34
|
+
// -----------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
// ||-------> aux function, finds the next consecutive index
|
|
37
|
+
// to loop from 0 to HIGHEST_INDEX (4) & again to 0
|
|
38
|
+
const getNextIndex = (currentIndex: number): number =>
|
|
39
|
+
currentIndex === HIGHEST_INDEX ? 0 : currentIndex + 1;
|
|
40
|
+
|
|
41
|
+
// ||-------> calculates which should be the next step to visit (as there can be already answered slides &&
|
|
42
|
+
// they have to be skipped)
|
|
43
|
+
const calculateNextStepIndex = (
|
|
44
|
+
currentStepNumber: number,
|
|
45
|
+
finishedSlides: FinishedSlides,
|
|
46
|
+
lastVisitedIndex: number | null = null
|
|
47
|
+
): number => {
|
|
48
|
+
// only one slide remaining, the step should stay on the same number
|
|
49
|
+
if (lastVisitedIndex === currentStepNumber) {
|
|
50
|
+
return currentStepNumber;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const indexToVisit = getNextIndex(isNil(lastVisitedIndex) ? currentStepNumber : lastVisitedIndex);
|
|
54
|
+
|
|
55
|
+
// if the index is already included in the correctly answered (finished) slides, then we proceed to check for the next one
|
|
56
|
+
return has(indexToVisit, finishedSlides)
|
|
57
|
+
? calculateNextStepIndex(currentStepNumber, finishedSlides, indexToVisit)
|
|
58
|
+
: indexToVisit;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
type StepItemToUpdateProps =
|
|
62
|
+
| Pick<StepItem, 'icon'>
|
|
63
|
+
| (Pick<StepItem, 'current'> & {nextIndex: SlideNumber});
|
|
64
|
+
|
|
65
|
+
const recalculateStepItemsState = (
|
|
66
|
+
state: State,
|
|
67
|
+
stepNumber,
|
|
68
|
+
stepItemToUpdateProps: StepItemToUpdateProps
|
|
69
|
+
): State => {
|
|
70
|
+
const nextIndex: number | null = get('nextIndex', stepItemToUpdateProps);
|
|
71
|
+
const _state: State = {
|
|
72
|
+
slideNumbers
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// eslint-disable-next-line lodash-fp/no-unused-result
|
|
76
|
+
map(index => {
|
|
77
|
+
const previousValue = state[index];
|
|
78
|
+
if (stepNumber === index)
|
|
79
|
+
_state[stepNumber] = {
|
|
80
|
+
...previousValue,
|
|
81
|
+
...stepItemToUpdateProps
|
|
82
|
+
};
|
|
83
|
+
/* only for UPDATE_STEP_ITEMS_ON_NEXT -> */ else if (nextIndex === index) {
|
|
84
|
+
_state[index] = {...previousValue, current: true};
|
|
85
|
+
} else _state[index] = previousValue;
|
|
86
|
+
}, state.slideNumbers);
|
|
87
|
+
|
|
88
|
+
return _state;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// -----------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
// eslint-disable-next-line default-param-last
|
|
94
|
+
const reducer = (state: State = initialState, action: StepItemsAction): State => {
|
|
95
|
+
switch (action.type) {
|
|
96
|
+
case UPDATE_STEP_ITEMS_ON_VALIDATION: {
|
|
97
|
+
const {stepNumber, icon} = action.payload;
|
|
98
|
+
return recalculateStepItemsState(state, stepNumber, {icon});
|
|
99
|
+
}
|
|
100
|
+
case UPDATE_STEP_ITEMS_ON_NEXT: {
|
|
101
|
+
const {stepNumber, finishedSlides, current} = action.payload;
|
|
102
|
+
const nextIndex = !current ? calculateNextStepIndex(stepNumber, finishedSlides) : null;
|
|
103
|
+
return recalculateStepItemsState(state, stepNumber, {current, nextIndex});
|
|
104
|
+
}
|
|
105
|
+
default:
|
|
106
|
+
return state;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// -----------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
export default reducer;
|
package/src/reducers/token.ts
CHANGED
|
@@ -1,26 +1,17 @@
|
|
|
1
|
-
import
|
|
1
|
+
import set from 'lodash/fp/set';
|
|
2
|
+
import {StoreState} from '../types/store-state';
|
|
3
|
+
import {DataState} from '../types/data';
|
|
2
4
|
|
|
3
5
|
// -----------------------------------------------------------------------------
|
|
4
6
|
|
|
5
|
-
export
|
|
6
|
-
export const initialState: State = null;
|
|
7
|
+
export const initialState: DataState['token'] = null;
|
|
7
8
|
|
|
8
9
|
// -----------------------------------------------------------------------------
|
|
9
10
|
|
|
10
|
-
const
|
|
11
|
-
switch (action.type) {
|
|
12
|
-
case STORE_TOKEN: {
|
|
13
|
-
return action.payload;
|
|
14
|
-
}
|
|
15
|
-
default:
|
|
16
|
-
return state;
|
|
17
|
-
}
|
|
18
|
-
};
|
|
11
|
+
export const setToken = (token: string, state: DataState): DataState => set('token', token, state);
|
|
19
12
|
|
|
20
13
|
// -----------------------------------------------------------------------------
|
|
21
14
|
|
|
22
|
-
export const getToken = appState => appState.token;
|
|
15
|
+
export const getToken = (appState: StoreState): StoreState['data']['token'] => appState.data.token;
|
|
23
16
|
|
|
24
17
|
// -----------------------------------------------------------------------------
|
|
25
|
-
|
|
26
|
-
export default reducer;
|
|
@@ -18,7 +18,7 @@ type QueryParams = {
|
|
|
18
18
|
[key: string]: string | number | boolean;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
-
const buildUrlQueryParams = (params: QueryParams) =>
|
|
21
|
+
const buildUrlQueryParams = (params: QueryParams): string =>
|
|
22
22
|
Object.keys(params)
|
|
23
23
|
.map(key => {
|
|
24
24
|
const value = params[key].toString();
|
|
@@ -28,22 +28,24 @@ const buildUrlQueryParams = (params: QueryParams) =>
|
|
|
28
28
|
|
|
29
29
|
// -----------------------------------------------------------------------------
|
|
30
30
|
|
|
31
|
-
const fetchCourse =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
const fetchCourse =
|
|
32
|
+
(fetch: Fetch) =>
|
|
33
|
+
async (token, language, universalRef): Promise<Course> => {
|
|
34
|
+
const jwt: JWT = decode(token);
|
|
35
|
+
const query = {
|
|
36
|
+
type: 'course',
|
|
37
|
+
universalRef,
|
|
38
|
+
lang: language
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const response = await fetch(`${jwt.host}/api/v2/contents?${buildUrlQueryParams(query)}`, {
|
|
42
|
+
headers: {authorization: token}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const {hits} = (await toJSON(response)) as CatalogHits;
|
|
46
|
+
return hits?.[0];
|
|
37
47
|
};
|
|
38
48
|
|
|
39
|
-
const response = await fetch(`${jwt.host}/api/v2/contents?${buildUrlQueryParams(query)}`, {
|
|
40
|
-
headers: {authorization: token}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
const {hits} = (await toJSON(response)) as CatalogHits;
|
|
44
|
-
return hits?.[0];
|
|
45
|
-
};
|
|
46
|
-
|
|
47
49
|
// -----------------------------------------------------------------------------
|
|
48
50
|
|
|
49
51
|
export type Course = {title};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type _Response = {
|
|
2
2
|
ok: boolean;
|
|
3
3
|
status: number;
|
|
4
4
|
statusText: string;
|
|
@@ -9,19 +9,19 @@ export type Response = {
|
|
|
9
9
|
export type Fetch = (
|
|
10
10
|
url?: string,
|
|
11
11
|
options?: {headers: {authorization: string}}
|
|
12
|
-
) => Promise<
|
|
12
|
+
) => Promise<_Response>;
|
|
13
13
|
|
|
14
14
|
export class ResponseError extends Error {
|
|
15
15
|
public statusCode: number;
|
|
16
16
|
|
|
17
|
-
public constructor(
|
|
17
|
+
public constructor(statusCode: number, statusText: string, body = '') {
|
|
18
18
|
super(`${statusText} ${body}`.trim());
|
|
19
19
|
this.name = 'ResponseError';
|
|
20
|
-
this.statusCode =
|
|
20
|
+
this.statusCode = statusCode;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export const toJSON = async (response:
|
|
24
|
+
export const toJSON = async (response: _Response): Promise<unknown> => {
|
|
25
25
|
if (!response.ok) {
|
|
26
26
|
const body = await response.text();
|
|
27
27
|
throw new ResponseError(response.status, response.statusText, body);
|
|
@@ -34,7 +34,7 @@ export const toJSON = async (response: Response): Promise<unknown> => {
|
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
export const toText = async (response:
|
|
37
|
+
export const toText = async (response: _Response): Promise<string> => {
|
|
38
38
|
const body = await response.text();
|
|
39
39
|
|
|
40
40
|
if (!response.ok) {
|
|
@@ -44,7 +44,7 @@ export const toText = async (response: Response): Promise<string> => {
|
|
|
44
44
|
return body;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
export const toVoid = async (response:
|
|
47
|
+
export const toVoid = async (response: _Response): Promise<void> => {
|
|
48
48
|
if (!response.ok) {
|
|
49
49
|
const body = await response.text();
|
|
50
50
|
throw new ResponseError(response.status, response.statusText, body);
|
|
@@ -1,26 +1,41 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {_Response} from './fetch-responses';
|
|
2
2
|
|
|
3
|
-
export const toResponse = (
|
|
3
|
+
export const toResponse = (
|
|
4
|
+
statusCode: number,
|
|
5
|
+
statusText: string,
|
|
6
|
+
body: string
|
|
7
|
+
): Promise<_Response> =>
|
|
4
8
|
Promise.resolve({
|
|
5
|
-
ok:
|
|
6
|
-
status,
|
|
9
|
+
ok: statusCode < 400,
|
|
10
|
+
status: statusCode,
|
|
7
11
|
statusText,
|
|
8
12
|
json: (): Promise<unknown> => Promise.resolve(body).then(JSON.parse),
|
|
9
13
|
text: (): Promise<string> => Promise.resolve(body)
|
|
10
14
|
});
|
|
11
15
|
|
|
12
|
-
type FetchFunction = () => Promise<
|
|
16
|
+
type FetchFunction = () => Promise<_Response>;
|
|
13
17
|
|
|
14
|
-
export const okJSONResponse =
|
|
15
|
-
|
|
18
|
+
export const okJSONResponse =
|
|
19
|
+
(body: unknown): FetchFunction =>
|
|
20
|
+
() =>
|
|
21
|
+
toResponse(200, 'OK', JSON.stringify(body));
|
|
16
22
|
|
|
17
|
-
export const internalServerErrorJSONResponse =
|
|
18
|
-
|
|
23
|
+
export const internalServerErrorJSONResponse =
|
|
24
|
+
(body: unknown): FetchFunction =>
|
|
25
|
+
() =>
|
|
26
|
+
toResponse(500, 'InternalServerError', JSON.stringify(body));
|
|
19
27
|
|
|
20
|
-
export const notFoundJSONResponse =
|
|
21
|
-
|
|
28
|
+
export const notFoundJSONResponse =
|
|
29
|
+
(body: unknown): FetchFunction =>
|
|
30
|
+
() =>
|
|
31
|
+
toResponse(404, 'Not Found', JSON.stringify(body));
|
|
22
32
|
|
|
23
|
-
export const okTextResponse =
|
|
33
|
+
export const okTextResponse =
|
|
34
|
+
(body: string): FetchFunction =>
|
|
35
|
+
() =>
|
|
36
|
+
toResponse(200, 'OK', body);
|
|
24
37
|
|
|
25
|
-
export const internalServerErrorTextResponse =
|
|
26
|
-
|
|
38
|
+
export const internalServerErrorTextResponse =
|
|
39
|
+
(body: string): FetchFunction =>
|
|
40
|
+
() =>
|
|
41
|
+
toResponse(500, 'InternalServerError', body);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {AnswerUI} from '../slides';
|
|
2
|
+
|
|
3
|
+
export type DataState = {
|
|
4
|
+
isFetching?: boolean;
|
|
5
|
+
slideValidationResult?: {
|
|
6
|
+
slideNumber: number;
|
|
7
|
+
result: 'success' | 'failure';
|
|
8
|
+
// pass down the successExitNode if all slides are finished, not before
|
|
9
|
+
exitNode?: 'successExitNode' | 'failExitNode';
|
|
10
|
+
nextSlide: {
|
|
11
|
+
questionText?: string;
|
|
12
|
+
answerUI?: AnswerUI;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
token?: string;
|
|
16
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {validateSlide} from '../actions/data/validate-slide';
|
|
2
|
+
import {updateFinishedSlides} from '../actions/finished-slides';
|
|
3
|
+
import {navigateBack, navigateTo} from '../actions/navigation';
|
|
4
|
+
import {updateReviewStatus} from '../actions/review-status';
|
|
5
|
+
import {updateSlidesOnValidation, updateSlidesOnNext} from '../actions/slides';
|
|
6
|
+
import {updateStepItemsOnValidation, updateStepItemsOnNext} from '../actions/step-items';
|
|
7
|
+
|
|
8
|
+
export type Dispatchers = {
|
|
9
|
+
navigateTo: typeof navigateTo;
|
|
10
|
+
navigateBack: typeof navigateBack;
|
|
11
|
+
validateSlide: typeof validateSlide;
|
|
12
|
+
updateSlidesOnValidation: typeof updateSlidesOnValidation;
|
|
13
|
+
updateSlidesOnNext: typeof updateSlidesOnNext;
|
|
14
|
+
updateReviewStatus: typeof updateReviewStatus;
|
|
15
|
+
updateStepItemsOnValidation: typeof updateStepItemsOnValidation;
|
|
16
|
+
updateStepItemsOnNext: typeof updateStepItemsOnNext;
|
|
17
|
+
updateFinishedSlides: typeof updateFinishedSlides;
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type UpdateReviewStatusPayload = 'finished';
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {WithRequired} from './utils';
|
|
2
|
+
|
|
3
|
+
export type Choice = {
|
|
4
|
+
order?: number;
|
|
5
|
+
selected: boolean;
|
|
6
|
+
title: string;
|
|
7
|
+
help: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type QcmDrag = {
|
|
11
|
+
type: 'qcmDrag';
|
|
12
|
+
choices: Choice[];
|
|
13
|
+
help: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type Qcm = {
|
|
17
|
+
type: 'qcm';
|
|
18
|
+
choices: (Choice & {ariaLabel: string})[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type QcmGraphic = {
|
|
22
|
+
type: 'qcmGraphic';
|
|
23
|
+
choices: (Choice & {image: string})[];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type FreeText = {
|
|
27
|
+
type: 'freeText';
|
|
28
|
+
placeholder: string;
|
|
29
|
+
value: string;
|
|
30
|
+
onChange: Function;
|
|
31
|
+
ariaLabel: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type DropDownOption = {
|
|
35
|
+
name: string;
|
|
36
|
+
value: string | number;
|
|
37
|
+
selected: boolean;
|
|
38
|
+
validOption: boolean;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type DropDown = {
|
|
42
|
+
type: 'dropDown';
|
|
43
|
+
onChange: Function;
|
|
44
|
+
options: DropDownOption[];
|
|
45
|
+
theme:
|
|
46
|
+
| 'filter'
|
|
47
|
+
| 'invalid'
|
|
48
|
+
| 'header'
|
|
49
|
+
| 'mooc'
|
|
50
|
+
| 'question'
|
|
51
|
+
| 'sort'
|
|
52
|
+
| 'thematiques'
|
|
53
|
+
| 'player'
|
|
54
|
+
| 'template'
|
|
55
|
+
| 'coorpmanager';
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export type QuestionRange = {
|
|
59
|
+
type: 'slider';
|
|
60
|
+
onChange: Function;
|
|
61
|
+
onChangeEnd: Function;
|
|
62
|
+
multi: boolean;
|
|
63
|
+
value: number | number[];
|
|
64
|
+
title: string;
|
|
65
|
+
minLabel: string;
|
|
66
|
+
maxLabel: string;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export type Template = {
|
|
70
|
+
type: 'template';
|
|
71
|
+
template: string;
|
|
72
|
+
choices: (DropDown & {type?: string})[];
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export type AnswerUI = {
|
|
76
|
+
model: QcmDrag | Qcm | QcmGraphic | FreeText | DropDown | QuestionRange | Template;
|
|
77
|
+
help: string;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export type SlideNumber = number;
|
|
81
|
+
|
|
82
|
+
export type SlideUIAnimations = 'unstack' | 'restack';
|
|
83
|
+
|
|
84
|
+
export type Slide = {
|
|
85
|
+
hidden?: boolean;
|
|
86
|
+
position?: number;
|
|
87
|
+
animationType?: SlideUIAnimations;
|
|
88
|
+
validationResult?: 'success' | 'failure';
|
|
89
|
+
endReview?: boolean;
|
|
90
|
+
questionText?: string;
|
|
91
|
+
answerUI?: AnswerUI;
|
|
92
|
+
nextSlide?: Omit<Slide, 'endReview' | 'hidden' | 'position' | 'nextSlide'>;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export type Slides = {
|
|
96
|
+
slideNumbers: SlideNumber[];
|
|
97
|
+
[key: SlideNumber]: Slide;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export type UpdateSlidesOnValidationPayload = {
|
|
101
|
+
slideNumber: SlideNumber;
|
|
102
|
+
newSlideContent: Slide;
|
|
103
|
+
numberOfFinishedSlides: number;
|
|
104
|
+
nextSlide: WithRequired<Slide['nextSlide'], 'answerUI' | 'questionText'>;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export type UpdateSlidesOnNextPayload = Omit<UpdateSlidesOnValidationPayload, 'nextSlide'>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export {Slides as SlidesState} from './slides';
|
|
2
|
+
export {DataState} from './data';
|
|
3
|
+
export {StepItems as StepItemsState} from './step-items';
|
|
4
|
+
export {FinishedSlides as FinishedSlidesState} from './finished-slides';
|
|
5
|
+
export {State as ReviewStatusState} from '../reducers/review-status';
|
|
6
|
+
export type NavigationState = Array<'home' | 'onboarding' | 'slides'>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {SlideNumber} from './slides';
|
|
2
|
+
import {FinishedSlides} from './finished-slides';
|
|
3
|
+
|
|
4
|
+
const ICON_VALUES = {
|
|
5
|
+
right: 'right',
|
|
6
|
+
wrong: 'wrong'
|
|
7
|
+
} as const;
|
|
8
|
+
|
|
9
|
+
type IconValue = keyof typeof ICON_VALUES;
|
|
10
|
+
|
|
11
|
+
export type StepItem = {
|
|
12
|
+
current: boolean;
|
|
13
|
+
icon: IconValue;
|
|
14
|
+
value: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type StepItems = {
|
|
18
|
+
slideNumbers: SlideNumber[];
|
|
19
|
+
[key: SlideNumber]: StepItem;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type UpdateStepItemsOnValidationClickPayload = {
|
|
23
|
+
stepNumber: SlideNumber;
|
|
24
|
+
icon: IconValue;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type UpdateStepItemsOnNextClickPayload = {
|
|
28
|
+
stepNumber: SlideNumber;
|
|
29
|
+
finishedSlides: FinishedSlides;
|
|
30
|
+
current: boolean;
|
|
31
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DataState,
|
|
3
|
+
FinishedSlidesState,
|
|
4
|
+
NavigationState,
|
|
5
|
+
ReviewStatusState,
|
|
6
|
+
SlidesState,
|
|
7
|
+
StepItemsState
|
|
8
|
+
} from './states';
|
|
9
|
+
|
|
10
|
+
export type StoreState = {
|
|
11
|
+
ui: {
|
|
12
|
+
courses: unknown;
|
|
13
|
+
navigation: NavigationState;
|
|
14
|
+
slides: SlidesState;
|
|
15
|
+
stepItems: StepItemsState;
|
|
16
|
+
finishedSlides: FinishedSlidesState;
|
|
17
|
+
reviewStatus: ReviewStatusState;
|
|
18
|
+
};
|
|
19
|
+
data: DataState;
|
|
20
|
+
};
|