From ac0d47a9dc59044c2305b01021b5cad9356a926f Mon Sep 17 00:00:00 2001 From: Bastien DUMONT <bdumont@grandlyon.com> Date: Fri, 7 Jul 2023 09:04:53 +0000 Subject: [PATCH] chore(redux-rtk): typed hooks --- src/components/Action/ActionBegin.tsx | 5 ++--- src/components/Action/ActionCard.tsx | 7 ++----- src/components/Action/ActionDone.spec.tsx | 3 --- src/components/Action/ActionDone.tsx | 7 +++---- src/components/Action/ActionList.tsx | 5 ++--- src/components/Action/ActionModal.spec.tsx | 4 ---- src/components/Action/ActionModal.tsx | 7 +++---- src/components/Action/ActionView.tsx | 7 ++----- src/components/Analysis/AnalysisView.spec.tsx | 11 ++++------- src/components/Analysis/AnalysisView.tsx | 11 +++++------ .../Analysis/Comparison/Comparison.tsx | 9 ++++----- .../ElecHalfHourMonthlyAnalysis.tsx | 7 ++----- .../MaxConsumptionCard/MaxConsumptionCard.tsx | 11 ++++------- src/components/Analysis/MonthlyAnalysis.tsx | 7 ++----- .../ProfileComparator/ProfileComparator.tsx | 5 ++--- .../TotalAnalysisChart/TotalAnalysisChart.tsx | 11 ++++------- src/components/App.tsx | 5 ++--- .../Challenge/ChallengeCardOnGoing.tsx | 15 +++++++-------- .../Challenge/ChallengeCardUnlocked.tsx | 13 ++++++------- src/components/Challenge/ChallengeView.tsx | 7 ++----- src/components/Charts/AxisBottom.spec.tsx | 4 ---- src/components/Charts/AxisBottom.tsx | 5 ++--- src/components/Charts/Bar.spec.tsx | 3 --- src/components/Charts/Bar.tsx | 9 ++++----- src/components/Charts/BarChart.tsx | 5 ++--- src/components/Connection/Connection.spec.tsx | 4 +--- src/components/Connection/Connection.tsx | 7 +++---- src/components/Connection/ConnectionResult.tsx | 7 +++---- .../Connection/EPGLConnect/EpglBill.tsx | 7 +++---- .../Connection/EPGLConnect/EpglInit.tsx | 11 ++++------- .../Connection/ExpiredConsentModal.spec.tsx | 12 +++++------- .../Connection/ExpiredConsentModal.tsx | 10 ++++------ src/components/Connection/FormOAuth.tsx | 11 ++++------- .../Connection/GRDFConnect/GrdfBill.tsx | 7 +++---- .../Connection/GRDFConnect/GrdfForm.tsx | 7 +++---- .../Connection/GRDFConnect/GrdfInit.tsx | 11 ++++------- .../SGEConnect/SgeConnectView.spec.tsx | 13 +++++-------- .../Connection/SGEConnect/SgeConnectView.tsx | 15 +++++++-------- .../Connection/SGEConnect/SgeInit.tsx | 10 ++++------ .../ConsumptionVisualizer.tsx | 5 ++--- .../DataloadConsumptionVisualizer.tsx | 5 ++--- .../InfoDataConsumptionVisualizer.spec.tsx | 6 +++--- .../InfoDataConsumptionVisualizer.tsx | 11 ++++------- src/components/Content/Content.tsx | 9 ++++----- src/components/Duel/DuelBar.tsx | 7 ++----- src/components/Duel/DuelOngoing.tsx | 18 +++++------------- src/components/Duel/DuelUnlocked.tsx | 9 ++++----- src/components/Duel/DuelView.tsx | 7 ++----- src/components/Ecogesture/EcogestureModal.tsx | 7 ++----- src/components/Ecogesture/EcogestureView.tsx | 11 +++++------ src/components/Ecogesture/SingleEcogesture.tsx | 7 ++----- .../EcogestureForm/EcogestureFormEquipment.tsx | 7 +++---- .../EcogestureFormSingleChoice.spec.tsx | 4 ---- .../EcogestureFormSingleChoice.tsx | 7 +++---- .../EcogestureForm/EcogestureFormView.spec.tsx | 3 +++ .../EcogestureForm/EcogestureFormView.tsx | 9 +++++---- .../EcogestureSelection.tsx | 7 ++----- .../Exploration/ExplorationFinished.tsx | 9 ++++----- .../Exploration/ExplorationOngoing.tsx | 9 ++++----- src/components/Exploration/ExplorationView.tsx | 7 ++----- src/components/Feedback/FeedbackModal.spec.tsx | 5 ----- src/components/Feedback/FeedbackModal.tsx | 1 + src/components/FluidChart/FluidChart.tsx | 9 ++++----- src/components/FluidChart/FluidChartSlide.tsx | 9 ++++----- src/components/FluidChart/FluidChartSwipe.tsx | 12 +++++------- src/components/Header/CozyBar.tsx | 9 ++++----- src/components/Header/Header.tsx | 10 ++++------ .../Home/ConsumptionDetails.spec.tsx | 4 ---- src/components/Home/ConsumptionDetails.tsx | 7 +++---- src/components/Home/ConsumptionView.spec.tsx | 8 -------- src/components/Home/ConsumptionView.tsx | 9 ++++----- src/components/Home/FluidButton.tsx | 7 +++---- src/components/Home/ReleaseNotesModal.tsx | 5 ++--- src/components/Hooks/useExploration.spec.tsx | 18 ++++++++++-------- src/components/Hooks/useExploration.tsx | 11 ++++------- src/components/Hooks/useKonnectorAuth.tsx | 10 ++++------ .../Konnector/KonnectorViewerCard.tsx | 15 ++++----------- .../Konnector/KonnectorViewerList.tsx | 5 ++--- src/components/Navbar/Navbar.tsx | 5 ++--- src/components/Options/HelpLink/HelpLink.tsx | 7 +++---- .../ProfileTypeOptions/ProfileTypeOptions.tsx | 7 ++----- .../Options/ReportOptions/ReportOptions.tsx | 9 ++++----- .../Options/Unsubscribe/UnSubscribe.tsx | 11 +++++------ .../ProfileType/ProfileTypeFinished.tsx | 11 +++++------ .../ProfileType/ProfileTypeFormMultiChoice.tsx | 7 +++---- .../ProfileType/ProfileTypeFormNumber.tsx | 7 +++---- .../ProfileTypeFormNumberSelection.tsx | 7 +++---- .../ProfileTypeFormSingleChoice.tsx | 5 ++--- src/components/ProfileType/ProfileTypeView.tsx | 7 +++---- src/components/Quiz/QuizBegin.tsx | 7 +++---- .../Quiz/QuizCustomQuestionContent.tsx | 11 +++++------ src/components/Quiz/QuizFinish.tsx | 9 ++++----- src/components/Quiz/QuizQuestion.spec.tsx | 3 --- src/components/Quiz/QuizQuestion.tsx | 5 ++--- src/components/Quiz/QuizQuestionContent.tsx | 8 +++----- src/components/Quiz/QuizView.spec.tsx | 3 --- src/components/Quiz/QuizView.tsx | 7 ++----- src/components/Splash/SplashRoot.spec.tsx | 3 --- src/components/Splash/SplashRoot.tsx | 2 +- .../Terms/DataShareConsentContent.tsx | 7 ++----- src/components/Terms/TermsView.spec.tsx | 6 +++--- src/components/Terms/TermsView.tsx | 9 ++++----- .../TimeStepSelector/TimeStepSelector.spec.tsx | 5 ----- .../TimeStepSelector/TimeStepSelector.tsx | 13 ++++++------- .../TotalConsumption/TotalConsumption.tsx | 7 +++---- src/components/WelcomeModal/WelcomeModal.tsx | 7 +++---- src/store/hooks.ts | 9 +++++++++ src/store/profile/profile.actions.ts | 2 +- src/store/profile/profile.reducer.spec.ts | 2 +- .../profileEcogesture.actions.ts | 2 +- .../profileEcogesture.reducer.spec.ts | 2 +- src/store/{index.ts => store.ts} | 5 +++++ src/targets/browser/index.tsx | 2 +- tests/__mocks__/store/store.ts | 2 +- 114 files changed, 336 insertions(+), 518 deletions(-) create mode 100644 src/store/hooks.ts rename src/store/{index.ts => store.ts} (91%) diff --git a/src/components/Action/ActionBegin.tsx b/src/components/Action/ActionBegin.tsx index f039432d3..52b1c8a3f 100644 --- a/src/components/Action/ActionBegin.tsx +++ b/src/components/Action/ActionBegin.tsx @@ -7,9 +7,8 @@ import { Client, useClient } from 'cozy-client' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { Ecogesture, UserChallenge } from 'models' import React, { useCallback, useEffect, useState } from 'react' -import { useSelector } from 'react-redux' import ActionService from 'services/action.service' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import { importIconById } from 'utils/utils' import './actionBegin.scss' @@ -29,7 +28,7 @@ const ActionBegin = ({ const { global: { fluidTypes }, profile: { isProfileTypeCompleted }, - } = useSelector((state: AppStore) => state.ecolyo) + } = useAppSelector(state => state.ecolyo) const [currentAction, setCurrentAction] = useState<Ecogesture>() const [actionIcon, setActionIcon] = useState<string>('') const [openLaunchModal, setOpenLaunchModal] = useState<boolean>(false) diff --git a/src/components/Action/ActionCard.tsx b/src/components/Action/ActionCard.tsx index 9b491e035..18da28fd6 100644 --- a/src/components/Action/ActionCard.tsx +++ b/src/components/Action/ActionCard.tsx @@ -6,9 +6,8 @@ import { useClient } from 'cozy-client' import { UsageEventType } from 'enum/usageEvent.enum' import { Ecogesture } from 'models' import React, { useCallback, useEffect, useState } from 'react' -import { useSelector } from 'react-redux' import UsageEventService from 'services/usageEvent.service' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import { importIconById } from 'utils/utils' import './actionList.scss' @@ -26,9 +25,7 @@ const ActionCard = ({ const [actionIcon, setActionIcon] = useState<string>('') const [openEcogestureModal, setOpenEcogestureModal] = useState<boolean>(false) const client = useClient() - const { currentChallenge } = useSelector( - (state: AppStore) => state.ecolyo.challenge - ) + const { currentChallenge } = useAppSelector(state => state.ecolyo.challenge) const toggleModal = useCallback(() => { setOpenEcogestureModal(prev => !prev) }, []) diff --git a/src/components/Action/ActionDone.spec.tsx b/src/components/Action/ActionDone.spec.tsx index 8cd504e7c..49823ad46 100644 --- a/src/components/Action/ActionDone.spec.tsx +++ b/src/components/Action/ActionDone.spec.tsx @@ -2,7 +2,6 @@ import { Button } from '@material-ui/core' import { mount } from 'enzyme' import toJson from 'enzyme-to-json' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import UsageEventService from 'services/usageEvent.service' import * as challengeActions from 'store/challenge/challenge.slice' @@ -46,13 +45,11 @@ describe('ActionDone component', () => { expect(toJson(wrapper)).toMatchSnapshot() }) it('should click on button and update action to done', async () => { - const useDispatchSpy = jest.spyOn(reactRedux, 'useDispatch') const updateChallengeSpy = jest.spyOn( challengeActions, 'updateUserChallengeList' ) mockupdateUserChallenge.mockResolvedValueOnce(userChallengeData[1]) - useDispatchSpy.mockReturnValue(jest.fn()) const wrapper = mount( <Provider store={store}> <ActionDone currentChallenge={userChallengeData[1]} /> diff --git a/src/components/Action/ActionDone.tsx b/src/components/Action/ActionDone.tsx index b1ee82f5b..450868a8e 100644 --- a/src/components/Action/ActionDone.tsx +++ b/src/components/Action/ActionDone.tsx @@ -6,14 +6,13 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { UsageEventType } from 'enum/usageEvent.enum' import { UserChallengeUpdateFlag } from 'enum/userChallenge.enum' import { UserChallenge } from 'models' -import React, { Dispatch, useCallback } from 'react' -import { useDispatch } from 'react-redux' +import React, { useCallback } from 'react' 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.slice' import { toggleChallengeActionNotification } from 'store/global/global.slice' +import { useAppDispatch } from 'store/hooks' import './actionDone.scss' const ActionDone = ({ @@ -23,7 +22,7 @@ const ActionDone = ({ }) => { const { t } = useI18n() const client = useClient() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() const navigate = useNavigate() const handleEndAction = useCallback(async () => { const challengeService = new ChallengeService(client) diff --git a/src/components/Action/ActionList.tsx b/src/components/Action/ActionList.tsx index b218dd7b1..a5cb263d1 100644 --- a/src/components/Action/ActionList.tsx +++ b/src/components/Action/ActionList.tsx @@ -2,9 +2,8 @@ import ActionCard from 'components/Action/ActionCard' import { Client, useClient } from 'cozy-client' import { Ecogesture } from 'models' import React, { useEffect, useState } from 'react' -import { useSelector } from 'react-redux' import ActionService from 'services/action.service' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import './actionBegin.scss' interface ActionListProps { @@ -17,7 +16,7 @@ const ActionList = ({ setSelectedAction, setShowList }: ActionListProps) => { const { global: { fluidTypes }, profile: { isProfileTypeCompleted }, - } = useSelector((state: AppStore) => state.ecolyo) + } = useAppSelector(state => state.ecolyo) const [actions, setActions] = useState<Ecogesture[]>() useEffect(() => { diff --git a/src/components/Action/ActionModal.spec.tsx b/src/components/Action/ActionModal.spec.tsx index 153acd6fa..f0159a064 100644 --- a/src/components/Action/ActionModal.spec.tsx +++ b/src/components/Action/ActionModal.spec.tsx @@ -2,7 +2,6 @@ import { Button } from '@material-ui/core' import { mount } from 'enzyme' import toJson from 'enzyme-to-json' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import UsageEventService from 'services/usageEvent.service' import * as challengeActions from 'store/challenge/challenge.slice' @@ -46,14 +45,11 @@ describe('ActionModal component', () => { expect(toJson(wrapper)).toMatchSnapshot() }) it('should click on button and update action to ongoing', async () => { - const useDispatchSpy = jest.spyOn(reactRedux, 'useDispatch') const updateChallengeSpy = jest.spyOn( challengeActions, 'updateUserChallengeList' ) - mockupdateUserChallenge.mockResolvedValueOnce(userChallengeData[1]) - useDispatchSpy.mockReturnValue(jest.fn()) const wrapper = mount( <Provider store={store}> diff --git a/src/components/Action/ActionModal.tsx b/src/components/Action/ActionModal.tsx index b58e54f40..01aa9f3ec 100644 --- a/src/components/Action/ActionModal.tsx +++ b/src/components/Action/ActionModal.tsx @@ -6,11 +6,10 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n' import Icon from 'cozy-ui/transpiled/react/Icon' import { UserChallengeUpdateFlag } from 'enum/userChallenge.enum' import { Ecogesture, UserChallenge } from 'models' -import React, { Dispatch, useCallback } from 'react' -import { useDispatch } from 'react-redux' +import React, { useCallback } from 'react' import ChallengeService from 'services/challenge.service' -import { AppActionsTypes } from 'store' import { updateUserChallengeList } from 'store/challenge/challenge.slice' +import { useAppDispatch } from 'store/hooks' import './actionModal.scss' interface ActionModalProps { @@ -26,9 +25,9 @@ const ActionModal = ({ handleCloseClick, userChallenge, }: ActionModalProps) => { - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() const client = useClient() const { t } = useI18n() + const dispatch = useAppDispatch() const launchAction = useCallback(async () => { const challengeService = new ChallengeService(client) const updatedChallenge: UserChallenge = diff --git a/src/components/Action/ActionView.tsx b/src/components/Action/ActionView.tsx index 03ecb1cb1..ba49a6383 100644 --- a/src/components/Action/ActionView.tsx +++ b/src/components/Action/ActionView.tsx @@ -7,14 +7,11 @@ import Header from 'components/Header/Header' import { UserActionState } from 'enum/userAction.enum' import { UserChallenge } from 'models' import React, { useCallback, useState } from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' const ActionView = () => { + const { currentChallenge } = useAppSelector(state => state.ecolyo.challenge) const [headerHeight, setHeaderHeight] = useState<number>(0) - const { currentChallenge } = useSelector( - (state: AppStore) => state.ecolyo.challenge - ) const defineHeaderHeight = useCallback((height: number) => { setHeaderHeight(height) }, []) diff --git a/src/components/Analysis/AnalysisView.spec.tsx b/src/components/Analysis/AnalysisView.spec.tsx index 9e8e469ea..e112899bb 100644 --- a/src/components/Analysis/AnalysisView.spec.tsx +++ b/src/components/Analysis/AnalysisView.spec.tsx @@ -1,9 +1,9 @@ import AnalysisView from 'components/Analysis/AnalysisView' import { mount } from 'enzyme' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import * as globalActions from 'store/global/global.slice' +import * as storeHooks from 'store/hooks' import * as profileActions from 'store/profile/profile.actions' import { createMockEcolyoStore, @@ -30,8 +30,7 @@ jest.mock('react-router-dom', () => ({ }), })) -const useSelectorSpy = jest.spyOn(reactRedux, 'useSelector') -const useDispatchSpy = jest.spyOn(reactRedux, 'useDispatch') +const mockAppDispatch = jest.spyOn(storeHooks, 'useAppDispatch') const toggleAnalysisNotificationSpy = jest.spyOn( globalActions, 'toggleAnalysisNotification' @@ -42,14 +41,12 @@ describe('AnalysisView component', () => { const store = createMockEcolyoStore() beforeEach(() => { store.clearActions() - useSelectorSpy.mockClear() - useDispatchSpy.mockClear() + mockAppDispatch.mockClear() toggleAnalysisNotificationSpy.mockClear() updateProfileSpy.mockClear() }) it('should be rendered correctly', () => { - useDispatchSpy.mockReturnValue(jest.fn()) const wrapper = mount( <Provider store={store}> <AnalysisView /> @@ -68,7 +65,7 @@ describe('AnalysisView component', () => { global: { analysisNotification: true }, profile: { ...mockProfileState, haveSeenLastAnalysis: true }, }) - useDispatchSpy.mockReturnValue(jest.fn()) + mockAppDispatch.mockReturnValue(jest.fn()) const wrapper = mount( <Provider store={store}> <AnalysisView /> diff --git a/src/components/Analysis/AnalysisView.tsx b/src/components/Analysis/AnalysisView.tsx index 58ef3f06c..dea8806b2 100644 --- a/src/components/Analysis/AnalysisView.tsx +++ b/src/components/Analysis/AnalysisView.tsx @@ -7,28 +7,27 @@ import { useClient } from 'cozy-client' import { TimeStep } from 'enum/timeStep.enum' import { UsageEventType } from 'enum/usageEvent.enum' import { DateTime } from 'luxon' -import React, { Dispatch, useCallback, useEffect, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback, useEffect, useState } from 'react' import { useLocation } from 'react-router-dom' import DateChartService from 'services/dateChart.service' import UsageEventService from 'services/usageEvent.service' -import { AppActionsTypes, AppStore } from 'store' import { setAnalysisMonth } from 'store/analysis/analysis.slice' import { toggleAnalysisNotification } from 'store/global/global.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { updateProfile } from 'store/profile/profile.actions' import { isLastDateReached } from 'utils/date' import './analysisView.scss' const AnalysisView = () => { const client = useClient() - const [headerHeight, setHeaderHeight] = useState<number>(0) const { analysis: { analysisMonth }, chart: { selectedDate }, global: { analysisNotification }, profile: { monthlyAnalysisDate, mailToken }, - } = useSelector((state: AppStore) => state.ecolyo) - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + } = useAppSelector(state => state.ecolyo) + const dispatch = useAppDispatch() + const [headerHeight, setHeaderHeight] = useState<number>(0) const defineHeaderHeight = useCallback((height: number) => { setHeaderHeight(height) }, []) diff --git a/src/components/Analysis/Comparison/Comparison.tsx b/src/components/Analysis/Comparison/Comparison.tsx index 1a200e9d8..b9bad0ee5 100644 --- a/src/components/Analysis/Comparison/Comparison.tsx +++ b/src/components/Analysis/Comparison/Comparison.tsx @@ -5,11 +5,10 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' import { PerformanceIndicator } from 'models' -import React, { Dispatch, useEffect, useMemo, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useEffect, useMemo, useState } from 'react' import ConsumptionService from 'services/consumption.service' -import { AppActionsTypes, AppStore } from 'store' import { setPeriod } from 'store/analysis/analysis.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import FluidPerformanceIndicator from './FluidPerformanceIndicator' import './comparison.scss' @@ -25,8 +24,8 @@ const Comparison = ({ const { global: { fluidTypes }, analysis: { period, analysisMonth }, - } = useSelector((state: AppStore) => state.ecolyo) - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + } = useAppSelector(state => state.ecolyo) + const dispatch = useAppDispatch() const [yearPerformanceIndicators, setYearPerformanceIndicators] = useState< PerformanceIndicator[] >([]) diff --git a/src/components/Analysis/ElecHalfHourMonthlyAnalysis/ElecHalfHourMonthlyAnalysis.tsx b/src/components/Analysis/ElecHalfHourMonthlyAnalysis/ElecHalfHourMonthlyAnalysis.tsx index 2c3fb28c2..a62f302fa 100644 --- a/src/components/Analysis/ElecHalfHourMonthlyAnalysis/ElecHalfHourMonthlyAnalysis.tsx +++ b/src/components/Analysis/ElecHalfHourMonthlyAnalysis/ElecHalfHourMonthlyAnalysis.tsx @@ -15,11 +15,10 @@ import { EnedisMonthlyAnalysisData, } from 'models/enedisMonthlyAnalysis' import React, { useCallback, useEffect, useState } from 'react' -import { useSelector } from 'react-redux' import ConsumptionService from 'services/consumption.service' import EnedisMonthlyAnalysisDataService from 'services/enedisMonthlyAnalysisData.service' import FluidPricesService from 'services/fluidsPrices.service' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import { getNavPicto } from 'utils/picto' import ElecHalfHourChart from './ElecHalfHourChart' import ElecInfoModal from './ElecInfoModal' @@ -32,9 +31,7 @@ const ElecHalfHourMonthlyAnalysis = ({ }) => { const { t } = useI18n() const client = useClient() - const { analysisMonth } = useSelector( - (state: AppStore) => state.ecolyo.analysis - ) + const { analysisMonth } = useAppSelector(state => state.ecolyo.analysis) const [isWeekend, setIsWeekend] = useState<boolean>(true) const [isHalfHourActivated, setIsHalfHourActivated] = useState<boolean>(true) const [isLoading, setIsLoading] = useState<boolean>(true) diff --git a/src/components/Analysis/MaxConsumptionCard/MaxConsumptionCard.tsx b/src/components/Analysis/MaxConsumptionCard/MaxConsumptionCard.tsx index f64e1c96d..6f4999904 100644 --- a/src/components/Analysis/MaxConsumptionCard/MaxConsumptionCard.tsx +++ b/src/components/Analysis/MaxConsumptionCard/MaxConsumptionCard.tsx @@ -14,11 +14,10 @@ import { DataloadSectionType } from 'enum/dataload.enum' import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' import { Datachart, Dataload, TimePeriod } from 'models' -import React, { Dispatch, useEffect, useRef, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useEffect, useRef, useState } from 'react' import ConsumptionDataManager from 'services/consumption.service' -import { AppActionsTypes, AppStore } from 'store' import { setSelectedDate } from 'store/chart/chart.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import './maxConsumptionCard.scss' const MaxConsumptionCard = ({ @@ -28,10 +27,8 @@ const MaxConsumptionCard = ({ }) => { const { t } = useI18n() const client = useClient() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() - const { analysisMonth } = useSelector( - (state: AppStore) => state.ecolyo.analysis - ) + const dispatch = useAppDispatch() + const { analysisMonth } = useAppSelector(state => state.ecolyo.analysis) const [isLoading, setIsLoading] = useState<boolean>(false) const [maxDayData, setMaxDayData] = useState<Dataload | null>(null) const [chartData, setChartData] = useState<Datachart>({ diff --git a/src/components/Analysis/MonthlyAnalysis.tsx b/src/components/Analysis/MonthlyAnalysis.tsx index 7a0cb57b7..d61f48fde 100644 --- a/src/components/Analysis/MonthlyAnalysis.tsx +++ b/src/components/Analysis/MonthlyAnalysis.tsx @@ -4,10 +4,9 @@ import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' import { PerformanceIndicator, TimePeriod } from 'models' import React, { useEffect, useMemo, useState } from 'react' -import { useSelector } from 'react-redux' import ConsumptionService from 'services/consumption.service' import PerformanceIndicatorService from 'services/performanceIndicator.service' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import Comparison from './Comparison/Comparison' import ElecHalfHourMonthlyAnalysis from './ElecHalfHourMonthlyAnalysis/ElecHalfHourMonthlyAnalysis' import MaxConsumptionCard from './MaxConsumptionCard/MaxConsumptionCard' @@ -25,9 +24,7 @@ const MonthlyAnalysis = ({ scrollPosition, }: MonthlyAnalysisProps) => { const client = useClient() - const { analysisMonth } = useSelector( - (state: AppStore) => state.ecolyo.analysis - ) + const { analysisMonth } = useAppSelector(state => state.ecolyo.analysis) const consumptionService = useMemo( () => new ConsumptionService(client), diff --git a/src/components/Analysis/ProfileComparator/ProfileComparator.tsx b/src/components/Analysis/ProfileComparator/ProfileComparator.tsx index 88249ad39..081ec1053 100644 --- a/src/components/Analysis/ProfileComparator/ProfileComparator.tsx +++ b/src/components/Analysis/ProfileComparator/ProfileComparator.tsx @@ -17,11 +17,10 @@ import { FluidType } from 'enum/fluid.enum' import { PerformanceIndicator } from 'models' import { MonthlyForecast, ProfileType } from 'models/profileType.model' import React, { useCallback, useEffect, useState } from 'react' -import { useSelector } from 'react-redux' import { useNavigate } from 'react-router-dom' import ProfileTypeService from 'services/profileType.service' import ProfileTypeEntityService from 'services/profileTypeEntity.service' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import ProfileComparatorRow from './ProfileComparatorRow' import './profileComparator.scss' @@ -43,7 +42,7 @@ const ProfileComparator = ({ const { analysis: { analysisMonth }, profile, - } = useSelector((state: AppStore) => state.ecolyo) + } = useAppSelector(state => state.ecolyo) const [homePriceConsumption, setHomePriceConsumption] = useState<number>(0) const [forecast, setForecast] = useState<MonthlyForecast | null>(null) const [isLoading, setIsLoading] = useState<boolean>(true) diff --git a/src/components/Analysis/TotalAnalysisChart/TotalAnalysisChart.tsx b/src/components/Analysis/TotalAnalysisChart/TotalAnalysisChart.tsx index 1f0e646ee..f25be5017 100644 --- a/src/components/Analysis/TotalAnalysisChart/TotalAnalysisChart.tsx +++ b/src/components/Analysis/TotalAnalysisChart/TotalAnalysisChart.tsx @@ -6,9 +6,8 @@ import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' import { DataloadValueDetail, TimePeriod } from 'models' import React, { useEffect, useState } from 'react' -import { useSelector } from 'react-redux' import ConsumptionDataManager from 'services/consumption.service' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import { getNavPicto } from 'utils/picto' import { formatNumberValues, getMonthNameWithPrep } from 'utils/utils' import PieChart from './PieChart' @@ -19,15 +18,13 @@ const TotalAnalysisChart = ({ }: { fluidsWithData: FluidType[] }) => { - const { analysisMonth } = useSelector( - (state: AppStore) => state.ecolyo.analysis - ) + const { t } = useI18n() + const client = useClient() + const { analysisMonth } = useAppSelector(state => state.ecolyo.analysis) const [dataLoadValueDetailArray, setDataLoadValueDetailArray] = useState< DataloadValueDetail[] | null >(null) const [totalLoadValue, setTotalLoadValue] = useState<number>(0) - const client = useClient() - const { t } = useI18n() const [openEstimationModal, setOpenEstimationModal] = useState<boolean>(false) const arcWidth = 30 const radius = Math.min(375, innerWidth - 100) diff --git a/src/components/App.tsx b/src/components/App.tsx index e3ffd41d0..5cfeb0f21 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -5,9 +5,8 @@ import WelcomeModal from 'components/WelcomeModal/WelcomeModal' import { useWebviewIntent } from 'cozy-intent' import { Layout } from 'cozy-ui/transpiled/react/Layout' import React, { useEffect } from 'react' -import { useSelector } from 'react-redux' import { useLocation } from 'react-router-dom' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import MatomoTracker from 'utils/matomoTracker' interface AppProps { @@ -19,7 +18,7 @@ export const App = ({ tracker }: AppProps) => { const { global: { termsStatus }, profile: { onboarding }, - } = useSelector((state: AppStore) => state.ecolyo) + } = useAppSelector(state => state.ecolyo) const webviewIntent = useWebviewIntent() useEffect(() => { diff --git a/src/components/Challenge/ChallengeCardOnGoing.tsx b/src/components/Challenge/ChallengeCardOnGoing.tsx index 54eeab6a7..4157be83e 100644 --- a/src/components/Challenge/ChallengeCardOnGoing.tsx +++ b/src/components/Challenge/ChallengeCardOnGoing.tsx @@ -16,12 +16,11 @@ import { UserDuelState } from 'enum/userDuel.enum' import { UserExplorationState } from 'enum/userExploration.enum' import { UserQuizState } from 'enum/userQuiz.enum' import { UserChallenge } from 'models' -import React, { Dispatch, useCallback, useEffect, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback, useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' import ChallengeService from 'services/challenge.service' -import { AppActionsTypes, AppStore } from 'store' import { updateUserChallengeList } from 'store/challenge/challenge.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { getChallengeTitleWithLineReturn, importIconById } from 'utils/utils' import ChallengeNoFluidModal from './ChallengeNoFluidModal' import StarsContainer from './StarsContainer' @@ -34,16 +33,16 @@ const ChallengeCardOnGoing = ({ }) => { const client: Client = useClient() const { t } = useI18n() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() const navigate = useNavigate() + const dispatch = useAppDispatch() + const { + challenge: { currentDataload }, + global: { fluidTypes, fluidStatus }, + } = useAppSelector(state => state.ecolyo) const [isOneFluidUp, setIsOneFluidUp] = useState<boolean>(true) const [challengeIcon, setChallengeIcon] = useState<string>(defaultIcon) const [isDone, setIsDone] = useState<boolean>(false) const [isLoading, setIsLoading] = useState<boolean>(false) - const { - challenge: { currentDataload }, - global: { fluidTypes, fluidStatus }, - } = useSelector((state: AppStore) => state.ecolyo) const { progress: { actionProgress, explorationProgress, quizProgress }, target, diff --git a/src/components/Challenge/ChallengeCardUnlocked.tsx b/src/components/Challenge/ChallengeCardUnlocked.tsx index dc7688006..4276b6d54 100644 --- a/src/components/Challenge/ChallengeCardUnlocked.tsx +++ b/src/components/Challenge/ChallengeCardUnlocked.tsx @@ -6,12 +6,11 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { FluidState } from 'enum/fluid.enum' import { UsageEventType } from 'enum/usageEvent.enum' import { UserChallenge } from 'models' -import React, { Dispatch, useCallback, useEffect, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback, useEffect, useState } from 'react' import ChallengeService from 'services/challenge.service' import UsageEventService from 'services/usageEvent.service' -import { AppActionsTypes, AppStore } from 'store' import { updateUserChallengeList } from 'store/challenge/challenge.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { getChallengeTitleWithLineReturn, importIconById } from 'utils/utils' import ChallengeNoFluidModal from './ChallengeNoFluidModal' import './challengeCardUnlocked.scss' @@ -23,12 +22,12 @@ const ChallengeCardUnlocked = ({ }) => { const { t } = useI18n() const client: Client = useClient() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() - const [openNoFluidModal, setopenNoFluidModal] = useState(false) const { - global: { fluidTypes, fluidStatus }, challenge: { currentChallenge }, - } = useSelector((state: AppStore) => state.ecolyo) + global: { fluidTypes, fluidStatus }, + } = useAppSelector(state => state.ecolyo) + const dispatch = useAppDispatch() + const [openNoFluidModal, setopenNoFluidModal] = useState(false) const [challengeIcon, setChallengeIcon] = useState(defaultIcon) let statusRequirementOk = false diff --git a/src/components/Challenge/ChallengeView.tsx b/src/components/Challenge/ChallengeView.tsx index 94673b495..daa50f417 100644 --- a/src/components/Challenge/ChallengeView.tsx +++ b/src/components/Challenge/ChallengeView.tsx @@ -7,16 +7,13 @@ import Header from 'components/Header/Header' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { UserChallengeState } from 'enum/userChallenge.enum' import React, { useCallback, useEffect, useState } from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import ChallengeCard from './ChallengeCard' import './challengeView.scss' const ChallengeView = () => { const { t } = useI18n() - const { userChallengeList } = useSelector( - (state: AppStore) => state.ecolyo.challenge - ) + const { userChallengeList } = useAppSelector(state => state.ecolyo.challenge) const marginPx = 16 const cardWidth = diff --git a/src/components/Charts/AxisBottom.spec.tsx b/src/components/Charts/AxisBottom.spec.tsx index 11d3d39ca..8e1156059 100644 --- a/src/components/Charts/AxisBottom.spec.tsx +++ b/src/components/Charts/AxisBottom.spec.tsx @@ -3,7 +3,6 @@ import { TimeStep } from 'enum/timeStep.enum' import { mount } from 'enzyme' import { DateTime } from 'luxon' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import { graphData } from '../../../tests/__mocks__/chartData.mock' import { createMockEcolyoStore } from '../../../tests/__mocks__/store' @@ -22,8 +21,6 @@ const mockProps = { marginBottom: 10, } -const useDispatchSpy = jest.spyOn(reactRedux, 'useDispatch') - describe('AxisBottom component test', () => { const store = createMockEcolyoStore({ chart: { @@ -34,7 +31,6 @@ describe('AxisBottom component test', () => { }) beforeEach(() => { store.clearActions() - useDispatchSpy.mockClear() }) it('should correctly render YEAR format of AxisBottom', () => { diff --git a/src/components/Charts/AxisBottom.tsx b/src/components/Charts/AxisBottom.tsx index 0fcdc71c0..4e69f565b 100644 --- a/src/components/Charts/AxisBottom.tsx +++ b/src/components/Charts/AxisBottom.tsx @@ -4,9 +4,8 @@ import { capitalize } from 'lodash' import { DateTime } from 'luxon' import { Dataload } from 'models' import React from 'react' -import { useSelector } from 'react-redux' import DateChartService from 'services/dateChart.service' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' interface TextTypeProps { index: number @@ -139,7 +138,7 @@ const AxisBottom = ({ marginBottom, isDuel = false, }: AxisBottomProps) => { - const { selectedDate } = useSelector((state: AppStore) => state.ecolyo.chart) + const { selectedDate } = useAppSelector(state => state.ecolyo.chart) const dashArray = `${height / 30} ${height / 30}` const dateChartService = new DateChartService() const displayAllDays: boolean = isDuel && data.length <= 15 diff --git a/src/components/Charts/Bar.spec.tsx b/src/components/Charts/Bar.spec.tsx index 6ac99f422..764fef238 100644 --- a/src/components/Charts/Bar.spec.tsx +++ b/src/components/Charts/Bar.spec.tsx @@ -4,7 +4,6 @@ import { TimeStep } from 'enum/timeStep.enum' import { mount } from 'enzyme' import { DateTime } from 'luxon' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import * as chartActions from 'store/chart/chart.slice' import { graphData } from '../../../tests/__mocks__/chartData.mock' @@ -34,7 +33,6 @@ const mockParam = { isDuel: false, } -const useDispatchSpy = jest.spyOn(reactRedux, 'useDispatch') const setSelectedDateSpy = jest.spyOn(chartActions, 'setSelectedDate') const setCurrentDatachartIndexSpy = jest.spyOn( chartActions, @@ -44,7 +42,6 @@ const setCurrentDatachartIndexSpy = jest.spyOn( describe('Bar component test', () => { beforeEach(() => { store.clearActions() - useDispatchSpy.mockClear() setSelectedDateSpy.mockClear() setCurrentDatachartIndexSpy.mockClear() }) diff --git a/src/components/Charts/Bar.tsx b/src/components/Charts/Bar.tsx index 57f86f317..8b8a12f73 100644 --- a/src/components/Charts/Bar.tsx +++ b/src/components/Charts/Bar.tsx @@ -4,14 +4,13 @@ import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' import { DateTime } from 'luxon' import { Dataload } from 'models' -import React, { Dispatch, useEffect, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useEffect, useState } from 'react' import DateChartService from 'services/dateChart.service' -import { AppActionsTypes, AppStore } from 'store' import { setCurrentDataChartIndex, setSelectedDate, } from 'store/chart/chart.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' interface BarProps { index: number @@ -46,8 +45,8 @@ const Bar = ({ weekdays, clickable = true, }: BarProps) => { - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() - const { selectedDate } = useSelector((state: AppStore) => state.ecolyo.chart) + const dispatch = useAppDispatch() + const { selectedDate } = useAppSelector(state => state.ecolyo.chart) const [clicked, setClicked] = useState(false) const [animationEnded, setAnimationEnded] = useState(false) const [compareAnimationEnded, setCompareAnimationEnded] = useState(false) diff --git a/src/components/Charts/BarChart.tsx b/src/components/Charts/BarChart.tsx index 9483e03da..8cc731f8b 100644 --- a/src/components/Charts/BarChart.tsx +++ b/src/components/Charts/BarChart.tsx @@ -8,8 +8,7 @@ import { TimeStep } from 'enum/timeStep.enum' import { DateTime } from 'luxon' import { Datachart } from 'models' import React from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' export interface BarChartProps { chartData: Datachart @@ -38,7 +37,7 @@ const BarChart = ({ isSwitching, clickable = true, }: BarChartProps) => { - const { showCompare } = useSelector((state: AppStore) => state.ecolyo.chart) + const { showCompare } = useAppSelector(state => state.ecolyo.chart) const getContentWidth = () => { return width - marginLeft - marginRight } diff --git a/src/components/Connection/Connection.spec.tsx b/src/components/Connection/Connection.spec.tsx index 05bc2fcbb..51cf0a264 100644 --- a/src/components/Connection/Connection.spec.tsx +++ b/src/components/Connection/Connection.spec.tsx @@ -2,7 +2,6 @@ import Connection from 'components/Connection/Connection' import { mount } from 'enzyme' import toJson from 'enzyme-to-json' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import { fluidStatusConnectedData } from '../../../tests/__mocks__/fluidStatusData.mock' import { createMockEcolyoStore } from '../../../tests/__mocks__/store' @@ -29,7 +28,7 @@ jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useNavigate: () => mockedNavigate, })) -const useDispatchSpy = jest.spyOn(reactRedux, 'useDispatch') + describe('Connection component test', () => { const store = createMockEcolyoStore() it('should call GrdfInit', () => { @@ -42,7 +41,6 @@ describe('Connection component test', () => { }) it('should call EpglInit', () => { - useDispatchSpy.mockReturnValue(jest.fn()) const wrapper = mount( <Provider store={store}> <Connection fluidStatus={fluidStatusConnectedData[0]} /> diff --git a/src/components/Connection/Connection.tsx b/src/components/Connection/Connection.tsx index bf29cd77b..9a14f999e 100644 --- a/src/components/Connection/Connection.tsx +++ b/src/components/Connection/Connection.tsx @@ -1,16 +1,15 @@ import { FluidType } from 'enum/fluid.enum' import { FluidConnection, FluidStatus } from 'models' -import React, { Dispatch, useCallback } from 'react' -import { useDispatch } from 'react-redux' -import { AppActionsTypes } from 'store' +import React, { useCallback } from 'react' import { updateFluidConnection } from 'store/global/global.slice' +import { useAppDispatch } from 'store/hooks' import EpglInit from './EPGLConnect/EpglInit' import GrdfInit from './GRDFConnect/GrdfInit' import SgeInit from './SGEConnect/SgeInit' import './connection.scss' const Connection = ({ fluidStatus }: { fluidStatus: FluidStatus }) => { - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() const handleSuccess = useCallback(async () => { const updatedConnection: FluidConnection = { diff --git a/src/components/Connection/ConnectionResult.tsx b/src/components/Connection/ConnectionResult.tsx index 5a5390ec3..2c9684851 100644 --- a/src/components/Connection/ConnectionResult.tsx +++ b/src/components/Connection/ConnectionResult.tsx @@ -14,17 +14,16 @@ import { FluidStatus, Trigger, } from 'models' -import React, { Dispatch, useCallback, useEffect, useState } from 'react' -import { useDispatch } from 'react-redux' +import React, { useCallback, useEffect, useState } from 'react' import AccountService from 'services/account.service' import DateChartService from 'services/dateChart.service' import TriggerService from 'services/triggers.service' -import { AppActionsTypes } from 'store' import { setShouldRefreshConsent, updateFluidConnection, updateSgeStore, } from 'store/global/global.slice' +import { useAppDispatch } from 'store/hooks' import { getKonnectorUpdateError } from 'utils/utils' import DeleteGRDFAccountModal from './DeleteGRDFAccountModal' import './connectionResult.scss' @@ -42,7 +41,7 @@ const ConnectionResult = ({ }: ConnectionResultProps) => { const { t } = useI18n() const client = useClient() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() const account: Account | null = fluidStatus.connection.account const [deleting, setDeleting] = useState<boolean>(false) diff --git a/src/components/Connection/EPGLConnect/EpglBill.tsx b/src/components/Connection/EPGLConnect/EpglBill.tsx index ae01998eb..f39634b2c 100644 --- a/src/components/Connection/EPGLConnect/EpglBill.tsx +++ b/src/components/Connection/EPGLConnect/EpglBill.tsx @@ -3,17 +3,16 @@ import WaterBillIcon from 'assets/icons/visu/onboarding/water_bill.svg' import StyledIcon from 'components/CommonKit/Icon/StyledIcon' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { FluidStatus } from 'models' -import React, { Dispatch } from 'react' -import { useDispatch } from 'react-redux' -import { AppActionsTypes } from 'store' +import React from 'react' import { setShowOfflineData } from 'store/chart/chart.slice' +import { useAppDispatch } from 'store/hooks' import { openConnectionModal } from 'store/modal/modal.slice' import { decoreText } from 'utils/decoreText' import '../connection.scss' const EpglBill = ({ fluidStatus }: { fluidStatus: FluidStatus }) => { const { t } = useI18n() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() return ( <div className="connection-form"> diff --git a/src/components/Connection/EPGLConnect/EpglInit.tsx b/src/components/Connection/EPGLConnect/EpglInit.tsx index be04c95c0..23a1057a3 100644 --- a/src/components/Connection/EPGLConnect/EpglInit.tsx +++ b/src/components/Connection/EPGLConnect/EpglInit.tsx @@ -1,18 +1,15 @@ import EpglConnectModal from 'components/Connection/PartnerConnectModal/EpglConnectModal' import { FluidStatus } from 'models' -import React, { Dispatch, useCallback, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { AppActionsTypes, AppStore } from 'store' +import React, { useCallback, useState } from 'react' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { openConnectionModal } from 'store/modal/modal.slice' import '../connection.scss' import EpglBill from './EpglBill' import EpglForm from './EpglForm' const EpglInit = ({ fluidStatus }: { fluidStatus: FluidStatus }) => { - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() - const { - modal: { isConnectionModalOpen }, - } = useSelector((state: AppStore) => state.ecolyo) + const dispatch = useAppDispatch() + const { isConnectionModalOpen } = useAppSelector(state => state.ecolyo.modal) const siteLink: string = fluidStatus.connection.konnectorConfig.siteLink const [showForm, setShowForm] = useState(false) diff --git a/src/components/Connection/ExpiredConsentModal.spec.tsx b/src/components/Connection/ExpiredConsentModal.spec.tsx index a6f9f8fd4..0e87a5e2b 100644 --- a/src/components/Connection/ExpiredConsentModal.spec.tsx +++ b/src/components/Connection/ExpiredConsentModal.spec.tsx @@ -3,8 +3,8 @@ import { FluidType } from 'enum/fluid.enum' import { mount } from 'enzyme' import toJson from 'enzyme-to-json' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' +import * as storeHooks from 'store/hooks' import { fluidStatusConnectedData } from '../../../tests/__mocks__/fluidStatusData.mock' import { createMockEcolyoStore } from '../../../tests/__mocks__/store' import ExpiredConsentModal from './ExpiredConsentModal' @@ -19,17 +19,15 @@ jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useNavigate: () => mockedNavigate, })) +const mockAppDispatch = jest.spyOn(storeHooks, 'useAppDispatch') const mockToggleModal = jest.fn() const mockHandleCloseClick = jest.fn() describe('ExpiredConsentModal component', () => { - const useDispatchSpy = jest.spyOn(reactRedux, 'useDispatch') - useDispatchSpy.mockReturnValue(jest.fn()) const store = createMockEcolyoStore() beforeEach(() => { mockedNavigate.mockReset() - store.clearActions() - useDispatchSpy.mockClear() + mockAppDispatch.mockClear() }) it('should be rendered correctly', () => { const component = mount( @@ -56,7 +54,7 @@ describe('ExpiredConsentModal component', () => { </Provider> ) component.find(Button).at(1).simulate('click') - expect(useDispatchSpy).toHaveBeenCalledTimes(1) + expect(mockAppDispatch).toHaveBeenCalledTimes(1) expect(mockedNavigate).toHaveBeenCalledTimes(1) }) it('should launch the update consent process for Enedis', () => { @@ -74,7 +72,7 @@ describe('ExpiredConsentModal component', () => { </Provider> ) component.find(Button).at(1).simulate('click') - expect(useDispatchSpy).toHaveBeenCalledTimes(1) + expect(mockAppDispatch).toHaveBeenCalledTimes(1) expect(mockedNavigate).toHaveBeenCalledTimes(1) }) it('should click on close modal', () => { diff --git a/src/components/Connection/ExpiredConsentModal.tsx b/src/components/Connection/ExpiredConsentModal.tsx index 38b0f3c4b..872b048a8 100644 --- a/src/components/Connection/ExpiredConsentModal.tsx +++ b/src/components/Connection/ExpiredConsentModal.tsx @@ -7,14 +7,13 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n' import Icon from 'cozy-ui/transpiled/react/Icon' import { FluidType } from 'enum/fluid.enum' import { AccountSgeData } from 'models' -import React, { Dispatch, useCallback } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback } from 'react' import { useNavigate } from 'react-router-dom' -import { AppActionsTypes, AppStore } from 'store' import { setShouldRefreshConsent, updateSgeStore, } from 'store/global/global.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { getFluidName } from 'utils/utils' import './expiredConsentModal.scss' @@ -33,9 +32,8 @@ const ExpiredConsentModal = ({ }: ExpiredConsentModalProps) => { const { t } = useI18n() const navigate = useNavigate() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() - const { fluidStatus } = useSelector((state: AppStore) => state.ecolyo.global) - + const dispatch = useAppDispatch() + const { fluidStatus } = useAppSelector(state => state.ecolyo.global) const launchUpdateConsent = useCallback(() => { if (fluidType === FluidType.ELECTRICITY) { const accountData = fluidStatus[FluidType.ELECTRICITY].connection.account diff --git a/src/components/Connection/FormOAuth.tsx b/src/components/Connection/FormOAuth.tsx index 09fd2b99e..46df15916 100644 --- a/src/components/Connection/FormOAuth.tsx +++ b/src/components/Connection/FormOAuth.tsx @@ -4,11 +4,10 @@ import { OAuthWindow } from 'cozy-harvest-lib/dist/components/OAuthWindow' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { UsageEventType } from 'enum/usageEvent.enum' import { FluidStatus, Konnector } from 'models' -import React, { Dispatch, useCallback, useEffect, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback, useEffect, useState } from 'react' import UsageEventService from 'services/usageEvent.service' -import { AppActionsTypes, AppStore } from 'store' import { setShouldRefreshConsent } from 'store/global/global.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' interface FormOAuthProps { konnector: Konnector | null @@ -22,11 +21,9 @@ const FormOAuth = ({ konnector, onSuccess, fluidStatus }: FormOAuthProps) => { const { t } = useI18n() const client = useClient() + const { shouldRefreshConsent } = useAppSelector(state => state.ecolyo.global) + const dispatch = useAppDispatch() const [status, setStatus] = useState<string>(IDLE) - const { shouldRefreshConsent } = useSelector( - (state: AppStore) => state.ecolyo.global - ) - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() const endOAuth = useCallback(() => { setStatus(IDLE) // Set back to false the variable that allows to automatically refresh the consent (deletes and recreates the account) diff --git a/src/components/Connection/GRDFConnect/GrdfBill.tsx b/src/components/Connection/GRDFConnect/GrdfBill.tsx index ad07fb26e..d547388ff 100644 --- a/src/components/Connection/GRDFConnect/GrdfBill.tsx +++ b/src/components/Connection/GRDFConnect/GrdfBill.tsx @@ -3,17 +3,16 @@ import GasBillIcon from 'assets/icons/visu/onboarding/gas_bill.svg' import StyledIcon from 'components/CommonKit/Icon/StyledIcon' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { FluidStatus } from 'models' -import React, { Dispatch } from 'react' -import { useDispatch } from 'react-redux' -import { AppActionsTypes } from 'store' +import React from 'react' import { setShowOfflineData } from 'store/chart/chart.slice' +import { useAppDispatch } from 'store/hooks' import { openConnectionModal } from 'store/modal/modal.slice' import { decoreText } from 'utils/decoreText' import '../connection.scss' const GrdfBill = ({ fluidStatus }: { fluidStatus: FluidStatus }) => { const { t } = useI18n() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() return ( <div className="connection-form"> diff --git a/src/components/Connection/GRDFConnect/GrdfForm.tsx b/src/components/Connection/GRDFConnect/GrdfForm.tsx index b2f699e71..070083929 100644 --- a/src/components/Connection/GRDFConnect/GrdfForm.tsx +++ b/src/components/Connection/GRDFConnect/GrdfForm.tsx @@ -2,15 +2,14 @@ import Button from '@material-ui/core/Button' import iconGrdfLogo from 'assets/icons/visu/grdf-logo.svg' import StyledIcon from 'components/CommonKit/Icon/StyledIcon' import { useI18n } from 'cozy-ui/transpiled/react/I18n' -import React, { Dispatch } from 'react' -import { useDispatch } from 'react-redux' -import { AppActionsTypes } from 'store' +import React from 'react' +import { useAppDispatch } from 'store/hooks' import { openConnectionModal } from 'store/modal/modal.slice' import '../connection.scss' const GrdfForm = () => { const { t } = useI18n() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() return ( <div className="connection-form"> diff --git a/src/components/Connection/GRDFConnect/GrdfInit.tsx b/src/components/Connection/GRDFConnect/GrdfInit.tsx index 764509c9a..d86b90fca 100644 --- a/src/components/Connection/GRDFConnect/GrdfInit.tsx +++ b/src/components/Connection/GRDFConnect/GrdfInit.tsx @@ -2,13 +2,12 @@ import GrdfConnectModal from 'components/Connection/PartnerConnectModal/GrdfConn import { useClient } from 'cozy-client' import { UsageEventType } from 'enum/usageEvent.enum' import { FluidConnection, FluidStatus, Konnector, Trigger } from 'models' -import React, { Dispatch, useCallback, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback, useState } from 'react' import AccountService from 'services/account.service' import TriggerService from 'services/triggers.service' import UsageEventService from 'services/usageEvent.service' -import { AppActionsTypes, AppStore } from 'store' import { updateFluidConnection } from 'store/global/global.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { openConnectionModal } from 'store/modal/modal.slice' import '../connection.scss' import GrdfBill from './GrdfBill' @@ -21,10 +20,8 @@ interface GrdfInitProps { const GrdfInit = ({ fluidStatus, onSuccess }: GrdfInitProps) => { const client = useClient() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() - const { - modal: { isConnectionModalOpen }, - } = useSelector((state: AppStore) => state.ecolyo) + const dispatch = useAppDispatch() + const { isConnectionModalOpen } = useAppSelector(state => state.ecolyo.modal) const [showForm, setShowForm] = useState(false) diff --git a/src/components/Connection/SGEConnect/SgeConnectView.spec.tsx b/src/components/Connection/SGEConnect/SgeConnectView.spec.tsx index a65895112..ed87bc98d 100644 --- a/src/components/Connection/SGEConnect/SgeConnectView.spec.tsx +++ b/src/components/Connection/SGEConnect/SgeConnectView.spec.tsx @@ -2,8 +2,8 @@ import { SgeStep } from 'enum/sgeStep.enum' import { mount } from 'enzyme' import toJson from 'enzyme-to-json' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' +import * as storeHooks from 'store/hooks' import { createMockEcolyoStore, mockGlobalState, @@ -24,11 +24,11 @@ jest.mock('components/Content/Content', () => 'mock-content') jest.mock('components/FormGlobal/FormProgress', () => 'mock-formprogress') jest.mock('components/Header/CozyBar', () => 'mock-cozybar') -const useDispatchSpy = jest.spyOn(reactRedux, 'useDispatch') +const mockAppDispatch = jest.spyOn(storeHooks, 'useAppDispatch') describe('SgeConnectView component', () => { beforeEach(() => { - useDispatchSpy.mockReset() + jest.clearAllMocks() }) it('should be rendered correctly', () => { const store = createMockEcolyoStore() @@ -104,9 +104,6 @@ describe('SgeConnectView component', () => { expect(wrapper.find('.stepIdentity')).toBeTruthy() }) describe('SgeConnectView Navigation methods', () => { - beforeEach(() => { - useDispatchSpy.mockReset() - }) const store = createMockEcolyoStore() it('should call nextStep method', () => { const wrapper = mount( @@ -115,7 +112,7 @@ describe('SgeConnectView component', () => { </Provider> ) wrapper.find('.profile-navigation-button').last().simulate('click') - expect(useDispatchSpy).toHaveBeenCalled() + expect(mockAppDispatch).toHaveBeenCalled() }) it('should not call disabled nextStep method', () => { const wrapper = mount( @@ -124,7 +121,7 @@ describe('SgeConnectView component', () => { </Provider> ) wrapper.find('.profile-navigation-button').last().simulate('click') - expect(useDispatchSpy).toHaveBeenCalled() + expect(mockAppDispatch).toHaveBeenCalled() }) }) }) diff --git a/src/components/Connection/SGEConnect/SgeConnectView.tsx b/src/components/Connection/SGEConnect/SgeConnectView.tsx index 9619c6b72..f4e08fb84 100644 --- a/src/components/Connection/SGEConnect/SgeConnectView.tsx +++ b/src/components/Connection/SGEConnect/SgeConnectView.tsx @@ -5,10 +5,9 @@ import CozyBar from 'components/Header/CozyBar' import Header from 'components/Header/Header' import { SgeStep } from 'enum/sgeStep.enum' import { SgeStore } from 'models/sgeStore.model' -import React, { Dispatch, useCallback, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { AppActionsTypes, AppStore } from 'store' +import React, { useCallback, useState } from 'react' import { updateSgeStore } from 'store/global/global.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import './SgeConnect.scss' import StepAddress from './StepAddress' import StepConsent from './StepConsent' @@ -25,17 +24,17 @@ export type SGEKeysForm = | 'pdlConfirm' const SgeConnectView = () => { - const [headerHeight, setHeaderHeight] = useState<number>(0) - const { sgeConnect } = useSelector((state: AppStore) => state.ecolyo.global) - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() + const { sgeConnect } = useAppSelector(state => state.ecolyo.global) + const [isLoading, setIsLoading] = useState(false) + const [currentSgeState, setCurrentSgeState] = useState<SgeStore>(sgeConnect) const [currentStep, setCurrentStep] = useState<SgeStep>( sgeConnect.currentStep ) - const [currentSgeState, setCurrentSgeState] = useState<SgeStore>(sgeConnect) + const [headerHeight, setHeaderHeight] = useState<number>(0) const defineHeaderHeight = useCallback((height: number) => { setHeaderHeight(height) }, []) - const [isLoading, setIsLoading] = useState(false) const isNextValid = useCallback(() => { switch (currentStep) { diff --git a/src/components/Connection/SGEConnect/SgeInit.tsx b/src/components/Connection/SGEConnect/SgeInit.tsx index ecec4ebab..6294b8d3a 100644 --- a/src/components/Connection/SGEConnect/SgeInit.tsx +++ b/src/components/Connection/SGEConnect/SgeInit.tsx @@ -4,15 +4,14 @@ import StyledIcon from 'components/CommonKit/Icon/StyledIcon' import useKonnectorAuth from 'components/Hooks/useKonnectorAuth' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { Account, FluidStatus } from 'models' -import React, { Dispatch, useEffect } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useEffect } from 'react' import { useNavigate } from 'react-router-dom' -import { AppActionsTypes, AppStore } from 'store' import { setShowOfflineData } from 'store/chart/chart.slice' import { setShouldRefreshConsent, updateSgeStore, } from 'store/global/global.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { decoreText } from 'utils/decoreText' const SgeInit = ({ fluidStatus }: { fluidStatus: FluidStatus }) => { @@ -20,9 +19,8 @@ const SgeInit = ({ fluidStatus }: { fluidStatus: FluidStatus }) => { const navigate = useNavigate() const konnectorSlug: string = fluidStatus.connection.konnectorConfig.slug const account: Account | null = fluidStatus.connection.account - const { sgeConnect } = useSelector((state: AppStore) => state.ecolyo.global) - - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const { sgeConnect } = useAppSelector(state => state.ecolyo.global) + const dispatch = useAppDispatch() const [connect, update] = useKonnectorAuth(fluidStatus) useEffect(() => { diff --git a/src/components/ConsumptionVisualizer/ConsumptionVisualizer.tsx b/src/components/ConsumptionVisualizer/ConsumptionVisualizer.tsx index d497f4279..7fffa7d6a 100644 --- a/src/components/ConsumptionVisualizer/ConsumptionVisualizer.tsx +++ b/src/components/ConsumptionVisualizer/ConsumptionVisualizer.tsx @@ -3,8 +3,7 @@ import { FluidType } from 'enum/fluid.enum' import { DateTime } from 'luxon' import { Dataload } from 'models' import React from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import InfoDataConsumptionVisualizer from './InfoDataConsumptionVisualizer' import './consumptionVisualizer.scss' @@ -19,7 +18,7 @@ const ConsumptionVisualizer = ({ const { chart: { currentDatachart, currentDatachartIndex }, global: { fluidStatus, fluidTypes }, - } = useSelector((state: AppStore) => state.ecolyo) + } = useAppSelector(state => state.ecolyo) const dataload: Dataload = currentDatachart.actualData[currentDatachartIndex] const compareDataload: Dataload | null = currentDatachart.comparisonData diff --git a/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx b/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx index be0f81676..af620801a 100644 --- a/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx +++ b/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx @@ -2,8 +2,7 @@ import { DataloadSectionType, DataloadState } from 'enum/dataload.enum' import { FluidType } from 'enum/fluid.enum' import { Dataload } from 'models' import React, { useCallback, useState } from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import DataloadNoValue from './DataloadNoValue' import DataloadSection from './DataloadSection' import EstimatedConsumptionModal from './EstimatedConsumptionModal' @@ -21,7 +20,7 @@ const DataloadConsumptionVisualizer = ({ compareDataload, setActive, }: DataloadConsumptionVisualizerProps) => { - const { showCompare } = useSelector((state: AppStore) => state.ecolyo.chart) + const { showCompare } = useAppSelector(state => state.ecolyo.chart) const [openEstimationModal, setOpenEstimationModal] = useState<boolean>(false) const toggleEstimationModal = useCallback(() => { setOpenEstimationModal(prev => !prev) diff --git a/src/components/ConsumptionVisualizer/InfoDataConsumptionVisualizer.spec.tsx b/src/components/ConsumptionVisualizer/InfoDataConsumptionVisualizer.spec.tsx index 0de0cb23c..0c6bffa6d 100644 --- a/src/components/ConsumptionVisualizer/InfoDataConsumptionVisualizer.spec.tsx +++ b/src/components/ConsumptionVisualizer/InfoDataConsumptionVisualizer.spec.tsx @@ -5,8 +5,8 @@ import toJson from 'enzyme-to-json' import { DateTime } from 'luxon' import { Dataload } from 'models' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' +import * as storeHooks from 'store/hooks' import { baseDataLoad } from '../../../tests/__mocks__/chartData.mock' import { createMockEcolyoStore } from '../../../tests/__mocks__/store' import InfoDataConsumptionVisualizer from './InfoDataConsumptionVisualizer' @@ -18,7 +18,7 @@ jest.mock('cozy-ui/transpiled/react/I18n', () => ({ })), })) -const mockUseDispatch = jest.spyOn(reactRedux, 'useDispatch') +const mockAppDispatch = jest.spyOn(storeHooks, 'useAppDispatch') describe('InfoDataConsumptionVisualizer component', () => { const mockLastDataDate = DateTime.fromISO('2020-10-01T00:00:00.000Z', { @@ -227,6 +227,6 @@ describe('InfoDataConsumptionVisualizer component', () => { </Provider> ) wrapper.find('.error-line').simulate('click') - expect(mockUseDispatch).toHaveBeenCalled() + expect(mockAppDispatch).toHaveBeenCalled() }) }) diff --git a/src/components/ConsumptionVisualizer/InfoDataConsumptionVisualizer.tsx b/src/components/ConsumptionVisualizer/InfoDataConsumptionVisualizer.tsx index 4ac3bd966..0cb05b332 100644 --- a/src/components/ConsumptionVisualizer/InfoDataConsumptionVisualizer.tsx +++ b/src/components/ConsumptionVisualizer/InfoDataConsumptionVisualizer.tsx @@ -3,11 +3,10 @@ import { DataloadState } from 'enum/dataload.enum' import { FluidType } from 'enum/fluid.enum' import { DateTime } from 'luxon' import { Dataload } from 'models' -import React, { Dispatch, useCallback, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback, useState } from 'react' import DateChartService from 'services/dateChart.service' -import { AppActionsTypes, AppStore } from 'store' import { setCurrentIndex, setSelectedDate } from 'store/chart/chart.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import NoDataModal from './NoDataModal' import './infoDataConsumptionVisualizer.scss' @@ -23,10 +22,8 @@ const InfoDataConsumptionVisualizer = ({ lastDataDate, }: InfoDataConsumptionVisualizerProps) => { const { t } = useI18n() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() - const { currentTimeStep } = useSelector( - (state: AppStore) => state.ecolyo.chart - ) + const dispatch = useAppDispatch() + const { currentTimeStep } = useAppSelector(state => state.ecolyo.chart) const [openNodataModal, setopenNodataModal] = useState<boolean>(false) const toggleNoDataModal = useCallback(() => { diff --git a/src/components/Content/Content.tsx b/src/components/Content/Content.tsx index da2111065..72e9e0040 100644 --- a/src/components/Content/Content.tsx +++ b/src/components/Content/Content.tsx @@ -1,9 +1,8 @@ import FeedbackModal from 'components/Feedback/FeedbackModal' import { ScreenType } from 'enum/screen.enum' -import React, { Dispatch, useCallback, useEffect } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { AppActionsTypes, AppStore } from 'store' +import React, { useCallback, useEffect } from 'react' import { changeScreenType } from 'store/global/global.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { openFeedbackModal } from 'store/modal/modal.slice' import './content.scss' interface ContentProps { @@ -17,11 +16,11 @@ const Content = ({ height = 0, background = 'inherit', }: ContentProps) => { - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() const { global: { screenType }, modal: { isFeedbacksOpen }, - } = useSelector((state: AppStore) => state.ecolyo) + } = useAppSelector(state => state.ecolyo) const cozyBarHeight = 48 const cozyNavHeight = 56 diff --git a/src/components/Duel/DuelBar.tsx b/src/components/Duel/DuelBar.tsx index 99cfce827..7e2fb3d65 100644 --- a/src/components/Duel/DuelBar.tsx +++ b/src/components/Duel/DuelBar.tsx @@ -8,8 +8,7 @@ import { TimeStep } from 'enum/timeStep.enum' import { DateTime } from 'luxon' import { Dataload, UserChallenge } from 'models' import React from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' export interface BarChartProps { userChallenge: UserChallenge @@ -36,9 +35,7 @@ const DuelBar = ({ marginTop = 20, marginBottom = 50, }: BarChartProps) => { - const { currentDataload } = useSelector( - (state: AppStore) => state.ecolyo.challenge - ) + const { currentDataload } = useAppSelector(state => state.ecolyo.challenge) const dataload: Dataload[] = finishedDataLoad ? finishedDataLoad : currentDataload diff --git a/src/components/Duel/DuelOngoing.tsx b/src/components/Duel/DuelOngoing.tsx index 98f13ec4b..97804bdf2 100644 --- a/src/components/Duel/DuelOngoing.tsx +++ b/src/components/Duel/DuelOngoing.tsx @@ -15,24 +15,16 @@ import { } from 'enum/userChallenge.enum' import { UserDuelState } from 'enum/userDuel.enum' import { Dataload, UserChallenge, UserDuel } from 'models' -import React, { - Dispatch, - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useNavigate } from 'react-router-dom' import ChallengeService from 'services/challenge.service' import UsageEventService from 'services/usageEvent.service' -import { AppActionsTypes, AppStore } from 'store' import { unlockNextUserChallenge, updateUserChallengeList, } from 'store/challenge/challenge.slice' import { toggleChallengeDuelNotification } from 'store/global/global.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { formatNumberValues } from 'utils/utils' import './duelOngoing.scss' @@ -44,10 +36,10 @@ interface DuelOngoingProps { const DuelOngoing = ({ userChallenge, isFinished }: DuelOngoingProps) => { const client: Client = useClient() const { t } = useI18n() - const { currentDataload, userChallengeList } = useSelector( - (state: AppStore) => state.ecolyo.challenge + const { currentDataload, userChallengeList } = useAppSelector( + state => state.ecolyo.challenge ) - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() const navigate = useNavigate() const [resultModal, setResultModal] = useState<boolean>(false) const [winChallenge, setWinChallenge] = useState<boolean>(false) diff --git a/src/components/Duel/DuelUnlocked.tsx b/src/components/Duel/DuelUnlocked.tsx index c4b16b9e0..9ff52b9ec 100644 --- a/src/components/Duel/DuelUnlocked.tsx +++ b/src/components/Duel/DuelUnlocked.tsx @@ -7,19 +7,18 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { UsageEventType } from 'enum/usageEvent.enum' import { UserChallengeUpdateFlag } from 'enum/userChallenge.enum' import { UserChallenge } from 'models' -import React, { Dispatch, useCallback, useEffect, useState } from 'react' -import { useDispatch } from 'react-redux' +import React, { useCallback, useEffect, useState } from 'react' import ChallengeService from 'services/challenge.service' import UsageEventService from 'services/usageEvent.service' -import { AppActionsTypes } from 'store' import { setChallengeConsumption } from 'store/challenge/challenge.slice' +import { useAppDispatch } from 'store/hooks' import { formatNumberValues, importIconById } from 'utils/utils' import './duelUnlocked.scss' const DuelUnlocked = ({ userChallenge }: { userChallenge: UserChallenge }) => { - const client: Client = useClient() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() const { t } = useI18n() + const client: Client = useClient() + const dispatch = useAppDispatch() const [duelIcon, setDuelIcon] = useState(defaultIcon) const average: string = formatNumberValues( diff --git a/src/components/Duel/DuelView.tsx b/src/components/Duel/DuelView.tsx index b5a269f17..4704f47a8 100644 --- a/src/components/Duel/DuelView.tsx +++ b/src/components/Duel/DuelView.tsx @@ -7,17 +7,14 @@ import { UserChallengeState } from 'enum/userChallenge.enum' import { UserDuelState } from 'enum/userDuel.enum' import { UserChallenge } from 'models' import React, { useCallback, useState } from 'react' -import { useSelector } from 'react-redux' import { useLocation, useNavigate } from 'react-router-dom' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import DuelEmptyValueModal from './DuelEmptyValueModal' import DuelOngoing from './DuelOngoing' const DuelView = () => { + const { userChallengeList } = useAppSelector(state => state.ecolyo.challenge) const [headerHeight, setHeaderHeight] = useState<number>(0) - const { userChallengeList } = useSelector( - (state: AppStore) => state.ecolyo.challenge - ) const id = new URLSearchParams(useLocation().search).get('id') const challengeToDisplay: UserChallenge | undefined = userChallengeList.find( challenge => challenge.id === id diff --git a/src/components/Ecogesture/EcogestureModal.tsx b/src/components/Ecogesture/EcogestureModal.tsx index 64f363d9a..43a412bc9 100644 --- a/src/components/Ecogesture/EcogestureModal.tsx +++ b/src/components/Ecogesture/EcogestureModal.tsx @@ -10,8 +10,7 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n' import Icon from 'cozy-ui/transpiled/react/Icon' import { Ecogesture } from 'models' import React, { useEffect, useState } from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import { getPicto } from 'utils/picto' import { importIconById } from 'utils/utils' import EfficiencyRating from './EfficiencyRating' @@ -33,11 +32,9 @@ const EcogestureModal = ({ selectEcogesture, }: EcogestureModalProps) => { const { t } = useI18n() + const { currentChallenge } = useAppSelector(state => state.ecolyo.challenge) const [ecogestureIcon, setEcogestureIcon] = useState('') const [isMoreDetail, setIsMoreDetail] = useState(false) - const { currentChallenge } = useSelector( - (state: AppStore) => state.ecolyo.challenge - ) const [, setValidExploration] = useExploration() const toggleMoreDetail = () => { diff --git a/src/components/Ecogesture/EcogestureView.tsx b/src/components/Ecogesture/EcogestureView.tsx index 2ec75eacf..d2d65a8f4 100644 --- a/src/components/Ecogesture/EcogestureView.tsx +++ b/src/components/Ecogesture/EcogestureView.tsx @@ -9,11 +9,10 @@ import { useClient } from 'cozy-client' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { EcogestureTab } from 'enum/ecogesture.enum' import { Ecogesture } from 'models' -import React, { Dispatch, useCallback, useEffect, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback, useEffect, useState } from 'react' import { useLocation, useNavigate } from 'react-router-dom' import EcogestureService from 'services/ecogesture.service' -import { AppActionsTypes, AppStore } from 'store' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { updateProfile } from 'store/profile/profile.actions' import EcogestureEmptyList from './EcogestureEmptyList' import EcogestureInitModal from './EcogestureInitModal' @@ -46,9 +45,9 @@ const EcogestureView = () => { const { t } = useI18n() const client = useClient() const tab = new URLSearchParams(useLocation().search).get('tab') - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() - const { profile, profileEcogesture, profileType } = useSelector( - (state: AppStore) => state.ecolyo + const dispatch = useAppDispatch() + const { profile, profileEcogesture, profileType } = useAppSelector( + state => state.ecolyo ) const [tabValue, setTabValue] = useState<EcogestureTab>( diff --git a/src/components/Ecogesture/SingleEcogesture.tsx b/src/components/Ecogesture/SingleEcogesture.tsx index 2dfef6788..a6c2f8fc4 100644 --- a/src/components/Ecogesture/SingleEcogesture.tsx +++ b/src/components/Ecogesture/SingleEcogesture.tsx @@ -17,10 +17,9 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n' import Icon from 'cozy-ui/transpiled/react/Icon' import { Ecogesture } from 'models' import React, { useCallback, useEffect, useMemo, useState } from 'react' -import { useSelector } from 'react-redux' import { Location, useLocation, useNavigate, useParams } from 'react-router-dom' import EcogestureService from 'services/ecogesture.service' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import { importIconById } from 'utils/utils' import EfficiencyRating from './EfficiencyRating' import './singleEcogesture.scss' @@ -49,9 +48,7 @@ const SingleEcogesture = () => { () => new EcogestureService(client), [client] ) - const { currentChallenge } = useSelector( - (state: AppStore) => state.ecolyo.challenge - ) + const { currentChallenge } = useAppSelector(state => state.ecolyo.challenge) const [headerHeight, setHeaderHeight] = useState<number>(0) const defineHeaderHeight = (height: number) => { setHeaderHeight(height) diff --git a/src/components/EcogestureForm/EcogestureFormEquipment.tsx b/src/components/EcogestureForm/EcogestureFormEquipment.tsx index b26bd8eec..44e57d700 100644 --- a/src/components/EcogestureForm/EcogestureFormEquipment.tsx +++ b/src/components/EcogestureForm/EcogestureFormEquipment.tsx @@ -6,8 +6,7 @@ import { EcogestureStepForm } from 'enum/ecogestureForm.enum' import { ProfileTypeStepForm } from 'enum/profileType.enum' import { ProfileEcogesture, ProfileType } from 'models' import React, { useCallback, useState } from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import EquipmentIcon from './EquipmentIcon' import './ecogestureFormEquipment.scss' @@ -29,8 +28,8 @@ const EcogestureFormEquipment = ({ step, }: EcogestureFormEquipmentProps) => { const { t } = useI18n() - const { isProfileEcogestureCompleted } = useSelector( - (state: AppStore) => state.ecolyo.profile + const { isProfileEcogestureCompleted } = useAppSelector( + state => state.ecolyo.profile ) const previousEquipments = currentProfileType?.equipments || currentProfileEcogesture?.equipments || [] diff --git a/src/components/EcogestureForm/EcogestureFormSingleChoice.spec.tsx b/src/components/EcogestureForm/EcogestureFormSingleChoice.spec.tsx index ffcf2df03..e10245f14 100644 --- a/src/components/EcogestureForm/EcogestureFormSingleChoice.spec.tsx +++ b/src/components/EcogestureForm/EcogestureFormSingleChoice.spec.tsx @@ -1,9 +1,7 @@ -/* eslint-disable react/display-name */ import { Button } from '@material-ui/core' import { mount } from 'enzyme' import toJson from 'enzyme-to-json' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import { mockEcogestureAnswer, @@ -13,8 +11,6 @@ import { createMockEcolyoStore } from '../../../tests/__mocks__/store' import { waitForComponentToPaint } from '../../../tests/__mocks__/testUtils' import EcogestureFormSingleChoice from './EcogestureFormSingleChoice' -const useSelectorSpy = jest.spyOn(reactRedux, 'useSelector') - jest.mock('cozy-ui/transpiled/react/I18n', () => ({ useI18n: jest.fn(() => ({ t: (str: string) => str, diff --git a/src/components/EcogestureForm/EcogestureFormSingleChoice.tsx b/src/components/EcogestureForm/EcogestureFormSingleChoice.tsx index 5f1299666..f97c34100 100644 --- a/src/components/EcogestureForm/EcogestureFormSingleChoice.tsx +++ b/src/components/EcogestureForm/EcogestureFormSingleChoice.tsx @@ -9,8 +9,7 @@ import { ProfileEcogestureValues, } from 'models/profileEcogesture.model' import React, { useCallback, useState } from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import './ecogestureFormSingleChoice.scss' interface EcogestureFormSingleChoiceProps { step: EcogestureStepForm @@ -30,8 +29,8 @@ const EcogestureFormSingleChoice = ({ setPreviousStep, }: EcogestureFormSingleChoiceProps) => { const { t } = useI18n() - const { isProfileEcogestureCompleted } = useSelector( - (state: AppStore) => state.ecolyo.profile + const { isProfileEcogestureCompleted } = useAppSelector( + state => state.ecolyo.profile ) const [answer, setAnswer] = useState<ProfileEcogestureValues>( isProfileEcogestureCompleted || step < viewedStep diff --git a/src/components/EcogestureForm/EcogestureFormView.spec.tsx b/src/components/EcogestureForm/EcogestureFormView.spec.tsx index 75c3f6529..e8d5cf295 100644 --- a/src/components/EcogestureForm/EcogestureFormView.spec.tsx +++ b/src/components/EcogestureForm/EcogestureFormView.spec.tsx @@ -5,6 +5,7 @@ import toJson from 'enzyme-to-json' import React from 'react' import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' +import * as storeHooks from 'store/hooks' import { mockProfileEcogesture } from '../../../tests/__mocks__/profileEcogesture.mock' import { createMockEcolyoStore } from '../../../tests/__mocks__/store' import { waitForComponentToPaint } from '../../../tests/__mocks__/testUtils' @@ -23,6 +24,7 @@ jest.mock( () => 'mock-ecogesturelaunchmodal' ) const mockUseDispatch = jest.spyOn(reactRedux, 'useDispatch') +const mockAppDispatch = jest.spyOn(storeHooks, 'useAppDispatch') jest.mock('components/Content/Content', () => 'mock-content') const mockedNavigate = jest.fn() jest.mock('react-router-dom', () => ({ @@ -40,6 +42,7 @@ describe('EcogestureFormView component', () => { beforeEach(() => { store.clearActions() mockUseDispatch.mockClear() + mockAppDispatch.mockClear() }) it('should be rendered correctly', async () => { diff --git a/src/components/EcogestureForm/EcogestureFormView.tsx b/src/components/EcogestureForm/EcogestureFormView.tsx index b3faea70e..180018ebb 100644 --- a/src/components/EcogestureForm/EcogestureFormView.tsx +++ b/src/components/EcogestureForm/EcogestureFormView.tsx @@ -15,19 +15,20 @@ import { ProfileEcogestureAnswer, } from 'models/profileEcogesture.model' import React, { Dispatch, useCallback, useEffect, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import { useDispatch } from 'react-redux' import { useLocation, useNavigate } from 'react-router-dom' import ProfileEcogestureFormService from 'services/profileEcogestureForm.service' -import { AppActionsTypes, AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import { updateProfile } from 'store/profile/profile.actions' import { newProfileEcogestureEntry } from 'store/profileEcogesture/profileEcogesture.actions' +import { AppActionsTypes } from 'store/store' const EcogestureFormView = () => { - const dispatch: Dispatch<AppActionsTypes> = useDispatch() const { profile: { isProfileTypeCompleted }, profileEcogesture, - } = useSelector((state: AppStore) => state.ecolyo) + } = useAppSelector(state => state.ecolyo) + const dispatch: Dispatch<AppActionsTypes> = useDispatch() const navigate = useNavigate() const [headerHeight, setHeaderHeight] = useState<number>(0) const defineHeaderHeight = (height: number) => { diff --git a/src/components/EcogestureSelection/EcogestureSelection.tsx b/src/components/EcogestureSelection/EcogestureSelection.tsx index a04d39415..534f7cc7f 100644 --- a/src/components/EcogestureSelection/EcogestureSelection.tsx +++ b/src/components/EcogestureSelection/EcogestureSelection.tsx @@ -10,16 +10,16 @@ import { useClient } from 'cozy-client' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { Ecogesture } from 'models' import React, { useCallback, useEffect, useMemo, useState } from 'react' -import { useSelector } from 'react-redux' import { useNavigate } from 'react-router-dom' import EcogestureService from 'services/ecogesture.service' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import './ecogestureSelection.scss' const EcogestureSelection = () => { const { t } = useI18n() const client = useClient() const navigate = useNavigate() + const { profileEcogesture } = useAppSelector(state => state.ecolyo) const [isLoading, setIsLoading] = useState(true) const [headerHeight, setHeaderHeight] = useState<number>(0) const [indexEcogesture, setIndexEcogesture] = useState<number>(0) @@ -37,9 +37,6 @@ const EcogestureSelection = () => { () => new EcogestureService(client), [client] ) - const profileEcogesture = useSelector( - (state: AppStore) => state.ecolyo.profileEcogesture - ) const getTitle = useCallback((): string => { if ( diff --git a/src/components/Exploration/ExplorationFinished.tsx b/src/components/Exploration/ExplorationFinished.tsx index d0382020c..25750ed88 100644 --- a/src/components/Exploration/ExplorationFinished.tsx +++ b/src/components/Exploration/ExplorationFinished.tsx @@ -7,14 +7,13 @@ import { UsageEventType } from 'enum/usageEvent.enum' import { UserChallengeUpdateFlag } from 'enum/userChallenge.enum' import { UserExplorationState } from 'enum/userExploration.enum' import { UserChallenge } from 'models' -import React, { Dispatch, useCallback } from 'react' -import { useDispatch } from 'react-redux' +import React, { useCallback } from 'react' 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.slice' import { toggleChallengeExplorationNotification } from 'store/global/global.slice' +import { useAppDispatch } from 'store/hooks' import './explorationFinished.scss' interface ExplorationFinishedProps { @@ -22,9 +21,9 @@ interface ExplorationFinishedProps { } const ExplorationFinished = ({ userChallenge }: ExplorationFinishedProps) => { - const client: Client = useClient() const { t } = useI18n() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const client: Client = useClient() + const dispatch = useAppDispatch() const navigate = useNavigate() const checkNotificationToEnd = useCallback(async () => { diff --git a/src/components/Exploration/ExplorationOngoing.tsx b/src/components/Exploration/ExplorationOngoing.tsx index 68bec1b73..62d7c23a9 100644 --- a/src/components/Exploration/ExplorationOngoing.tsx +++ b/src/components/Exploration/ExplorationOngoing.tsx @@ -11,13 +11,12 @@ import { UserExplorationType, } from 'enum/userExploration.enum' import { UserChallenge } from 'models' -import React, { Dispatch, useCallback } from 'react' -import { useDispatch } from 'react-redux' +import React, { useCallback } from 'react' 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.slice' +import { useAppDispatch } from 'store/hooks' import './explorationOngoing.scss' interface ExplorationOngoingProps { @@ -25,9 +24,9 @@ interface ExplorationOngoingProps { } const ExplorationOngoing = ({ userChallenge }: ExplorationOngoingProps) => { - const client: Client = useClient() const { t } = useI18n() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const client: Client = useClient() + const dispatch = useAppDispatch() const navigate = useNavigate() const goBack = useCallback(() => { navigate(-1) diff --git a/src/components/Exploration/ExplorationView.tsx b/src/components/Exploration/ExplorationView.tsx index b08315fbb..d10e22917 100644 --- a/src/components/Exploration/ExplorationView.tsx +++ b/src/components/Exploration/ExplorationView.tsx @@ -4,17 +4,14 @@ import Header from 'components/Header/Header' import { UserExplorationState } from 'enum/userExploration.enum' import { UserChallenge } from 'models' import React, { useCallback, useState } from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import ExplorationError from './ExplorationError' import ExplorationFinished from './ExplorationFinished' import ExplorationOngoing from './ExplorationOngoing' const ExplorationView = () => { + const { currentChallenge } = useAppSelector(state => state.ecolyo.challenge) const [headerHeight, setHeaderHeight] = useState<number>(0) - const { currentChallenge } = useSelector( - (state: AppStore) => state.ecolyo.challenge - ) const defineHeaderHeight = useCallback((height: number) => { setHeaderHeight(height) diff --git a/src/components/Feedback/FeedbackModal.spec.tsx b/src/components/Feedback/FeedbackModal.spec.tsx index 361b5689c..4f9602cee 100644 --- a/src/components/Feedback/FeedbackModal.spec.tsx +++ b/src/components/Feedback/FeedbackModal.spec.tsx @@ -1,7 +1,6 @@ import FeedbackModal from 'components/Feedback/FeedbackModal' import { mount } from 'enzyme' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import { createMockEcolyoStore } from '../../../tests/__mocks__/store' import { waitForComponentToPaint } from '../../../tests/__mocks__/testUtils' @@ -25,8 +24,6 @@ jest.mock('services/environment.service', () => { const handleFeedbackModalClose = jest.fn() -const mockUseDispatch = jest.spyOn(reactRedux, 'useDispatch') - describe('FeedbackModal component', () => { const store = createMockEcolyoStore() beforeEach(() => { @@ -34,7 +31,6 @@ describe('FeedbackModal component', () => { }) it('should render the component', () => { // TODO fix empty snapshot - mockUseDispatch.mockReturnValue(jest.fn()) const component = mount( <Provider store={store}> <FeedbackModal @@ -48,7 +44,6 @@ describe('FeedbackModal component', () => { describe('FeedbackModal functionalities', () => { it('should close the modal', async () => { - mockUseDispatch.mockReturnValue(jest.fn()) const wrapper = mount( <Provider store={store}> <FeedbackModal diff --git a/src/components/Feedback/FeedbackModal.tsx b/src/components/Feedback/FeedbackModal.tsx index e3858f129..59f1f2b1d 100644 --- a/src/components/Feedback/FeedbackModal.tsx +++ b/src/components/Feedback/FeedbackModal.tsx @@ -6,6 +6,7 @@ import ecolyoIcon from 'assets/icons/ico/ecolyo.svg' import StyledIcon from 'components/CommonKit/Icon/StyledIcon' import useExploration from 'components/Hooks/useExploration' import { Client, useClient } from 'cozy-client' +// what is the type ? import { IuseI18n, useI18n } from 'cozy-ui/transpiled/react/I18n' import Icon from 'cozy-ui/transpiled/react/Icon' import { UserExplorationID } from 'enum/userExploration.enum' diff --git a/src/components/FluidChart/FluidChart.tsx b/src/components/FluidChart/FluidChart.tsx index 158f05147..9af868338 100644 --- a/src/components/FluidChart/FluidChart.tsx +++ b/src/components/FluidChart/FluidChart.tsx @@ -11,19 +11,18 @@ import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' import { UsageEventType } from 'enum/usageEvent.enum' import { UserExplorationID } from 'enum/userExploration.enum' -import React, { Dispatch, useCallback, useEffect, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback, useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' import ConsumptionService from 'services/consumption.service' import DateChartService from 'services/dateChart.service' import UsageEventService from 'services/usageEvent.service' -import { AppActionsTypes, AppStore } from 'store' import { setCurrentIndex, setSelectedDate, setShowCompare, setShowOfflineData, } from 'store/chart/chart.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { openConnectionModal } from 'store/modal/modal.slice' import { getFluidName, getKonnectorSlug, isKonnectorActive } from 'utils/utils' import FluidChartSwipe from './FluidChartSwipe' @@ -40,8 +39,8 @@ const FluidChart = ({ fluidType, setActive }: FluidChartProps) => { const { chart: { currentTimeStep, selectedDate, showCompare }, global: { fluidStatus }, - } = useSelector((state: AppStore) => state.ecolyo) - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + } = useAppSelector(state => state.ecolyo) + const dispatch = useAppDispatch() const navigate = useNavigate() const currentFluidStatus = fluidStatus[fluidType] diff --git a/src/components/FluidChart/FluidChartSlide.tsx b/src/components/FluidChart/FluidChartSlide.tsx index 321847898..22dcca206 100644 --- a/src/components/FluidChart/FluidChartSlide.tsx +++ b/src/components/FluidChart/FluidChartSlide.tsx @@ -6,12 +6,11 @@ import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' import { DateTime } from 'luxon' import { Datachart } from 'models' -import React, { Dispatch, useEffect, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useEffect, useState } from 'react' import ConsumptionService from 'services/consumption.service' import DateChartService from 'services/dateChart.service' -import { AppActionsTypes, AppStore } from 'store' import { setCurrentDataChart, setLoading } from 'store/chart/chart.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import './fluidChartSlide.scss' interface FluidChartSlideProps { @@ -32,11 +31,11 @@ const FluidChartSlide = ({ setActive, }: FluidChartSlideProps) => { const client = useClient() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() const { chart: { currentTimeStep, currentIndex }, global: { fluidStatus, fluidTypes }, - } = useSelector((state: AppStore) => state.ecolyo) + } = useAppSelector(state => state.ecolyo) const [chartData, setChartData] = useState<Datachart>({ actualData: [], diff --git a/src/components/FluidChart/FluidChartSwipe.tsx b/src/components/FluidChart/FluidChartSwipe.tsx index 403d914b9..0649c7191 100644 --- a/src/components/FluidChart/FluidChartSwipe.tsx +++ b/src/components/FluidChart/FluidChartSwipe.tsx @@ -2,13 +2,12 @@ import FluidChartSlide from 'components/FluidChart/FluidChartSlide' import { useChartResize } from 'components/Hooks/useChartResize' import { FluidType } from 'enum/fluid.enum' import { DateTime } from 'luxon' -import React, { Dispatch, useEffect, useRef, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useEffect, useRef, useState } from 'react' import SwipeableViews from 'react-swipeable-views' import { virtualize } from 'react-swipeable-views-utils' import DateChartService from 'services/dateChart.service' -import { AppActionsTypes, AppStore } from 'store' import { setCurrentIndex, setSelectedDate } from 'store/chart/chart.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import './fluidChartSwipe.scss' const VirtualizeSwipeableViews = virtualize(SwipeableViews) @@ -19,10 +18,9 @@ interface FluidChartSwipeProps { } const FluidChartSwipe = ({ fluidType, setActive }: FluidChartSwipeProps) => { - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() - const { currentIndex, currentTimeStep, selectedDate, loading } = useSelector( - (state: AppStore) => state.ecolyo.chart - ) + const dispatch = useAppDispatch() + const { currentIndex, currentTimeStep, selectedDate, loading } = + useAppSelector(state => state.ecolyo.chart) const swipe = useRef<HTMLDivElement>(null) const [isSwitching, setIsSwitching] = useState(false) diff --git a/src/components/Header/CozyBar.tsx b/src/components/Header/CozyBar.tsx index 0c0ca0078..7169d94f8 100644 --- a/src/components/Header/CozyBar.tsx +++ b/src/components/Header/CozyBar.tsx @@ -3,10 +3,9 @@ import FeedbackIcon from 'assets/icons/ico/feedback.svg' import StyledIconButton from 'components/CommonKit/IconButton/StyledIconButton' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { ScreenType } from 'enum/screen.enum' -import React, { Dispatch, useCallback } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback } from 'react' import { useNavigate } from 'react-router-dom' -import { AppActionsTypes, AppStore } from 'store' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { openFeedbackModal } from 'store/modal/modal.slice' import cozyBar from 'utils/cozyBar' @@ -24,9 +23,9 @@ const CozyBar = ({ }: CozyBarProps) => { const { t } = useI18n() const navigate = useNavigate() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() const { BarLeft, BarCenter, BarRight } = cozyBar - const { screenType } = useSelector((state: AppStore) => state.ecolyo.global) + const dispatch = useAppDispatch() + const { screenType } = useAppSelector(state => state.ecolyo.global) const handleClickBack = useCallback(() => { if (backFunction) { diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 428868001..907185e23 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -4,10 +4,9 @@ import FeedbackIcon from 'assets/icons/ico/feedback.svg' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import Icon from 'cozy-ui/transpiled/react/Icon' import { ScreenType } from 'enum/screen.enum' -import React, { Dispatch, useCallback, useEffect, useRef } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback, useEffect, useRef } from 'react' import { useNavigate } from 'react-router-dom' -import { AppActionsTypes, AppStore } from 'store' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { openFeedbackModal } from 'store/modal/modal.slice' import './header.scss' @@ -32,9 +31,8 @@ const Header = ({ const { t } = useI18n() const navigate = useNavigate() const header = useRef<HTMLDivElement>(null) - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() - const { screenType } = useSelector((state: AppStore) => state.ecolyo.global) - + const dispatch = useAppDispatch() + const { screenType } = useAppSelector(state => state.ecolyo.global) const cozyBarHeight = 48 const handleClickBack = useCallback(() => { diff --git a/src/components/Home/ConsumptionDetails.spec.tsx b/src/components/Home/ConsumptionDetails.spec.tsx index dc82714f2..dbe7e2e9d 100644 --- a/src/components/Home/ConsumptionDetails.spec.tsx +++ b/src/components/Home/ConsumptionDetails.spec.tsx @@ -2,7 +2,6 @@ import { FluidType } from 'enum/fluid.enum' import { mount } from 'enzyme' import toJson from 'enzyme-to-json' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import { createMockEcolyoStore } from '../../../tests/__mocks__/store' import { waitForComponentToPaint } from '../../../tests/__mocks__/testUtils' @@ -14,13 +13,10 @@ jest.mock('cozy-ui/transpiled/react/I18n', () => ({ })), })) -const mockUseSelector = jest.spyOn(reactRedux, 'useSelector') - describe('ConsumptionDetails component', () => { const store = createMockEcolyoStore() beforeEach(() => { store.clearActions() - mockUseSelector.mockClear() }) it('should be rendered correctly', async () => { const wrapper = mount( diff --git a/src/components/Home/ConsumptionDetails.tsx b/src/components/Home/ConsumptionDetails.tsx index 3d17fc620..3781a45da 100644 --- a/src/components/Home/ConsumptionDetails.tsx +++ b/src/components/Home/ConsumptionDetails.tsx @@ -3,15 +3,14 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' import React from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import { convertDateToShortDateString } from 'utils/date' import './consumptionDetails.scss' const ConsumptionDetails = ({ fluidType }: { fluidType: FluidType }) => { const { t } = useI18n() - const { currentTimeStep, currentDatachart, showCompare } = useSelector( - (state: AppStore) => state.ecolyo.chart + const { currentTimeStep, currentDatachart, showCompare } = useAppSelector( + state => state.ecolyo.chart ) return ( diff --git a/src/components/Home/ConsumptionView.spec.tsx b/src/components/Home/ConsumptionView.spec.tsx index a48484935..9cf225a57 100644 --- a/src/components/Home/ConsumptionView.spec.tsx +++ b/src/components/Home/ConsumptionView.spec.tsx @@ -3,7 +3,6 @@ import { FluidState, FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' import { mount } from 'enzyme' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import * as chartActions from 'store/chart/chart.slice' import { mockCustomPopup } from '../../../tests/__mocks__/customPopup.mock' @@ -62,7 +61,6 @@ jest.mock( () => 'mock-SgeConnectView' ) -const useDispatchSpy = jest.spyOn(reactRedux, 'useDispatch') const setCurrentTimeStepSpy = jest.spyOn(chartActions, 'setCurrentTimeStep') const mockFluidStatus = mockInitialEcolyoState.global.fluidStatus @@ -74,7 +72,6 @@ describe('ConsumptionView component', () => { const store = createMockEcolyoStore() beforeEach(() => { store.clearActions() - useDispatchSpy.mockClear() setCurrentTimeStepSpy.mockClear() }) @@ -213,7 +210,6 @@ describe('ConsumptionView component', () => { }, modal: { partnersIssueModal: { enedis: false, grdf: false, egl: true } }, }) - useDispatchSpy.mockReturnValue(jest.fn()) mockUpdateProfile.mockResolvedValue(mockTestProfile1) const wrapper = mount( <Provider store={store}> @@ -233,7 +229,6 @@ describe('ConsumptionView component', () => { }, modal: mockModalState, }) - useDispatchSpy.mockReturnValue(jest.fn()) const wrapper = mount( <Provider store={store}> <ConsumptionView fluidType={FluidType.GAS} /> @@ -252,7 +247,6 @@ describe('ConsumptionView component', () => { }, modal: mockModalState, }) - useDispatchSpy.mockReturnValue(jest.fn()) const wrapper = mount( <Provider store={store}> <ConsumptionView fluidType={FluidType.ELECTRICITY} /> @@ -273,7 +267,6 @@ describe('ConsumptionView component', () => { customPopupModal: mockCustomPopup, }, }) - useDispatchSpy.mockReturnValue(jest.fn()) mockUpdateProfile.mockResolvedValue(mockTestProfile1) const wrapper = mount( <Provider store={store}> @@ -294,7 +287,6 @@ describe('ConsumptionView component', () => { }, modal: mockModalState, }) - useDispatchSpy.mockReturnValue(jest.fn()) mockUpdateProfile.mockResolvedValue(mockTestProfile1) const wrapper = mount( <Provider store={store}> diff --git a/src/components/Home/ConsumptionView.tsx b/src/components/Home/ConsumptionView.tsx index c34985012..5e4d8924c 100644 --- a/src/components/Home/ConsumptionView.tsx +++ b/src/components/Home/ConsumptionView.tsx @@ -16,12 +16,10 @@ import { useClient } from 'cozy-client' import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' import { DateTime } from 'luxon' -import React, { Dispatch, useCallback, useEffect, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback, useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' import DateChartService from 'services/dateChart.service' import ProfileService from 'services/profile.service' -import { AppActionsTypes, AppStore } from 'store' import { setCurrentIndex, setCurrentTimeStep, @@ -29,6 +27,7 @@ import { setShowOfflineData, } from 'store/chart/chart.slice' import { showReleaseNotes } from 'store/global/global.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { openPartnersModal, setCustomPopup } from 'store/modal/modal.slice' import { isLastDateReached } from 'utils/date' import { @@ -42,7 +41,7 @@ import './consumptionView.scss' const ConsumptionView = ({ fluidType }: { fluidType: FluidType }) => { const navigate = useNavigate() const client = useClient() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() const isMulti = fluidType === FluidType.MULTIFLUID const { chart: { @@ -54,7 +53,7 @@ const ConsumptionView = ({ fluidType }: { fluidType: FluidType }) => { }, global: { fluidStatus, releaseNotes }, modal: { partnersIssueModal, customPopupModal }, - } = useSelector((state: AppStore) => state.ecolyo) + } = useAppSelector(state => state.ecolyo) const dateChartService = new DateChartService() diff --git a/src/components/Home/FluidButton.tsx b/src/components/Home/FluidButton.tsx index dcc93f534..9346e264f 100644 --- a/src/components/Home/FluidButton.tsx +++ b/src/components/Home/FluidButton.tsx @@ -6,11 +6,10 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { FluidState, FluidType } from 'enum/fluid.enum' import { UsageEventType } from 'enum/usageEvent.enum' import React, { useCallback, useEffect, useState } from 'react' -import { useSelector } from 'react-redux' import { useNavigate } from 'react-router-dom' import DateChartService from 'services/dateChart.service' import UsageEventService from 'services/usageEvent.service' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import { getNavPicto } from 'utils/picto' import { getFluidName, isKonnectorActive } from 'utils/utils' @@ -21,9 +20,9 @@ interface FluidButtonProps { const FluidButton = ({ fluidType, isActive }: FluidButtonProps) => { const { t } = useI18n() - const navigate = useNavigate() - const { fluidStatus } = useSelector((state: AppStore) => state.ecolyo.global) const client = useClient() + const navigate = useNavigate() + const { fluidStatus } = useAppSelector(state => state.ecolyo.global) const [showError, setShowError] = useState<boolean>(false) const isConnected = useCallback(() => { diff --git a/src/components/Home/ReleaseNotesModal.tsx b/src/components/Home/ReleaseNotesModal.tsx index 13dac570a..d775351c8 100644 --- a/src/components/Home/ReleaseNotesModal.tsx +++ b/src/components/Home/ReleaseNotesModal.tsx @@ -2,8 +2,7 @@ import Button from '@material-ui/core/Button' import Dialog from '@material-ui/core/Dialog' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import React from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import { decoreText } from 'utils/decoreText' import './ReleaseNotesModal.scss' @@ -17,7 +16,7 @@ const ReleaseNotesModal = ({ handleCloseClick, }: ReleaseNotesModalProps) => { const { t } = useI18n() - const { releaseNotes } = useSelector((state: AppStore) => state.ecolyo.global) + const { releaseNotes } = useAppSelector(state => state.ecolyo.global) return ( <Dialog diff --git a/src/components/Hooks/useExploration.spec.tsx b/src/components/Hooks/useExploration.spec.tsx index 8124169d7..44f3be563 100644 --- a/src/components/Hooks/useExploration.spec.tsx +++ b/src/components/Hooks/useExploration.spec.tsx @@ -1,19 +1,21 @@ import { act, renderHook } from '@testing-library/react-hooks' import useExploration from 'components/Hooks/useExploration' import { UserExplorationID } from 'enum/userExploration.enum' -import * as reactRedux from 'react-redux' +import React from 'react' +import { Provider } from 'react-redux' +import { createMockEcolyoStore } from '../../../tests/__mocks__/store' import { userChallengeExplo1OnGoing } from '../../../tests/__mocks__/userChallengeData.mock' -const dispatchMock = jest.fn() -const mockUseSelector = jest.spyOn(reactRedux, 'useSelector') -const mockUseDispatch = jest.spyOn(reactRedux, 'useDispatch') - describe('useExploration Hooks', () => { it('should return "EXPLORATION001"', () => { - mockUseDispatch.mockReturnValue(dispatchMock) - mockUseSelector.mockReturnValue(userChallengeExplo1OnGoing) + const store = createMockEcolyoStore({ + challenge: { currentChallenge: userChallengeExplo1OnGoing }, + }) - const { result } = renderHook(() => useExploration()) + const wrapper = ({ children }: { children: JSX.Element }) => ( + <Provider store={store}>{children}</Provider> + ) + const { result } = renderHook(() => useExploration(), { wrapper }) act(() => { result.current[1](UserExplorationID.EXPLORATION001) diff --git a/src/components/Hooks/useExploration.tsx b/src/components/Hooks/useExploration.tsx index 8874651fd..cb8dd408b 100644 --- a/src/components/Hooks/useExploration.tsx +++ b/src/components/Hooks/useExploration.tsx @@ -1,19 +1,16 @@ import { Client, useClient } from 'cozy-client' import { UserExplorationState } from 'enum/userExploration.enum' -import { ChallengeState, UserExploration } from 'models' +import { UserExploration } from 'models' 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.slice' import { toggleChallengeExplorationNotification } from 'store/global/global.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' const useExploration = (): [string, Dispatch<SetStateAction<string>>] => { const client: Client = useClient() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() - const { currentChallenge }: ChallengeState = useSelector( - (state: AppStore) => state.ecolyo.challenge - ) + const dispatch = useAppDispatch() + const { currentChallenge } = useAppSelector(state => state.ecolyo.challenge) const exploration: UserExploration | null = currentChallenge ? currentChallenge.exploration : null diff --git a/src/components/Hooks/useKonnectorAuth.tsx b/src/components/Hooks/useKonnectorAuth.tsx index 8d34bc5bf..10ae4f8bc 100644 --- a/src/components/Hooks/useKonnectorAuth.tsx +++ b/src/components/Hooks/useKonnectorAuth.tsx @@ -10,14 +10,13 @@ import { FluidStatus, UsageEvent, } from 'models' -import { Dispatch, useCallback, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import { useCallback, useState } from 'react' import AccountService from 'services/account.service' import ConnectionService from 'services/connection.service' import UsageEventService from 'services/usageEvent.service' -import { AppActionsTypes, AppStore } from 'store' import { setLoading } from 'store/chart/chart.slice' import { updateFluidConnection } from 'store/global/global.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import logApp from 'utils/logger' const useKonnectorAuth = ( @@ -27,11 +26,10 @@ const useKonnectorAuth = ( ): [() => Promise<null | undefined>, () => Promise<void>, string] => { const client: Client = useClient() const { t } = useI18n() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() const konnectorSlug: FluidSlugType = fluidStatus.connection.konnectorConfig.slug - const { sgeConnect } = useSelector((state: AppStore) => state.ecolyo.global) - + const { sgeConnect } = useAppSelector(state => state.ecolyo.global) const [connectError, setError] = useState<string>('') const onSuccess = useCallback(async () => { diff --git a/src/components/Konnector/KonnectorViewerCard.tsx b/src/components/Konnector/KonnectorViewerCard.tsx index fb5d5e4dc..54d191423 100644 --- a/src/components/Konnector/KonnectorViewerCard.tsx +++ b/src/components/Konnector/KonnectorViewerCard.tsx @@ -41,14 +41,7 @@ import { UsageEvent, } from 'models' import { PartnersInfo } from 'models/partnersInfo.model' -import React, { - Dispatch, - useCallback, - useEffect, - useMemo, - useState, -} from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import { useNavigate } from 'react-router-dom' import AccountService from 'services/account.service' import ChallengeService from 'services/challenge.service' @@ -56,7 +49,6 @@ import DateChartService from 'services/dateChart.service' 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.slice' import { setSelectedDate, setShowOfflineData } from 'store/chart/chart.slice' import { @@ -64,6 +56,7 @@ import { toggleChallengeDuelNotification, updatedFluidConnection, } from 'store/global/global.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { openConnectionModal } from 'store/modal/modal.slice' import { getAddPicto, getParamPicto } from 'utils/picto' import { getKonnectorSlug } from 'utils/utils' @@ -90,7 +83,7 @@ const KonnectorViewerCard = ({ }: KonnectorViewerCardProps) => { const { t } = useI18n() const client = useClient() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() const navigate = useNavigate() const fluidSlug = fluidStatus.connection.konnectorConfig.slug @@ -101,7 +94,7 @@ const KonnectorViewerCard = ({ const { challenge: { currentChallenge }, global: { fluidStatus: statusArray, shouldRefreshConsent, partnersInfo }, - } = useSelector((state: AppStore) => state.ecolyo) + } = useAppSelector(state => state.ecolyo) const [openModal, setOpenModal] = useState(false) const [isUpdating, setIsUpdating] = useState(false) diff --git a/src/components/Konnector/KonnectorViewerList.tsx b/src/components/Konnector/KonnectorViewerList.tsx index e4c0c6190..98acb829e 100644 --- a/src/components/Konnector/KonnectorViewerList.tsx +++ b/src/components/Konnector/KonnectorViewerList.tsx @@ -4,16 +4,15 @@ import Icon from 'cozy-ui/transpiled/react/Icon' import { FluidType } from 'enum/fluid.enum' import { FluidStatus } from 'models' import React, { useCallback } from 'react' -import { useSelector } from 'react-redux' import { useNavigate } from 'react-router-dom' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import { getAddPicto } from 'utils/picto' import { getFluidName } from 'utils/utils' import './konnectorViewerList.scss' const KonnectorViewerList = () => { const { t } = useI18n() - const { fluidStatus } = useSelector((state: AppStore) => state.ecolyo.global) + const { fluidStatus } = useAppSelector(state => state.ecolyo.global) const navigate = useNavigate() const goToFluid = useCallback( fluidType => { diff --git a/src/components/Navbar/Navbar.tsx b/src/components/Navbar/Navbar.tsx index 8a67ad38c..23f01ebda 100644 --- a/src/components/Navbar/Navbar.tsx +++ b/src/components/Navbar/Navbar.tsx @@ -15,10 +15,9 @@ import { useClient } from 'cozy-client' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { UsageEventType } from 'enum/usageEvent.enum' import React, { useCallback } from 'react' -import { useSelector } from 'react-redux' import { NavLink, useLocation } from 'react-router-dom' import UsageEventService from 'services/usageEvent.service' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import './navBar.scss' export const Navbar = () => { @@ -28,7 +27,7 @@ export const Navbar = () => { challengeActionNotification, challengeDuelNotification, analysisNotification, - } = useSelector((state: AppStore) => state.ecolyo.global) + } = useAppSelector(state => state.ecolyo.global) const { pathname } = useLocation() const client = useClient() diff --git a/src/components/Options/HelpLink/HelpLink.tsx b/src/components/Options/HelpLink/HelpLink.tsx index d463ce521..3a1c82dd6 100644 --- a/src/components/Options/HelpLink/HelpLink.tsx +++ b/src/components/Options/HelpLink/HelpLink.tsx @@ -2,15 +2,14 @@ import Link from '@material-ui/core/Link' import QuestionMarkIcon from 'assets/icons/ico/questionMark.svg' import StyledIcon from 'components/CommonKit/Icon/StyledIcon' import { useI18n } from 'cozy-ui/transpiled/react/I18n' -import React, { Dispatch } from 'react' -import { useDispatch } from 'react-redux' -import { AppActionsTypes } from 'store' +import React from 'react' +import { useAppDispatch } from 'store/hooks' import { openFeedbackModal } from 'store/modal/modal.slice' import './HelpLink.scss' const HelpLink = () => { const { t } = useI18n() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() return ( <div diff --git a/src/components/Options/ProfileTypeOptions/ProfileTypeOptions.tsx b/src/components/Options/ProfileTypeOptions/ProfileTypeOptions.tsx index 79fab1741..a09e959c0 100644 --- a/src/components/Options/ProfileTypeOptions/ProfileTypeOptions.tsx +++ b/src/components/Options/ProfileTypeOptions/ProfileTypeOptions.tsx @@ -20,15 +20,12 @@ import { } from 'enum/profileType.enum' import { UserExplorationID } from 'enum/userExploration.enum' import React, { useCallback, useState } from 'react' -import { useSelector } from 'react-redux' import { useNavigate } from 'react-router-dom' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import './profileTypeOptions.scss' const ProfileTypeOptions = () => { - const { profile, profileType } = useSelector( - (state: AppStore) => state.ecolyo - ) + const { profile, profileType } = useAppSelector(state => state.ecolyo) const { t } = useI18n() const navigate = useNavigate() const [, setValidExploration] = useExploration() diff --git a/src/components/Options/ReportOptions/ReportOptions.tsx b/src/components/Options/ReportOptions/ReportOptions.tsx index f24ed8ad6..5853217c6 100644 --- a/src/components/Options/ReportOptions/ReportOptions.tsx +++ b/src/components/Options/ReportOptions/ReportOptions.tsx @@ -6,21 +6,20 @@ import { FluidState, FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' import { DateTime } from 'luxon' import { Dataload, TimePeriod } from 'models' -import React, { Dispatch, useCallback, useEffect, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback, useEffect, useState } from 'react' import ConsumptionDataManager from 'services/consumption.service' -import { AppActionsTypes, AppStore } from 'store' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { updateProfile } from 'store/profile/profile.actions' import './reportOptions.scss' const ReportOptions = () => { const { t } = useI18n() const client = useClient() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() const { global: { fluidStatus }, profile, - } = useSelector((state: AppStore) => state.ecolyo) + } = useAppSelector(state => state.ecolyo) const [maxDayData, setLastSemesterMaxDay] = useState<Dataload | null>(null) const updateProfileReport = async (value: boolean) => { diff --git a/src/components/Options/Unsubscribe/UnSubscribe.tsx b/src/components/Options/Unsubscribe/UnSubscribe.tsx index 1ce8beeaa..ed3637f26 100644 --- a/src/components/Options/Unsubscribe/UnSubscribe.tsx +++ b/src/components/Options/Unsubscribe/UnSubscribe.tsx @@ -5,21 +5,20 @@ import Content from 'components/Content/Content' import CozyBar from 'components/Header/CozyBar' import Header from 'components/Header/Header' import { useI18n } from 'cozy-ui/transpiled/react/I18n' -import React, { Dispatch, useState } from 'react' -import { useDispatch } from 'react-redux' +import React, { useState } from 'react' import { useNavigate } from 'react-router-dom' -import { AppActionsTypes } from 'store' +import { useAppDispatch } from 'store/hooks' import { updateProfile } from 'store/profile/profile.actions' import './unSubscribe.scss' const UnSubscribe = () => { + const { t } = useI18n() + const navigate = useNavigate() + const dispatch = useAppDispatch() const [headerHeight, setHeaderHeight] = useState<number>(0) const defineHeaderHeight = (height: number) => { setHeaderHeight(height) } - const { t } = useI18n() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() - const navigate = useNavigate() const unSubscribe = async () => { dispatch(updateProfile({ sendAnalysisNotification: false })) navigate('/consumption') diff --git a/src/components/ProfileType/ProfileTypeFinished.tsx b/src/components/ProfileType/ProfileTypeFinished.tsx index c47d58667..e8a2b8fcf 100644 --- a/src/components/ProfileType/ProfileTypeFinished.tsx +++ b/src/components/ProfileType/ProfileTypeFinished.tsx @@ -12,26 +12,25 @@ import { UserExplorationID } from 'enum/userExploration.enum' import { DateTime } from 'luxon' import { TimePeriod } from 'models' import { ProfileType } from 'models/profileType.model' -import React, { Dispatch, useEffect, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useEffect, useState } from 'react' import { useLocation, useNavigate } from 'react-router-dom' import ProfileTypeService from 'services/profileType.service' import ProfileTypeEntityService from 'services/profileTypeEntity.service' import UsageEventService from 'services/usageEvent.service' -import { AppActionsTypes, AppStore } from 'store' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { updateProfile } from 'store/profile/profile.actions' import { setProfileType } from 'store/profileType/profileType.slice' const ProfileTypeFinished = ({ profileType }: { profileType: ProfileType }) => { const { t } = useI18n() + const client = useClient() const location = useLocation() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() const navigate = useNavigate() - const client = useClient() + const dispatch = useAppDispatch() const { challenge: { currentChallenge }, profile, - } = useSelector((state: AppStore) => state.ecolyo) + } = useAppSelector(state => state.ecolyo) const handleClick = () => { if (location?.pathname === '/ecogesture-form') { navigate('/ecogesture-selection') diff --git a/src/components/ProfileType/ProfileTypeFormMultiChoice.tsx b/src/components/ProfileType/ProfileTypeFormMultiChoice.tsx index 95c275ca6..ffa3adc18 100644 --- a/src/components/ProfileType/ProfileTypeFormMultiChoice.tsx +++ b/src/components/ProfileType/ProfileTypeFormMultiChoice.tsx @@ -7,8 +7,7 @@ import { ProfileTypeStepForm } from 'enum/profileType.enum' import { remove } from 'lodash' import { ProfileType, ProfileTypeAnswer } from 'models/profileType.model' import React, { useCallback, useEffect, useState } from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' interface ProfileTypeFormMultiChoiceProps { step: ProfileTypeStepForm @@ -28,8 +27,8 @@ const ProfileTypeFormMultiChoice = ({ setPreviousStep, }: ProfileTypeFormMultiChoiceProps) => { const { t } = useI18n() - const { isProfileTypeCompleted } = useSelector( - (state: AppStore) => state.ecolyo.profile + const { isProfileTypeCompleted } = useAppSelector( + state => state.ecolyo.profile ) const [answer, setAnswer] = useState<string[]>([]) diff --git a/src/components/ProfileType/ProfileTypeFormNumber.tsx b/src/components/ProfileType/ProfileTypeFormNumber.tsx index 2b700fedc..bcbee75fd 100644 --- a/src/components/ProfileType/ProfileTypeFormNumber.tsx +++ b/src/components/ProfileType/ProfileTypeFormNumber.tsx @@ -9,8 +9,7 @@ import { ProfileTypeValues, } from 'models/profileType.model' import React, { useCallback, useEffect, useState } from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' interface ProfileTypeFormNumberProps { step: ProfileTypeStepForm @@ -30,8 +29,8 @@ const ProfileTypeFormNumber = ({ setPreviousStep, }: ProfileTypeFormNumberProps) => { const { t } = useI18n() - const { isProfileTypeCompleted } = useSelector( - (state: AppStore) => state.ecolyo.profile + const { isProfileTypeCompleted } = useAppSelector( + state => state.ecolyo.profile ) const [answer, setAnswer] = useState<ProfileTypeValues>('') diff --git a/src/components/ProfileType/ProfileTypeFormNumberSelection.tsx b/src/components/ProfileType/ProfileTypeFormNumberSelection.tsx index 034e8bde4..16734ea06 100644 --- a/src/components/ProfileType/ProfileTypeFormNumberSelection.tsx +++ b/src/components/ProfileType/ProfileTypeFormNumberSelection.tsx @@ -10,8 +10,7 @@ import { ProfileTypeValues, } from 'models/profileType.model' import React, { useCallback, useEffect, useState } from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' interface ProfileTypeFormNumberSelectionProps { step: ProfileTypeStepForm @@ -31,8 +30,8 @@ const ProfileTypeFormNumberSelection = ({ setPreviousStep, }: ProfileTypeFormNumberSelectionProps) => { const { t } = useI18n() - const { isProfileTypeCompleted } = useSelector( - (state: AppStore) => state.ecolyo.profile + const { isProfileTypeCompleted } = useAppSelector( + state => state.ecolyo.profile ) const [answer, setAnswer] = useState<ProfileTypeValues>('') const [index, setIndex] = useState<number>(0) diff --git a/src/components/ProfileType/ProfileTypeFormSingleChoice.tsx b/src/components/ProfileType/ProfileTypeFormSingleChoice.tsx index 85f3a0e17..d48e92b70 100644 --- a/src/components/ProfileType/ProfileTypeFormSingleChoice.tsx +++ b/src/components/ProfileType/ProfileTypeFormSingleChoice.tsx @@ -11,8 +11,7 @@ import { ProfileTypeValues, } from 'models/profileType.model' import React, { useCallback, useEffect, useState } from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' interface ProfileTypeFormSingleChoiceProps { step: ProfileTypeStepForm @@ -35,7 +34,7 @@ const ProfileTypeFormSingleChoice = ({ const { profile: { isProfileTypeCompleted, isProfileEcogestureCompleted }, profileEcogesture, - } = useSelector((state: AppStore) => state.ecolyo) + } = useAppSelector(state => state.ecolyo) const [answer, setAnswer] = useState<ProfileTypeValues>('') const handlePrevious = useCallback(() => { diff --git a/src/components/ProfileType/ProfileTypeView.tsx b/src/components/ProfileType/ProfileTypeView.tsx index f5c18d2bc..5fdbf3506 100644 --- a/src/components/ProfileType/ProfileTypeView.tsx +++ b/src/components/ProfileType/ProfileTypeView.tsx @@ -27,14 +27,13 @@ import { import { DateTime } from 'luxon' import { ProfileType, ProfileTypeAnswer } from 'models' import React, { useCallback, useEffect, useState } from 'react' -import { useSelector } from 'react-redux' import ProfileTypeFormService from 'services/profileTypeForm.service' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import ProfileTypeFormDateSelection from './ProfileTypeFormDateSelection' const ProfileTypeView = () => { - const { profile, profileType, profileEcogesture } = useSelector( - (state: AppStore) => state.ecolyo + const { profile, profileType, profileEcogesture } = useAppSelector( + state => state.ecolyo ) const [headerHeight, setHeaderHeight] = useState<number>(0) diff --git a/src/components/Quiz/QuizBegin.tsx b/src/components/Quiz/QuizBegin.tsx index 5ba3ea40a..634f32421 100644 --- a/src/components/Quiz/QuizBegin.tsx +++ b/src/components/Quiz/QuizBegin.tsx @@ -6,17 +6,16 @@ import { Client, useClient } from 'cozy-client' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { UserChallengeUpdateFlag } from 'enum/userChallenge.enum' import { UserChallenge } from 'models' -import React, { Dispatch } from 'react' -import { useDispatch } from 'react-redux' +import React from 'react' import ChallengeService from 'services/challenge.service' -import { AppActionsTypes } from 'store' import { updateUserChallengeList } from 'store/challenge/challenge.slice' +import { useAppDispatch } from 'store/hooks' import './quizBegin.scss' const QuizBegin = ({ userChallenge }: { userChallenge: UserChallenge }) => { const client: Client = useClient() const { t } = useI18n() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() const launchQuiz = async () => { const challengeService: ChallengeService = new ChallengeService(client) const userChallengeUpdated: UserChallenge = diff --git a/src/components/Quiz/QuizCustomQuestionContent.tsx b/src/components/Quiz/QuizCustomQuestionContent.tsx index 1c4efae26..21a37a7a6 100644 --- a/src/components/Quiz/QuizCustomQuestionContent.tsx +++ b/src/components/Quiz/QuizCustomQuestionContent.tsx @@ -8,13 +8,12 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { UsageEventType } from 'enum/usageEvent.enum' import { UserChallengeUpdateFlag } from 'enum/userChallenge.enum' import { Answer, QuestionEntity, UserChallenge, UserQuiz } from 'models' -import React, { Dispatch, useState } from 'react' -import { useDispatch } from 'react-redux' +import React, { useState } from 'react' 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.slice' +import { useAppDispatch } from 'store/hooks' import './quizQuestion.scss' interface QuizCustomQuestionContent { @@ -31,12 +30,12 @@ const QuizCustomQuestionContent = ({ isLoading, }: QuizCustomQuestionContent) => { const { t } = useI18n() + const client: Client = useClient() + const dispatch = useAppDispatch() + const [userChoice, setUserChoice] = useState<string>('') const [openModal, setOpenModal] = useState<boolean>(false) const [answerIndex, setAnswerIndex] = useState<number>(0) - const client: Client = useClient() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() - const quizService: QuizService = new QuizService(client) const challengeService: ChallengeService = new ChallengeService(client) diff --git a/src/components/Quiz/QuizFinish.tsx b/src/components/Quiz/QuizFinish.tsx index e3abbcf0a..65eac3431 100644 --- a/src/components/Quiz/QuizFinish.tsx +++ b/src/components/Quiz/QuizFinish.tsx @@ -5,19 +5,18 @@ import { Client, useClient } from 'cozy-client' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { UserChallengeUpdateFlag } from 'enum/userChallenge.enum' import { UserChallenge } from 'models' -import React, { Dispatch, useCallback, useMemo } from 'react' -import { useDispatch } from 'react-redux' +import React, { useCallback, useMemo } from 'react' import { useNavigate } from 'react-router-dom' import ChallengeService from 'services/challenge.service' -import { AppActionsTypes } from 'store' import { updateUserChallengeList } from 'store/challenge/challenge.slice' +import { useAppDispatch } from 'store/hooks' import './quizFinish.scss' const QuizFinish = ({ userChallenge }: { userChallenge: UserChallenge }) => { - const client: Client = useClient() const { t } = useI18n() + const client: Client = useClient() const navigate = useNavigate() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() const challengeService: ChallengeService = useMemo( () => new ChallengeService(client), [client] diff --git a/src/components/Quiz/QuizQuestion.spec.tsx b/src/components/Quiz/QuizQuestion.spec.tsx index 8fade6b52..5f145d4dc 100644 --- a/src/components/Quiz/QuizQuestion.spec.tsx +++ b/src/components/Quiz/QuizQuestion.spec.tsx @@ -2,7 +2,6 @@ import { UserQuestionState } from 'enum/userQuiz.enum' import { mount } from 'enzyme' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import { createMockEcolyoStore } from '../../../tests/__mocks__/store' import { userChallengeData } from '../../../tests/__mocks__/userChallengeData.mock' @@ -37,13 +36,11 @@ jest.mock('components/Quiz/QuizCustomQuestionContent', () => () => ( jest.mock('components/Quiz/QuizQuestionContent', () => () => ( <div id="quizquestioncontent" /> )) -const useSelectorSpy = jest.spyOn(reactRedux, 'useSelector') describe('QuizQuestion component', () => { const store = createMockEcolyoStore() beforeEach(() => { store.clearActions() - useSelectorSpy.mockClear() }) it('should be rendered correctly with question', () => { diff --git a/src/components/Quiz/QuizQuestion.tsx b/src/components/Quiz/QuizQuestion.tsx index b433733c0..fb4704664 100644 --- a/src/components/Quiz/QuizQuestion.tsx +++ b/src/components/Quiz/QuizQuestion.tsx @@ -3,10 +3,9 @@ import QuizQuestionContent from 'components/Quiz/QuizQuestionContent' import { Client, useClient } from 'cozy-client' import { QuestionEntity, UserChallenge } from 'models' import React, { useEffect, useState } from 'react' -import { useSelector } from 'react-redux' import { useNavigate } from 'react-router-dom' import QuizService from 'services/quiz.service' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import './quizQuestion.scss' const QuizQuestion = ({ userChallenge }: { userChallenge: UserChallenge }) => { @@ -20,7 +19,7 @@ const QuizQuestion = ({ userChallenge }: { userChallenge: UserChallenge }) => { const [customQuestionLoading, setCustomQuestionLoading] = useState<boolean>(false) const client: Client = useClient() - const { fluidTypes } = useSelector((state: AppStore) => state.ecolyo.global) + const { fluidTypes } = useAppSelector(state => state.ecolyo.global) const navigate = useNavigate() const goBack = () => { diff --git a/src/components/Quiz/QuizQuestionContent.tsx b/src/components/Quiz/QuizQuestionContent.tsx index 3007c8cc2..28842fe8c 100644 --- a/src/components/Quiz/QuizQuestionContent.tsx +++ b/src/components/Quiz/QuizQuestionContent.tsx @@ -7,11 +7,10 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { UserChallengeUpdateFlag } from 'enum/userChallenge.enum' import { Answer, UserChallenge, UserQuiz } from 'models' import React, { Dispatch, SetStateAction, useCallback, useState } from 'react' -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.slice' +import { useAppDispatch } from 'store/hooks' import './quizQuestion.scss' interface QuizQuestionContent { @@ -26,6 +25,8 @@ const QuizQuestionContent = ({ goBack, }: QuizQuestionContent) => { const { t } = useI18n() + const client: Client = useClient() + const dispatch = useAppDispatch() const questionIndexLocked = userChallenge.quiz.questions.findIndex( answer => answer.result == 0 ) @@ -35,9 +36,6 @@ const QuizQuestionContent = ({ const [questionIndex, setQuestionIndex] = useState<number>(questionIndexLocked) - const client: Client = useClient() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() - const quizService: QuizService = new QuizService(client) const challengeService: ChallengeService = new ChallengeService(client) diff --git a/src/components/Quiz/QuizView.spec.tsx b/src/components/Quiz/QuizView.spec.tsx index 9d3ccb2c8..afa4acf44 100644 --- a/src/components/Quiz/QuizView.spec.tsx +++ b/src/components/Quiz/QuizView.spec.tsx @@ -3,7 +3,6 @@ import { UserQuizState } from 'enum/userQuiz.enum' import { mount } from 'enzyme' import { ChallengeState } from 'models' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import { challengeStateData } from '../../../tests/__mocks__/challengeStateData.mock' import { @@ -29,10 +28,8 @@ jest.mock('react-router-dom', () => ({ jest.mock('components/Header/CozyBar', () => 'mock-cozybar') jest.mock('components/Header/Header', () => 'mock-header') jest.mock('components/Content/Content', () => 'mock-content') -const mockUseSelector = jest.spyOn(reactRedux, 'useSelector') describe('QuizView component', () => { - const store = createMockEcolyoStore() it('should be rendered with QuizBegin component when quiz state = unlocked', () => { const updatedUserChallenge = { ...userChallengeData[0], diff --git a/src/components/Quiz/QuizView.tsx b/src/components/Quiz/QuizView.tsx index 4888e43e1..05caa6e91 100644 --- a/src/components/Quiz/QuizView.tsx +++ b/src/components/Quiz/QuizView.tsx @@ -4,17 +4,14 @@ import Header from 'components/Header/Header' import { UserQuizState } from 'enum/userQuiz.enum' import { UserChallenge } from 'models' import React, { useCallback, useState } from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import QuizBegin from './QuizBegin' import QuizFinish from './QuizFinish' import QuizQuestion from './QuizQuestion' const QuizView = () => { + const { currentChallenge } = useAppSelector(state => state.ecolyo.challenge) const [headerHeight, setHeaderHeight] = useState<number>(0) - const { currentChallenge } = useSelector( - (state: AppStore) => state.ecolyo.challenge - ) const defineHeaderHeight = useCallback((height: number) => { setHeaderHeight(height) diff --git a/src/components/Splash/SplashRoot.spec.tsx b/src/components/Splash/SplashRoot.spec.tsx index 66bceb670..c07b9314a 100644 --- a/src/components/Splash/SplashRoot.spec.tsx +++ b/src/components/Splash/SplashRoot.spec.tsx @@ -1,7 +1,6 @@ import { mount } from 'enzyme' import toJson from 'enzyme-to-json' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import { createMockEcolyoStore } from '../../../tests/__mocks__/store' import SplashRoot from './SplashRoot' @@ -17,12 +16,10 @@ jest.mock('cozy-ui/transpiled/react/I18n', () => ({ t: (str: string) => str, })), })) -const mockUseDispatch = jest.spyOn(reactRedux, 'useDispatch') describe('SplashRoot component', () => { it('should be rendered correctly', async () => { const store = createMockEcolyoStore() - mockUseDispatch.mockReturnValue(jest.fn()) const component = mount( <Provider store={store}> <SplashRoot>children</SplashRoot> diff --git a/src/components/Splash/SplashRoot.tsx b/src/components/Splash/SplashRoot.tsx index 8dc9e0c78..6d535d224 100644 --- a/src/components/Splash/SplashRoot.tsx +++ b/src/components/Splash/SplashRoot.tsx @@ -38,7 +38,6 @@ import InitializationService from 'services/initialization.service' import PartnersInfoService from 'services/partnersInfo.service' import ProfileTypeEntityService from 'services/profileTypeEntity.service' import UsageEventService from 'services/usageEvent.service' -import { AppActionsTypes } from 'store' import { setAnalysisMonth } from 'store/analysis/analysis.slice' import { setChallengeConsumption, @@ -59,6 +58,7 @@ import { openPartnersModal, setCustomPopup } from 'store/modal/modal.slice' import { updateProfile } from 'store/profile/profile.actions' import { updateProfileEcogestureSuccess } from 'store/profileEcogesture/profileEcogesture.actions' import { setProfileType } from 'store/profileType/profileType.slice' +import { AppActionsTypes } from 'store/store' import { logDuration } from 'utils/duration' import logApp from 'utils/logger' import { getTodayDate } from 'utils/utils' diff --git a/src/components/Terms/DataShareConsentContent.tsx b/src/components/Terms/DataShareConsentContent.tsx index 8a561a265..8ea9515c0 100644 --- a/src/components/Terms/DataShareConsentContent.tsx +++ b/src/components/Terms/DataShareConsentContent.tsx @@ -1,15 +1,12 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n' import React from 'react' -import { useSelector } from 'react-redux' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import { decoreText } from 'utils/decoreText' import './termsView.scss' const DataShareConsentContent = () => { const { t } = useI18n() - const { isFirstConnection } = useSelector( - (state: AppStore) => state.ecolyo.profile - ) + const { isFirstConnection } = useAppSelector(state => state.ecolyo.profile) return ( <div className="dataShare-content-root"> diff --git a/src/components/Terms/TermsView.spec.tsx b/src/components/Terms/TermsView.spec.tsx index 1ed51854d..a987bed91 100644 --- a/src/components/Terms/TermsView.spec.tsx +++ b/src/components/Terms/TermsView.spec.tsx @@ -2,8 +2,8 @@ import { Button } from '@material-ui/core' import { mount } from 'enzyme' import toJson from 'enzyme-to-json' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' +import * as storeHooks from 'store/hooks' import { createMockEcolyoStore } from '../../../tests/__mocks__/store' import { mockUpToDateTerm } from '../../../tests/__mocks__/termsData.mock' import TermsView from './TermsView' @@ -30,7 +30,7 @@ jest.mock('react-router-dom', () => ({ useNavigate: () => mockedNavigate, })) -const mockUseDispatch = jest.spyOn(reactRedux, 'useDispatch') +const mockAppDispatch = jest.spyOn(storeHooks, 'useAppDispatch') const mockUpdateProfile = jest.fn() jest.mock('services/profile.service', () => { @@ -63,7 +63,7 @@ describe('TermsView component', () => { expect(wrapper.find('input').at(1).props().checked).toEqual(true) expect(wrapper.find(Button).first().hasClass('disabled')).toBeFalsy() wrapper.find(Button).first().simulate('click') - expect(mockUseDispatch).toHaveBeenCalledTimes(3) + expect(mockAppDispatch).toHaveBeenCalledTimes(3) }) it('should be rendered correctly', () => { const component = mount( diff --git a/src/components/Terms/TermsView.tsx b/src/components/Terms/TermsView.tsx index 58e2860a0..6e79eafa7 100644 --- a/src/components/Terms/TermsView.tsx +++ b/src/components/Terms/TermsView.tsx @@ -2,12 +2,11 @@ import { Button } from '@material-ui/core' import classNames from 'classnames' import { useClient } from 'cozy-client' import { useI18n } from 'cozy-ui/transpiled/react/I18n' -import React, { Dispatch, useCallback, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useCallback, useState } from 'react' import { useNavigate } from 'react-router-dom' import TermsService from 'services/terms.service' -import { AppActionsTypes, AppStore } from 'store' import { updateTermsStatus } from 'store/global/global.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { decoreText } from 'utils/decoreText' import CGUModal from './CGUModal' import DataShareConsentContent from './DataShareConsentContent' @@ -18,9 +17,9 @@ import './termsView.scss' const TermsView = () => { const { t } = useI18n() const client = useClient() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() - const { termsStatus } = useSelector((state: AppStore) => state.ecolyo.global) const navigate = useNavigate() + const dispatch = useAppDispatch() + const { termsStatus } = useAppSelector(state => state.ecolyo.global) const [GCUValidation, setGCUValidation] = useState<boolean>(false) const [dataConsentValidation, setDataConsentValidation] = useState<boolean>(false) diff --git a/src/components/TimeStepSelector/TimeStepSelector.spec.tsx b/src/components/TimeStepSelector/TimeStepSelector.spec.tsx index 5961840a9..216566537 100644 --- a/src/components/TimeStepSelector/TimeStepSelector.spec.tsx +++ b/src/components/TimeStepSelector/TimeStepSelector.spec.tsx @@ -6,7 +6,6 @@ import { mount } from 'enzyme' import toJson from 'enzyme-to-json' import { DateTime } from 'luxon' import React from 'react' -import * as reactRedux from 'react-redux' import { Provider } from 'react-redux' import UsageEventService from 'services/usageEvent.service' import * as chartActions from 'store/chart/chart.slice' @@ -22,16 +21,12 @@ jest.mock('services/usageEvent.service') const mockAddEvent = jest.fn() UsageEventService.addEvent = mockAddEvent -const useSelectorSpy = jest.spyOn(reactRedux, 'useSelector') -const useDispatchSpy = jest.spyOn(reactRedux, 'useDispatch') const setCurrentTimeStepSpy = jest.spyOn(chartActions, 'setCurrentTimeStep') const setCurrentIndexSpy = jest.spyOn(chartActions, 'setCurrentIndex') const setSelectedDateSpy = jest.spyOn(chartActions, 'setSelectedDate') describe('TimeStepSelector component', () => { beforeEach(() => { - useSelectorSpy.mockClear() - useDispatchSpy.mockClear() setCurrentTimeStepSpy.mockClear() }) diff --git a/src/components/TimeStepSelector/TimeStepSelector.tsx b/src/components/TimeStepSelector/TimeStepSelector.tsx index 6a72bc1eb..ff58ddf61 100644 --- a/src/components/TimeStepSelector/TimeStepSelector.tsx +++ b/src/components/TimeStepSelector/TimeStepSelector.tsx @@ -5,25 +5,24 @@ import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' import { UsageEventType } from 'enum/usageEvent.enum' import { DateTime } from 'luxon' -import React, { Dispatch } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React from 'react' import DateChartService from 'services/dateChart.service' import UsageEventService from 'services/usageEvent.service' -import { AppActionsTypes, AppStore } from 'store' import { setCurrentIndex, setCurrentTimeStep, setSelectedDate, } from 'store/chart/chart.slice' +import { useAppDispatch, useAppSelector } from 'store/hooks' import { getFluidName } from 'utils/utils' import './timeStepSelector.scss' const TimeStepSelector = ({ fluidType }: { fluidType: FluidType }) => { - const { currentTimeStep, selectedDate } = useSelector( - (state: AppStore) => state.ecolyo.chart - ) const { t } = useI18n() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() + const { currentTimeStep, selectedDate } = useAppSelector( + state => state.ecolyo.chart + ) const dateChartService = new DateChartService() const client = useClient() const timeStepElecArray: TimeStep[] = [ diff --git a/src/components/TotalConsumption/TotalConsumption.tsx b/src/components/TotalConsumption/TotalConsumption.tsx index 032b6a78c..6f82323b5 100644 --- a/src/components/TotalConsumption/TotalConsumption.tsx +++ b/src/components/TotalConsumption/TotalConsumption.tsx @@ -6,16 +6,15 @@ import { FluidType } from 'enum/fluid.enum' import { TimeStep } from 'enum/timeStep.enum' import { Dataload } from 'models' import React, { useCallback, useEffect, useState } from 'react' -import { useSelector } from 'react-redux' import ConsumptionService from 'services/consumption.service' import ConverterService from 'services/converter.service' -import { AppStore } from 'store' +import { useAppSelector } from 'store/hooks' import { formatNumberValues } from 'utils/utils' import './totalConsumption.scss' const TotalConsumption = ({ fluidType }: { fluidType: FluidType }) => { - const { currentTimeStep, showCompare, currentDatachart } = useSelector( - (state: AppStore) => state.ecolyo.chart + const { currentTimeStep, showCompare, currentDatachart } = useAppSelector( + state => state.ecolyo.chart ) const client = useClient() const [totalValue, setTotalValue] = useState<string>('-----') diff --git a/src/components/WelcomeModal/WelcomeModal.tsx b/src/components/WelcomeModal/WelcomeModal.tsx index a4fcf032d..3e8fdf83b 100644 --- a/src/components/WelcomeModal/WelcomeModal.tsx +++ b/src/components/WelcomeModal/WelcomeModal.tsx @@ -5,11 +5,10 @@ import useUserInstanceSettings from 'components/Hooks/useUserInstanceSettings' import { useClient } from 'cozy-client' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import Icon from 'cozy-ui/transpiled/react/Icon' -import React, { Dispatch, useCallback } from 'react' -import { useDispatch } from 'react-redux' +import React, { useCallback } from 'react' import EnvironmentService from 'services/environment.service' import MailService from 'services/mail.service' -import { AppActionsTypes } from 'store' +import { useAppDispatch } from 'store/hooks' import { updateProfile } from 'store/profile/profile.actions' import './welcomeModal.scss' @@ -23,7 +22,7 @@ interface WelcomeModalProps { const WelcomeModal = ({ open }: WelcomeModalProps) => { const { t } = useI18n() const client = useClient() - const dispatch = useDispatch<Dispatch<AppActionsTypes>>() + const dispatch = useAppDispatch() const { data: instanceSettings } = useUserInstanceSettings() const setWelcomeModalViewed = useCallback(async () => { diff --git a/src/store/hooks.ts b/src/store/hooks.ts new file mode 100644 index 000000000..0bca4f1d3 --- /dev/null +++ b/src/store/hooks.ts @@ -0,0 +1,9 @@ +import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux' +import { AppDispatch, AppStore } from './store' + +// Typed hooks +// https://redux.js.org/tutorials/typescript-quick-start#define-typed-hooks + +export const useAppDispatch: () => AppDispatch = useDispatch +// TODO maybe use AppEcolyoStore +export const useAppSelector: TypedUseSelectorHook<AppStore> = useSelector diff --git a/src/store/profile/profile.actions.ts b/src/store/profile/profile.actions.ts index 4d3808f88..a05d598b5 100644 --- a/src/store/profile/profile.actions.ts +++ b/src/store/profile/profile.actions.ts @@ -2,7 +2,7 @@ import { Client } from 'cozy-client' import { Profile } from 'models' import { Dispatch } from 'react' import ProfileService from 'services/profile.service' -import { AppStore, defaultAction } from 'store' +import { AppStore, defaultAction } from 'store/store' export const UPDATE_PROFILE = 'UPDATE_PROFILE' diff --git a/src/store/profile/profile.reducer.spec.ts b/src/store/profile/profile.reducer.spec.ts index ed3adbd7f..7f3ef296d 100644 --- a/src/store/profile/profile.reducer.spec.ts +++ b/src/store/profile/profile.reducer.spec.ts @@ -1,4 +1,4 @@ -import { defaultAction } from 'store' +import { defaultAction } from 'store/store' import { mockProfileState } from '../../../tests/__mocks__/store' import { UPDATE_PROFILE } from './profile.actions' import { profileReducer } from './profile.reducer' diff --git a/src/store/profileEcogesture/profileEcogesture.actions.ts b/src/store/profileEcogesture/profileEcogesture.actions.ts index 771405465..6c170945f 100644 --- a/src/store/profileEcogesture/profileEcogesture.actions.ts +++ b/src/store/profileEcogesture/profileEcogesture.actions.ts @@ -3,7 +3,7 @@ import { PROFILEECOGESTURE_DOCTYPE } from 'doctypes/com-grandlyon-ecolyo-profile import { ProfileEcogesture } from 'models/profileEcogesture.model' import { Dispatch } from 'react' import ProfileEcogestureService from 'services/profileEcogesture.service' -import { AppStore, defaultAction } from 'store' +import { AppStore, defaultAction } from 'store/store' // TODO never used ? export const CREATE_NEW_PROFILE_ECOGESTURE = 'CREATE_NEW_PROFILE_ECOGESTURE' diff --git a/src/store/profileEcogesture/profileEcogesture.reducer.spec.ts b/src/store/profileEcogesture/profileEcogesture.reducer.spec.ts index 1158a9881..fad1d9947 100644 --- a/src/store/profileEcogesture/profileEcogesture.reducer.spec.ts +++ b/src/store/profileEcogesture/profileEcogesture.reducer.spec.ts @@ -1,4 +1,4 @@ -import { defaultAction } from 'store' +import { defaultAction } from 'store/store' import { mockProfileEcogesture, mockProfileEcogestureUpdated, diff --git a/src/store/index.ts b/src/store/store.ts similarity index 91% rename from src/store/index.ts rename to src/store/store.ts index 9bcaeb06d..0e40d9fa0 100644 --- a/src/store/index.ts +++ b/src/store/store.ts @@ -119,3 +119,8 @@ const configureStore = (client: Client, persistedState: any) => { return store } export default configureStore + +// Infer the `RootState` and `AppDispatch` types from the store itself +export type AppEcolyoState = ReturnType<typeof ecolyoReducer> // TODO should use "ReturnType<typeof store.getState>" when using configureStore +export type AppStores = ReturnType<typeof configureStore> +export type AppDispatch = AppStores['dispatch'] diff --git a/src/targets/browser/index.tsx b/src/targets/browser/index.tsx index d8800d9b4..54ba4fbb6 100644 --- a/src/targets/browser/index.tsx +++ b/src/targets/browser/index.tsx @@ -20,7 +20,7 @@ import { render } from 'react-dom' import { Provider } from 'react-redux' import { HashRouter } from 'react-router-dom' import EnvironmentService from 'services/environment.service' -import configureStore from 'store' +import configureStore from 'store/store' import cozyBar from 'utils/cozyBar' import logApp from 'utils/logger' import MatomoTracker from 'utils/matomoTracker' diff --git a/tests/__mocks__/store/store.ts b/tests/__mocks__/store/store.ts index 7f39d022f..edcf8340d 100644 --- a/tests/__mocks__/store/store.ts +++ b/tests/__mocks__/store/store.ts @@ -1,6 +1,6 @@ import configureStore from 'redux-mock-store' import thunkMiddleware from 'redux-thunk' -import { AppActionsTypes, EcolyoState, MockEcolyoState } from 'store' +import { AppActionsTypes, EcolyoState, MockEcolyoState } from 'store/store' import mockClient from '../client' import { mockProfileEcogesture } from '../profileEcogesture.mock' import { mockAnalysisState } from './analysis.state.mock' -- GitLab