@coorpacademy/app-review 0.4.5 → 0.5.1-alpha.26

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.
Files changed (87) hide show
  1. package/es/actions/ui/next-slide.d.ts +4 -2
  2. package/es/actions/ui/next-slide.js +7 -4
  3. package/es/actions/ui/quit-popin.d.ts +8 -0
  4. package/es/actions/ui/quit-popin.js +8 -0
  5. package/es/actions/ui/slides.d.ts +2 -3
  6. package/es/common/index.d.ts +2 -0
  7. package/es/common/index.js +9 -0
  8. package/es/reducers/index.d.ts +7 -1
  9. package/es/reducers/ui/answers.d.ts +2 -2
  10. package/es/reducers/ui/current-slide-ref.d.ts +3 -2
  11. package/es/reducers/ui/current-slide-ref.js +3 -0
  12. package/es/reducers/ui/index.d.ts +11 -1
  13. package/es/reducers/ui/index.js +5 -1
  14. package/es/reducers/ui/positions.d.ts +5 -0
  15. package/es/reducers/ui/positions.js +25 -0
  16. package/es/reducers/ui/quit-popin.d.ts +4 -0
  17. package/es/reducers/ui/quit-popin.js +16 -0
  18. package/es/reducers/ui/slide.d.ts +2 -2
  19. package/es/views/slides/index.d.ts +19 -2
  20. package/es/views/slides/index.js +45 -25
  21. package/lib/actions/ui/next-slide.d.ts +4 -2
  22. package/lib/actions/ui/next-slide.js +7 -4
  23. package/lib/actions/ui/quit-popin.d.ts +8 -0
  24. package/lib/actions/ui/quit-popin.js +8 -0
  25. package/lib/actions/ui/slides.d.ts +2 -3
  26. package/lib/common/index.d.ts +2 -0
  27. package/lib/common/index.js +9 -0
  28. package/lib/reducers/index.d.ts +7 -1
  29. package/lib/reducers/ui/answers.d.ts +2 -2
  30. package/lib/reducers/ui/current-slide-ref.d.ts +3 -2
  31. package/lib/reducers/ui/current-slide-ref.js +3 -0
  32. package/lib/reducers/ui/index.d.ts +11 -1
  33. package/lib/reducers/ui/index.js +5 -1
  34. package/lib/reducers/ui/positions.d.ts +5 -0
  35. package/lib/reducers/ui/positions.js +26 -0
  36. package/lib/reducers/ui/quit-popin.d.ts +4 -0
  37. package/lib/reducers/ui/quit-popin.js +17 -0
  38. package/lib/reducers/ui/slide.d.ts +2 -2
  39. package/lib/views/slides/index.d.ts +19 -2
  40. package/lib/views/slides/index.js +47 -26
  41. package/package.json +10 -11
  42. package/src/actions/api/test/fetch-correction.test.ts +3 -1
  43. package/src/actions/api/test/fetch-rank.test.ts +3 -1
  44. package/src/actions/api/test/fetch-skills.test.ts +3 -1
  45. package/src/actions/api/test/fetch-slide.test.ts +3 -1
  46. package/src/actions/api/test/post-answer.test.ts +6 -2
  47. package/src/actions/api/test/post-progression.test.ts +3 -1
  48. package/src/actions/data/test/token.test.ts +3 -1
  49. package/src/actions/ui/next-slide.ts +15 -9
  50. package/src/actions/ui/quit-popin.ts +10 -0
  51. package/src/actions/ui/slides.ts +2 -4
  52. package/src/actions/ui/test/answers.test.ts +3 -1
  53. package/src/actions/ui/test/next-slide.test.ts +9 -3
  54. package/src/actions/ui/test/quit-popin.test.ts +38 -0
  55. package/src/actions/ui/test/slides.test.ts +3 -1
  56. package/src/common/index.ts +12 -0
  57. package/src/common/test/get-progression-slide-ref.test.ts +35 -0
  58. package/src/reducers/ui/answers.ts +2 -2
  59. package/src/reducers/ui/current-slide-ref.ts +5 -2
  60. package/src/reducers/ui/index.ts +7 -1
  61. package/src/reducers/ui/positions.ts +32 -0
  62. package/src/reducers/ui/quit-popin.ts +22 -0
  63. package/src/reducers/ui/slide.ts +7 -2
  64. package/src/reducers/ui/test/answers.test.ts +3 -1
  65. package/src/reducers/ui/test/current-slide-ref.test.ts +2 -2
  66. package/src/reducers/ui/test/positions.test.ts +68 -0
  67. package/src/reducers/ui/test/quit-popin.test.ts +24 -0
  68. package/src/reducers/ui/test/slide.test.ts +3 -1
  69. package/src/types/common.ts +0 -1
  70. package/src/views/skills/test/skills.test.ts +6 -2
  71. package/src/views/slides/index.ts +70 -32
  72. package/src/views/slides/test/header.on-click.test.ts +42 -0
  73. package/src/views/slides/test/index.test.ts +90 -42
  74. package/src/views/slides/test/on-quit-popin.on-click.test.ts +64 -0
  75. package/src/views/slides/test/slide.free-text.on-change.test.ts +3 -2
  76. package/src/views/slides/test/slide.next-slide.on-click.test.ts +10 -2
  77. package/src/views/slides/test/slide.qcm-drag.on-click.test.ts +3 -2
  78. package/src/views/slides/test/slide.qcm-graphic.on-click.test.ts +3 -2
  79. package/src/views/slides/test/slide.qcm.on-click.test.ts +3 -2
  80. package/src/views/slides/test/slide.slider.on-change.test.ts +3 -2
  81. package/src/views/slides/test/slide.slider.on-slider-change.test.ts +17 -4
  82. package/src/views/slides/test/slide.template.on-change.test.ts +11 -11
  83. package/es/actions/ui/is-fetching.d.ts +0 -10
  84. package/es/actions/ui/is-fetching.js +0 -4
  85. package/lib/actions/ui/is-fetching.d.ts +0 -10
  86. package/lib/actions/ui/is-fetching.js +0 -4
  87. package/src/actions/ui/is-fetching.ts +0 -13
@@ -0,0 +1,38 @@
1
+ import test from 'ava';
2
+ import {createTestStore} from '../../test/create-test-store';
3
+ import type {StoreState} from '../../../reducers';
4
+ import {closeQuitPopin, CLOSE_POPIN, openQuitPopin, OPEN_POPIN} from '../quit-popin';
5
+ import {services} from '../../../test/util/services.mock';
6
+
7
+ const initialState: StoreState = {
8
+ data: {
9
+ progression: null,
10
+ slides: {},
11
+ skills: [],
12
+ token: '1234',
13
+ corrections: {},
14
+ rank: {}
15
+ },
16
+ ui: {
17
+ currentSlideRef: '',
18
+ navigation: [],
19
+ answers: {},
20
+ slide: {},
21
+ positions: [],
22
+ showQuitPopin: false
23
+ }
24
+ };
25
+
26
+ test('should dispatch OPEN_POPIN when openQuitPopin action is called', async t => {
27
+ const expectedAction = [{type: OPEN_POPIN}];
28
+
29
+ const {dispatch} = createTestStore(t, initialState, services, expectedAction);
30
+ await dispatch(openQuitPopin);
31
+ });
32
+
33
+ test('should dispatch CLOSE_POPIN when closeQuitPopin action is called', async t => {
34
+ const expectedAction = [{type: CLOSE_POPIN}];
35
+
36
+ const {dispatch} = createTestStore(t, initialState, services, expectedAction);
37
+ await dispatch(closeQuitPopin);
38
+ });
@@ -17,8 +17,10 @@ const initialState: StoreState = {
17
17
  ui: {
18
18
  currentSlideRef: '',
19
19
  navigation: [],
20
+ positions: [0, 1, 2, 3, 4],
20
21
  answers: {},
21
- slide: {}
22
+ slide: {},
23
+ showQuitPopin: false
22
24
  }
23
25
  };
24
26
 
@@ -1,3 +1,7 @@
1
+ import concat from 'lodash/fp/concat';
2
+ import slice from 'lodash/fp/slice';
3
+ import type {ProgressionFromAPI} from '../types/common';
4
+
1
5
  export const VIEWS: {
2
6
  readonly skills: 'skills';
3
7
  readonly onboarding: 'onboarding';
@@ -11,3 +15,11 @@ export const VIEWS: {
11
15
  export const slideIndexes = ['0', '1', '2', '3', '4'] as const;
12
16
 
13
17
  export type SlideIndexes = typeof slideIndexes[number];
18
+
19
+ export const getProgressionSlidesRefs = (progression: ProgressionFromAPI): string[] => {
20
+ if (progression.state.step.current <= 5) {
21
+ const slideRef = progression.state.nextContent.ref;
22
+ return concat(progression.state.slides, [slideRef]);
23
+ }
24
+ return slice(0, 5, progression.state.slides);
25
+ };
@@ -0,0 +1,35 @@
1
+ import test from 'ava';
2
+ import {
3
+ postAnswerResponses,
4
+ postProgressionResponse,
5
+ progressionSlideWithPendingSlide
6
+ } from '../../test/util/services.mock';
7
+ import {templateSlide} from '../../views/slides/test/fixtures/template';
8
+ import {getProgressionSlidesRefs} from '..';
9
+
10
+ test('should return first slide for a created slide', t => {
11
+ const slides = getProgressionSlidesRefs(postProgressionResponse);
12
+ t.deepEqual(slides, ['sli_VJYjJnJhg']);
13
+ });
14
+
15
+ test('should return all 5 slides when user is going to answer the 5th slide', t => {
16
+ const slides = getProgressionSlidesRefs(postAnswerResponses[templateSlide._id]);
17
+ t.deepEqual(slides, [
18
+ 'sli_VJYjJnJhg',
19
+ 'sli_VkSQroQnx',
20
+ 'sli_N1XACJobn',
21
+ 'sli_VkAzsCLKb',
22
+ 'sli_N13-hG3kX'
23
+ ]);
24
+ });
25
+
26
+ test('should return all 5 slides when progression has a nextContent ref that is a pending slide', t => {
27
+ const slides = getProgressionSlidesRefs(progressionSlideWithPendingSlide);
28
+ t.deepEqual(slides, [
29
+ 'sli_VJYjJnJhg',
30
+ 'sli_VkSQroQnx',
31
+ 'sli_N1XACJobn',
32
+ 'sli_VkAzsCLKb',
33
+ 'sli_N13-hG3kX'
34
+ ]);
35
+ });
@@ -8,7 +8,7 @@ import {
8
8
  EDIT_SLIDER,
9
9
  EDIT_TEMPLATE
10
10
  } from '../../actions/ui/answers';
11
- import {NextSlide, NEXT_SLIDE} from '../../actions/ui/next-slide';
11
+ import {NextSlideAction, NEXT_SLIDE} from '../../actions/ui/next-slide';
12
12
 
13
13
  export type UISlideAnswer = string[];
14
14
 
@@ -19,7 +19,7 @@ export const initialState: UIAnswerState = {};
19
19
  const reducer = (
20
20
  // eslint-disable-next-line default-param-last
21
21
  state: UIAnswerState = initialState,
22
- action: EditAnswerAction | NextSlide
22
+ action: EditAnswerAction | NextSlideAction
23
23
  ): UIAnswerState => {
24
24
  switch (action.type) {
25
25
  case EDIT_QCM:
@@ -1,13 +1,16 @@
1
- import {SET_CURRENT_SLIDE, UISlidesAction} from '../../actions/ui/slides';
1
+ import {NEXT_SLIDE, NextSlideAction} from '../../actions/ui/next-slide';
2
+ import {SET_CURRENT_SLIDE, SetCurrentSlideAction} from '../../actions/ui/slides';
2
3
 
3
4
  export type CurrentSlideRefState = string;
4
5
 
5
6
  const reducer = (
6
7
  // eslint-disable-next-line default-param-last
7
8
  state: CurrentSlideRefState = '',
8
- action: UISlidesAction
9
+ action: SetCurrentSlideAction | NextSlideAction
9
10
  ): CurrentSlideRefState => {
10
11
  switch (action.type) {
12
+ case NEXT_SLIDE:
13
+ return action.payload.nextSlideRef;
11
14
  case SET_CURRENT_SLIDE: {
12
15
  return action.payload._id;
13
16
  }
@@ -3,18 +3,24 @@ import {combineReducers} from 'redux';
3
3
  import currentSlideRef, {CurrentSlideRefState} from './current-slide-ref';
4
4
  import navigation, {NavigationState} from './navigation';
5
5
  import answers, {UIAnswerState} from './answers';
6
+ import positions, {UIPositionState} from './positions';
6
7
  import slide, {UISlideState} from './slide';
8
+ import showQuitPopin, {type ShowQuitPopinState} from './quit-popin';
7
9
 
8
10
  export type UIState = {
9
11
  currentSlideRef: CurrentSlideRefState;
10
12
  navigation: NavigationState;
11
13
  answers: UIAnswerState;
12
14
  slide: UISlideState;
15
+ positions: UIPositionState;
16
+ showQuitPopin: ShowQuitPopinState;
13
17
  };
14
18
 
15
19
  export default combineReducers({
16
20
  currentSlideRef,
17
21
  navigation,
18
22
  answers,
19
- slide
23
+ slide,
24
+ positions,
25
+ showQuitPopin
20
26
  });
@@ -0,0 +1,32 @@
1
+ import findIndex from 'lodash/fp/findIndex';
2
+ import map from 'lodash/fp/map';
3
+ import set from 'lodash/fp/set';
4
+ import {ReceivedProgression, POST_PROGRESSION_SUCCESS} from '../../actions/api/post-progression';
5
+ import {NextSlideAction, NEXT_SLIDE} from '../../actions/ui/next-slide';
6
+
7
+ export type UIPositionState = number[];
8
+ const initialState: UIPositionState = [0, 1, 2, 3, 4];
9
+
10
+ const reducer = (
11
+ // eslint-disable-next-line default-param-last
12
+ state: UIPositionState = initialState,
13
+ action: NextSlideAction | ReceivedProgression
14
+ ): UIPositionState => {
15
+ switch (action.type) {
16
+ case POST_PROGRESSION_SUCCESS: {
17
+ return initialState;
18
+ }
19
+ case NEXT_SLIDE: {
20
+ const {totalCorrectAnswers, answeredSlides, currentSlideRef, animationType} = action.payload;
21
+
22
+ const nextCurrentSlidePosition = animationType === 'unstack' ? -1 : 4 - totalCorrectAnswers;
23
+ const currentSlideIndex = findIndex(ref => ref === currentSlideRef, answeredSlides);
24
+ const newState = map(position => (position === -1 ? position : position - 1), state);
25
+ return set([`${currentSlideIndex}`], nextCurrentSlidePosition)(newState);
26
+ }
27
+ default:
28
+ return state;
29
+ }
30
+ };
31
+
32
+ export default reducer;
@@ -0,0 +1,22 @@
1
+ import {closeQuitPopin, CLOSE_POPIN, openQuitPopin, OPEN_POPIN} from '../../actions/ui/quit-popin';
2
+
3
+ export type ShowQuitPopinState = boolean;
4
+
5
+ const reducer = (
6
+ // eslint-disable-next-line default-param-last
7
+ state: ShowQuitPopinState = false,
8
+ action: typeof openQuitPopin | typeof closeQuitPopin
9
+ ): ShowQuitPopinState => {
10
+ switch (action.type) {
11
+ case OPEN_POPIN: {
12
+ return true;
13
+ }
14
+ case CLOSE_POPIN: {
15
+ return false;
16
+ }
17
+ default:
18
+ return state;
19
+ }
20
+ };
21
+
22
+ export default reducer;
@@ -15,7 +15,7 @@ import {
15
15
  import {PostAnswerRequestAction, POST_ANSWER_REQUEST} from '../../actions/api/post-answer';
16
16
  import {ReceivedCorrection, CORRECTION_FETCH_SUCCESS} from '../../actions/api/fetch-correction';
17
17
  import {FetchSlide, SLIDE_FETCH_REQUEST} from '../../actions/api/fetch-slide';
18
- import {NextSlide, NEXT_SLIDE} from '../../actions/ui/next-slide';
18
+ import {NextSlideAction, NEXT_SLIDE} from '../../actions/ui/next-slide';
19
19
  import {SlideUIAnimations} from '../../types/slides';
20
20
 
21
21
  export type UISlide = {
@@ -32,7 +32,12 @@ export const initialState: UISlideState = {};
32
32
  const reducer = (
33
33
  // eslint-disable-next-line default-param-last
34
34
  state: UISlideState = initialState,
35
- action: FetchSlide | PostAnswerRequestAction | EditAnswerAction | ReceivedCorrection | NextSlide
35
+ action:
36
+ | FetchSlide
37
+ | PostAnswerRequestAction
38
+ | EditAnswerAction
39
+ | ReceivedCorrection
40
+ | NextSlideAction
36
41
  ): UISlideState => {
37
42
  switch (action.type) {
38
43
  case SLIDE_FETCH_REQUEST: {
@@ -94,7 +94,9 @@ test('should set the next slide ref with an empty array if NEXT_SLIDE action is
94
94
  payload: {
95
95
  currentSlideRef: '1234',
96
96
  nextSlideRef: '5678',
97
- animationType: 'unstack'
97
+ animationType: 'unstack',
98
+ totalCorrectAnswers: 1,
99
+ answeredSlides: ['1234']
98
100
  }
99
101
  }
100
102
  );
@@ -1,10 +1,10 @@
1
1
  import test from 'ava';
2
2
  import reducer from '../current-slide-ref';
3
- import {SetCurrentSlide, SET_CURRENT_SLIDE} from '../../../actions/ui/slides';
3
+ import {SetCurrentSlideAction, SET_CURRENT_SLIDE} from '../../../actions/ui/slides';
4
4
  import {freeTextSlide} from '../../../views/slides/test/fixtures/free-text';
5
5
 
6
6
  test('should have initial value', t => {
7
- const state = reducer(undefined, {} as SetCurrentSlide);
7
+ const state = reducer(undefined, {} as SetCurrentSlideAction);
8
8
  t.is(state, '');
9
9
  });
10
10
 
@@ -0,0 +1,68 @@
1
+ import test from 'ava';
2
+ import reducer from '../positions';
3
+ import {POST_PROGRESSION_SUCCESS, ReceivedProgression} from '../../../actions/api/post-progression';
4
+ import {NEXT_SLIDE} from '../../../actions/ui/next-slide';
5
+
6
+ test('should have initial value', t => {
7
+ const state = reducer(undefined, {} as ReceivedProgression);
8
+ t.deepEqual(state, [0, 1, 2, 3, 4]);
9
+ });
10
+
11
+ test('should reset state to initial value when POST_PROGRESSION_SUCCESS action', t => {
12
+ const state = reducer([-1, -1, -1, -1, -1], {
13
+ type: POST_PROGRESSION_SUCCESS
14
+ } as ReceivedProgression);
15
+ t.deepEqual(state, [0, 1, 2, 3, 4]);
16
+ });
17
+
18
+ test('should set position for currentSlide according to the animationType and the position for the next slides', t => {
19
+ const state = reducer([-1, -1, 0, 1, 2], {
20
+ type: NEXT_SLIDE,
21
+ payload: {
22
+ totalCorrectAnswers: 2,
23
+ answeredSlides: ['sli_1', 'sli_2', 'sli_3'],
24
+ currentSlideRef: 'sli_3',
25
+ nextSlideRef: 'sli_4',
26
+ animationType: 'restack'
27
+ }
28
+ });
29
+ t.deepEqual(state, [-1, -1, 2, 0, 1]);
30
+
31
+ const _state = reducer([-1, -1, 0, 1, 2], {
32
+ type: NEXT_SLIDE,
33
+ payload: {
34
+ totalCorrectAnswers: 2,
35
+ answeredSlides: ['sli_1', 'sli_2', 'sli_3'],
36
+ currentSlideRef: 'sli_3',
37
+ nextSlideRef: 'sli_4',
38
+ animationType: 'unstack'
39
+ }
40
+ });
41
+ t.deepEqual(_state, [-1, -1, -1, 0, 1]);
42
+ });
43
+
44
+ test('should set position for currentSlide according to the animationType and the position for the next slides when answering already bad slide', t => {
45
+ const state = reducer([-1, 2, -1, 0, 1], {
46
+ type: NEXT_SLIDE,
47
+ payload: {
48
+ totalCorrectAnswers: 2,
49
+ answeredSlides: ['sli_1', 'sli_2', 'sli_3', 'sli_4', 'sli_5'],
50
+ currentSlideRef: 'sli_4',
51
+ nextSlideRef: 'sli_5',
52
+ animationType: 'restack'
53
+ }
54
+ });
55
+ t.deepEqual(state, [-1, 1, -1, 2, 0]);
56
+
57
+ const _state = reducer([-1, 1, -1, 2, 0], {
58
+ type: NEXT_SLIDE,
59
+ payload: {
60
+ totalCorrectAnswers: 2,
61
+ answeredSlides: ['sli_1', 'sli_2', 'sli_3', 'sli_4', 'sli_5'],
62
+ currentSlideRef: 'sli_5',
63
+ nextSlideRef: 'sli_1',
64
+ animationType: 'unstack'
65
+ }
66
+ });
67
+ t.deepEqual(_state, [-1, 0, -1, 1, -1]);
68
+ });
@@ -0,0 +1,24 @@
1
+ import test from 'ava';
2
+ import {CLOSE_POPIN, OPEN_POPIN} from '../../../actions/ui/quit-popin';
3
+ import reducer from '../quit-popin';
4
+
5
+ test('should have showQuitPopin intial value to false', t => {
6
+ const state = reducer(undefined, {
7
+ type: '@@ui/CLOSE_POPIN'
8
+ });
9
+ t.is(state, false);
10
+ });
11
+
12
+ test('should set showQuitPopin to true if OPEN_POPIN is received', t => {
13
+ const state = reducer(false, {
14
+ type: OPEN_POPIN
15
+ });
16
+ t.is(state, true);
17
+ });
18
+
19
+ test('should set showQuitPopin to false if CLOSE_POPIN is received', t => {
20
+ const state = reducer(true, {
21
+ type: CLOSE_POPIN
22
+ });
23
+ t.is(state, false);
24
+ });
@@ -66,7 +66,9 @@ test('should set animateCorrectionPopin to false and animationType to unstack or
66
66
  payload: {
67
67
  currentSlideRef: '1234',
68
68
  nextSlideRef: '5678',
69
- animationType: 'unstack'
69
+ animationType: 'unstack',
70
+ totalCorrectAnswers: 1,
71
+ answeredSlides: ['1234']
70
72
  }
71
73
  }
72
74
  );
@@ -1,4 +1,3 @@
1
- // TODO: EDIT_CHOICES -> specify typing
2
1
  export type ChoiceFromAPI = {
3
2
  _id: string;
4
3
  id?: string;
@@ -16,9 +16,11 @@ test('should create initial props when there are no skills on the state', t => {
16
16
  },
17
17
  ui: {
18
18
  currentSlideRef: '',
19
+ positions: [0, 1, 2, 3, 4],
19
20
  navigation: ['loader', 'skills'],
20
21
  answers: {},
21
- slide: {}
22
+ slide: {},
23
+ showQuitPopin: false
22
24
  }
23
25
  };
24
26
 
@@ -60,9 +62,11 @@ test('should create initial props when skills on the state', t => {
60
62
  },
61
63
  ui: {
62
64
  currentSlideRef: '',
65
+ positions: [0, 1, 2, 3, 4],
63
66
  navigation: ['loader', 'skills'],
64
67
  answers: {},
65
- slide: {}
68
+ slide: {},
69
+ showQuitPopin: false
66
70
  }
67
71
  };
68
72
 
@@ -1,16 +1,15 @@
1
- import concat from 'lodash/fp/concat';
2
1
  import findLast from 'lodash/fp/findLast';
3
2
  import get from 'lodash/fp/get';
4
3
  import getOr from 'lodash/fp/getOr';
5
4
  import last from 'lodash/fp/last';
6
5
  import reduce from 'lodash/fp/reduce';
7
6
  import set from 'lodash/fp/set';
8
- import slice from 'lodash/fp/slice';
9
7
  import toInteger from 'lodash/fp/toInteger';
10
8
  import type {Dispatch} from 'redux';
11
9
  import join from 'lodash/fp/join';
12
- import type {ProgressionAnswerItem, ProgressionFromAPI} from '../../types/common';
13
- import type {SlideIndexes} from '../../common';
10
+ import {closeQuitPopin, openQuitPopin} from '../../actions/ui/quit-popin';
11
+ import type {ProgressionAnswerItem} from '../../types/common';
12
+ import {getProgressionSlidesRefs, type SlideIndexes} from '../../common';
14
13
  import type {StoreState} from '../../reducers';
15
14
  import type {AnswerUI} from '../../types/slides';
16
15
  import {postAnswer} from '../../actions/api/post-answer';
@@ -34,7 +33,6 @@ type StepItem = {
34
33
  type SlideUIAnimations = 'unstack' | 'restack';
35
34
 
36
35
  export type ReviewSlide = {
37
- hidden: boolean;
38
36
  position: number;
39
37
  loading: boolean;
40
38
  showCorrectionPopin?: boolean;
@@ -65,6 +63,16 @@ type CorrectionPopinNext = {
65
63
  onClick: Function;
66
64
  };
67
65
 
66
+ type QuitPopinButton = {
67
+ label: string;
68
+ type: string;
69
+ customStyle?: {
70
+ color: string;
71
+ };
72
+ handleOnclick: Function;
73
+ ariaLabel: string;
74
+ };
75
+
68
76
  export type CorrectionPopinProps = {
69
77
  klf?: CorrectionPopinKlf;
70
78
  information: CorrectionPopinInformation;
@@ -73,6 +81,15 @@ export type CorrectionPopinProps = {
73
81
  type: 'right' | 'wrong';
74
82
  };
75
83
 
84
+ export type QuitPopinProps = {
85
+ content: string;
86
+ icon: string;
87
+ mode: string;
88
+ descriptionText: string;
89
+ firstButton: QuitPopinButton;
90
+ secondButton: QuitPopinButton;
91
+ };
92
+
76
93
  export type SlidesViewProps = {
77
94
  header: {
78
95
  mode: string;
@@ -113,44 +130,33 @@ export type SlidesViewProps = {
113
130
  type: string;
114
131
  };
115
132
  };
133
+ quitPopin?: QuitPopinProps;
116
134
  };
117
135
 
136
+ // TODO replace this, position no more needed
118
137
  export const initialState: SlidesStack = {
119
138
  '0': {
120
- hidden: false,
121
139
  position: 0,
122
140
  loading: true
123
141
  },
124
142
  '1': {
125
- hidden: false,
126
143
  position: 1,
127
144
  loading: true
128
145
  },
129
146
  '2': {
130
- hidden: false,
131
147
  position: 2,
132
148
  loading: true
133
149
  },
134
150
  '3': {
135
- hidden: false,
136
151
  position: 3,
137
152
  loading: true
138
153
  },
139
154
  '4': {
140
- hidden: false,
141
155
  position: 4,
142
156
  loading: true
143
157
  }
144
158
  };
145
159
 
146
- const getProgressionSlidesRefs = (progression: ProgressionFromAPI): string[] => {
147
- if (progression.state.step.current < 5) {
148
- const slideRef = progression.state.nextContent.ref;
149
- return concat(progression.state.slides, [slideRef]);
150
- }
151
- return slice(0, 5, progression.state.slides);
152
- };
153
-
154
160
  const buildStackSlides = (state: StoreState, dispatch: Dispatch): SlidesStack => {
155
161
  const currentSlideRef = state.ui.currentSlideRef;
156
162
  const progression = state.data.progression;
@@ -160,26 +166,30 @@ const buildStackSlides = (state: StoreState, dispatch: Dispatch): SlidesStack =>
160
166
 
161
167
  // @ts-expect-error typescript does not support capped versions of lodash functions
162
168
  const stack = reduce.convert({cap: false})(
163
- (acc: SlidesStack, uiSlide: ReviewSlide, index: string): SlidesStack => {
164
- const slideRef = slideRefs[toInteger(index)];
165
- if (!slideRef) return set(index, uiSlide, acc);
169
+ (acc: SlidesStack, uiSlide: ReviewSlide, _index: string): SlidesStack => {
170
+ const index = toInteger(_index);
171
+
172
+ const positions = state.ui.positions;
173
+ const position = positions[index];
174
+
175
+ const slideRef = slideRefs[index];
176
+ if (!slideRef) return set(index, {...uiSlide, position}, acc);
166
177
  const slideFromAPI = get(slideRef, state.data.slides);
167
- if (!slideFromAPI) return set(index, uiSlide, acc);
178
+ if (!slideFromAPI) return set(index, {...uiSlide, position}, acc);
168
179
 
169
180
  const answers = getOr([], ['ui', 'answers', slideRef], state);
170
181
  const {questionText, answerUI} = mapApiSlideToUi(dispatch)(slideFromAPI, answers);
171
- const parentContentTitle = getOr('', 'parentContentTitle.title', slideFromAPI);
172
- const parentContentType = getOr('', 'parentContentTitle.type', slideFromAPI);
182
+ const {title: parentContentTitle, type: parentContentType} = slideFromAPI.parentContentTitle;
173
183
 
174
184
  const isCurrentSlideRef = currentSlideRef === slideRef;
175
- const animateCorrectionPopin =
176
- isCurrentSlideRef && get(['ui', 'slide', slideRef, 'animateCorrectionPopin'], state);
177
- const showCorrectionPopin =
178
- isCurrentSlideRef && get(['ui', 'slide', slideRef, 'showCorrectionPopin'], state);
179
- const animationType = get(['ui', 'slide', slideRef, 'animationType'], state);
185
+ const slideUI = get(['ui', 'slide', slideRef], state);
186
+ const animateCorrectionPopin = isCurrentSlideRef && slideUI.animateCorrectionPopin;
187
+ const showCorrectionPopin = isCurrentSlideRef && slideUI.showCorrectionPopin;
188
+ const animationType = slideUI.animationType;
180
189
 
181
190
  const updatedUiSlide = {
182
191
  ...uiSlide,
192
+ position,
183
193
  showCorrectionPopin,
184
194
  animateCorrectionPopin,
185
195
  loading: false,
@@ -304,6 +314,33 @@ const getCorrectionPopinProps =
304
314
  };
305
315
  };
306
316
 
317
+ const buildQuitPopinProps =
318
+ (dispatch: Dispatch) =>
319
+ (onQuitClick: Function): QuitPopinProps => {
320
+ return {
321
+ content: `Tu t'en vas déjà ?`,
322
+ icon: `MoonRocket`,
323
+ mode: 'alert',
324
+ descriptionText: `Tu vas t'en sortir ! Si tu arrêtes maintenant, tu vas perdre ta progression.`,
325
+ firstButton: {
326
+ label: 'Arrêter ma session',
327
+ type: 'tertiary',
328
+ customStyle: {
329
+ color: '#ED3436'
330
+ },
331
+ handleOnclick: onQuitClick,
332
+ ariaLabel: 'Stop session'
333
+ },
334
+ secondButton: {
335
+ label: `Continuer d'apprendre`,
336
+ type: 'primary',
337
+ handleOnclick: (): void => {
338
+ dispatch(closeQuitPopin);
339
+ },
340
+ ariaLabel: 'Continue review'
341
+ }
342
+ };
343
+ };
307
344
  export const mapStateToSlidesProps = (
308
345
  state: StoreState,
309
346
  dispatch: Dispatch,
@@ -313,12 +350,12 @@ export const mapStateToSlidesProps = (
313
350
  const correction = get(['data', 'corrections', currentSlideRef], state);
314
351
  const isCorrect = get(['data', 'progression', 'state', 'isCorrect'], state);
315
352
  const klf = getOr('', ['data', 'slides', currentSlideRef, 'klf'], state);
316
-
353
+ const showQuitPopin = get(['ui', 'showQuitPopin'], state);
317
354
  return {
318
355
  header: {
319
356
  mode: '__revision_mode',
320
357
  skillName: '__agility',
321
- onQuitClick,
358
+ onQuitClick: () => dispatch(openQuitPopin),
322
359
  'aria-label': 'aria-header-wrapper',
323
360
  closeButtonAriaLabel: 'aria-close-button',
324
361
  steps: buildStepItems(state)
@@ -336,6 +373,7 @@ export const mapStateToSlidesProps = (
336
373
  correction && getCorrectionPopinProps(dispatch)(isCorrect, correction.correctAnswer, klf),
337
374
  endReview: false
338
375
  },
339
- congrats: undefined
376
+ congrats: undefined,
377
+ quitPopin: showQuitPopin === true ? buildQuitPopinProps(dispatch)(onQuitClick) : undefined
340
378
  };
341
379
  };
@@ -0,0 +1,42 @@
1
+ import test from 'ava';
2
+ import identity from 'lodash/fp/identity';
3
+ import {createTestStore} from '../../../actions/test/create-test-store';
4
+ import {incorrectFreeTextPostAnswerResponse, services} from '../../../test/util/services.mock';
5
+ import {StoreState} from '../../../reducers';
6
+ import {OPEN_POPIN} from '../../../actions/ui/quit-popin';
7
+ import {mapStateToSlidesProps} from '..';
8
+
9
+ const state: StoreState = {
10
+ data: {
11
+ progression: incorrectFreeTextPostAnswerResponse,
12
+ skills: [],
13
+ slides: {
14
+ sli_N1XACJobn: null
15
+ },
16
+ token: '1234',
17
+ corrections: {},
18
+ rank: {}
19
+ },
20
+ ui: {
21
+ currentSlideRef: '',
22
+ navigation: ['loader', 'slides'],
23
+ answers: {},
24
+ positions: [0, 1, 2, 3, 4],
25
+ slide: {
26
+ sli_N1XACJobn: {
27
+ validateButton: false,
28
+ animateCorrectionPopin: false,
29
+ showCorrectionPopin: false
30
+ }
31
+ },
32
+ showQuitPopin: false
33
+ }
34
+ };
35
+
36
+ test('should dispatch OPEN_POPIN action after a click on close button in header', async t => {
37
+ const expectedAction = [{type: OPEN_POPIN}];
38
+ const {dispatch, getState} = createTestStore(t, state, services, expectedAction);
39
+ const props = mapStateToSlidesProps(getState(), dispatch, identity);
40
+ await props.header.onQuitClick();
41
+ t.pass();
42
+ });