diff --git a/src/components/Action/ActionDone.spec.tsx b/src/components/Action/ActionDone.spec.tsx index b5071b647b31878ff649846f558a1ac7c4f44079..8045aff87de16951cb4b32f0389cea29ec56a9a4 100644 --- a/src/components/Action/ActionDone.spec.tsx +++ b/src/components/Action/ActionDone.spec.tsx @@ -6,7 +6,7 @@ import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import configureStore from 'redux-mock-store' import UsageEventService from 'services/usageEvent.service' -import * as challengeActions from 'store/challenge/challenge.actions' +import * as challengeActions from 'store/challenge/challenge.slice' import { globalStateData } from '../../../tests/__mocks__/globalStateData.mock' import { profileData } from '../../../tests/__mocks__/profileData.mock' import { userChallengeData } from '../../../tests/__mocks__/userChallengeData.mock' diff --git a/src/components/Action/ActionDone.tsx b/src/components/Action/ActionDone.tsx index 1d94980dc928cb805053a714e54311329c086e66..c2f8db980f4339ca7c80a2a1a215c7a69e6c8248 100644 --- a/src/components/Action/ActionDone.tsx +++ b/src/components/Action/ActionDone.tsx @@ -12,7 +12,7 @@ import { useNavigate } from 'react-router-dom' import ChallengeService from 'services/challenge.service' import UsageEventService from 'services/usageEvent.service' import { AppActionsTypes } from 'store' -import { updateUserChallengeList } from 'store/challenge/challenge.actions' +import { updateUserChallengeList } from 'store/challenge/challenge.slice' import { toggleChallengeActionNotification } from 'store/global/global.actions' import './actionDone.scss' interface ActionDoneProps { diff --git a/src/components/Action/ActionModal.spec.tsx b/src/components/Action/ActionModal.spec.tsx index 7c9fb2e01730b74655a7d1a978a834ea17630a52..650e249029ddcaf463d20c7358a37e0c7de74f61 100644 --- a/src/components/Action/ActionModal.spec.tsx +++ b/src/components/Action/ActionModal.spec.tsx @@ -6,7 +6,7 @@ import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import configureStore from 'redux-mock-store' import UsageEventService from 'services/usageEvent.service' -import * as challengeActions from 'store/challenge/challenge.actions' +import * as challengeActions from 'store/challenge/challenge.slice' import { defaultEcogestureData } from '../../../tests/__mocks__/actionData.mock' import { globalStateData } from '../../../tests/__mocks__/globalStateData.mock' import { profileData } from '../../../tests/__mocks__/profileData.mock' diff --git a/src/components/Action/ActionModal.tsx b/src/components/Action/ActionModal.tsx index cb209b25e13a42f4eaa1091e940078fb5c0f02c5..a74f9a6bc70ccbf807702c8ce3ee3af6ab62ded7 100644 --- a/src/components/Action/ActionModal.tsx +++ b/src/components/Action/ActionModal.tsx @@ -10,7 +10,7 @@ import React, { Dispatch, useCallback } from 'react' import { useDispatch } from 'react-redux' import ChallengeService from 'services/challenge.service' import { AppActionsTypes } from 'store' -import { updateUserChallengeList } from 'store/challenge/challenge.actions' +import { updateUserChallengeList } from 'store/challenge/challenge.slice' import './actionModal.scss' interface ActionModalProps { diff --git a/src/components/Challenge/ChallengeCardOnGoing.tsx b/src/components/Challenge/ChallengeCardOnGoing.tsx index 6d0f31dca0523ef8fb7822da271c25107a2294ce..92094c9f35e53795ac7a860c5261f68d8372c351 100644 --- a/src/components/Challenge/ChallengeCardOnGoing.tsx +++ b/src/components/Challenge/ChallengeCardOnGoing.tsx @@ -21,7 +21,7 @@ import { useDispatch, useSelector } from 'react-redux' import { useNavigate } from 'react-router-dom' import ChallengeService from 'services/challenge.service' import { AppActionsTypes, AppStore } from 'store' -import { updateUserChallengeList } from 'store/challenge/challenge.actions' +import { updateUserChallengeList } from 'store/challenge/challenge.slice' import { getChallengeTitleWithLineReturn, importIconById } from 'utils/utils' import ChallengeNoFluidModal from './ChallengeNoFluidModal' import StarsContainer from './StarsContainer' diff --git a/src/components/Challenge/ChallengeCardUnlocked.tsx b/src/components/Challenge/ChallengeCardUnlocked.tsx index 68cfcd89f0dfbedb7bf825ba236900cf0531ecf2..b5f8912cdeb5aba3fa99720866f61851ad00e08c 100644 --- a/src/components/Challenge/ChallengeCardUnlocked.tsx +++ b/src/components/Challenge/ChallengeCardUnlocked.tsx @@ -11,10 +11,10 @@ import { useDispatch, useSelector } from 'react-redux' import ChallengeService from 'services/challenge.service' import UsageEventService from 'services/usageEvent.service' import { AppActionsTypes, AppStore } from 'store' -import { updateUserChallengeList } from 'store/challenge/challenge.actions' +import { updateUserChallengeList } from 'store/challenge/challenge.slice' import { getChallengeTitleWithLineReturn, importIconById } from 'utils/utils' -import './challengeCardUnlocked.scss' import ChallengeNoFluidModal from './ChallengeNoFluidModal' +import './challengeCardUnlocked.scss' interface ChallengeCardUnlockedProps { userChallenge: UserChallenge diff --git a/src/components/Charts/UncomingBar.tsx b/src/components/Charts/UncomingBar.tsx index 9a8ae0f69fe023a36ffc6103aba489f7e59d17b6..3a252cb967e7dfb7bfbf363a57276966fa34e2ef 100644 --- a/src/components/Charts/UncomingBar.tsx +++ b/src/components/Charts/UncomingBar.tsx @@ -21,7 +21,6 @@ const UncomingBar = ({ height, average, }: BarProps) => { - dataload.value = average const [animationEnded, setAnimationEnded] = useState(false) const browser = detect() const onAnimationEnd = () => { @@ -93,18 +92,18 @@ const UncomingBar = ({ /> </g> )} - {height > 0 && dataload.value >= 0 && ( + {height > 0 && average >= 0 && ( <g transform={`translate(${xScale( dataload.date.toLocaleString(DateTime.DATETIME_SHORT) - )}, ${yScale(dataload.value)})`} + )}, ${yScale(average)})`} > <path d={topRoundedRectDashedLine( 0, 0, getBandWidth(), - height - yScale(dataload.value) + height - yScale(average) )} fill="url(#gradient)" strokeDasharray="5" diff --git a/src/components/Duel/DuelBar.tsx b/src/components/Duel/DuelBar.tsx index 1fbe4960739cec6af7b2113a147ac6d09963cfc7..89b8805d1a7d6cf7bca1c9df812dcd5a3984a65c 100644 --- a/src/components/Duel/DuelBar.tsx +++ b/src/components/Duel/DuelBar.tsx @@ -24,31 +24,18 @@ export interface BarChartProps { marginBottom?: number } -interface DefaultProps { - width: number - height: number - marginLeft: number - marginRight: number - marginTop: number - marginBottom: number -} - -type PropsWithDefaults = BarChartProps & DefaultProps - -const DuelBar: React.FC<BarChartProps> = (props: BarChartProps) => { - const { - userChallenge, - finishedDataLoad, - timeStep, - average, - width, - height, - marginLeft, - marginRight, - marginTop, - marginBottom, - } = props as PropsWithDefaults - +const DuelBar: React.FC<BarChartProps> = ({ + userChallenge, + finishedDataLoad, + timeStep, + average, + width = 600, + height = 400, + marginLeft = 10, + marginRight = 50, + marginTop = 20, + marginBottom = 50, +}) => { const { currentDataload } = useSelector( (state: AppStore) => state.ecolyo.challenge ) @@ -168,13 +155,4 @@ const DuelBar: React.FC<BarChartProps> = (props: BarChartProps) => { ) } -DuelBar.defaultProps = { - width: 600, - height: 400, - marginLeft: 10, - marginRight: 50, - marginTop: 20, - marginBottom: 50, -} - export default DuelBar diff --git a/src/components/Duel/DuelOngoing.tsx b/src/components/Duel/DuelOngoing.tsx index 56de0d8ce5a2826d3cb80f34598daf92d53aa408..b54938d773ca4971c4144aa31b16a1110b521a9a 100644 --- a/src/components/Duel/DuelOngoing.tsx +++ b/src/components/Duel/DuelOngoing.tsx @@ -31,7 +31,7 @@ import { AppActionsTypes, AppStore } from 'store' import { unlockNextUserChallenge, updateUserChallengeList, -} from 'store/challenge/challenge.actions' +} from 'store/challenge/challenge.slice' import { toggleChallengeDuelNotification } from 'store/global/global.actions' import { formatNumberValues } from 'utils/utils' import './duelOngoing.scss' diff --git a/src/components/Duel/DuelUnlocked.tsx b/src/components/Duel/DuelUnlocked.tsx index 8128a5539b3a88afaad6e58948667c2d0c0639f2..1d230a3a2d5e590bc73d3e7ef57bfb8a6b9f83ea 100644 --- a/src/components/Duel/DuelUnlocked.tsx +++ b/src/components/Duel/DuelUnlocked.tsx @@ -12,7 +12,7 @@ import { useDispatch } from 'react-redux' import ChallengeService from 'services/challenge.service' import UsageEventService from 'services/usageEvent.service' import { AppActionsTypes } from 'store' -import { setChallengeConsumption } from 'store/challenge/challenge.actions' +import { setChallengeConsumption } from 'store/challenge/challenge.slice' import { formatNumberValues, importIconById } from 'utils/utils' import './duelUnlocked.scss' @@ -50,7 +50,12 @@ const DuelUnlocked: React.FC<DuelUnlockedProps> = ({ target: userChallenge.duel.id, context: userChallenge.id, }) - dispatch(setChallengeConsumption(updatedChallenge, dataloads)) + dispatch( + setChallengeConsumption({ + userChallenge: updatedChallenge, + currentDataload: dataloads, + }) + ) }, [client, dispatch, userChallenge]) useEffect(() => { diff --git a/src/components/Exploration/ExplorationFinished.tsx b/src/components/Exploration/ExplorationFinished.tsx index 09b753bf24cef157536e9c76d17214255ca62a4e..3603d1fe456bf0a4bd632e2359817ce5e6153643 100644 --- a/src/components/Exploration/ExplorationFinished.tsx +++ b/src/components/Exploration/ExplorationFinished.tsx @@ -13,7 +13,7 @@ import { useNavigate } from 'react-router-dom' import ChallengeService from 'services/challenge.service' import UsageEventService from 'services/usageEvent.service' import { AppActionsTypes } from 'store' -import { updateUserChallengeList } from 'store/challenge/challenge.actions' +import { updateUserChallengeList } from 'store/challenge/challenge.slice' import { toggleChallengeExplorationNotification } from 'store/global/global.actions' import './explorationFinished.scss' diff --git a/src/components/Exploration/ExplorationOngoing.tsx b/src/components/Exploration/ExplorationOngoing.tsx index 29eadebfcb8d76af3356bd02dc736665d02c8412..4d58ed5bc6eee7a94149848a558b352c26046963 100644 --- a/src/components/Exploration/ExplorationOngoing.tsx +++ b/src/components/Exploration/ExplorationOngoing.tsx @@ -17,7 +17,7 @@ import { useNavigate } from 'react-router-dom' import ChallengeService from 'services/challenge.service' import UsageEventService from 'services/usageEvent.service' import { AppActionsTypes } from 'store' -import { updateUserChallengeList } from 'store/challenge/challenge.actions' +import { updateUserChallengeList } from 'store/challenge/challenge.slice' import './explorationOngoing.scss' interface ExplorationOngoingProps { diff --git a/src/components/Hooks/useExploration.tsx b/src/components/Hooks/useExploration.tsx index 0858343b897348ec9bd9b2fa2edf505721423634..a42e644f23f0b7807f8badc83361f100124988d2 100644 --- a/src/components/Hooks/useExploration.tsx +++ b/src/components/Hooks/useExploration.tsx @@ -5,7 +5,7 @@ import { Dispatch, SetStateAction, useEffect, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import ExplorationService from 'services/exploration.service' import { AppActionsTypes, AppStore } from 'store' -import { updateUserChallengeList } from 'store/challenge/challenge.actions' +import { updateUserChallengeList } from 'store/challenge/challenge.slice' import { toggleChallengeExplorationNotification } from 'store/global/global.actions' const useExploration = (): [string, Dispatch<SetStateAction<string>>] => { diff --git a/src/components/Konnector/KonnectorViewerCard.tsx b/src/components/Konnector/KonnectorViewerCard.tsx index f1f613b0236e10717c1976db947c67be6c4a8d9b..0086b5b92d4dacb73706d1f7bec25609891c4cfd 100644 --- a/src/components/Konnector/KonnectorViewerCard.tsx +++ b/src/components/Konnector/KonnectorViewerCard.tsx @@ -57,7 +57,7 @@ import FluidService from 'services/fluid.service' import PartnersInfoService from 'services/partnersInfo.service' import UsageEventService from 'services/usageEvent.service' import { AppActionsTypes, AppStore } from 'store' -import { setChallengeConsumption } from 'store/challenge/challenge.actions' +import { setChallengeConsumption } from 'store/challenge/challenge.slice' import { setSelectedDate, setShowOfflineData } from 'store/chart/chart.slice' import { setFluidStatus, @@ -163,7 +163,12 @@ const KonnectorViewerCard: React.FC<KonnectorViewerCardProps> = ({ const challengeService = new ChallengeService(client) const { updatedUserChallenge, dataloads } = await challengeService.initChallengeDuelProgress(currentChallenge) - dispatch(setChallengeConsumption(updatedUserChallenge, dataloads)) + dispatch( + setChallengeConsumption({ + userChallenge: updatedUserChallenge, + currentDataload: dataloads, + }) + ) // Check is duel is done and display notification const { isDone } = await challengeService.isChallengeDone( updatedUserChallenge, diff --git a/src/components/Quiz/QuizBegin.tsx b/src/components/Quiz/QuizBegin.tsx index 8ab9c1f630d666b358d7e94de51ec8765b9f48a6..375bbcc63e01c221e9e4157614c755e4640521e7 100644 --- a/src/components/Quiz/QuizBegin.tsx +++ b/src/components/Quiz/QuizBegin.tsx @@ -10,7 +10,7 @@ import React, { Dispatch } from 'react' import { useDispatch } from 'react-redux' import ChallengeService from 'services/challenge.service' import { AppActionsTypes } from 'store' -import { updateUserChallengeList } from 'store/challenge/challenge.actions' +import { updateUserChallengeList } from 'store/challenge/challenge.slice' import './quizBegin.scss' interface QuizBeginProps { diff --git a/src/components/Quiz/QuizCustomQuestionContent.tsx b/src/components/Quiz/QuizCustomQuestionContent.tsx index f5b80fd56b71d2be0448957d72038564242ba907..90369f131dd56ae7c2ed324f8cb62b7aa5ca4f74 100644 --- a/src/components/Quiz/QuizCustomQuestionContent.tsx +++ b/src/components/Quiz/QuizCustomQuestionContent.tsx @@ -14,7 +14,7 @@ import ChallengeService from 'services/challenge.service' import QuizService from 'services/quiz.service' import UsageEventService from 'services/usageEvent.service' import { AppActionsTypes } from 'store' -import { updateUserChallengeList } from 'store/challenge/challenge.actions' +import { updateUserChallengeList } from 'store/challenge/challenge.slice' import './quizQuestion.scss' interface QuizCustomQuestionContent { diff --git a/src/components/Quiz/QuizFinish.tsx b/src/components/Quiz/QuizFinish.tsx index cc0274f473352cabd845a7853d5623a1fbe4f550..6c44d04492e013ed4cd31be435be8023f787839a 100644 --- a/src/components/Quiz/QuizFinish.tsx +++ b/src/components/Quiz/QuizFinish.tsx @@ -10,7 +10,7 @@ import { useDispatch } from 'react-redux' import { useNavigate } from 'react-router-dom' import ChallengeService from 'services/challenge.service' import { AppActionsTypes } from 'store' -import { updateUserChallengeList } from 'store/challenge/challenge.actions' +import { updateUserChallengeList } from 'store/challenge/challenge.slice' import './quizFinish.scss' interface QuizFinishProps { diff --git a/src/components/Quiz/QuizQuestionContent.tsx b/src/components/Quiz/QuizQuestionContent.tsx index 4b12b75b792bbcba8310dd932b0ac577656f8ba2..cf2b10172dcdfe5c6135556166bc559e81ad7b63 100644 --- a/src/components/Quiz/QuizQuestionContent.tsx +++ b/src/components/Quiz/QuizQuestionContent.tsx @@ -11,7 +11,7 @@ import { useDispatch } from 'react-redux' import ChallengeService from 'services/challenge.service' import QuizService from 'services/quiz.service' import { AppActionsTypes } from 'store' -import { updateUserChallengeList } from 'store/challenge/challenge.actions' +import { updateUserChallengeList } from 'store/challenge/challenge.slice' import './quizQuestion.scss' interface QuizQuestionContent { diff --git a/src/components/Splash/SplashRoot.tsx b/src/components/Splash/SplashRoot.tsx index a486f8ae2d3271ce11c31668e91e32a53e1d3141..ead877e8d9d7f143bafad4c8d2b0af41d485f5e6 100644 --- a/src/components/Splash/SplashRoot.tsx +++ b/src/components/Splash/SplashRoot.tsx @@ -43,7 +43,7 @@ import { setChallengeConsumption, setUserChallengeList, updateUserChallengeList, -} from 'store/challenge/challenge.actions' +} from 'store/challenge/challenge.slice' import { setSelectedDate } from 'store/chart/chart.slice' import { setFluidStatus, @@ -322,7 +322,12 @@ const SplashRoot = ({ fadeTimer = 1000, children }: SplashRootProps) => { filteredCurrentDuelChallenge[0] ) if (subscribed) { - dispatch(setChallengeConsumption(updatedUserChallenge, dataloads)) + dispatch( + setChallengeConsumption({ + userChallenge: updatedUserChallenge, + currentDataload: dataloads, + }) + ) // Check is duel is done and display notification const challengeService = new ChallengeService(client) const { isDone } = await challengeService.isChallengeDone( diff --git a/src/services/challenge.service.ts b/src/services/challenge.service.ts index 1822ae431e32ad085a782bbcd471e3b16428e653..b9de549d948d7fc7019cc42bc7b339dcc2268459 100644 --- a/src/services/challenge.service.ts +++ b/src/services/challenge.service.ts @@ -555,22 +555,26 @@ export default class ChallengeService { public async startUserChallenge( userChallenge: UserChallenge ): Promise<UserChallenge> { - userChallenge.state = UserChallengeState.ONGOING - userChallenge.progress.actionProgress = 0 - userChallenge.progress.explorationProgress = 0 - userChallenge.progress.quizProgress = 0 - userChallenge.startDate = DateTime.local() - .setZone('utc', { - keepLocalTime: true, - }) - .startOf('day') - userChallenge.success = UserChallengeSuccess.ONGOING + const newChallenge: UserChallenge = { + ...userChallenge, + state: UserChallengeState.ONGOING, + progress: { + actionProgress: 0, + explorationProgress: 0, + quizProgress: 0, + }, + startDate: DateTime.local() + .setZone('utc', { keepLocalTime: true }) + .startOf('day'), + success: UserChallengeSuccess.ONGOING, + } + try { const { data: updatedUserChallengeEntity, }: QueryResult<UserChallengeEntity> = await this._client.create( USERCHALLENGE_DOCTYPE, - userChallenge + newChallenge ) const updatedUserChallenge: UserChallenge = this.parseUserChallengeEntityToUserChallenge(updatedUserChallengeEntity) @@ -676,19 +680,19 @@ export default class ChallengeService { quiz: updatedQuiz, } break - case UserChallengeUpdateFlag.QUIZ_DONE: - if (userChallenge.quiz.result > userChallenge.progress.quizProgress) { - userChallenge.progress.quizProgress = userChallenge.quiz.result - } - if (userChallenge.progress.quizProgress > 5) { - userChallenge.progress.quizProgress = 5 - } + case UserChallengeUpdateFlag.QUIZ_DONE: { + const updateQuizProgress = Math.min(userChallenge.quiz.result, 5) updatedQuiz = await quizService.endUserQuiz(userChallenge.quiz) updatedUserChallenge = { ...userChallenge, quiz: updatedQuiz, + progress: { + ...userChallenge.progress, + quizProgress: updateQuizProgress, + }, } break + } case UserChallengeUpdateFlag.QUIZ_UPDATE: updatedUserChallenge = { ...userChallenge, diff --git a/src/services/quiz.service.ts b/src/services/quiz.service.ts index 3dfc34ff58d4ab5055188992e0f0d43a683ffbdc..c553c7f7d53869a308a38bd0af9af923eb7b266c 100644 --- a/src/services/quiz.service.ts +++ b/src/services/quiz.service.ts @@ -176,20 +176,19 @@ export default class QuizService { * @returns {UserQuiz} */ public async startUserQuiz(userQuiz: UserQuiz): Promise<UserQuiz> { - const { questions } = userQuiz - questions.forEach( - question => (question.answers = shuffle(question.answers)) - ) - const randomizedQuestions = shuffle(questions) - const updatedUserQuiz: UserQuiz = { + const questions = userQuiz.questions.map(question => ({ + ...question, + answers: shuffle(question.answers), + })) + + return { ...userQuiz, - questions: randomizedQuestions, + questions: shuffle(questions), state: UserQuizState.ONGOING, startDate: DateTime.local().setZone('utc', { keepLocalTime: true, }), } - return updatedUserQuiz } /** * Return quiz with updated state to UserQuizState.UNLOCKED and updated questions with false result @@ -197,22 +196,23 @@ export default class QuizService { * @returns {UserQuiz} */ public async resetUserQuiz(userQuiz: UserQuiz): Promise<UserQuiz> { - const updatedQuestions: UserQuestion[] = userQuiz.questions - updatedQuestions.forEach(question => { - question.result = UserQuestionState.UNLOCKED - }) + const updatedQuestions: UserQuestion[] = userQuiz.questions.map( + question => ({ + ...question, + result: UserQuestionState.UNLOCKED, + }) + ) const updatedCustomQuestion = { ...userQuiz.customQuestion, result: UserQuestionState.UNLOCKED, } - const updatedUserQuiz: UserQuiz = { + return { ...userQuiz, customQuestion: updatedCustomQuestion, questions: updatedQuestions, result: 0, state: UserQuizState.UNLOCKED, } - return updatedUserQuiz } /** @@ -240,30 +240,36 @@ export default class QuizService { questionResult: boolean, questionIndex?: number ): Promise<UserQuiz> { - if (questionIndex !== undefined) { - const updatedQuestion: UserQuestion = { - ...userQuiz.questions[questionIndex], - result: questionResult - ? UserQuestionState.CORRECT - : UserQuestionState.UNCORRECT, - } - userQuiz.questions[questionIndex] = updatedQuestion - } else { - const updatedQuestion: UserCustomQuestion = { - ...userQuiz.customQuestion, - result: questionResult - ? UserQuestionState.CORRECT - : UserQuestionState.UNCORRECT, + const result = questionResult + ? UserQuestionState.CORRECT + : UserQuestionState.UNCORRECT + + const updatedQuestions = userQuiz.questions.map((question, index) => { + if (index === questionIndex) { + return { + ...question, + result: result, + } } - userQuiz.customQuestion = updatedQuestion + return question + }) + + const updatedCustomQuestion = { + ...userQuiz.customQuestion, + result: result, } - let quizScoreLimit = userQuiz.result - quizScoreLimit = quizScoreLimit + 1 > 5 ? 5 : quizScoreLimit + 1 - const updatedUserQuiz: UserQuiz = { + + return { ...userQuiz, - result: questionResult ? quizScoreLimit : userQuiz.result, + questions: updatedQuestions, + customQuestion: + questionIndex === undefined + ? updatedCustomQuestion + : userQuiz.customQuestion, + result: questionResult + ? Math.min(userQuiz.result + 1, 5) + : userQuiz.result, } - return updatedUserQuiz } /** diff --git a/src/store/challenge/challenge.action.spec.ts b/src/store/challenge/challenge.action.spec.ts deleted file mode 100644 index 66bc6f0e6009b3cb8793e0358543e9432b7f2328..0000000000000000000000000000000000000000 --- a/src/store/challenge/challenge.action.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { userChallengeData } from '../../../tests/__mocks__/userChallengeData.mock' -import { - setChallengeConsumption, - setUserChallengeList, - SET_CHALLENGE_CONSUMPTION, - SET_USER_CHALLENGE_LIST, - unlockNextUserChallenge, - UNLOCK_NEXT_USER_CHALLENGE, - updateUserChallengeList, - UPDATE_USER_CHALLENGE_LIST, -} from './challenge.actions' - -describe('challenge actions', () => { - it('should create an action to set userChallengeList', () => { - const expectedAction = { - type: SET_USER_CHALLENGE_LIST, - payload: userChallengeData, - } - expect(setUserChallengeList(userChallengeData)).toEqual(expectedAction) - }) - - it('should create an action to update the userChallengeList', () => { - const expectedAction = { - type: UPDATE_USER_CHALLENGE_LIST, - payload: userChallengeData[0], - } - expect(updateUserChallengeList(userChallengeData[0])).toEqual( - expectedAction - ) - }) - - it('should create an action to unlock next challenge in the list', () => { - const expectedAction = { - type: UNLOCK_NEXT_USER_CHALLENGE, - payload: userChallengeData[0], - } - expect(unlockNextUserChallenge(userChallengeData[0])).toEqual( - expectedAction - ) - }) - - it('should create an action to update challenge with consumption in the list and set current data load', () => { - const expectedAction = { - type: SET_CHALLENGE_CONSUMPTION, - payload: { userChallenge: userChallengeData[0], currentDataload: [] }, - } - expect(setChallengeConsumption(userChallengeData[0], [])).toEqual( - expectedAction - ) - }) -}) diff --git a/src/store/challenge/challenge.actions.ts b/src/store/challenge/challenge.actions.ts deleted file mode 100644 index 03ab3e29d5de88293b56567af01eefced45b3729..0000000000000000000000000000000000000000 --- a/src/store/challenge/challenge.actions.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { Dataload, UserChallenge } from 'models' -import { defaultAction } from 'store' - -export const SET_USER_CHALLENGE_LIST = 'SET_USER_CHALLENGE_LIST' -export const UPDATE_USER_CHALLENGE_LIST = 'UPDATE_USER_CHALLENGE_LIST' -export const UNLOCK_NEXT_USER_CHALLENGE = 'UNLOCK_NEXT_USER_CHALLENGE' -export const SET_CHALLENGE_CONSUMPTION = 'SET_CHALLENGE_CONSUMPTION' - -interface SetUserChallengeList { - type: typeof SET_USER_CHALLENGE_LIST - payload?: UserChallenge[] -} - -interface UpdateUserChallengeList { - type: typeof UPDATE_USER_CHALLENGE_LIST - payload?: UserChallenge -} - -interface UnlockNextUserChallenge { - type: typeof UNLOCK_NEXT_USER_CHALLENGE - payload?: UserChallenge -} - -interface SetChallengeConsumption { - type: typeof SET_CHALLENGE_CONSUMPTION - payload?: { userChallenge: UserChallenge; currentDataload: Dataload[] } -} - -export type ChallengeActionTypes = - | SetUserChallengeList - | UpdateUserChallengeList - | UnlockNextUserChallenge - | SetChallengeConsumption - | typeof defaultAction - -export function setUserChallengeList( - userChallengeList: UserChallenge[] -): ChallengeActionTypes { - return { - type: SET_USER_CHALLENGE_LIST, - payload: userChallengeList, - } -} - -export function updateUserChallengeList( - userChallenge: UserChallenge -): ChallengeActionTypes { - return { - type: UPDATE_USER_CHALLENGE_LIST, - payload: userChallenge, - } -} - -export function unlockNextUserChallenge( - userChallenge: UserChallenge -): ChallengeActionTypes { - return { - type: UNLOCK_NEXT_USER_CHALLENGE, - payload: userChallenge, - } -} - -export function setChallengeConsumption( - userChallenge: UserChallenge, - currentDataload: Dataload[] -): ChallengeActionTypes { - return { - type: SET_CHALLENGE_CONSUMPTION, - payload: { userChallenge, currentDataload }, - } -} diff --git a/src/store/challenge/challenge.reducer.ts b/src/store/challenge/challenge.reducer.ts deleted file mode 100644 index 4d85c630a4d8a864f9fcd467884f624e1cc48e34..0000000000000000000000000000000000000000 --- a/src/store/challenge/challenge.reducer.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { UserChallengeState } from 'enum/userChallenge.enum' -import { ChallengeState } from 'models' -import { Reducer } from 'redux' -import { - ChallengeActionTypes, - SET_CHALLENGE_CONSUMPTION, - SET_USER_CHALLENGE_LIST, - UNLOCK_NEXT_USER_CHALLENGE, - UPDATE_USER_CHALLENGE_LIST, -} from './challenge.actions' - -const initialState: ChallengeState = { - userChallengeList: [], - currentChallenge: null, - currentDataload: [], -} - -export const challengeReducer: Reducer<ChallengeState, ChallengeActionTypes> = ( - state = initialState, - action -) => { - if (action.payload == undefined) return state - - const updateState = (updates: Partial<ChallengeState>): ChallengeState => ({ - ...state, - ...updates, - }) - switch (action.type) { - case SET_USER_CHALLENGE_LIST: { - const filteredCurrentChallenge = action.payload.filter( - challenge => - challenge.state === UserChallengeState.ONGOING || - challenge.state === UserChallengeState.DUEL - ) - const currentChallenge = filteredCurrentChallenge[0] || null - return updateState({ - userChallengeList: action.payload, - currentChallenge: currentChallenge, - }) - } - - case UPDATE_USER_CHALLENGE_LIST: { - const id = action.payload.id - const currentChallenge = - action.payload.state === UserChallengeState.ONGOING || - action.payload.state === UserChallengeState.DUEL - ? action.payload - : null - const updatedList = [...state.userChallengeList] - const findIndex = updatedList.findIndex(challenge => challenge.id === id) - updatedList[findIndex] = action.payload - return updateState({ - userChallengeList: updatedList, - currentChallenge: currentChallenge || state.currentChallenge, - }) - } - case UNLOCK_NEXT_USER_CHALLENGE: { - const id = action.payload.id - const updatedList = [...state.userChallengeList] - const findIndex = updatedList.findIndex(challenge => challenge.id === id) - updatedList[findIndex] = action.payload - if (typeof updatedList[findIndex + 1] !== 'undefined') { - updatedList[findIndex + 1] = { - ...updatedList[findIndex + 1], - state: UserChallengeState.UNLOCKED, - } - } - return updateState({ - userChallengeList: updatedList, - currentChallenge: null, - }) - } - case SET_CHALLENGE_CONSUMPTION: { - const id = action.payload.userChallenge.id - const updatedList = [...state.userChallengeList] - const findIndex = updatedList.findIndex(challenge => challenge.id === id) - updatedList[findIndex] = action.payload.userChallenge - return updateState({ - userChallengeList: updatedList, - currentChallenge: - state.currentChallenge && - state.currentChallenge.id === action.payload.userChallenge.id - ? action.payload.userChallenge - : state.currentChallenge, - currentDataload: action.payload.currentDataload, - }) - } - default: - return state - } -} diff --git a/src/store/challenge/challenge.reducer.spec.ts b/src/store/challenge/challenge.slice.spec.ts similarity index 57% rename from src/store/challenge/challenge.reducer.spec.ts rename to src/store/challenge/challenge.slice.spec.ts index f2ea8117f83c58af7190224ee047e35d8215f1c6..8adbc4cc7271313784926b77b6d50388fd62c6b2 100644 --- a/src/store/challenge/challenge.reducer.spec.ts +++ b/src/store/challenge/challenge.slice.spec.ts @@ -1,162 +1,134 @@ -import { DataloadState } from 'enum/dataload.enum' -import { UserChallengeState } from 'enum/userChallenge.enum' -import { DateTime } from 'luxon' -import { ChallengeState, Dataload, UserChallenge } from 'models' -import { defaultAction } from 'store' -import { mockInitialChallengeState } from '../../../tests/__mocks__/store' -import { - userChallengeData, - userChallengeDefault, -} from '../../../tests/__mocks__/userChallengeData.mock' -import { - SET_CHALLENGE_CONSUMPTION, - SET_USER_CHALLENGE_LIST, - UNLOCK_NEXT_USER_CHALLENGE, - UPDATE_USER_CHALLENGE_LIST, -} from './challenge.actions' -import { challengeReducer } from './challenge.reducer' - -describe('challenge reducer', () => { - it('should return the initial state', () => { - const state = challengeReducer(undefined, { ...defaultAction }) - expect(state).toEqual(mockInitialChallengeState) - }) - - it('should handle SET_USER_CHALLENGE_LIST with payload', () => { - const state = challengeReducer(mockInitialChallengeState, { - type: SET_USER_CHALLENGE_LIST, - payload: userChallengeData, - }) - const expectedResult: ChallengeState = { - userChallengeList: userChallengeData, - currentChallenge: userChallengeData[2], - currentDataload: [], - } - expect(state).toEqual(expectedResult) - }) - - it('should handle SET_USER_CHALLENGE_LIST without payload', () => { - const state = challengeReducer(mockInitialChallengeState, { - type: SET_USER_CHALLENGE_LIST, - }) - expect(state).toEqual(mockInitialChallengeState) - }) - - it('should handle UPDATE_USER_CHALLENGE_LIST with payload', () => { - const updatedMockInitialChallengeState = { - ...mockInitialChallengeState, - userChallengeList: userChallengeDefault, - } - const updatedUserChallenge: UserChallenge = { - ...userChallengeDefault[0], - state: UserChallengeState.ONGOING, - } - const state = challengeReducer(updatedMockInitialChallengeState, { - type: UPDATE_USER_CHALLENGE_LIST, - payload: updatedUserChallenge, - }) - const expectedResult: ChallengeState = { - userChallengeList: [ - updatedUserChallenge, - ...userChallengeDefault.slice(1), - ], - currentChallenge: updatedUserChallenge, - currentDataload: [], - } - expect(state).toEqual(expectedResult) - }) - - it('should handle UPDATE_USER_CHALLENGE_LIST without payload', () => { - const state = challengeReducer(mockInitialChallengeState, { - type: UPDATE_USER_CHALLENGE_LIST, - }) - expect(state).toEqual(mockInitialChallengeState) - }) - - it('should handle UNLOCK_NEXT_USER_CHALLENGE with payload', () => { - const updatedMockInitialChallengeState = { - ...mockInitialChallengeState, - userChallengeList: userChallengeDefault, - } - const updatedUserChallenge: UserChallenge = { - ...userChallengeDefault[0], - state: UserChallengeState.DONE, - } - const unlockedUserChallenge: UserChallenge = { - ...userChallengeDefault[1], - state: UserChallengeState.UNLOCKED, - } - const state = challengeReducer(updatedMockInitialChallengeState, { - type: UNLOCK_NEXT_USER_CHALLENGE, - payload: updatedUserChallenge, - }) - const expectedResult: ChallengeState = { - userChallengeList: [ - updatedUserChallenge, - unlockedUserChallenge, - ...userChallengeDefault.slice(2, 6), - ], - currentChallenge: null, - currentDataload: [], - } - expect(state).toEqual(expectedResult) - }) - - it('should handle UNLOCK_NEXT_USER_CHALLENGE without payload', () => { - const state = challengeReducer(mockInitialChallengeState, { - type: UNLOCK_NEXT_USER_CHALLENGE, - }) - expect(state).toEqual(mockInitialChallengeState) - }) - - it('should handle SET_CHALLENGE_CONSUMPTION with payload', () => { - const updatedMockInitialChallengeState = { - ...mockInitialChallengeState, - userChallengeList: userChallengeDefault, - } - const dataload: Dataload[] = [ - { - date: DateTime.fromISO('2020-10-01T00:00:00.000Z', { - zone: 'utc', - }), - value: 20, - valueDetail: [ - { state: DataloadState.VALID, value: 5.0 }, - { state: DataloadState.VALID, value: 10.0 }, - { state: DataloadState.VALID, value: 5.0 }, - ], - state: DataloadState.VALID, - }, - ] - const updatedUserChallenge: UserChallenge = { - ...userChallengeDefault[0], - duel: { - ...userChallengeDefault[0].duel, - userConsumption: 20, - }, - } - const expectedResult: ChallengeState = { - userChallengeList: [ - updatedUserChallenge, - ...userChallengeDefault.slice(1, 6), - ], - currentChallenge: null, - currentDataload: dataload, - } - const state = challengeReducer(updatedMockInitialChallengeState, { - type: SET_CHALLENGE_CONSUMPTION, - payload: { - userChallenge: updatedUserChallenge, - currentDataload: dataload, - }, - }) - expect(state).toEqual(expectedResult) - }) - - it('should handle SET_CHALLENGE_CONSUMPTION without payload', () => { - const state = challengeReducer(mockInitialChallengeState, { - type: SET_CHALLENGE_CONSUMPTION, - }) - expect(state).toEqual(mockInitialChallengeState) - }) -}) +import { DataloadState } from 'enum/dataload.enum' +import { UserChallengeState } from 'enum/userChallenge.enum' +import { DateTime } from 'luxon' +import { ChallengeState, Dataload, UserChallenge } from 'models' +import { mockInitialChallengeState } from '../../../tests/__mocks__/store' +import { + userChallengeData, + userChallengeDefault, +} from '../../../tests/__mocks__/userChallengeData.mock' +import { + challengeSlice, + setChallengeConsumption, + setUserChallengeList, + unlockNextUserChallenge, + updateUserChallengeList, +} from './challenge.slice' + +describe('challenge reducer', () => { + it('should return the initial state', () => { + const state = challengeSlice.reducer(undefined, { type: undefined }) + expect(state).toEqual(mockInitialChallengeState) + }) + + it('should handle setUserChallengeList', () => { + const state = challengeSlice.reducer( + mockInitialChallengeState, + setUserChallengeList(userChallengeData) + ) + const expectedResult: ChallengeState = { + userChallengeList: userChallengeData, + currentChallenge: userChallengeData[2], + currentDataload: [], + } + expect(state).toEqual(expectedResult) + }) + + it('should handle updateUserChallengeList ', () => { + const updatedMockInitialChallengeState = { + ...mockInitialChallengeState, + userChallengeList: userChallengeDefault, + } + const updatedUserChallenge: UserChallenge = { + ...userChallengeDefault[0], + state: UserChallengeState.ONGOING, + } + const state = challengeSlice.reducer( + updatedMockInitialChallengeState, + updateUserChallengeList(updatedUserChallenge) + ) + const expectedResult: ChallengeState = { + userChallengeList: [ + updatedUserChallenge, + ...userChallengeDefault.slice(1), + ], + currentChallenge: updatedUserChallenge, + currentDataload: [], + } + expect(state).toEqual(expectedResult) + }) + + it('should handle unlockNextUserChallenge ', () => { + const updatedMockInitialChallengeState = { + ...mockInitialChallengeState, + userChallengeList: userChallengeDefault, + } + const updatedUserChallenge: UserChallenge = { + ...userChallengeDefault[0], + state: UserChallengeState.DONE, + } + const unlockedUserChallenge: UserChallenge = { + ...userChallengeDefault[1], + state: UserChallengeState.UNLOCKED, + } + const state = challengeSlice.reducer( + updatedMockInitialChallengeState, + unlockNextUserChallenge(updatedUserChallenge) + ) + + const expectedResult: ChallengeState = { + userChallengeList: [ + updatedUserChallenge, + unlockedUserChallenge, + ...userChallengeDefault.slice(2, 6), + ], + currentChallenge: null, + currentDataload: [], + } + expect(state).toEqual(expectedResult) + }) + + it('should handle SET_CHALLENGE_CONSUMPTION ', () => { + const updatedMockInitialChallengeState = { + ...mockInitialChallengeState, + userChallengeList: userChallengeDefault, + } + const dataload: Dataload[] = [ + { + date: DateTime.fromISO('2020-10-01T00:00:00.000Z', { + zone: 'utc', + }), + value: 20, + valueDetail: [ + { state: DataloadState.VALID, value: 5.0 }, + { state: DataloadState.VALID, value: 10.0 }, + { state: DataloadState.VALID, value: 5.0 }, + ], + state: DataloadState.VALID, + }, + ] + const updatedUserChallenge: UserChallenge = { + ...userChallengeDefault[0], + duel: { + ...userChallengeDefault[0].duel, + userConsumption: 20, + }, + } + const expectedResult: ChallengeState = { + userChallengeList: [ + updatedUserChallenge, + ...userChallengeDefault.slice(1, 6), + ], + currentChallenge: null, + currentDataload: dataload, + } + const state = challengeSlice.reducer( + updatedMockInitialChallengeState, + setChallengeConsumption({ + userChallenge: updatedUserChallenge, + currentDataload: dataload, + }) + ) + expect(state).toEqual(expectedResult) + }) +}) diff --git a/src/store/challenge/challenge.slice.ts b/src/store/challenge/challenge.slice.ts new file mode 100644 index 0000000000000000000000000000000000000000..eae360e05ee1019d0aa87b11f70174bfae4b7792 --- /dev/null +++ b/src/store/challenge/challenge.slice.ts @@ -0,0 +1,92 @@ +import { PayloadAction, createSlice } from '@reduxjs/toolkit' +import { UserChallengeState } from 'enum/userChallenge.enum' +import { ChallengeState, Dataload, UserChallenge } from 'models' + +const initialState: ChallengeState = { + userChallengeList: [], + currentChallenge: null, + currentDataload: [], +} + +type SetUserChallengeList = PayloadAction<UserChallenge[]> +type UpdateUserChallengeList = PayloadAction<UserChallenge> +type UnlockNextUserChallenge = PayloadAction<UserChallenge> +type SetChallengeConsumption = PayloadAction<{ + userChallenge: UserChallenge + currentDataload: Dataload[] +}> + +export type ChallengeActionTypes = + | SetUserChallengeList + | UpdateUserChallengeList + | UnlockNextUserChallenge + | SetChallengeConsumption + +export const challengeSlice = createSlice({ + name: 'challenge', + initialState, + reducers: { + setUserChallengeList: (state, action: SetUserChallengeList) => { + const filteredCurrentChallenge = action.payload.filter( + challenge => + challenge.state === UserChallengeState.ONGOING || + challenge.state === UserChallengeState.DUEL + ) + const currentChallenge = filteredCurrentChallenge[0] || null + + state.userChallengeList = action.payload + state.currentChallenge = currentChallenge + }, + updateUserChallengeList: (state, action: UpdateUserChallengeList) => { + const id = action.payload.id + const currentChallenge = + action.payload.state === UserChallengeState.ONGOING || + action.payload.state === UserChallengeState.DUEL + ? action.payload + : null + const updatedList = [...state.userChallengeList] + const findIndex = updatedList.findIndex(challenge => challenge.id === id) + updatedList[findIndex] = action.payload + + state.userChallengeList = updatedList + state.currentChallenge = currentChallenge || state.currentChallenge + }, + unlockNextUserChallenge: (state, action: UnlockNextUserChallenge) => { + const id = action.payload.id + const updatedList = [...state.userChallengeList] + const findIndex = updatedList.findIndex(challenge => challenge.id === id) + updatedList[findIndex] = action.payload + if (typeof updatedList[findIndex + 1] !== 'undefined') { + updatedList[findIndex + 1] = { + ...updatedList[findIndex + 1], + state: UserChallengeState.UNLOCKED, + } + } + + state.userChallengeList = updatedList + state.currentChallenge = null + }, + setChallengeConsumption: (state, action: SetChallengeConsumption) => { + const id = action.payload.userChallenge.id + const updatedList = [...state.userChallengeList] + const findIndex = updatedList.findIndex(challenge => challenge.id === id) + updatedList[findIndex] = action.payload.userChallenge + + state.userChallengeList = updatedList + state.currentDataload = action.payload.currentDataload + if ( + state.currentChallenge && + state.currentChallenge.id === action.payload.userChallenge.id + ) { + state.currentChallenge = action.payload.userChallenge + } + }, + }, +}) + +export const { + setChallengeConsumption, + setUserChallengeList, + unlockNextUserChallenge, + updateUserChallengeList, +} = challengeSlice.actions diff --git a/src/store/index.ts b/src/store/index.ts index 3f16e08ff6fc7a77456555af1c0693a8a4c99664..6b9fba03a4b81e92180d8dc1127a39c5ee37e39d 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -21,8 +21,10 @@ import { composeWithDevTools } from 'redux-devtools-extension' import thunkMiddleware from 'redux-thunk' import { globalReducer } from 'store/global/global.reducer' import { AnalysisActionTypes, analysisSlice } from './analysis/analysis.slice' -import { ChallengeActionTypes } from './challenge/challenge.actions' -import { challengeReducer } from './challenge/challenge.reducer' +import { + ChallengeActionTypes, + challengeSlice, +} from './challenge/challenge.slice' import { ChartActionTypes, chartSlice } from './chart/chart.slice' import { GlobalActionTypes } from './global/global.actions' import { ModalActionTypes, modalSlice } from './modal/modal.slice' @@ -50,7 +52,7 @@ export const defaultAction = { type: null, payload: undefined } const ecolyoReducer = combineReducers({ analysis: analysisSlice.reducer, - challenge: challengeReducer, + challenge: challengeSlice.reducer, chart: chartSlice.reducer, global: globalReducer, modal: modalSlice.reducer,