From 2a76cb4695f1531a870847bf267c8bd082c98856 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Wed, 20 Dec 2023 19:52:42 +0700 Subject: [PATCH 01/83] Refactor configureStore --- packages/components/src/components/App.tsx | 6 +++++- packages/components/src/redux/store.ts | 23 ++++++++++++---------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/packages/components/src/components/App.tsx b/packages/components/src/components/App.tsx index 71cbe366f..ccf12fc07 100644 --- a/packages/components/src/components/App.tsx +++ b/packages/components/src/components/App.tsx @@ -9,8 +9,12 @@ import { SafeAreaView } from 'react-navigation' import SplashScreen from 'react-native-splash-screen' import { Platform } from 'react-native' import Orientation from 'react-native-orientation-locker' +import { config } from '../redux/config' -const { persistor, store } = configureStore() +const { persistor, store } = configureStore({ + key: 'primary', + secretKey: config.REDUX_ENCRYPT_KEY, +}) export default function App() { React.useEffect(() => { diff --git a/packages/components/src/redux/store.ts b/packages/components/src/redux/store.ts index 67c563b38..587970a0b 100644 --- a/packages/components/src/redux/store.ts +++ b/packages/components/src/redux/store.ts @@ -8,17 +8,21 @@ import { rootReducer } from './reducers' import { rootSaga } from './sagas' import { config } from './config' -const encryptor = encryptTransform({ - secretKey: config.REDUX_ENCRYPT_KEY, - onError(error) { - // Handle the error. - }, -}) +// Function to create an encryptor +const createEncryptor = (secretKey) => + encryptTransform({ + secretKey, + onError(error) { + // Handle the error. + }, + }) export const version = -1 -export function configureStore(key = 'primary') { - const persistConfig: PersistConfig = { +export function configureStore({ key, secretKey }) { + const encryptor = createEncryptor(secretKey) + + const persistConfig = { version, key, storage, @@ -27,12 +31,11 @@ export function configureStore(key = 'primary') { blacklist: [], transforms: [encryptor], } + const persistedReducer = persistReducer(persistConfig, rootReducer) const composeEnhancers = composeWithDevTools({ - // port: 8081, realtime: true, port: 8000, - hostname: '', // add your computer's IP }) const sagaMiddleware = createSagaMiddleware() From 72a7989331e02c175d7317a37eae8d333cf809ef Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Thu, 21 Dec 2023 12:08:46 +0700 Subject: [PATCH 02/83] Refactor configureStore 2 --- packages/components/src/components/App.tsx | 4 ++++ packages/components/src/redux/store.ts | 10 ++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/components/src/components/App.tsx b/packages/components/src/components/App.tsx index ccf12fc07..b76e94419 100644 --- a/packages/components/src/components/App.tsx +++ b/packages/components/src/components/App.tsx @@ -10,10 +10,14 @@ import SplashScreen from 'react-native-splash-screen' import { Platform } from 'react-native' import Orientation from 'react-native-orientation-locker' import { config } from '../redux/config' +import { rootReducer } from '../redux/reducers' +import { rootSaga } from '../redux/sagas' const { persistor, store } = configureStore({ key: 'primary', secretKey: config.REDUX_ENCRYPT_KEY, + rootReducer, + rootSaga, }) export default function App() { diff --git a/packages/components/src/redux/store.ts b/packages/components/src/redux/store.ts index 587970a0b..7c2bc318f 100644 --- a/packages/components/src/redux/store.ts +++ b/packages/components/src/redux/store.ts @@ -1,12 +1,10 @@ import { applyMiddleware, createStore } from 'redux' -import { PersistConfig, persistReducer, persistStore } from 'redux-persist' +import { persistReducer, persistStore } from 'redux-persist' import { encryptTransform } from 'redux-persist-transform-encrypt' import storage from 'redux-persist/lib/storage' import createSagaMiddleware from 'redux-saga' import { composeWithDevTools } from 'remote-redux-devtools' -import { rootReducer } from './reducers' -import { rootSaga } from './sagas' -import { config } from './config' +import { rootReducer as commonRootReducer } from './reducers' // Function to create an encryptor const createEncryptor = (secretKey) => @@ -19,7 +17,7 @@ const createEncryptor = (secretKey) => export const version = -1 -export function configureStore({ key, secretKey }) { +export function configureStore({ key, secretKey, rootReducer, rootSaga }) { const encryptor = createEncryptor(secretKey) const persistConfig = { @@ -48,4 +46,4 @@ export function configureStore({ key, secretKey }) { return { store, persistor } } -export type ReduxState = ReturnType +export type ReduxState = ReturnType From 884f09728deffadf309b4954851faa966f53fd3f Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 22 Dec 2023 12:21:10 +0700 Subject: [PATCH 03/83] Move original redux into common sub folder --- .../__tests__/redux/actions/appActions.test.ts | 5 +---- .../__tests__/redux/reducers/authReducer.test.ts | 4 ++-- packages/components/src/components/App.tsx | 4 ++-- .../src/components/common/Avatar/Avatar.tsx | 2 +- .../src/components/common/CalendarList.tsx | 2 +- .../components/src/components/common/DateBadge.tsx | 2 +- .../components/src/components/common/DayBadge.tsx | 2 +- .../src/components/common/LanguageSelect.tsx | 4 ++-- .../src/components/context/DisplayTextContext.tsx | 2 +- .../src/components/context/PredictionProvider.tsx | 2 +- .../src/components/context/ThemeContext.tsx | 2 +- packages/components/src/config/env.ts | 2 +- packages/components/src/hooks/useSelector.ts | 7 ++----- .../components/src/hooks/useTextToSpeechHook.ts | 2 +- .../components/src/prediction/PredictionState.ts | 1 - .../redux/{ => common}/actions/analyticsActions.ts | 0 .../redux/{ => common}/actions/answerActions.ts | 2 +- .../src/redux/{ => common}/actions/appActions.ts | 0 .../src/redux/{ => common}/actions/authActions.ts | 0 .../redux/{ => common}/actions/contentActions.ts | 2 +- .../src/redux/{ => common}/actions/index.ts | 0 .../{ => common}/actions/newPredictionAction.ts | 0 .../{ => common}/actions/predictionActions.ts | 2 +- .../src/redux/{ => common}/helpers/index.ts | 5 +---- .../{ => common}/reducers/analyticsReducer.ts | 5 +---- .../redux/{ => common}/reducers/answerReducer.ts | 4 ++-- .../src/redux/{ => common}/reducers/appReducer.ts | 2 +- .../src/redux/{ => common}/reducers/authReducer.ts | 0 .../redux/{ => common}/reducers/contentReducer.ts | 2 +- .../src/redux/{ => common}/reducers/index.ts | 2 ++ .../{ => common}/reducers/predictionReducer.ts | 3 +-- .../src/redux/{ => common}/sagas/analyticsSaga.ts | 4 ++-- .../src/redux/{ => common}/sagas/appSaga.ts | 9 ++++----- .../src/redux/{ => common}/sagas/authSaga.ts | 13 ++++++------- .../src/redux/{ => common}/sagas/contentSaga.ts | 4 ++-- .../src/redux/{ => common}/sagas/index.ts | 0 .../{ => common}/sagas/smartPredictionSaga.ts | 4 ++-- .../{ => common}/selectors/analyticsSelectors.ts | 2 +- .../{ => common}/selectors/answerSelectors.ts | 14 +++++++------- .../redux/{ => common}/selectors/appSelectors.ts | 2 +- .../redux/{ => common}/selectors/authSelectors.ts | 2 +- .../{ => common}/selectors/contentSelectors.ts | 3 +-- .../src/redux/{ => common}/selectors/index.ts | 0 .../src/redux/{ => common}/sync/index.ts | 5 +---- .../src/redux/{ => common}/types/index.ts | 7 ++++--- .../src/redux/{ => common}/types/types.ts | 13 ++++++------- packages/components/src/redux/store.ts | 3 --- packages/components/src/screens/ArticlesScreen.tsx | 2 +- .../src/screens/AvatarAndThemeScreen.tsx | 4 ++-- .../components/src/screens/ContactUsScreen.tsx | 2 +- packages/components/src/screens/DayScreen.tsx | 2 +- .../components/src/screens/EditProfileScreen.tsx | 4 ++-- .../components/src/screens/EncyclopediaScreen.tsx | 2 +- packages/components/src/screens/FindHelpScreen.tsx | 2 +- packages/components/src/screens/MainScreen.tsx | 4 ++-- .../components/src/screens/OnboardingScreen.tsx | 2 +- .../src/screens/PasswordRequestScreen.tsx | 4 ++-- packages/components/src/screens/ProfileScreen.tsx | 4 ++-- packages/components/src/screens/SettingsScreen.tsx | 4 ++-- packages/components/src/screens/SplashScreen.tsx | 4 ++-- .../components/src/screens/TutorialFirstScreen.tsx | 4 ++-- .../src/screens/TutorialSecondScreen.tsx | 4 ++-- packages/components/src/screens/VideosScreen.tsx | 2 +- .../components/src/screens/authScreen/Login.tsx | 2 +- .../components/src/screens/authScreen/SignUp.tsx | 2 +- .../src/screens/authScreen/signUp/AskLocation.tsx | 2 +- .../src/screens/dayScreen/DayCarousel.tsx | 4 ++-- .../src/screens/dayScreen/DayCarouselItem.tsx | 2 +- .../src/screens/dayScreen/DidYouKnowCard.tsx | 2 +- .../components/src/screens/dayScreen/NoteCard.tsx | 4 ++-- .../components/src/screens/dayScreen/QuizCard.tsx | 4 ++-- .../src/screens/dayScreen/SurveyCard.tsx | 4 ++-- .../src/screens/encyclopediaScreen/SearchBar.tsx | 2 +- .../src/screens/journeyScreen/FinalJourneyCard.tsx | 2 +- .../src/screens/journeyScreen/JourneyCard.tsx | 2 +- .../components/src/screens/mainScreen/Calendar.tsx | 2 +- .../src/screens/mainScreen/ColourButtons.tsx | 4 ++-- .../screens/mainScreen/wheelCarousel/Carousel.tsx | 2 +- .../mainScreen/wheelCarousel/CarouselElement.tsx | 2 +- .../mainScreen/wheelCarousel/CircularElement.tsx | 4 ++-- .../mainScreen/wheelCarousel/CircularSelection.tsx | 4 ++-- .../src/screens/profileScreen/CycleCard.tsx | 2 +- .../src/screens/settings/AboutScreen.tsx | 2 +- .../src/screens/settings/AccessScreen.tsx | 4 ++-- .../src/screens/settings/PrivacyScreen.tsx | 2 +- .../src/screens/settings/TermsScreen.tsx | 2 +- .../src/screens/tutorial/CalendarAssetDemo.tsx | 2 +- .../src/screens/tutorial/DayAssetDemo.tsx | 2 +- .../src/screens/tutorial/NoteAssetDemo.tsx | 2 +- 89 files changed, 127 insertions(+), 148 deletions(-) rename packages/components/src/redux/{ => common}/actions/analyticsActions.ts (100%) rename packages/components/src/redux/{ => common}/actions/answerActions.ts (97%) rename packages/components/src/redux/{ => common}/actions/appActions.ts (100%) rename packages/components/src/redux/{ => common}/actions/authActions.ts (100%) rename packages/components/src/redux/{ => common}/actions/contentActions.ts (98%) rename packages/components/src/redux/{ => common}/actions/index.ts (100%) rename packages/components/src/redux/{ => common}/actions/newPredictionAction.ts (100%) rename packages/components/src/redux/{ => common}/actions/predictionActions.ts (95%) rename packages/components/src/redux/{ => common}/helpers/index.ts (74%) rename packages/components/src/redux/{ => common}/reducers/analyticsReducer.ts (83%) rename packages/components/src/redux/{ => common}/reducers/answerReducer.ts (98%) rename packages/components/src/redux/{ => common}/reducers/appReducer.ts (98%) rename packages/components/src/redux/{ => common}/reducers/authReducer.ts (100%) rename packages/components/src/redux/{ => common}/reducers/contentReducer.ts (99%) rename packages/components/src/redux/{ => common}/reducers/index.ts (95%) rename packages/components/src/redux/{ => common}/reducers/predictionReducer.ts (90%) rename packages/components/src/redux/{ => common}/sagas/analyticsSaga.ts (92%) rename packages/components/src/redux/{ => common}/sagas/appSaga.ts (83%) rename packages/components/src/redux/{ => common}/sagas/authSaga.ts (95%) rename packages/components/src/redux/{ => common}/sagas/contentSaga.ts (98%) rename packages/components/src/redux/{ => common}/sagas/index.ts (100%) rename packages/components/src/redux/{ => common}/sagas/smartPredictionSaga.ts (93%) rename packages/components/src/redux/{ => common}/selectors/analyticsSelectors.ts (75%) rename packages/components/src/redux/{ => common}/selectors/answerSelectors.ts (93%) rename packages/components/src/redux/{ => common}/selectors/appSelectors.ts (97%) rename packages/components/src/redux/{ => common}/selectors/authSelectors.ts (86%) rename packages/components/src/redux/{ => common}/selectors/contentSelectors.ts (98%) rename packages/components/src/redux/{ => common}/selectors/index.ts (100%) rename packages/components/src/redux/{ => common}/sync/index.ts (91%) rename packages/components/src/redux/{ => common}/types/index.ts (63%) rename packages/components/src/redux/{ => common}/types/types.ts (82%) diff --git a/packages/components/__tests__/redux/actions/appActions.test.ts b/packages/components/__tests__/redux/actions/appActions.test.ts index 1aa04ccc3..799303958 100644 --- a/packages/components/__tests__/redux/actions/appActions.test.ts +++ b/packages/components/__tests__/redux/actions/appActions.test.ts @@ -1,10 +1,7 @@ -import { v4 as uuidv4 } from 'uuid' -import moment from 'moment' import configureStore from 'redux-mock-store' import _ from 'lodash' -import * as actions from '../../../src/redux/actions' -import { authReducer } from '../../../src/redux/reducers/authReducer' +import * as actions from '../../../src/redux/common/actions' const middleWares = [] const mockStore = configureStore(middleWares) diff --git a/packages/components/__tests__/redux/reducers/authReducer.test.ts b/packages/components/__tests__/redux/reducers/authReducer.test.ts index 9f4b0ec7f..e1c970e1f 100644 --- a/packages/components/__tests__/redux/reducers/authReducer.test.ts +++ b/packages/components/__tests__/redux/reducers/authReducer.test.ts @@ -3,8 +3,8 @@ import moment from 'moment' import configureStore from 'redux-mock-store' import _ from 'lodash' -import * as actions from '../../../src/redux/actions' -import { authReducer } from '../../../src/redux/reducers/authReducer' +import * as actions from '../../../src/redux/common/actions' +import { authReducer } from '../../../src/redux/common/reducers/authReducer' const middleWares = [] const mockStore = configureStore(middleWares) diff --git a/packages/components/src/components/App.tsx b/packages/components/src/components/App.tsx index b76e94419..a6bcf6192 100644 --- a/packages/components/src/components/App.tsx +++ b/packages/components/src/components/App.tsx @@ -10,8 +10,8 @@ import SplashScreen from 'react-native-splash-screen' import { Platform } from 'react-native' import Orientation from 'react-native-orientation-locker' import { config } from '../redux/config' -import { rootReducer } from '../redux/reducers' -import { rootSaga } from '../redux/sagas' +import { rootReducer } from '../redux/common/reducers' +import { rootSaga } from '../redux/common/sagas' const { persistor, store } = configureStore({ key: 'primary', diff --git a/packages/components/src/components/common/Avatar/Avatar.tsx b/packages/components/src/components/common/Avatar/Avatar.tsx index ff20b5b6a..1a6c77338 100644 --- a/packages/components/src/components/common/Avatar/Avatar.tsx +++ b/packages/components/src/components/common/Avatar/Avatar.tsx @@ -9,7 +9,7 @@ import styled from 'styled-components/native' import { Icon } from '../Icon' import { HeartAnimation } from './HeartAnimation' import { useSelector } from '../../../hooks/useSelector' -import * as selectors from '../../../redux/selectors/index' +import * as selectors from '../../../redux/common/selectors/index' import moment from 'moment' import { useDisplayText } from '../../context/DisplayTextContext' import { useTodayPrediction } from '../../context/PredictionProvider' diff --git a/packages/components/src/components/common/CalendarList.tsx b/packages/components/src/components/common/CalendarList.tsx index 471c8bb35..325149f7c 100644 --- a/packages/components/src/components/common/CalendarList.tsx +++ b/packages/components/src/components/common/CalendarList.tsx @@ -4,7 +4,7 @@ import { CalendarList as DefaultCalendarList, LocaleConfig } from 'react-native- import momentTimezone from 'moment-timezone' import { assets } from '../../assets/index' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' import { calendarTranslations } from '@oky/core' LocaleConfig.locales = { diff --git a/packages/components/src/components/common/DateBadge.tsx b/packages/components/src/components/common/DateBadge.tsx index 40d6783ce..0a26432f2 100644 --- a/packages/components/src/components/common/DateBadge.tsx +++ b/packages/components/src/components/common/DateBadge.tsx @@ -7,7 +7,7 @@ import { TouchableOpacity } from 'react-native' import _ from 'lodash' import moment from 'moment' import { useSelector } from 'react-redux' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' import { useTodayPrediction, useActualCurrentStartDateSelector, diff --git a/packages/components/src/components/common/DayBadge.tsx b/packages/components/src/components/common/DayBadge.tsx index 5f1eb78df..30deb93c9 100644 --- a/packages/components/src/components/common/DayBadge.tsx +++ b/packages/components/src/components/common/DayBadge.tsx @@ -5,7 +5,7 @@ import { TextWithoutTranslation, Text } from './Text' import _ from 'lodash' import moment from 'moment' import { useSelector } from 'react-redux' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' import { useTodayPrediction, useActualCurrentStartDateSelector, diff --git a/packages/components/src/components/common/LanguageSelect.tsx b/packages/components/src/components/common/LanguageSelect.tsx index 24f91470a..4a4290b21 100644 --- a/packages/components/src/components/common/LanguageSelect.tsx +++ b/packages/components/src/components/common/LanguageSelect.tsx @@ -1,8 +1,8 @@ import React from 'react' import styled from 'styled-components/native' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/selectors' -import * as actions from '../../redux/actions/index' +import * as selectors from '../../redux/common/selectors' +import * as actions from '../../redux/common/actions/index' import { useDispatch } from 'react-redux' import { PrimaryButton } from './buttons/PrimaryButton' diff --git a/packages/components/src/components/context/DisplayTextContext.tsx b/packages/components/src/components/context/DisplayTextContext.tsx index 028c94a75..dff4b43b4 100644 --- a/packages/components/src/components/context/DisplayTextContext.tsx +++ b/packages/components/src/components/context/DisplayTextContext.tsx @@ -3,7 +3,7 @@ import Tts from 'react-native-tts' import _ from 'lodash' import { translate } from '../../i18n' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' interface Props { text: null | string diff --git a/packages/components/src/components/context/PredictionProvider.tsx b/packages/components/src/components/context/PredictionProvider.tsx index 358ebac94..2eea0b365 100644 --- a/packages/components/src/components/context/PredictionProvider.tsx +++ b/packages/components/src/components/context/PredictionProvider.tsx @@ -5,7 +5,7 @@ import { PredictionState, PredictionEngine } from '../../prediction' import { useSelector } from '../../hooks/useSelector' import { useDispatch } from 'react-redux' -import * as actions from '../../redux/actions' +import * as actions from '../../redux/common/actions' type PredictionDispatch = typeof PredictionEngine.prototype.userInputDispatch diff --git a/packages/components/src/components/context/ThemeContext.tsx b/packages/components/src/components/context/ThemeContext.tsx index cc6cc2222..6a1c44fce 100644 --- a/packages/components/src/components/context/ThemeContext.tsx +++ b/packages/components/src/components/context/ThemeContext.tsx @@ -1,7 +1,7 @@ import React from 'react' import { ThemeContext, ThemeProvider as StyledThemeProvider } from 'styled-components' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' import { themes } from '@oky/core' export function ThemeProvider({ children }) { diff --git a/packages/components/src/config/env.ts b/packages/components/src/config/env.ts index 5fa59c71e..5ff5b763e 100644 --- a/packages/components/src/config/env.ts +++ b/packages/components/src/config/env.ts @@ -6,4 +6,4 @@ export const PREDICTION_ENDPOINT = env.PREDICTION_ENDPOINT export const WEBSITE_URL = env.WEBSITE_URL // Development purposes only -export const FAST_SIGN_UP = false +export const FAST_SIGN_UP = true diff --git a/packages/components/src/hooks/useSelector.ts b/packages/components/src/hooks/useSelector.ts index 0bda9bb8e..e17ebf669 100644 --- a/packages/components/src/hooks/useSelector.ts +++ b/packages/components/src/hooks/useSelector.ts @@ -1,7 +1,4 @@ -import { - useSelector as useReduxSelector, - TypedUseSelectorHook, -} from 'react-redux' -import { ReduxState } from '../redux/store' +import { useSelector as useReduxSelector, TypedUseSelectorHook } from 'react-redux' +import { ReduxState } from '../redux/common/reducers' export const useSelector: TypedUseSelectorHook = useReduxSelector diff --git a/packages/components/src/hooks/useTextToSpeechHook.ts b/packages/components/src/hooks/useTextToSpeechHook.ts index 6cc09d92b..2a2ec7d02 100644 --- a/packages/components/src/hooks/useTextToSpeechHook.ts +++ b/packages/components/src/hooks/useTextToSpeechHook.ts @@ -1,7 +1,7 @@ import React from 'react' import { speakArray, clearTTSQueue } from '../services/textToSpeech' import { useSelector } from './useSelector' -import * as selectors from '../redux/selectors' +import * as selectors from '../redux/common/selectors' export function useTextToSpeechHook({ navigation, text }) { const hasTtsActive = useSelector(selectors.isTtsActiveSelector) diff --git a/packages/components/src/prediction/PredictionState.ts b/packages/components/src/prediction/PredictionState.ts index 9bed219f0..d1fdb7e83 100644 --- a/packages/components/src/prediction/PredictionState.ts +++ b/packages/components/src/prediction/PredictionState.ts @@ -1,5 +1,4 @@ import moment, { Moment } from 'moment' -import { AppState } from '../redux/reducers/appReducer' import CircularBuffer from 'circular-buffer' interface CurrentCycle { diff --git a/packages/components/src/redux/actions/analyticsActions.ts b/packages/components/src/redux/common/actions/analyticsActions.ts similarity index 100% rename from packages/components/src/redux/actions/analyticsActions.ts rename to packages/components/src/redux/common/actions/analyticsActions.ts diff --git a/packages/components/src/redux/actions/answerActions.ts b/packages/components/src/redux/common/actions/answerActions.ts similarity index 97% rename from packages/components/src/redux/actions/answerActions.ts rename to packages/components/src/redux/common/actions/answerActions.ts index 9332dbee0..adb5a7be1 100644 --- a/packages/components/src/redux/actions/answerActions.ts +++ b/packages/components/src/redux/common/actions/answerActions.ts @@ -1,6 +1,6 @@ import { createAction } from '../helpers' import { Moment } from 'moment' -import { CardName, DailyCard } from '../../types' +import { CardName, DailyCard } from '../../../types' import { AnswerForUserState } from '../reducers/answerReducer' import { User } from '../reducers/authReducer' diff --git a/packages/components/src/redux/actions/appActions.ts b/packages/components/src/redux/common/actions/appActions.ts similarity index 100% rename from packages/components/src/redux/actions/appActions.ts rename to packages/components/src/redux/common/actions/appActions.ts diff --git a/packages/components/src/redux/actions/authActions.ts b/packages/components/src/redux/common/actions/authActions.ts similarity index 100% rename from packages/components/src/redux/actions/authActions.ts rename to packages/components/src/redux/common/actions/authActions.ts diff --git a/packages/components/src/redux/actions/contentActions.ts b/packages/components/src/redux/common/actions/contentActions.ts similarity index 98% rename from packages/components/src/redux/actions/contentActions.ts rename to packages/components/src/redux/common/actions/contentActions.ts index d804b760f..7cfd0dacf 100644 --- a/packages/components/src/redux/actions/contentActions.ts +++ b/packages/components/src/redux/common/actions/contentActions.ts @@ -14,7 +14,7 @@ import { AllSurveys, CompletedSurveys, Videos, -} from '../../types' +} from '../../../types' export function initStaleContent(payload: { articles: Articles diff --git a/packages/components/src/redux/actions/index.ts b/packages/components/src/redux/common/actions/index.ts similarity index 100% rename from packages/components/src/redux/actions/index.ts rename to packages/components/src/redux/common/actions/index.ts diff --git a/packages/components/src/redux/actions/newPredictionAction.ts b/packages/components/src/redux/common/actions/newPredictionAction.ts similarity index 100% rename from packages/components/src/redux/actions/newPredictionAction.ts rename to packages/components/src/redux/common/actions/newPredictionAction.ts diff --git a/packages/components/src/redux/actions/predictionActions.ts b/packages/components/src/redux/common/actions/predictionActions.ts similarity index 95% rename from packages/components/src/redux/actions/predictionActions.ts rename to packages/components/src/redux/common/actions/predictionActions.ts index bb0c12427..a96bced4d 100644 --- a/packages/components/src/redux/actions/predictionActions.ts +++ b/packages/components/src/redux/common/actions/predictionActions.ts @@ -1,5 +1,5 @@ import { createAction } from '../helpers' -import { PredictionState } from '../../prediction' +import { PredictionState } from '../../../prediction' import { Moment } from 'moment' export function setPredictionEngineState(predictionState: PredictionState) { diff --git a/packages/components/src/redux/helpers/index.ts b/packages/components/src/redux/common/helpers/index.ts similarity index 74% rename from packages/components/src/redux/helpers/index.ts rename to packages/components/src/redux/common/helpers/index.ts index 7a015b893..8e18f509d 100644 --- a/packages/components/src/redux/helpers/index.ts +++ b/packages/components/src/redux/common/helpers/index.ts @@ -2,10 +2,7 @@ import { Action } from '../types/types' export function createAction(type: T): Action -export function createAction( - type: T, - payload: P, -): Action +export function createAction(type: T, payload: P): Action export function createAction(type: T, payload?: P) { const action = payload === undefined ? { type } : { type, payload } diff --git a/packages/components/src/redux/reducers/analyticsReducer.ts b/packages/components/src/redux/common/reducers/analyticsReducer.ts similarity index 83% rename from packages/components/src/redux/reducers/analyticsReducer.ts rename to packages/components/src/redux/common/reducers/analyticsReducer.ts index b1d50e682..9d1636359 100644 --- a/packages/components/src/redux/reducers/analyticsReducer.ts +++ b/packages/components/src/redux/common/reducers/analyticsReducer.ts @@ -9,10 +9,7 @@ export type AnalyticsState = Array<{ const initialState: AnalyticsState = [] -export function analyticsReducer( - state = initialState, - action: Actions, -): AnalyticsState { +export function analyticsReducer(state = initialState, action: Actions): AnalyticsState { switch (action.type) { case 'QUEUE_EVENT': return state.concat({ diff --git a/packages/components/src/redux/reducers/answerReducer.ts b/packages/components/src/redux/common/reducers/answerReducer.ts similarity index 98% rename from packages/components/src/redux/reducers/answerReducer.ts rename to packages/components/src/redux/common/reducers/answerReducer.ts index 389d27cf1..b9339d10b 100644 --- a/packages/components/src/redux/reducers/answerReducer.ts +++ b/packages/components/src/redux/common/reducers/answerReducer.ts @@ -1,7 +1,7 @@ import { Actions } from '../types' -import { DailyCard } from '../../types' import { combineReducers } from 'redux' -import { toShortISO } from '../../services/dateUtils' +import { toShortISO } from '../../../services/dateUtils' +import { DailyCard } from '../../../types' export interface AnswerForUserState { surveys: { diff --git a/packages/components/src/redux/reducers/appReducer.ts b/packages/components/src/redux/common/reducers/appReducer.ts similarity index 98% rename from packages/components/src/redux/reducers/appReducer.ts rename to packages/components/src/redux/common/reducers/appReducer.ts index 27e48e41c..d5049e205 100644 --- a/packages/components/src/redux/reducers/appReducer.ts +++ b/packages/components/src/redux/common/reducers/appReducer.ts @@ -1,6 +1,6 @@ import _ from 'lodash' import { Actions } from '../types' -import { currentLocale } from '../../i18n' +import { currentLocale } from '../../../i18n' import DeviceInfo from 'react-native-device-info' import { AvatarName, ThemeName, defaultAvatar, defaultTheme } from '@oky/core' diff --git a/packages/components/src/redux/reducers/authReducer.ts b/packages/components/src/redux/common/reducers/authReducer.ts similarity index 100% rename from packages/components/src/redux/reducers/authReducer.ts rename to packages/components/src/redux/common/reducers/authReducer.ts diff --git a/packages/components/src/redux/reducers/contentReducer.ts b/packages/components/src/redux/common/reducers/contentReducer.ts similarity index 99% rename from packages/components/src/redux/reducers/contentReducer.ts rename to packages/components/src/redux/common/reducers/contentReducer.ts index a468ca9e2..9bdfeb16f 100644 --- a/packages/components/src/redux/reducers/contentReducer.ts +++ b/packages/components/src/redux/common/reducers/contentReducer.ts @@ -14,7 +14,7 @@ import { AllSurveys, CompletedSurveys, Videos, -} from '../../types' +} from '../../../types' import { Actions } from '../types/index' export interface ContentState { diff --git a/packages/components/src/redux/reducers/index.ts b/packages/components/src/redux/common/reducers/index.ts similarity index 95% rename from packages/components/src/redux/reducers/index.ts rename to packages/components/src/redux/common/reducers/index.ts index 28c05a5f8..376de497a 100644 --- a/packages/components/src/redux/reducers/index.ts +++ b/packages/components/src/redux/common/reducers/index.ts @@ -37,3 +37,5 @@ export function rootReducer(state, action: Actions) { return reducer(state, action) } } + +export type ReduxState = ReturnType diff --git a/packages/components/src/redux/reducers/predictionReducer.ts b/packages/components/src/redux/common/reducers/predictionReducer.ts similarity index 90% rename from packages/components/src/redux/reducers/predictionReducer.ts rename to packages/components/src/redux/common/reducers/predictionReducer.ts index 22d521208..1113c8bec 100644 --- a/packages/components/src/redux/reducers/predictionReducer.ts +++ b/packages/components/src/redux/common/reducers/predictionReducer.ts @@ -1,6 +1,5 @@ import _ from 'lodash' -import moment from 'moment' -import { PredictionSerializableState } from '../../prediction' +import { PredictionSerializableState } from '../../../prediction' import { Actions } from '../types/index' diff --git a/packages/components/src/redux/sagas/analyticsSaga.ts b/packages/components/src/redux/common/sagas/analyticsSaga.ts similarity index 92% rename from packages/components/src/redux/sagas/analyticsSaga.ts rename to packages/components/src/redux/common/sagas/analyticsSaga.ts index 5016b5b97..2f850856a 100644 --- a/packages/components/src/redux/sagas/analyticsSaga.ts +++ b/packages/components/src/redux/common/sagas/analyticsSaga.ts @@ -1,8 +1,8 @@ import { all, delay, fork, put, select, takeLatest } from 'redux-saga/effects' import { v4 as uuidv4 } from 'uuid' import moment from 'moment' -import { fetchNetworkConnectionStatus } from '../../services/network' -import { httpClient } from '../../services/HttpClient' +import { fetchNetworkConnectionStatus } from '../../../services/network' +import { httpClient } from '../../../services/HttpClient' import * as actions from '../actions' import * as selectors from '../selectors' import { ActionTypes } from '../types' diff --git a/packages/components/src/redux/sagas/appSaga.ts b/packages/components/src/redux/common/sagas/appSaga.ts similarity index 83% rename from packages/components/src/redux/sagas/appSaga.ts rename to packages/components/src/redux/common/sagas/appSaga.ts index 3b918247c..9ff8fd3ac 100644 --- a/packages/components/src/redux/sagas/appSaga.ts +++ b/packages/components/src/redux/common/sagas/appSaga.ts @@ -1,14 +1,13 @@ import _ from 'lodash' import { all, delay, fork, put, select, takeLatest } from 'redux-saga/effects' -import { httpClient } from '../../services/HttpClient' -import { fetchNetworkConnectionStatus } from '../../services/network' +import { httpClient } from '../../../services/HttpClient' +import { fetchNetworkConnectionStatus } from '../../../services/network' import { extractReducerState } from '../sync' -import { exportReducerNames } from '../reducers' -import { version as storeVersion, ReduxState } from '../store' +import { ReduxState, exportReducerNames } from '../reducers' +import { version as storeVersion } from '../../store' import * as actions from '../actions' import * as selectors from '../selectors' import messaging from '@react-native-firebase/messaging' -import { ExtractActionFromActionType } from '../types' function* syncAppState() { let lastAppState diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/common/sagas/authSaga.ts similarity index 95% rename from packages/components/src/redux/sagas/authSaga.ts rename to packages/components/src/redux/common/sagas/authSaga.ts index 609061b2e..f5e5224be 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/common/sagas/authSaga.ts @@ -3,16 +3,15 @@ import { REHYDRATE } from 'redux-persist' import { Alert } from 'react-native' import { v4 as uuidv4 } from 'uuid' import { ExtractActionFromActionType } from '../types' -import { httpClient } from '../../services/HttpClient' -import { exportReducerNames } from '../reducers' -import { ReduxState } from '../store' +import { httpClient } from '../../../services/HttpClient' +import { ReduxState, exportReducerNames } from '../reducers' import * as actions from '../actions' import * as selectors from '../selectors' -import { navigateAndReset } from '../../services/navigationService' -import { PredictionState } from '../../prediction' +import { navigateAndReset } from '../../../services/navigationService' +import { PredictionState } from '../../../prediction' import moment from 'moment' -import { closeOutTTs } from '../../services/textToSpeech' -import { fetchNetworkConnectionStatus } from '../../services/network' +import { closeOutTTs } from '../../../services/textToSpeech' +import { fetchNetworkConnectionStatus } from '../../../services/network' // unwrap promise type Await = T extends Promise ? U : T diff --git a/packages/components/src/redux/sagas/contentSaga.ts b/packages/components/src/redux/common/sagas/contentSaga.ts similarity index 98% rename from packages/components/src/redux/sagas/contentSaga.ts rename to packages/components/src/redux/common/sagas/contentSaga.ts index 08b21197b..1f0c8d67d 100644 --- a/packages/components/src/redux/sagas/contentSaga.ts +++ b/packages/components/src/redux/common/sagas/contentSaga.ts @@ -10,12 +10,12 @@ import { fromAvatarMessages, liveContent as staleContent, } from '@oky/core' -import { httpClient } from '../../services/HttpClient' +import { httpClient } from '../../../services/HttpClient' import * as selectors from '../selectors' import * as actions from '../actions' import _ from 'lodash' import messaging from '@react-native-firebase/messaging' -import { closeOutTTs } from '../../services/textToSpeech' +import { closeOutTTs } from '../../../services/textToSpeech' function* onRehydrate(action: RehydrateAction) { const locale = yield select(selectors.currentLocaleSelector) diff --git a/packages/components/src/redux/sagas/index.ts b/packages/components/src/redux/common/sagas/index.ts similarity index 100% rename from packages/components/src/redux/sagas/index.ts rename to packages/components/src/redux/common/sagas/index.ts diff --git a/packages/components/src/redux/sagas/smartPredictionSaga.ts b/packages/components/src/redux/common/sagas/smartPredictionSaga.ts similarity index 93% rename from packages/components/src/redux/sagas/smartPredictionSaga.ts rename to packages/components/src/redux/common/sagas/smartPredictionSaga.ts index e8964e8a0..8aecbc86a 100644 --- a/packages/components/src/redux/sagas/smartPredictionSaga.ts +++ b/packages/components/src/redux/common/sagas/smartPredictionSaga.ts @@ -1,11 +1,11 @@ import { all, put, takeLatest } from 'redux-saga/effects' import { ExtractActionFromActionType } from '../types' -import { httpClient } from '../../services/HttpClient' +import { httpClient } from '../../../services/HttpClient' import * as actions from '../actions' import _ from 'lodash' -import { PredictionState } from '../../prediction' +import { PredictionState } from '../../../prediction' function* onFetchUpdatedPredictedCycles( action: ExtractActionFromActionType<'SMART_PREDICTION_REQUEST'>, diff --git a/packages/components/src/redux/selectors/analyticsSelectors.ts b/packages/components/src/redux/common/selectors/analyticsSelectors.ts similarity index 75% rename from packages/components/src/redux/selectors/analyticsSelectors.ts rename to packages/components/src/redux/common/selectors/analyticsSelectors.ts index 2c2c2770a..9780d5425 100644 --- a/packages/components/src/redux/selectors/analyticsSelectors.ts +++ b/packages/components/src/redux/common/selectors/analyticsSelectors.ts @@ -1,4 +1,4 @@ -import { ReduxState } from '../store' +import { ReduxState } from '../reducers' const s = (state: ReduxState) => state.analytics diff --git a/packages/components/src/redux/selectors/answerSelectors.ts b/packages/components/src/redux/common/selectors/answerSelectors.ts similarity index 93% rename from packages/components/src/redux/selectors/answerSelectors.ts rename to packages/components/src/redux/common/selectors/answerSelectors.ts index 2357eb532..e73b9e696 100644 --- a/packages/components/src/redux/selectors/answerSelectors.ts +++ b/packages/components/src/redux/common/selectors/answerSelectors.ts @@ -1,10 +1,9 @@ -import { ReduxState } from '../store' -// import { allSurveysSelectors, allQuizzesSelectors } from './contentSelectors' import { allQuizzesSelectors } from './contentSelectors' import { Moment } from 'moment' -import { toShortISO } from '../../services/dateUtils' +import { toShortISO } from '../../../services/dateUtils' import _ from 'lodash' +import { ReduxState } from '../reducers' const s = (state: ReduxState) => state.answer @@ -50,12 +49,13 @@ export const cardAnswerSelector = (state: ReduxState, date: Moment) => { export const verifyPeriodDaySelectorWithDate = (state: ReduxState, date: Moment) => { if (!state.auth.user) return {} // for the use case on info screen where there is no authed user if (!s(state)[state.auth.user.id]) return {} - if(s(state)[state.auth.user.id]?.verifiedDates){ - return s(state)[state.auth.user.id]?.verifiedDates[toShortISO(date)] - } else return {} + if (s(state)[state.auth.user.id]?.verifiedDates) { + return s(state)[state.auth.user.id]?.verifiedDates[toShortISO(date)] + } + return {} // return s(state)[state.auth.user.id]?.verifiedDates[toShortISO(date)] || {} } -export const allCardAnswersSelector = (state: ReduxState) => { +export const allCardAnswersSelector = (state: ReduxState) => { if (!state.auth.user) return {} // for the use case on info screen where there is no authed user if (!s(state)[state.auth.user.id]) return {} return s(state)[state.auth.user.id]?.verifiedDates || {} diff --git a/packages/components/src/redux/selectors/appSelectors.ts b/packages/components/src/redux/common/selectors/appSelectors.ts similarity index 97% rename from packages/components/src/redux/selectors/appSelectors.ts rename to packages/components/src/redux/common/selectors/appSelectors.ts index f91052901..e0ec924bb 100644 --- a/packages/components/src/redux/selectors/appSelectors.ts +++ b/packages/components/src/redux/common/selectors/appSelectors.ts @@ -1,4 +1,4 @@ -import { ReduxState } from '../store' +import { ReduxState } from '../reducers' const s = (state: ReduxState) => state.app const predictionS = (state: ReduxState) => state.prediction diff --git a/packages/components/src/redux/selectors/authSelectors.ts b/packages/components/src/redux/common/selectors/authSelectors.ts similarity index 86% rename from packages/components/src/redux/selectors/authSelectors.ts rename to packages/components/src/redux/common/selectors/authSelectors.ts index 1afaf550c..baae80918 100644 --- a/packages/components/src/redux/selectors/authSelectors.ts +++ b/packages/components/src/redux/common/selectors/authSelectors.ts @@ -1,4 +1,4 @@ -import { ReduxState } from '../store' +import { ReduxState } from '../reducers' const s = (state: ReduxState) => state.auth diff --git a/packages/components/src/redux/selectors/contentSelectors.ts b/packages/components/src/redux/common/selectors/contentSelectors.ts similarity index 98% rename from packages/components/src/redux/selectors/contentSelectors.ts rename to packages/components/src/redux/common/selectors/contentSelectors.ts index 61fcc83e2..034038718 100644 --- a/packages/components/src/redux/selectors/contentSelectors.ts +++ b/packages/components/src/redux/common/selectors/contentSelectors.ts @@ -1,6 +1,5 @@ -import { ReduxState } from '../store' -import moment from 'moment' import _ from 'lodash' +import { ReduxState } from '../reducers' const s = (state: ReduxState) => state.content diff --git a/packages/components/src/redux/selectors/index.ts b/packages/components/src/redux/common/selectors/index.ts similarity index 100% rename from packages/components/src/redux/selectors/index.ts rename to packages/components/src/redux/common/selectors/index.ts diff --git a/packages/components/src/redux/sync/index.ts b/packages/components/src/redux/common/sync/index.ts similarity index 91% rename from packages/components/src/redux/sync/index.ts rename to packages/components/src/redux/common/sync/index.ts index 5d12ad4d0..2e81c498c 100644 --- a/packages/components/src/redux/sync/index.ts +++ b/packages/components/src/redux/common/sync/index.ts @@ -1,10 +1,7 @@ import _ from 'lodash' import { Action, Reducer, ReducersMapObject } from 'redux' -function syncReducerFactory( - innerReducer: Reducer, - reducerName: string, -): Reducer { +function syncReducerFactory(innerReducer: Reducer, reducerName: string): Reducer { return (state, action) => { if (action.type === 'REFRESH_STORE' && action.payload[reducerName]) { return { diff --git a/packages/components/src/redux/types/index.ts b/packages/components/src/redux/common/types/index.ts similarity index 63% rename from packages/components/src/redux/types/index.ts rename to packages/components/src/redux/common/types/index.ts index c5e3bc7c8..a71885637 100644 --- a/packages/components/src/redux/types/index.ts +++ b/packages/components/src/redux/common/types/index.ts @@ -5,6 +5,7 @@ export type Actions = ActionsUnion export type ActionTypes = Actions[keyof Actions] -export type ExtractActionFromActionType< - ActionType extends string -> = ActionsOfType +export type ExtractActionFromActionType = ActionsOfType< + Actions, + ActionType +> diff --git a/packages/components/src/redux/types/types.ts b/packages/components/src/redux/common/types/types.ts similarity index 82% rename from packages/components/src/redux/types/types.ts rename to packages/components/src/redux/common/types/types.ts index 799fb673e..53f4233ce 100644 --- a/packages/components/src/redux/types/types.ts +++ b/packages/components/src/redux/common/types/types.ts @@ -21,14 +21,13 @@ export type Action = P extends void * get Actions types union from Action creators object. * it is recommended to export them under the same name as your Actions object, to leverage token merging */ -export type ActionsUnion> = ReturnType< - A[keyof A] -> +export type ActionsUnion> = ReturnType /** * gets particular Action type from ActionsUnion */ -export type ActionsOfType< - ActionUnion, - ActionType extends string -> = ActionUnion extends Action ? ActionUnion : never +export type ActionsOfType = ActionUnion extends Action< + ActionType +> + ? ActionUnion + : never diff --git a/packages/components/src/redux/store.ts b/packages/components/src/redux/store.ts index 7c2bc318f..b6492f9ee 100644 --- a/packages/components/src/redux/store.ts +++ b/packages/components/src/redux/store.ts @@ -4,7 +4,6 @@ import { encryptTransform } from 'redux-persist-transform-encrypt' import storage from 'redux-persist/lib/storage' import createSagaMiddleware from 'redux-saga' import { composeWithDevTools } from 'remote-redux-devtools' -import { rootReducer as commonRootReducer } from './reducers' // Function to create an encryptor const createEncryptor = (secretKey) => @@ -45,5 +44,3 @@ export function configureStore({ key, secretKey, rootReducer, rootSaga }) { return { store, persistor } } - -export type ReduxState = ReturnType diff --git a/packages/components/src/screens/ArticlesScreen.tsx b/packages/components/src/screens/ArticlesScreen.tsx index 8a369a9e2..7be187867 100644 --- a/packages/components/src/screens/ArticlesScreen.tsx +++ b/packages/components/src/screens/ArticlesScreen.tsx @@ -4,7 +4,7 @@ import { FlatList } from 'react-native' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/selectors' +import * as selectors from '../redux/common/selectors' import { Header } from '../components/common/Header' import { TextWithoutTranslation } from '../components/common/Text' import { useTextToSpeechHook } from '../hooks/useTextToSpeechHook' diff --git a/packages/components/src/screens/AvatarAndThemeScreen.tsx b/packages/components/src/screens/AvatarAndThemeScreen.tsx index 70eb5d707..f0930211d 100644 --- a/packages/components/src/screens/AvatarAndThemeScreen.tsx +++ b/packages/components/src/screens/AvatarAndThemeScreen.tsx @@ -6,12 +6,12 @@ import { ThemeSelect } from './avatarAndTheme/ThemeSelect' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { PrimaryButton } from '../components/common/buttons/PrimaryButton' -import * as actions from '../redux/actions/index' +import * as actions from '../redux/common/actions/index' import { Header } from '../components/common/Header' import { useTheme } from '../components/context/ThemeContext' import { BackOneScreen, navigate } from '../services/navigationService' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/selectors' +import * as selectors from '../redux/common/selectors' import styled from 'styled-components/native' import { Text } from '../components/common/Text' import { ScrollView } from 'react-native-gesture-handler' diff --git a/packages/components/src/screens/ContactUsScreen.tsx b/packages/components/src/screens/ContactUsScreen.tsx index 91e0a72c6..df86d7e86 100644 --- a/packages/components/src/screens/ContactUsScreen.tsx +++ b/packages/components/src/screens/ContactUsScreen.tsx @@ -10,7 +10,7 @@ import { TextInput } from '../components/common/TextInput' import { KeyboardAwareAvoidance } from '../components/common/KeyboardAwareAvoidance' import { httpClient } from '../services/HttpClient' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/selectors' +import * as selectors from '../redux/common/selectors' import moment from 'moment' import { Text } from '../components/common/Text' import { ThemedModal } from '../components/common/ThemedModal' diff --git a/packages/components/src/screens/DayScreen.tsx b/packages/components/src/screens/DayScreen.tsx index 96e079f75..8847cb017 100644 --- a/packages/components/src/screens/DayScreen.tsx +++ b/packages/components/src/screens/DayScreen.tsx @@ -12,7 +12,7 @@ import { assets } from '../assets' import { usePredictDay } from '../components/context/PredictionProvider' import { ThemedModal } from '../components/common/ThemedModal' import { ColourButtons } from './mainScreen/ColourButtons' -import * as selectors from '../redux/selectors' +import * as selectors from '../redux/common/selectors' import { useSelector } from '../hooks/useSelector' import moment from 'moment' diff --git a/packages/components/src/screens/EditProfileScreen.tsx b/packages/components/src/screens/EditProfileScreen.tsx index a50287b47..36d473cd0 100644 --- a/packages/components/src/screens/EditProfileScreen.tsx +++ b/packages/components/src/screens/EditProfileScreen.tsx @@ -9,8 +9,8 @@ import { SelectBox } from '../components/common/SelectBox' import { DateOfBirthInput } from '../components/common/DateOfBirthInput' import { assets } from '../assets/index' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/selectors' -import * as actions from '../redux/actions' +import * as selectors from '../redux/common/selectors' +import * as actions from '../redux/common/actions' import { useDispatch } from 'react-redux' import { BackOneScreen } from '../services/navigationService' import { httpClient } from '../services/HttpClient' diff --git a/packages/components/src/screens/EncyclopediaScreen.tsx b/packages/components/src/screens/EncyclopediaScreen.tsx index 8c24d304d..726d85875 100644 --- a/packages/components/src/screens/EncyclopediaScreen.tsx +++ b/packages/components/src/screens/EncyclopediaScreen.tsx @@ -4,7 +4,7 @@ import { ScrollView, Animated } from 'react-native' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/selectors' +import * as selectors from '../redux/common/selectors' import { Category } from './encyclopediaScreen/Category' import { SubCategoryCard, VideoSubCategoryCard } from './encyclopediaScreen/SubCategoryCard' import Accordion from 'react-native-collapsible/Accordion' diff --git a/packages/components/src/screens/FindHelpScreen.tsx b/packages/components/src/screens/FindHelpScreen.tsx index a836ae382..f6b8e8991 100644 --- a/packages/components/src/screens/FindHelpScreen.tsx +++ b/packages/components/src/screens/FindHelpScreen.tsx @@ -8,7 +8,7 @@ import { TextWithoutTranslation, Text } from '../components/common/Text' import { SwiperContainer } from '../components/common/SwiperContainer' import { PageContainer } from '../components/layout/PageContainer' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/selectors' +import * as selectors from '../redux/common/selectors' import { useTextToSpeechHook } from '../hooks/useTextToSpeechHook' import analytics from '@react-native-firebase/analytics' import { fetchNetworkConnectionStatus } from '../services/network' diff --git a/packages/components/src/screens/MainScreen.tsx b/packages/components/src/screens/MainScreen.tsx index 056434406..a085abd14 100644 --- a/packages/components/src/screens/MainScreen.tsx +++ b/packages/components/src/screens/MainScreen.tsx @@ -20,10 +20,10 @@ import { import { useRandomText } from '../hooks/useRandomText' import { InformationButton } from '../components/common/InformationButton' import { assets } from '../assets' -import * as actions from '../redux/actions' +import * as actions from '../redux/common/actions' import { useDispatch } from 'react-redux' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/selectors' +import * as selectors from '../redux/common/selectors' import moment from 'moment' import { FlowerButton, FlowerModal } from '../optional/Flower' diff --git a/packages/components/src/screens/OnboardingScreen.tsx b/packages/components/src/screens/OnboardingScreen.tsx index f1cb5da20..d359eb978 100644 --- a/packages/components/src/screens/OnboardingScreen.tsx +++ b/packages/components/src/screens/OnboardingScreen.tsx @@ -7,7 +7,7 @@ import { OnboardingCard } from './onboardingScreen/OnboardingCard' import { assets } from '../assets/index' import { PrimaryButton } from '../components/common/buttons/PrimaryButton' import { navigateAndReset } from '../services/navigationService' -import * as actions from '../redux/actions' +import * as actions from '../redux/common/actions' import { useDispatch } from 'react-redux' import { Animated } from 'react-native' import { Text } from '../components/common/Text' diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index b3d9eeaf9..05f1584ff 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -1,10 +1,10 @@ import React from 'react' import styled from 'styled-components/native' import { useDispatch } from 'react-redux' -import * as actions from '../redux/actions' +import * as actions from '../redux/common/actions' import { Text } from '../components/common/Text' import { TextInput } from '../components/common/TextInput' -import * as selectors from '../redux/selectors' +import * as selectors from '../redux/common/selectors' import { navigateAndReset } from '../services/navigationService' import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { PageContainer } from '../components/layout/PageContainer' diff --git a/packages/components/src/screens/ProfileScreen.tsx b/packages/components/src/screens/ProfileScreen.tsx index f255364d9..c3ad9daa5 100644 --- a/packages/components/src/screens/ProfileScreen.tsx +++ b/packages/components/src/screens/ProfileScreen.tsx @@ -18,8 +18,8 @@ import { AvatarOption } from './avatarAndTheme/avatarSelect/AvatarOption' import { ThemeSelectItem } from './avatarAndTheme/ThemeSelectItem' import { useHistoryPrediction, useTodayPrediction } from '../components/context/PredictionProvider' import { useSelector } from '../hooks/useSelector' -import * as actions from '../redux/actions' -import * as selectors from '../redux/selectors' +import * as actions from '../redux/common/actions' +import * as selectors from '../redux/common/selectors' import { translate } from '../i18n/index' import { IconButton } from '../components/common/buttons/IconButton' import Modal from 'react-native-modal' diff --git a/packages/components/src/screens/SettingsScreen.tsx b/packages/components/src/screens/SettingsScreen.tsx index 402d44c3c..dcc9b562a 100644 --- a/packages/components/src/screens/SettingsScreen.tsx +++ b/packages/components/src/screens/SettingsScreen.tsx @@ -8,10 +8,10 @@ import { PrimaryButton } from '../components/common/buttons/PrimaryButton' import { Switcher } from './settings/Switcher' import { navigate } from '../services/navigationService' import { useDispatch } from 'react-redux' -import * as actions from '../redux/actions' +import * as actions from '../redux/common/actions' import { ConfirmAlert } from '../components/common/ConfirmAlert' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/selectors' +import * as selectors from '../redux/common/selectors' import { translate } from '../i18n/index' import { SpinLoader } from '../components/common/SpinLoader' import { settingsScreenText } from '../config' diff --git a/packages/components/src/screens/SplashScreen.tsx b/packages/components/src/screens/SplashScreen.tsx index d710cafd1..5d2f0f2c5 100644 --- a/packages/components/src/screens/SplashScreen.tsx +++ b/packages/components/src/screens/SplashScreen.tsx @@ -4,8 +4,8 @@ import { PageContainer } from '../components/layout/PageContainer' import { assets } from '../assets/index' import styled from 'styled-components/native' import { useSelector, useDispatch } from 'react-redux' -import * as selectors from '../redux/selectors' -import * as actions from '../redux/actions' +import * as selectors from '../redux/common/selectors' +import * as actions from '../redux/common/actions' import { navigateAndReset } from '../services/navigationService' import { Animated, Easing } from 'react-native' import { createNotificationChannel, requestUserPermission } from '../services/notifications' diff --git a/packages/components/src/screens/TutorialFirstScreen.tsx b/packages/components/src/screens/TutorialFirstScreen.tsx index b76795004..f375c92c8 100644 --- a/packages/components/src/screens/TutorialFirstScreen.tsx +++ b/packages/components/src/screens/TutorialFirstScreen.tsx @@ -11,7 +11,7 @@ import { useInfiniteScroll } from './mainScreen/wheelCarousel/useInfiniteScroll' import { navigateAndReset } from '../services/navigationService' import { Animated, Dimensions, Platform } from 'react-native' import { useDispatch } from 'react-redux' -import * as actions from '../redux/actions' +import * as actions from '../redux/common/actions' import { Text } from '../components/common/Text' import { Icon } from '../components/common/Icon' import { assets } from '../assets' @@ -19,7 +19,7 @@ import { ColourButtonsDemo } from './tutorial/ColourButtonsDemo' import { SpinLoader } from '../components/common/SpinLoader' import DeviceInfo from 'react-native-device-info' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/selectors' +import * as selectors from '../redux/common/selectors' import moment from 'moment' import Tts from 'react-native-tts' import { translate } from '../i18n' diff --git a/packages/components/src/screens/TutorialSecondScreen.tsx b/packages/components/src/screens/TutorialSecondScreen.tsx index 876d70079..0998886d7 100644 --- a/packages/components/src/screens/TutorialSecondScreen.tsx +++ b/packages/components/src/screens/TutorialSecondScreen.tsx @@ -11,7 +11,7 @@ import { useInfiniteScroll } from './mainScreen/wheelCarousel/useInfiniteScroll' import { navigateAndReset } from '../services/navigationService' import { Animated, Dimensions, Image, Platform, View } from 'react-native' import { useDispatch } from 'react-redux' -import * as actions from '../redux/actions' +import * as actions from '../redux/common/actions' import { Text } from '../components/common/Text' import { Icon } from '../components/common/Icon' import { assets } from '../assets' @@ -20,7 +20,7 @@ import { CalendarAssetDemo } from './tutorial/CalendarAssetDemo' import { SpinLoader } from '../components/common/SpinLoader' import { NoteAssetDemo } from './tutorial/NoteAssetDemo' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/selectors' +import * as selectors from '../redux/common/selectors' import moment from 'moment' import Tts from 'react-native-tts' import { translate } from '../i18n' diff --git a/packages/components/src/screens/VideosScreen.tsx b/packages/components/src/screens/VideosScreen.tsx index 9003238dc..eaa0e69f1 100644 --- a/packages/components/src/screens/VideosScreen.tsx +++ b/packages/components/src/screens/VideosScreen.tsx @@ -4,7 +4,7 @@ import { FlatList } from 'react-native' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/selectors' +import * as selectors from '../redux/common/selectors' import { Header } from '../components/common/Header' import { TextWithoutTranslation } from '../components/common/Text' import { VideoData } from '../types' diff --git a/packages/components/src/screens/authScreen/Login.tsx b/packages/components/src/screens/authScreen/Login.tsx index 88bfd06a3..1202ef435 100644 --- a/packages/components/src/screens/authScreen/Login.tsx +++ b/packages/components/src/screens/authScreen/Login.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components/native' import { Text } from '../../components/common/Text' import { TextInput } from '../../components/common/TextInput' import { useDispatch } from 'react-redux' -import * as actions from '../../redux/actions' +import * as actions from '../../redux/common/actions' import { useSelector } from '../../hooks/useSelector' import { SpinLoader } from '../../components/common/SpinLoader' import _ from 'lodash' diff --git a/packages/components/src/screens/authScreen/SignUp.tsx b/packages/components/src/screens/authScreen/SignUp.tsx index c4cbbfe87..e1672f036 100644 --- a/packages/components/src/screens/authScreen/SignUp.tsx +++ b/packages/components/src/screens/authScreen/SignUp.tsx @@ -8,7 +8,7 @@ import { AskAge } from './signUp/AskAge' import { AskLocation } from './signUp/AskLocation' import { AskUserConfirmation } from './signUp/AskUserConfirmation' import { navigate } from '../../services/navigationService' -import * as actions from '../../redux/actions' +import * as actions from '../../redux/common/actions' import _ from 'lodash' import { FAST_SIGN_UP } from '../../config' diff --git a/packages/components/src/screens/authScreen/signUp/AskLocation.tsx b/packages/components/src/screens/authScreen/signUp/AskLocation.tsx index 2bd201378..73acc37fc 100644 --- a/packages/components/src/screens/authScreen/signUp/AskLocation.tsx +++ b/packages/components/src/screens/authScreen/signUp/AskLocation.tsx @@ -8,7 +8,7 @@ import { GenderSelectItem } from '../../../components/common/GenderSelectItem' import { formHeights } from './FormHeights' import { ModalSearchBox } from '../../../components/common/ModalSearchBox' import { useSelector } from '../../../hooks/useSelector' -import * as selectors from '../../../redux/selectors' +import * as selectors from '../../../redux/common/selectors' import { translate } from '../../../i18n' import { FAST_SIGN_UP } from '../../../config' diff --git a/packages/components/src/screens/dayScreen/DayCarousel.tsx b/packages/components/src/screens/dayScreen/DayCarousel.tsx index 0176a4cff..d36eb4454 100644 --- a/packages/components/src/screens/dayScreen/DayCarousel.tsx +++ b/packages/components/src/screens/dayScreen/DayCarousel.tsx @@ -3,13 +3,13 @@ import _ from 'lodash' import { FlatList, Dimensions, KeyboardAvoidingView } from 'react-native' import { DayCarouselItem } from './DayCarouselItem' import { useDispatch } from 'react-redux' -import * as actions from '../../redux/actions' +import * as actions from '../../redux/common/actions' import { NoteCard } from './NoteCard' import { QuizCard } from './QuizCard' import { DidYouKnowCard } from './DidYouKnowCard' import { SurveyCard } from './SurveyCard' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { translate } from '../../i18n' import { ThemedModal } from '../../components/common/ThemedModal' diff --git a/packages/components/src/screens/dayScreen/DayCarouselItem.tsx b/packages/components/src/screens/dayScreen/DayCarouselItem.tsx index 43b79cb0f..d78f25a0d 100644 --- a/packages/components/src/screens/dayScreen/DayCarouselItem.tsx +++ b/packages/components/src/screens/dayScreen/DayCarouselItem.tsx @@ -6,7 +6,7 @@ import { assets } from '../../assets/index' import { Text } from '../../components/common/Text' import { EmojiSelector } from '../../components/common/EmojiSelector' import { Icon } from '../../components/common/Icon' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' import { useSelector } from '../../hooks/useSelector' import { useColor } from '../../hooks/useColor' import { translate } from '../../i18n' diff --git a/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx b/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx index d670d28c1..03c850443 100644 --- a/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx +++ b/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx @@ -4,7 +4,7 @@ import styled from 'styled-components/native' import { TextWithoutTranslation, Text } from '../../components/common/Text' import { useSelector } from '../../hooks/useSelector' import _ from 'lodash' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' import { TitleText } from '../../components/common/TitleText' const deviceWidth = Dimensions.get('window').width diff --git a/packages/components/src/screens/dayScreen/NoteCard.tsx b/packages/components/src/screens/dayScreen/NoteCard.tsx index 1db4ff84e..db9f77c5b 100644 --- a/packages/components/src/screens/dayScreen/NoteCard.tsx +++ b/packages/components/src/screens/dayScreen/NoteCard.tsx @@ -1,8 +1,8 @@ import React from 'react' import { Dimensions } from 'react-native' import styled from 'styled-components/native' -import * as selectors from '../../redux/selectors' -import * as actions from '../../redux/actions' +import * as selectors from '../../redux/common/selectors' +import * as actions from '../../redux/common/actions' import { useSelector } from '../../hooks/useSelector' import { TextInput } from '../../components/common/TextInput' import { useDispatch } from 'react-redux' diff --git a/packages/components/src/screens/dayScreen/QuizCard.tsx b/packages/components/src/screens/dayScreen/QuizCard.tsx index 1b4f5e649..bc2a7ce8f 100644 --- a/packages/components/src/screens/dayScreen/QuizCard.tsx +++ b/packages/components/src/screens/dayScreen/QuizCard.tsx @@ -6,8 +6,8 @@ import { EmojiSelector } from '../../components/common/EmojiSelector' import { TitleText } from '../../components/common/TitleText' import { useSelector } from '../../hooks/useSelector' import _ from 'lodash' -import * as selectors from '../../redux/selectors' -import * as actions from '../../redux/actions' +import * as selectors from '../../redux/common/selectors' +import * as actions from '../../redux/common/actions' import { useDispatch } from 'react-redux' const deviceWidth = Dimensions.get('window').width diff --git a/packages/components/src/screens/dayScreen/SurveyCard.tsx b/packages/components/src/screens/dayScreen/SurveyCard.tsx index e41d64f23..c197eb33c 100644 --- a/packages/components/src/screens/dayScreen/SurveyCard.tsx +++ b/packages/components/src/screens/dayScreen/SurveyCard.tsx @@ -6,8 +6,8 @@ import { EmojiSelector } from '../../components/common/EmojiSelector' import { TitleText } from '../../components/common/TitleText' import { useSelector } from '../../hooks/useSelector' import _ from 'lodash' -import * as selectors from '../../redux/selectors' -import * as actions from '../../redux/actions' +import * as selectors from '../../redux/common/selectors' +import * as actions from '../../redux/common/actions' import { useDispatch } from 'react-redux' import { TextInput } from '../../components/common/TextInput' import { SurveyInformationButton } from '../../components/common/SurveyInformationButton' diff --git a/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx b/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx index be34cc546..70477b5bc 100644 --- a/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx +++ b/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx @@ -7,7 +7,7 @@ import styled from 'styled-components/native' import { handleCategoriesFilter, handleSearchResult } from './searchFunctions' import { EmojiSelector } from '../../components/common/EmojiSelector' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' import { translate } from '../../i18n' export const SearchBar = ({ diff --git a/packages/components/src/screens/journeyScreen/FinalJourneyCard.tsx b/packages/components/src/screens/journeyScreen/FinalJourneyCard.tsx index 1d22f3525..378d86c3a 100644 --- a/packages/components/src/screens/journeyScreen/FinalJourneyCard.tsx +++ b/packages/components/src/screens/journeyScreen/FinalJourneyCard.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components/native' import { Text, TextWithoutTranslation } from '../../components/common/Text' import { assets } from '../../assets/index' import { translate } from '../../i18n/index' -import * as actions from '../../redux/actions/index' +import * as actions from '../../redux/common/actions/index' import { useDispatch } from 'react-redux' import moment from 'moment' import { SpinLoader } from '../../components/common/SpinLoader' diff --git a/packages/components/src/screens/journeyScreen/JourneyCard.tsx b/packages/components/src/screens/journeyScreen/JourneyCard.tsx index 9b1df9c61..429070371 100644 --- a/packages/components/src/screens/journeyScreen/JourneyCard.tsx +++ b/packages/components/src/screens/journeyScreen/JourneyCard.tsx @@ -6,7 +6,7 @@ import { WheelPickerContent } from '../../components/WheelPickerContent' import { Avatar } from '../../components/common/Avatar/Avatar' import { assets } from '../../assets' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' export function JourneyCard({ question, diff --git a/packages/components/src/screens/mainScreen/Calendar.tsx b/packages/components/src/screens/mainScreen/Calendar.tsx index fec017595..d2e0a95a3 100644 --- a/packages/components/src/screens/mainScreen/Calendar.tsx +++ b/packages/components/src/screens/mainScreen/Calendar.tsx @@ -22,7 +22,7 @@ import { translate } from '../../i18n' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { calendarScreenSpeech } from '../../config' import { useSelector } from 'react-redux' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' const width = Dimensions.get('window').width const height = Dimensions.get('window').height diff --git a/packages/components/src/screens/mainScreen/ColourButtons.tsx b/packages/components/src/screens/mainScreen/ColourButtons.tsx index 95fe0b44a..aa10e6e62 100644 --- a/packages/components/src/screens/mainScreen/ColourButtons.tsx +++ b/packages/components/src/screens/mainScreen/ColourButtons.tsx @@ -18,8 +18,8 @@ import { InformationButton } from '../../components/common/InformationButton' import { decisionProcessNonPeriod, decisionProcessPeriod } from './predictionLogic/predictionLogic' import { translate } from '../../i18n' import { useDispatch } from 'react-redux' -import * as actions from '../../redux/actions/index' -import * as selectors from '../../redux/selectors' +import * as actions from '../../redux/common/actions/index' +import * as selectors from '../../redux/common/selectors' import analytics from '@react-native-firebase/analytics' import moment from 'moment' import { fetchNetworkConnectionStatus } from '../../services/network' diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx index 65c0d5156..763b759e5 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx @@ -8,7 +8,7 @@ import { navigate, navigateAndReset } from '../../../services/navigationService' import moment from 'moment' import { useDisplayText } from '../../../components/context/DisplayTextContext' import { useSelector } from '../../../hooks/useSelector' -import * as selectors from '../../../redux/selectors' +import * as selectors from '../../../redux/common/selectors' import { SpinLoader } from '../../../components/common/SpinLoader' const screenWidth = Dimensions.get('window').width diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx index 2624b454b..4a380a168 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx @@ -8,7 +8,7 @@ import { assets } from '../../../assets/index' import { EmojiSelector } from '../../../components/common/EmojiSelector' import { useSelector } from '../../../hooks/useSelector' import { emojis } from '../../../config' -import * as selectors from '../../../redux/selectors' +import * as selectors from '../../../redux/common/selectors' import { useColor } from '../../../hooks/useColor' import styled from 'styled-components/native' import moment from 'moment' diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx index 2b03c3a42..dee5ea705 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx @@ -7,8 +7,8 @@ import { TouchableOpacity } from 'react-native-gesture-handler' import { assets } from '../../../assets/index' import { translate } from '../../../i18n' import { useSelector, useDispatch } from 'react-redux' -import * as actions from '../../../redux/actions/index' -import * as selectors from '../../../redux/selectors' +import * as actions from '../../../redux/common/actions/index' +import * as selectors from '../../../redux/common/selectors' import moment from 'moment' import { useTodayPrediction, diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx index 44b3c18b1..984b29e90 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx @@ -7,13 +7,13 @@ import { PanGesture } from './PanGesture' import { TouchableOpacity } from 'react-native-gesture-handler' import { ColourButtons } from '../ColourButtons' import { useSelector } from '../../../hooks/useSelector' -import * as selectors from '../../../redux/selectors' +import * as selectors from '../../../redux/common/selectors' import { navigateAndReset } from '../../../services/navigationService' import { useCheckDayWarning } from '../../../hooks/usePredictionWarnings' import { ThemedModal } from '../../../components/common/ThemedModal' import { SpinLoader } from '../../../components/common/SpinLoader' import moment from 'moment' -import { ReduxState } from '../../../redux/store' +import { ReduxState } from '../../../redux/common/reducers' const reduxState = (state: ReduxState) => state diff --git a/packages/components/src/screens/profileScreen/CycleCard.tsx b/packages/components/src/screens/profileScreen/CycleCard.tsx index 17e9c161c..806127c8f 100644 --- a/packages/components/src/screens/profileScreen/CycleCard.tsx +++ b/packages/components/src/screens/profileScreen/CycleCard.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components/native' import { Text } from '../../components/common/Text' import { Icon } from '../../components/common/Icon' import { assets } from '../../assets/index' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' import { EmojiSelector } from '../../components/common/EmojiSelector' import { useSelector } from '../../hooks/useSelector' import { emojis } from '../../config' diff --git a/packages/components/src/screens/settings/AboutScreen.tsx b/packages/components/src/screens/settings/AboutScreen.tsx index b496c68fa..c698c541d 100644 --- a/packages/components/src/screens/settings/AboutScreen.tsx +++ b/packages/components/src/screens/settings/AboutScreen.tsx @@ -5,7 +5,7 @@ import { TextWithoutTranslation } from '../../components/common/Text' import { Header } from '../../components/common/Header' import { Icon } from '../../components/common/Icon' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { aboutScreenText } from '../../config' import { Dimensions } from 'react-native' diff --git a/packages/components/src/screens/settings/AccessScreen.tsx b/packages/components/src/screens/settings/AccessScreen.tsx index 404846c70..d26f7df0b 100644 --- a/packages/components/src/screens/settings/AccessScreen.tsx +++ b/packages/components/src/screens/settings/AccessScreen.tsx @@ -5,8 +5,8 @@ import styled from 'styled-components/native' import { ListItem } from './accessScreen/ListItem' import { Header } from '../../components/common/Header' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/selectors' -import * as actions from '../../redux/actions/index' +import * as selectors from '../../redux/common/selectors' +import * as actions from '../../redux/common/actions/index' import { useDispatch } from 'react-redux' import { navigateAndReset } from '../../services/navigationService' import { TouchableOpacity } from 'react-native' diff --git a/packages/components/src/screens/settings/PrivacyScreen.tsx b/packages/components/src/screens/settings/PrivacyScreen.tsx index 3549c1fb4..ede766525 100644 --- a/packages/components/src/screens/settings/PrivacyScreen.tsx +++ b/packages/components/src/screens/settings/PrivacyScreen.tsx @@ -5,7 +5,7 @@ import { Header } from '../../components/common/Header' import { TextWithoutTranslation } from '../../components/common/Text' import { ScrollView, Dimensions } from 'react-native' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' import { chunk } from 'lodash' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' diff --git a/packages/components/src/screens/settings/TermsScreen.tsx b/packages/components/src/screens/settings/TermsScreen.tsx index 0d94ce23c..d4722ac54 100644 --- a/packages/components/src/screens/settings/TermsScreen.tsx +++ b/packages/components/src/screens/settings/TermsScreen.tsx @@ -5,7 +5,7 @@ import { Header } from '../../components/common/Header' import { TextWithoutTranslation } from '../../components/common/Text' import { ScrollView, Dimensions, Platform } from 'react-native' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' import { chunk } from 'lodash' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' diff --git a/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx b/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx index c13081d7a..779e8105a 100644 --- a/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx +++ b/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx @@ -3,7 +3,7 @@ import { Dimensions } from 'react-native' import styled from 'styled-components/native' import { assets } from '../../assets/index' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' const deviceWidth = Dimensions.get('window').width const deviceHeight = Dimensions.get('screen').height diff --git a/packages/components/src/screens/tutorial/DayAssetDemo.tsx b/packages/components/src/screens/tutorial/DayAssetDemo.tsx index e300fc38c..93eedb6a3 100644 --- a/packages/components/src/screens/tutorial/DayAssetDemo.tsx +++ b/packages/components/src/screens/tutorial/DayAssetDemo.tsx @@ -9,7 +9,7 @@ import { EmojiSelector } from '../../components/common/EmojiSelector' import { translate } from '../../i18n' import Tts from 'react-native-tts' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' const deviceWidth = Dimensions.get('window').width const deviceHeight = Dimensions.get('window').height diff --git a/packages/components/src/screens/tutorial/NoteAssetDemo.tsx b/packages/components/src/screens/tutorial/NoteAssetDemo.tsx index d212e2cb0..f0d5bd1d2 100644 --- a/packages/components/src/screens/tutorial/NoteAssetDemo.tsx +++ b/packages/components/src/screens/tutorial/NoteAssetDemo.tsx @@ -8,7 +8,7 @@ import { Text } from '../../components/common/Text' import { translate } from '../../i18n' import Tts from 'react-native-tts' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/selectors' +import * as selectors from '../../redux/common/selectors' const deviceWidth = Dimensions.get('window').width const deviceHeight = Dimensions.get('screen').height From 73d0e6835f86b69c3b6b76c115782f7560e27db5 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 22 Dec 2023 12:31:15 +0700 Subject: [PATCH 04/83] Rename actions as commonActions --- .../redux/actions/appActions.test.ts | 6 +-- .../redux/reducers/authReducer.test.ts | 10 ++-- .../src/components/common/LanguageSelect.tsx | 4 +- .../components/context/PredictionProvider.tsx | 8 +-- .../src/redux/common/actions/index.ts | 21 +++++--- .../src/redux/common/sagas/analyticsSaga.ts | 6 +-- .../src/redux/common/sagas/appSaga.ts | 6 +-- .../src/redux/common/sagas/authSaga.ts | 52 +++++++++---------- .../src/redux/common/sagas/contentSaga.ts | 20 +++---- .../redux/common/sagas/smartPredictionSaga.ts | 11 ++-- .../src/redux/common/types/index.ts | 4 +- .../src/screens/AvatarAndThemeScreen.tsx | 6 +-- .../src/screens/EditProfileScreen.tsx | 10 ++-- .../components/src/screens/MainScreen.tsx | 4 +- .../src/screens/OnboardingScreen.tsx | 8 +-- .../src/screens/PasswordRequestScreen.tsx | 4 +- .../components/src/screens/ProfileScreen.tsx | 4 +- .../components/src/screens/SettingsScreen.tsx | 10 ++-- .../components/src/screens/SplashScreen.tsx | 8 +-- .../src/screens/TutorialFirstScreen.tsx | 6 +-- .../src/screens/TutorialSecondScreen.tsx | 6 +-- .../src/screens/authScreen/Login.tsx | 4 +- .../src/screens/authScreen/SignUp.tsx | 4 +- .../src/screens/dayScreen/DayCarousel.tsx | 16 +++--- .../src/screens/dayScreen/NoteCard.tsx | 16 ++++-- .../src/screens/dayScreen/QuizCard.tsx | 4 +- .../src/screens/dayScreen/SurveyCard.tsx | 10 ++-- .../journeyScreen/FinalJourneyCard.tsx | 4 +- .../src/screens/mainScreen/ColourButtons.tsx | 18 ++++--- .../wheelCarousel/CircularElement.tsx | 3 +- .../src/screens/settings/AccessScreen.tsx | 6 +-- 31 files changed, 161 insertions(+), 138 deletions(-) diff --git a/packages/components/__tests__/redux/actions/appActions.test.ts b/packages/components/__tests__/redux/actions/appActions.test.ts index 799303958..8f951bc71 100644 --- a/packages/components/__tests__/redux/actions/appActions.test.ts +++ b/packages/components/__tests__/redux/actions/appActions.test.ts @@ -1,7 +1,7 @@ import configureStore from 'redux-mock-store' import _ from 'lodash' -import * as actions from '../../../src/redux/common/actions' +import { commonActions } from '../../../src/redux/common/actions' const middleWares = [] const mockStore = configureStore(middleWares) @@ -11,7 +11,7 @@ describe('appActions', () => { const store = mockStore(initialState) it('Set Avatar action', () => { - const action = actions.setAvatar('nur') + const action = commonActions.setAvatar('nur') // Dispatch the action store.dispatch(action) // Test if your store dispatched the expected actions @@ -21,7 +21,7 @@ describe('appActions', () => { }) it('Set Theme action', () => { - const action = actions.setTheme('hills') + const action = commonActions.setTheme('hills') // Dispatch the action store.dispatch(action) // Test if your store dispatched the expected actions diff --git a/packages/components/__tests__/redux/reducers/authReducer.test.ts b/packages/components/__tests__/redux/reducers/authReducer.test.ts index e1c970e1f..d207ddc5a 100644 --- a/packages/components/__tests__/redux/reducers/authReducer.test.ts +++ b/packages/components/__tests__/redux/reducers/authReducer.test.ts @@ -3,7 +3,7 @@ import moment from 'moment' import configureStore from 'redux-mock-store' import _ from 'lodash' -import * as actions from '../../../src/redux/common/actions' +import { commonActions } from '../../../src/redux/common/actions' import { authReducer } from '../../../src/redux/common/reducers/authReducer' const middleWares = [] @@ -32,7 +32,7 @@ describe('authReducer', () => { }) it('Create account request actions', () => { - const action = actions.createAccountRequest({ ...mockPayload, id: undefined }) + const action = commonActions.createAccountRequest({ ...mockPayload, id: undefined }) // Dispatch the action store.dispatch(action) // Test if your store dispatched the expected actions @@ -41,7 +41,7 @@ describe('authReducer', () => { expect(scopedActions[0].type).toEqual(expectedType) }) it('Login As guest account', () => { - const action = actions.loginSuccessAsGuestAccount(mockPayload) + const action = commonActions.loginSuccessAsGuestAccount(mockPayload) const newStore = authReducer(undefined, action) // Dispatch the action @@ -49,14 +49,14 @@ describe('authReducer', () => { expect(newStore?.user?.isGuest).toEqual(true) }) it('Login Out guest account', () => { - const action = actions.logout() + const action = commonActions.logout() const newStore = authReducer(undefined, action) // Dispatch the action expect(newStore.user).toEqual(null) }) it('Create account faiure', () => { - const action = actions.createAccountFailure() + const action = commonActions.createAccountFailure() const newStore = authReducer(undefined, action) // Dispatch the action diff --git a/packages/components/src/components/common/LanguageSelect.tsx b/packages/components/src/components/common/LanguageSelect.tsx index 4a4290b21..9d457b085 100644 --- a/packages/components/src/components/common/LanguageSelect.tsx +++ b/packages/components/src/components/common/LanguageSelect.tsx @@ -2,7 +2,7 @@ import React from 'react' import styled from 'styled-components/native' import { useSelector } from '../../hooks/useSelector' import * as selectors from '../../redux/common/selectors' -import * as actions from '../../redux/common/actions/index' +import { commonActions } from '../../redux/common/actions/index' import { useDispatch } from 'react-redux' import { PrimaryButton } from './buttons/PrimaryButton' @@ -27,7 +27,7 @@ export const LanguageSelect = ({ style = null, textStyle = null, onPress = null }} onModalHide={() => { if (lang === '') return - dispatch(actions.setLocale(lang)) + dispatch(commonActions.setLocale(lang)) }} setIsVisible={setModalVisible} isVisible={modalVisible} diff --git a/packages/components/src/components/context/PredictionProvider.tsx b/packages/components/src/components/context/PredictionProvider.tsx index 2eea0b365..988a09bba 100644 --- a/packages/components/src/components/context/PredictionProvider.tsx +++ b/packages/components/src/components/context/PredictionProvider.tsx @@ -5,7 +5,7 @@ import { PredictionState, PredictionEngine } from '../../prediction' import { useSelector } from '../../hooks/useSelector' import { useDispatch } from 'react-redux' -import * as actions from '../../redux/common/actions' +import { commonActions } from '../../redux/common/actions' type PredictionDispatch = typeof PredictionEngine.prototype.userInputDispatch @@ -38,21 +38,21 @@ export function PredictionProvider({ children }) { (action) => { setPredictionSnapshots((snapshots) => snapshots.concat(predictionState)) predictionEngine.userInputDispatch(action) - reduxDispatch(actions.adjustPrediction(action)) + reduxDispatch(commonActions.adjustPrediction(action)) }, [predictionState, reduxDispatch, predictionEngine], ) React.useEffect(() => { return predictionEngine.subscribe((nextPredictionState) => { - reduxDispatch(actions.setPredictionEngineState(nextPredictionState)) + reduxDispatch(commonActions.setPredictionEngineState(nextPredictionState)) }) }, [reduxDispatch, predictionEngine]) const undo = React.useCallback(() => { if (predictionSnapshots.length > 0) { const lastSnapshot = _.last(predictionSnapshots) - reduxDispatch(actions.setPredictionEngineState(PredictionState.fromJSON(lastSnapshot))) + reduxDispatch(commonActions.setPredictionEngineState(PredictionState.fromJSON(lastSnapshot))) setPredictionSnapshots((snapshots) => snapshots.slice(0, -1)) } }, [predictionSnapshots]) diff --git a/packages/components/src/redux/common/actions/index.ts b/packages/components/src/redux/common/actions/index.ts index df4826cce..bef24dbf6 100644 --- a/packages/components/src/redux/common/actions/index.ts +++ b/packages/components/src/redux/common/actions/index.ts @@ -1,6 +1,15 @@ -export * from './analyticsActions' -export * from './answerActions' -export * from './appActions' -export * from './authActions' -export * from './contentActions' -export * from './predictionActions' +import * as analyticsActions from './analyticsActions' +import * as answerActions from './answerActions' +import * as appActions from './appActions' +import * as authActions from './authActions' +import * as contentActions from './contentActions' +import * as predictionActions from './predictionActions' + +export const commonActions = { + ...analyticsActions, + ...answerActions, + ...appActions, + ...authActions, + ...contentActions, + ...predictionActions, +} diff --git a/packages/components/src/redux/common/sagas/analyticsSaga.ts b/packages/components/src/redux/common/sagas/analyticsSaga.ts index 2f850856a..e852c1fce 100644 --- a/packages/components/src/redux/common/sagas/analyticsSaga.ts +++ b/packages/components/src/redux/common/sagas/analyticsSaga.ts @@ -3,7 +3,7 @@ import { v4 as uuidv4 } from 'uuid' import moment from 'moment' import { fetchNetworkConnectionStatus } from '../../../services/network' import { httpClient } from '../../../services/HttpClient' -import * as actions from '../actions' +import { commonActions } from '../actions' import * as selectors from '../selectors' import { ActionTypes } from '../types' @@ -23,7 +23,7 @@ const ACTIONS_TO_TRACK: ActionTypes[] = [ function* onTrackAction(action) { const currentUser = yield select(selectors.currentUserSelector) yield put( - actions.queueEvent({ + commonActions.queueEvent({ id: uuidv4(), type: action.type, payload: action.payload || {}, @@ -56,7 +56,7 @@ function* processEventQueue() { try { yield httpClient.appendEvents({ events, appToken }) - yield put(actions.resetQueue()) + yield put(commonActions.resetQueue()) } catch (err) { // ignore error, we'll try later } diff --git a/packages/components/src/redux/common/sagas/appSaga.ts b/packages/components/src/redux/common/sagas/appSaga.ts index 9ff8fd3ac..6f20e9c62 100644 --- a/packages/components/src/redux/common/sagas/appSaga.ts +++ b/packages/components/src/redux/common/sagas/appSaga.ts @@ -5,7 +5,7 @@ import { fetchNetworkConnectionStatus } from '../../../services/network' import { extractReducerState } from '../sync' import { ReduxState, exportReducerNames } from '../reducers' import { version as storeVersion } from '../../store' -import * as actions from '../actions' +import { commonActions } from '../actions' import * as selectors from '../selectors' import messaging from '@react-native-firebase/messaging' @@ -42,7 +42,7 @@ function* syncAppState() { appToken, }) - const temp = yield put(actions.syncStore()) + const temp = yield put(commonActions.syncStore()) lastAppState = appState } catch (err) { @@ -55,7 +55,7 @@ function* onRequestStoreFirebaseKey() { if (yield fetchNetworkConnectionStatus()) { // no internet connection const firebaseToken = yield messaging().getToken() - yield put(actions.storeFirebaseKey(firebaseToken)) + yield put(commonActions.storeFirebaseKey(firebaseToken)) } } diff --git a/packages/components/src/redux/common/sagas/authSaga.ts b/packages/components/src/redux/common/sagas/authSaga.ts index f5e5224be..7aceb18b8 100644 --- a/packages/components/src/redux/common/sagas/authSaga.ts +++ b/packages/components/src/redux/common/sagas/authSaga.ts @@ -5,7 +5,7 @@ import { v4 as uuidv4 } from 'uuid' import { ExtractActionFromActionType } from '../types' import { httpClient } from '../../../services/HttpClient' import { ReduxState, exportReducerNames } from '../reducers' -import * as actions from '../actions' +import { commonActions } from '../actions' import * as selectors from '../selectors' import { navigateAndReset } from '../../../services/navigationService' import { PredictionState } from '../../../prediction' @@ -24,7 +24,7 @@ function* onRehydrate() { // convert guest account if (!appToken && user && user.isGuest) { - yield put(actions.convertGuestAccount(user)) + yield put(commonActions.convertGuestAccount(user)) } } @@ -43,7 +43,7 @@ function* onConvertGuestAccount(action: ExtractActionFromActionType<'CONVERT_GUE } = action.payload yield put( - actions.createAccountRequest({ + commonActions.createAccountRequest({ id, name, dateOfBirth, @@ -62,7 +62,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { const { name, password } = action.payload const stateRedux: ReduxState = yield select() const localeapp = selectors.currentLocaleSelector(stateRedux) - yield actions.setLocale(localeapp) + yield commonActions.setLocale(localeapp) try { const { @@ -75,7 +75,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { }) yield put( - actions.loginSuccess({ + commonActions.loginSuccess({ appToken, user: { id: user.id, @@ -106,7 +106,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { }, {}) // @TODO: execute migration based on storeVersion - yield put(actions.refreshStore(newAppState)) + yield put(commonActions.refreshStore(newAppState)) } yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones @@ -122,7 +122,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { } } yield put( - actions.loginFailure({ + commonActions.loginFailure({ error: errorMessage, }), ) @@ -162,7 +162,7 @@ function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACC } yield put( - actions.createAccountSuccess({ + commonActions.createAccountSuccess({ appToken, user: { id: user.id, @@ -181,11 +181,11 @@ function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACC } catch (error) { const errorStatusCode = error && error.response && error.response.status ? error.response.status : null // to check various error codes and respond accordingly - yield put(actions.setAuthError({ error: errorStatusCode })) - yield put(actions.createAccountFailure()) + yield put(commonActions.setAuthError({ error: errorStatusCode })) + yield put(commonActions.createAccountFailure()) yield put( - actions.loginSuccessAsGuestAccount({ + commonActions.loginSuccessAsGuestAccount({ id: id || uuidv4(), name, dateOfBirth, @@ -204,7 +204,7 @@ function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACC function* onCreateAccountSuccess(action: ExtractActionFromActionType<'CREATE_ACCOUNT_SUCCESS'>) { const { appToken, user } = action.payload yield put( - actions.loginSuccess({ + commonActions.loginSuccess({ appToken, user: { id: user.id, @@ -232,17 +232,17 @@ function* onDeleteAccountRequest(action: ExtractActionFromActionType<'DELETE_ACC name, password, }) - yield put(actions.updateAllSurveyContent([])) // TODO_ALEX - yield put(actions.updateCompletedSurveys([])) // TODO_ALEX + yield put(commonActions.updateAllSurveyContent([])) // TODO_ALEX + yield put(commonActions.updateCompletedSurveys([])) // TODO_ALEX yield put( - actions.fetchSurveyContentSuccess({ + commonActions.fetchSurveyContentSuccess({ surveys: null, }), // TODO_ALEX ) yield call(navigateAndReset, 'LoginStack', null) if (user) { - yield put(actions.logout()) + yield put(commonActions.logout()) } } catch (err) { setLoading(false) @@ -255,18 +255,18 @@ function* onLogoutRequest() { if (isTtsActive) { yield call(closeOutTTs) - yield put(actions.setTtsActive(false)) - yield put(actions.verifyPeriodDayByUser([])) // TODO_ALEX: survey + yield put(commonActions.setTtsActive(false)) + yield put(commonActions.verifyPeriodDayByUser([])) // TODO_ALEX: survey } - yield put(actions.updateAllSurveyContent([])) // TODO_ALEX: survey + yield put(commonActions.updateAllSurveyContent([])) // TODO_ALEX: survey yield put( - actions.fetchSurveyContentSuccess({ + commonActions.fetchSurveyContentSuccess({ surveys: null, }), ) - yield put(actions.updateCompletedSurveys([])) // TODO_ALEX: survey + yield put(commonActions.updateCompletedSurveys([])) // TODO_ALEX: survey yield call(navigateAndReset, 'LoginStack', null) - yield put(actions.logout()) + yield put(commonActions.logout()) } function* onJourneyCompletion(action: ExtractActionFromActionType<'JOURNEY_COMPLETION'>) { @@ -296,10 +296,10 @@ function* onJourneyCompletion(action: ExtractActionFromActionType<'JOURNEY_COMPL history: [], }) - yield put(actions.setPredictionEngineState(stateToSet)) - yield put(actions.updateFuturePrediction(true, null)) - yield put(actions.setTutorialOneActive(true)) - yield put(actions.setTutorialTwoActive(true)) + yield put(commonActions.setPredictionEngineState(stateToSet)) + yield put(commonActions.updateFuturePrediction(true, null)) + yield put(commonActions.setTutorialOneActive(true)) + yield put(commonActions.setTutorialTwoActive(true)) yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones yield call(navigateAndReset, 'MainStack', null) } diff --git a/packages/components/src/redux/common/sagas/contentSaga.ts b/packages/components/src/redux/common/sagas/contentSaga.ts index 1f0c8d67d..2ad34ac7f 100644 --- a/packages/components/src/redux/common/sagas/contentSaga.ts +++ b/packages/components/src/redux/common/sagas/contentSaga.ts @@ -12,7 +12,7 @@ import { } from '@oky/core' import { httpClient } from '../../../services/HttpClient' import * as selectors from '../selectors' -import * as actions from '../actions' +import { commonActions } from '../actions' import _ from 'lodash' import messaging from '@react-native-firebase/messaging' import { closeOutTTs } from '../../../services/textToSpeech' @@ -23,7 +23,7 @@ function* onRehydrate(action: RehydrateAction) { const hasPreviousContentFromStorage = action.payload && action.payload.content if (!hasPreviousContentFromStorage) { - yield put(actions.initStaleContent(staleContent[locale])) + yield put(commonActions.initStaleContent(staleContent[locale])) } const now = new Date().getTime() @@ -33,7 +33,7 @@ function* onRehydrate(action: RehydrateAction) { const shouldFetch = !timeFetched || timeFetched + fetchInterval < now if (shouldFetch) { - yield put(actions.fetchContentRequest(locale)) + yield put(commonActions.fetchContentRequest(locale)) } } @@ -65,7 +65,7 @@ function* onFetchSurveyContent( } }) - yield put(actions.updateAllSurveyContent(finalArr)) + yield put(commonActions.updateAllSurveyContent(finalArr)) } catch (error) { // } @@ -150,7 +150,7 @@ function* onFetchContentRequest(action: ExtractActionFromActionType<'FETCH_CONTE const aboutBannerData = yield fetchAboutBannerConditional() yield put( - actions.fetchContentSuccess({ + commonActions.fetchContentSuccess({ timeFetched: new Date().getTime(), articles: _.isEmpty(articles.allIds) ? staleContent[locale].articles : articles, videos: _.isEmpty(videos.allIds) ? staleContent[locale].videos : videos, @@ -176,11 +176,11 @@ function* onFetchContentRequest(action: ExtractActionFromActionType<'FETCH_CONTE }), ) } catch (error) { - yield put(actions.fetchContentFailure()) + yield put(commonActions.fetchContentFailure()) const aboutContent = yield select(selectors.aboutContent) if (!aboutContent) { const localeInit = yield select(selectors.currentLocaleSelector) - yield put(actions.initStaleContent(staleContent[localeInit])) + yield put(commonActions.initStaleContent(staleContent[localeInit])) } } } @@ -191,7 +191,7 @@ function* onSetLocale(action: ExtractActionFromActionType<'SET_LOCALE'>) { if (isTtsActive) { // TODO_ALEX why? yield call(closeOutTTs) - yield put(actions.setTtsActive(false)) + yield put(commonActions.setTtsActive(false)) } // unsubscribe from topic // TODO_ALEX: use locales from submodule @@ -199,9 +199,9 @@ function* onSetLocale(action: ExtractActionFromActionType<'SET_LOCALE'>) { messaging().unsubscribeFromTopic('oky_id_notifications') messaging().unsubscribeFromTopic('oky_mn_notifications') messaging().subscribeToTopic(`oky_${locale}_notifications`) - yield put(actions.initStaleContent(staleContent[locale])) + yield put(commonActions.initStaleContent(staleContent[locale])) - yield put(actions.fetchContentRequest(locale)) + yield put(commonActions.fetchContentRequest(locale)) } export function* contentSaga() { diff --git a/packages/components/src/redux/common/sagas/smartPredictionSaga.ts b/packages/components/src/redux/common/sagas/smartPredictionSaga.ts index 8aecbc86a..f62ae1621 100644 --- a/packages/components/src/redux/common/sagas/smartPredictionSaga.ts +++ b/packages/components/src/redux/common/sagas/smartPredictionSaga.ts @@ -3,7 +3,7 @@ import { ExtractActionFromActionType } from '../types' import { httpClient } from '../../../services/HttpClient' -import * as actions from '../actions' +import { commonActions } from '../actions' import _ from 'lodash' import { PredictionState } from '../../../prediction' @@ -34,12 +34,15 @@ function* onFetchUpdatedPredictedCycles( history: predictionFullState.history, actualCurrentStartDate: predictionFullState.currentCycle, }) - yield put(actions.setPredictionEngineState(stateToSet)) + yield put(commonActions.setPredictionEngineState(stateToSet)) yield put( - actions.updateFuturePrediction(futurePredictionStatus, predictionFullState.currentCycle), + commonActions.updateFuturePrediction( + futurePredictionStatus, + predictionFullState.currentCycle, + ), ) } catch (error) { - yield put(actions.setSmartPredictionFailure(error)) + yield put(commonActions.setSmartPredictionFailure(error)) } } diff --git a/packages/components/src/redux/common/types/index.ts b/packages/components/src/redux/common/types/index.ts index a71885637..d16edabc8 100644 --- a/packages/components/src/redux/common/types/index.ts +++ b/packages/components/src/redux/common/types/index.ts @@ -1,7 +1,7 @@ -import * as actions from '../actions' +import { commonActions } from '../actions' import { ActionsUnion, ActionsOfType } from './types' -export type Actions = ActionsUnion +export type Actions = ActionsUnion export type ActionTypes = Actions[keyof Actions] diff --git a/packages/components/src/screens/AvatarAndThemeScreen.tsx b/packages/components/src/screens/AvatarAndThemeScreen.tsx index f0930211d..fc9e00a47 100644 --- a/packages/components/src/screens/AvatarAndThemeScreen.tsx +++ b/packages/components/src/screens/AvatarAndThemeScreen.tsx @@ -6,7 +6,7 @@ import { ThemeSelect } from './avatarAndTheme/ThemeSelect' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { PrimaryButton } from '../components/common/buttons/PrimaryButton' -import * as actions from '../redux/common/actions/index' +import { commonActions } from '../redux/common/actions/index' import { Header } from '../components/common/Header' import { useTheme } from '../components/context/ThemeContext' import { BackOneScreen, navigate } from '../services/navigationService' @@ -57,7 +57,7 @@ export function AvatarAndThemeScreen({ navigation }) { dispatch(actions.setAvatar(avatar))} + onSelect={(avatar) => dispatch(commonActions.setAvatar(avatar))} /> { - dispatch(actions.setTheme(theme)) + dispatch(commonActions.setTheme(theme)) }) } }} diff --git a/packages/components/src/screens/EditProfileScreen.tsx b/packages/components/src/screens/EditProfileScreen.tsx index 36d473cd0..64849efa3 100644 --- a/packages/components/src/screens/EditProfileScreen.tsx +++ b/packages/components/src/screens/EditProfileScreen.tsx @@ -10,7 +10,7 @@ import { DateOfBirthInput } from '../components/common/DateOfBirthInput' import { assets } from '../assets/index' import { useSelector } from '../hooks/useSelector' import * as selectors from '../redux/common/selectors' -import * as actions from '../redux/common/actions' +import { commonActions } from '../redux/common/actions' import { useDispatch } from 'react-redux' import { BackOneScreen } from '../services/navigationService' import { httpClient } from '../services/HttpClient' @@ -117,7 +117,7 @@ export function EditProfileScreen() { }) dispatch( - actions.editUser({ + commonActions.editUser({ name, dateOfBirth, gender, @@ -144,7 +144,7 @@ export function EditProfileScreen() { }) dispatch( - actions.editUser({ + commonActions.editUser({ secretAnswer: _.toLower(secretAnswer).trim(), }), ) @@ -182,7 +182,7 @@ export function EditProfileScreen() { }) dispatch( - actions.editUser({ + commonActions.editUser({ password: _.toLower(password).trim(), }), ) @@ -205,7 +205,7 @@ export function EditProfileScreen() { } dispatch( - actions.editUser({ + commonActions.editUser({ name, dateOfBirth, gender, diff --git a/packages/components/src/screens/MainScreen.tsx b/packages/components/src/screens/MainScreen.tsx index a085abd14..157fb2ad1 100644 --- a/packages/components/src/screens/MainScreen.tsx +++ b/packages/components/src/screens/MainScreen.tsx @@ -20,7 +20,7 @@ import { import { useRandomText } from '../hooks/useRandomText' import { InformationButton } from '../components/common/InformationButton' import { assets } from '../assets' -import * as actions from '../redux/common/actions' +import { commonActions } from '../redux/common/actions' import { useDispatch } from 'react-redux' import { useSelector } from '../hooks/useSelector' import * as selectors from '../redux/common/selectors' @@ -51,7 +51,7 @@ const MainScreenContainer = ({ navigation }) => { // @TODO: careful note here, may be worth the performance increase though May not work with Memo now React.useEffect(() => { - dispatch(actions.fetchSurveyContentRequest(userID)) + dispatch(commonActions.fetchSurveyContentRequest(userID)) }, []) useRandomText({ navigation }) diff --git a/packages/components/src/screens/OnboardingScreen.tsx b/packages/components/src/screens/OnboardingScreen.tsx index d359eb978..ae6e615e6 100644 --- a/packages/components/src/screens/OnboardingScreen.tsx +++ b/packages/components/src/screens/OnboardingScreen.tsx @@ -7,7 +7,7 @@ import { OnboardingCard } from './onboardingScreen/OnboardingCard' import { assets } from '../assets/index' import { PrimaryButton } from '../components/common/buttons/PrimaryButton' import { navigateAndReset } from '../services/navigationService' -import * as actions from '../redux/common/actions' +import { commonActions } from '../redux/common/actions' import { useDispatch } from 'react-redux' import { Animated } from 'react-native' import { Text } from '../components/common/Text' @@ -28,8 +28,8 @@ export function OnboardingScreen() { // @TODO: LANGUAGES This is commented in case the client wants multiple languages // const onPenalCodeComplete = lang => { - // dispatch(actions.setChosenRegion(lang)) - // dispatch(actions.setLocale(lang)) + // dispatch(commonActions.setChosenRegion(lang)) + // dispatch(commonActions.setLocale(lang)) // } return ( @@ -70,7 +70,7 @@ export function OnboardingScreen() { }} textStyle={{ color: 'white' }} onPress={() => { - dispatch(actions.setHasOpened(true)) + dispatch(commonActions.setHasOpened(true)) navigateAndReset('LoginStack', null) }} > diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index 05f1584ff..45bed8837 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -1,7 +1,7 @@ import React from 'react' import styled from 'styled-components/native' import { useDispatch } from 'react-redux' -import * as actions from '../redux/common/actions' +import { commonActions } from '../redux/common/actions' import { Text } from '../components/common/Text' import { TextInput } from '../components/common/TextInput' import * as selectors from '../redux/common/selectors' @@ -91,7 +91,7 @@ export function PasswordRequestScreen() { { - dispatch(actions.logoutRequest()) + dispatch(commonActions.logoutRequest()) }} > { setError(false) dispatch( - actions.convertGuestAccount({ + commonActions.convertGuestAccount({ id: currentUser.id, name: currentUser.name, dateOfBirth: currentUser.dateOfBirth, diff --git a/packages/components/src/screens/SettingsScreen.tsx b/packages/components/src/screens/SettingsScreen.tsx index dcc9b562a..c82c8e91b 100644 --- a/packages/components/src/screens/SettingsScreen.tsx +++ b/packages/components/src/screens/SettingsScreen.tsx @@ -8,7 +8,7 @@ import { PrimaryButton } from '../components/common/buttons/PrimaryButton' import { Switcher } from './settings/Switcher' import { navigate } from '../services/navigationService' import { useDispatch } from 'react-redux' -import * as actions from '../redux/common/actions' +import { commonActions } from '../redux/common/actions' import { ConfirmAlert } from '../components/common/ConfirmAlert' import { useSelector } from '../hooks/useSelector' import * as selectors from '../redux/common/selectors' @@ -64,7 +64,7 @@ export function SettingsScreen({ navigation }) { } } closeOutTTs() - dispatch(actions.setTtsActive(val)) + dispatch(commonActions.setTtsActive(val)) }} /> )} @@ -77,7 +77,7 @@ export function SettingsScreen({ navigation }) { value={hasFuturePredictionActive?.futurePredictionStatus} onSwitch={(val) => { const currentStartDate = currentCycleInfo - dispatch(actions.updateFuturePrediction(val, currentStartDate)) + dispatch(commonActions.updateFuturePrediction(val, currentStartDate)) }} /> )} @@ -94,7 +94,7 @@ export function SettingsScreen({ navigation }) { () => { setLoading(true) setTimeout(() => { - dispatch(actions.logoutRequest()) + dispatch(commonActions.logoutRequest()) }, 100) }, ) @@ -114,7 +114,7 @@ export function SettingsScreen({ navigation }) { analytics().logEvent('delete_account', { user: currentUser }) } dispatch( - actions.deleteAccountRequest({ + commonActions.deleteAccountRequest({ name: currentUser.name, password: currentUser.password, setLoading, diff --git a/packages/components/src/screens/SplashScreen.tsx b/packages/components/src/screens/SplashScreen.tsx index 5d2f0f2c5..db68d1378 100644 --- a/packages/components/src/screens/SplashScreen.tsx +++ b/packages/components/src/screens/SplashScreen.tsx @@ -5,7 +5,7 @@ import { assets } from '../assets/index' import styled from 'styled-components/native' import { useSelector, useDispatch } from 'react-redux' import * as selectors from '../redux/common/selectors' -import * as actions from '../redux/common/actions' +import { commonActions } from '../redux/common/actions' import { navigateAndReset } from '../services/navigationService' import { Animated, Easing } from 'react-native' import { createNotificationChannel, requestUserPermission } from '../services/notifications' @@ -57,12 +57,12 @@ export function SplashScreen() { messaging().unsubscribeFromTopic('oky_mn_notifications') messaging().subscribeToTopic(`oky_${locale}_notifications`) if (currentAppVersion !== DeviceInfo.getVersion()) { - dispatch(actions.setUpdatedVersion()) - dispatch(actions.updateFuturePrediction(true, null)) + dispatch(commonActions.setUpdatedVersion()) + dispatch(commonActions.updateFuturePrediction(true, null)) } if (fetchNetworkConnectionStatus()) { if (currentFirebaseToken === null) { - dispatch(actions.requestStoreFirebaseKey()) + dispatch(commonActions.requestStoreFirebaseKey()) } } diff --git a/packages/components/src/screens/TutorialFirstScreen.tsx b/packages/components/src/screens/TutorialFirstScreen.tsx index f375c92c8..5b7da1ca8 100644 --- a/packages/components/src/screens/TutorialFirstScreen.tsx +++ b/packages/components/src/screens/TutorialFirstScreen.tsx @@ -11,7 +11,7 @@ import { useInfiniteScroll } from './mainScreen/wheelCarousel/useInfiniteScroll' import { navigateAndReset } from '../services/navigationService' import { Animated, Dimensions, Platform } from 'react-native' import { useDispatch } from 'react-redux' -import * as actions from '../redux/common/actions' +import { commonActions } from '../redux/common/actions' import { Text } from '../components/common/Text' import { Icon } from '../components/common/Icon' import { assets } from '../assets' @@ -204,7 +204,7 @@ export function TutorialFirstScreen() { flag.current = true } if (flag.current) { - dispatch(actions.setTutorialOneActive(false)) + dispatch(commonActions.setTutorialOneActive(false)) setLoading(true) requestAnimationFrame(() => { setTimeout(() => { @@ -260,7 +260,7 @@ export function TutorialFirstScreen() { const skip = () => { flag.current = true - dispatch(actions.setTutorialOneActive(false)) + dispatch(commonActions.setTutorialOneActive(false)) setLoading(true) requestAnimationFrame(() => { setTimeout(() => { diff --git a/packages/components/src/screens/TutorialSecondScreen.tsx b/packages/components/src/screens/TutorialSecondScreen.tsx index 0998886d7..0496efda2 100644 --- a/packages/components/src/screens/TutorialSecondScreen.tsx +++ b/packages/components/src/screens/TutorialSecondScreen.tsx @@ -11,7 +11,7 @@ import { useInfiniteScroll } from './mainScreen/wheelCarousel/useInfiniteScroll' import { navigateAndReset } from '../services/navigationService' import { Animated, Dimensions, Image, Platform, View } from 'react-native' import { useDispatch } from 'react-redux' -import * as actions from '../redux/common/actions' +import { commonActions } from '../redux/common/actions' import { Text } from '../components/common/Text' import { Icon } from '../components/common/Icon' import { assets } from '../assets' @@ -209,7 +209,7 @@ export function TutorialSecondScreen({ navigation }) { flag.current = true } if (flag.current) { - dispatch(actions.setTutorialTwoActive(false)) + dispatch(commonActions.setTutorialTwoActive(false)) setLoading(true) requestAnimationFrame(() => { setTimeout(() => { @@ -381,7 +381,7 @@ export function TutorialSecondScreen({ navigation }) { } const skip = () => { - dispatch(actions.setTutorialTwoActive(false)) + dispatch(commonActions.setTutorialTwoActive(false)) setLoading(true) requestAnimationFrame(() => { setTimeout(() => { diff --git a/packages/components/src/screens/authScreen/Login.tsx b/packages/components/src/screens/authScreen/Login.tsx index 1202ef435..337388716 100644 --- a/packages/components/src/screens/authScreen/Login.tsx +++ b/packages/components/src/screens/authScreen/Login.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components/native' import { Text } from '../../components/common/Text' import { TextInput } from '../../components/common/TextInput' import { useDispatch } from 'react-redux' -import * as actions from '../../redux/common/actions' +import { commonActions } from '../../redux/common/actions' import { useSelector } from '../../hooks/useSelector' import { SpinLoader } from '../../components/common/SpinLoader' import _ from 'lodash' @@ -46,7 +46,7 @@ export function Login() { onPress={() => { setLoading(true) requestAnimationFrame(() => { - dispatch(actions.loginRequest({ name, password: _.toLower(password).trim() })) + dispatch(commonActions.loginRequest({ name, password: _.toLower(password).trim() })) }) }} > diff --git a/packages/components/src/screens/authScreen/SignUp.tsx b/packages/components/src/screens/authScreen/SignUp.tsx index e1672f036..3ab1b7311 100644 --- a/packages/components/src/screens/authScreen/SignUp.tsx +++ b/packages/components/src/screens/authScreen/SignUp.tsx @@ -8,7 +8,7 @@ import { AskAge } from './signUp/AskAge' import { AskLocation } from './signUp/AskLocation' import { AskUserConfirmation } from './signUp/AskUserConfirmation' import { navigate } from '../../services/navigationService' -import * as actions from '../../redux/common/actions' +import { commonActions } from '../../redux/common/actions' import _ from 'lodash' import { FAST_SIGN_UP } from '../../config' @@ -64,7 +64,7 @@ export function SignUp({ heightInner }) { answer, }) => { dispatch( - actions.createAccountRequest({ + commonActions.createAccountRequest({ id: uuidv4(), name, dateOfBirth, diff --git a/packages/components/src/screens/dayScreen/DayCarousel.tsx b/packages/components/src/screens/dayScreen/DayCarousel.tsx index d36eb4454..2a8762a2e 100644 --- a/packages/components/src/screens/dayScreen/DayCarousel.tsx +++ b/packages/components/src/screens/dayScreen/DayCarousel.tsx @@ -3,7 +3,7 @@ import _ from 'lodash' import { FlatList, Dimensions, KeyboardAvoidingView } from 'react-native' import { DayCarouselItem } from './DayCarouselItem' import { useDispatch } from 'react-redux' -import * as actions from '../../redux/common/actions' +import { commonActions } from '../../redux/common/actions' import { NoteCard } from './NoteCard' import { QuizCard } from './QuizCard' import { DidYouKnowCard } from './DidYouKnowCard' @@ -225,7 +225,7 @@ export function DayCarousel({ navigation, dataEntry }) { setEndSurvey(true) }, 5000) dispatch( - actions.answerSurvey({ + commonActions.answerSurvey({ id: newSurveys?.id, isCompleted: true, isSurveyAnswered: true, @@ -238,10 +238,10 @@ export function DayCarousel({ navigation, dataEntry }) { if (allSurveys?.length) { const tempData = allSurveys const tempCompletedSurveys = completedSurveys ? completedSurveys : [] - dispatch(actions.updateCompletedSurveys([tempData[0], ...tempCompletedSurveys])) + dispatch(commonActions.updateCompletedSurveys([tempData[0], ...tempCompletedSurveys])) tempData.shift() - dispatch(actions.updateAllSurveyContent(tempData)) + dispatch(commonActions.updateAllSurveyContent(tempData)) } }, 2000) } else { @@ -261,7 +261,7 @@ export function DayCarousel({ navigation, dataEntry }) { currentSurvey.answeredQuestion = currQuestion?.is_multiple ? tempAnswer : answersArray const tempData = [...allSurveys] tempData[0] = currentSurvey - dispatch(actions.updateAllSurveyContent(tempData)) + dispatch(commonActions.updateAllSurveyContent(tempData)) } } } @@ -332,7 +332,7 @@ export function DayCarousel({ navigation, dataEntry }) { setTempCardAnswer(answer) setIsVisible(true) dispatch( - actions.answerDailyCard({ + commonActions.answerDailyCard({ cardName: tempCardName, answer: tempCardAnswer, userID, @@ -344,7 +344,7 @@ export function DayCarousel({ navigation, dataEntry }) { return } dispatch( - actions.answerDailyCard({ + commonActions.answerDailyCard({ cardName, answer, userID, @@ -370,7 +370,7 @@ export function DayCarousel({ navigation, dataEntry }) { hide={() => { setIsVisible(false) dispatch( - actions.answerDailyCard({ + commonActions.answerDailyCard({ cardName: tempCardName, answer: tempCardAnswer, userID, diff --git a/packages/components/src/screens/dayScreen/NoteCard.tsx b/packages/components/src/screens/dayScreen/NoteCard.tsx index db9f77c5b..2d1b2d79b 100644 --- a/packages/components/src/screens/dayScreen/NoteCard.tsx +++ b/packages/components/src/screens/dayScreen/NoteCard.tsx @@ -2,7 +2,7 @@ import React from 'react' import { Dimensions } from 'react-native' import styled from 'styled-components/native' import * as selectors from '../../redux/common/selectors' -import * as actions from '../../redux/common/actions' +import { commonActions } from '../../redux/common/actions' import { useSelector } from '../../hooks/useSelector' import { TextInput } from '../../components/common/TextInput' import { useDispatch } from 'react-redux' @@ -49,7 +49,12 @@ export function NoteCard({ dataEntry }) { onChange={(text) => setTitle(text)} onEndEditing={() => dispatch( - actions.answerNotesCard({ title, notes, userID, utcDateTime: dataEntry.date }), + commonActions.answerNotesCard({ + title, + notes, + userID, + utcDateTime: dataEntry.date, + }), ) } label={titlePlaceholder} @@ -69,7 +74,12 @@ export function NoteCard({ dataEntry }) { onBlur={() => setNotesPlaceholder('daily_note_description')} onEndEditing={() => dispatch( - actions.answerNotesCard({ title, notes, userID, utcDateTime: dataEntry.date }), + commonActions.answerNotesCard({ + title, + notes, + userID, + utcDateTime: dataEntry.date, + }), ) } label={notesPlaceholder} diff --git a/packages/components/src/screens/dayScreen/QuizCard.tsx b/packages/components/src/screens/dayScreen/QuizCard.tsx index bc2a7ce8f..4eb150468 100644 --- a/packages/components/src/screens/dayScreen/QuizCard.tsx +++ b/packages/components/src/screens/dayScreen/QuizCard.tsx @@ -7,7 +7,7 @@ import { TitleText } from '../../components/common/TitleText' import { useSelector } from '../../hooks/useSelector' import _ from 'lodash' import * as selectors from '../../redux/common/selectors' -import * as actions from '../../redux/common/actions' +import { commonActions } from '../../redux/common/actions' import { useDispatch } from 'react-redux' const deviceWidth = Dimensions.get('window').width @@ -40,7 +40,7 @@ export const QuizCard = React.memo<{ dataEntry: any; index: number }>(({ dataEnt color={'pink'} onPress={() => dispatch( - actions.answerQuiz({ + commonActions.answerQuiz({ id: selectedQuestion.id, answerID: ind + 1, question: selectedQuestion.question, diff --git a/packages/components/src/screens/dayScreen/SurveyCard.tsx b/packages/components/src/screens/dayScreen/SurveyCard.tsx index c197eb33c..f165f17cf 100644 --- a/packages/components/src/screens/dayScreen/SurveyCard.tsx +++ b/packages/components/src/screens/dayScreen/SurveyCard.tsx @@ -7,7 +7,7 @@ import { TitleText } from '../../components/common/TitleText' import { useSelector } from '../../hooks/useSelector' import _ from 'lodash' import * as selectors from '../../redux/common/selectors' -import * as actions from '../../redux/common/actions' +import { commonActions } from '../../redux/common/actions' import { useDispatch } from 'react-redux' import { TextInput } from '../../components/common/TextInput' import { SurveyInformationButton } from '../../components/common/SurveyInformationButton' @@ -57,7 +57,7 @@ export const SurveyCard = React.memo<{ // TODO_ALEX: Does this do anything? dispatch( - actions.answerSurvey({ + commonActions.answerSurvey({ id: dataEntry.surveyId, isCompleted: true, isSurveyAnswered: false, @@ -68,10 +68,10 @@ export const SurveyCard = React.memo<{ ) const tempData = allSurveys const tempCompletedSurveys = completedSurveys ? completedSurveys : [] - dispatch(actions.updateCompletedSurveys([tempData[0], ...tempCompletedSurveys])) + dispatch(commonActions.updateCompletedSurveys([tempData[0], ...tempCompletedSurveys])) tempData.shift() - dispatch(actions.updateAllSurveyContent(tempData)) - dispatch(actions.fetchSurveyContentRequest(userID)) + dispatch(commonActions.updateAllSurveyContent(tempData)) + dispatch(commonActions.fetchSurveyContentRequest(userID)) } } else { setTimeout(() => { diff --git a/packages/components/src/screens/journeyScreen/FinalJourneyCard.tsx b/packages/components/src/screens/journeyScreen/FinalJourneyCard.tsx index 378d86c3a..c7b9eb885 100644 --- a/packages/components/src/screens/journeyScreen/FinalJourneyCard.tsx +++ b/packages/components/src/screens/journeyScreen/FinalJourneyCard.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components/native' import { Text, TextWithoutTranslation } from '../../components/common/Text' import { assets } from '../../assets/index' import { translate } from '../../i18n/index' -import * as actions from '../../redux/common/actions/index' +import { commonActions } from '../../redux/common/actions/index' import { useDispatch } from 'react-redux' import moment from 'moment' import { SpinLoader } from '../../components/common/SpinLoader' @@ -74,7 +74,7 @@ export function FinalJourneyCard({ cards, questionAnswers, goToQuestion }) { onPress={() => { setLoading(true) requestAnimationFrame(() => { - dispatch(actions.journeyCompletion(questionAnswers)) + dispatch(commonActions.journeyCompletion(questionAnswers)) }) }} > diff --git a/packages/components/src/screens/mainScreen/ColourButtons.tsx b/packages/components/src/screens/mainScreen/ColourButtons.tsx index aa10e6e62..39828405a 100644 --- a/packages/components/src/screens/mainScreen/ColourButtons.tsx +++ b/packages/components/src/screens/mainScreen/ColourButtons.tsx @@ -18,7 +18,7 @@ import { InformationButton } from '../../components/common/InformationButton' import { decisionProcessNonPeriod, decisionProcessPeriod } from './predictionLogic/predictionLogic' import { translate } from '../../i18n' import { useDispatch } from 'react-redux' -import * as actions from '../../redux/common/actions/index' +import { commonActions } from '../../redux/common/actions/index' import * as selectors from '../../redux/common/selectors' import analytics from '@react-native-firebase/analytics' import moment from 'moment' @@ -128,7 +128,7 @@ export function ColourButtons({ } } appDispatch( - actions.smartPredictionRequest({ + commonActions.smartPredictionRequest({ cycle_lengths: tempPeriodsCycles, period_lengths: tempPeriodsLength, age: moment().diff(moment(currentUser.dateOfBirth), 'years'), @@ -138,7 +138,9 @@ export function ColourButtons({ ) } } - appDispatch(actions.updateFuturePrediction(futurePredictionStatus, fullState.currentCycle)) + appDispatch( + commonActions.updateFuturePrediction(futurePredictionStatus, fullState.currentCycle), + ) } const actionPink = decisionProcessPeriod({ @@ -215,7 +217,7 @@ export function ColourButtons({ }) } appDispatch( - actions.answerVerifyDates({ + commonActions.answerVerifyDates({ userID, utcDateTime: inputDay, periodDay: true, @@ -235,7 +237,7 @@ export function ColourButtons({ if (addNewCycleHistory) { if (selectedDayInfo.onPeriod) { appDispatch( - actions.answerVerifyDates({ + commonActions.answerVerifyDates({ userID, utcDateTime: inputDay, periodDay: true, @@ -250,7 +252,7 @@ export function ColourButtons({ getPredictedCycles, }) appDispatch( - actions.answerVerifyDates({ + commonActions.answerVerifyDates({ userID, utcDateTime: inputDay, periodDay: true, @@ -261,7 +263,7 @@ export function ColourButtons({ } else { if (selectedDayInfo.onPeriod) { appDispatch( - actions.answerVerifyDates({ + commonActions.answerVerifyDates({ userID, utcDateTime: inputDay, periodDay: true, @@ -294,7 +296,7 @@ export function ColourButtons({ getPredictedCycles, }) appDispatch( - actions.answerVerifyDates({ + commonActions.answerVerifyDates({ userID, utcDateTime: inputDay, periodDay: false, diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx index dee5ea705..49b47a8a5 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx @@ -6,8 +6,7 @@ import { useTheme } from '../../../components/context/ThemeContext' import { TouchableOpacity } from 'react-native-gesture-handler' import { assets } from '../../../assets/index' import { translate } from '../../../i18n' -import { useSelector, useDispatch } from 'react-redux' -import * as actions from '../../../redux/common/actions/index' +import { useSelector } from 'react-redux' import * as selectors from '../../../redux/common/selectors' import moment from 'moment' import { diff --git a/packages/components/src/screens/settings/AccessScreen.tsx b/packages/components/src/screens/settings/AccessScreen.tsx index d26f7df0b..820770e59 100644 --- a/packages/components/src/screens/settings/AccessScreen.tsx +++ b/packages/components/src/screens/settings/AccessScreen.tsx @@ -6,7 +6,7 @@ import { ListItem } from './accessScreen/ListItem' import { Header } from '../../components/common/Header' import { useSelector } from '../../hooks/useSelector' import * as selectors from '../../redux/common/selectors' -import * as actions from '../../redux/common/actions/index' +import { commonActions } from '../../redux/common/actions/index' import { useDispatch } from 'react-redux' import { navigateAndReset } from '../../services/navigationService' import { TouchableOpacity } from 'react-native' @@ -27,7 +27,7 @@ export function AccessScreen({ navigation }) { const speechText = privacyContent.map((item) => item.content) const shareLink = () => { // @TODO: app event - dispatch(actions.shareApp()) + dispatch(commonActions.shareApp()) const options = { url: WEBSITE_URL, message: translate('join_oky_message'), @@ -57,7 +57,7 @@ export function AccessScreen({ navigation }) { if (lang !== locale) { setLoading(true) requestAnimationFrame(() => { - dispatch(actions.setLocale(lang)) + dispatch(commonActions.setLocale(lang)) }) } }} From 1b06594aa40ffa578e850b14f4a15407e11ef3ae Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 22 Dec 2023 12:35:35 +0700 Subject: [PATCH 05/83] Rename selectors as commonSelectors --- .../src/components/common/Avatar/Avatar.tsx | 8 +++++--- .../src/components/common/CalendarList.tsx | 4 ++-- .../src/components/common/DateBadge.tsx | 4 ++-- .../src/components/common/DayBadge.tsx | 4 ++-- .../src/components/common/LanguageSelect.tsx | 4 ++-- .../components/context/DisplayTextContext.tsx | 6 +++--- .../src/components/context/ThemeContext.tsx | 4 ++-- .../src/hooks/useTextToSpeechHook.ts | 4 ++-- .../src/redux/common/sagas/analyticsSaga.ts | 8 ++++---- .../src/redux/common/sagas/appSaga.ts | 4 ++-- .../src/redux/common/sagas/authSaga.ts | 14 +++++++------- .../src/redux/common/sagas/contentSaga.ts | 18 +++++++++--------- .../src/redux/common/selectors/index.ts | 18 +++++++++++++----- .../components/src/screens/ArticlesScreen.tsx | 8 ++++---- packages/components/src/screens/AuthScreen.tsx | 2 +- .../src/screens/AvatarAndThemeScreen.tsx | 4 ++-- .../components/src/screens/ContactUsScreen.tsx | 6 +++--- packages/components/src/screens/DayScreen.tsx | 4 ++-- .../src/screens/EditProfileScreen.tsx | 6 +++--- .../src/screens/EncyclopediaScreen.tsx | 12 ++++++------ .../components/src/screens/FindHelpScreen.tsx | 4 ++-- packages/components/src/screens/MainScreen.tsx | 10 +++++----- .../src/screens/OnboardingScreen.tsx | 2 +- .../src/screens/PasswordRequestScreen.tsx | 4 ++-- .../components/src/screens/ProfileScreen.tsx | 8 ++++---- .../components/src/screens/SettingsScreen.tsx | 8 ++++---- .../components/src/screens/SplashScreen.tsx | 14 +++++++------- .../src/screens/TutorialFirstScreen.tsx | 6 +++--- .../src/screens/TutorialSecondScreen.tsx | 6 +++--- .../components/src/screens/VideosScreen.tsx | 6 +++--- .../screens/authScreen/signUp/AskLocation.tsx | 4 ++-- .../src/screens/dayScreen/DayCarousel.tsx | 8 ++++---- .../src/screens/dayScreen/DayCarouselItem.tsx | 6 ++++-- .../src/screens/dayScreen/DidYouKnowCard.tsx | 4 ++-- .../src/screens/dayScreen/NoteCard.tsx | 6 +++--- .../src/screens/dayScreen/QuizCard.tsx | 12 +++++++----- .../src/screens/dayScreen/SurveyCard.tsx | 8 ++++---- .../screens/encyclopediaScreen/SearchBar.tsx | 6 +++--- .../src/screens/journeyScreen/JourneyCard.tsx | 4 ++-- .../src/screens/mainScreen/Calendar.tsx | 8 +++++--- .../src/screens/mainScreen/ColourButtons.tsx | 10 +++++----- .../mainScreen/wheelCarousel/Carousel.tsx | 4 ++-- .../wheelCarousel/CarouselElement.tsx | 6 +++--- .../wheelCarousel/CircularElement.tsx | 4 ++-- .../wheelCarousel/CircularSelection.tsx | 9 ++++++--- .../src/screens/profileScreen/CycleCard.tsx | 4 ++-- .../src/screens/settings/AboutScreen.tsx | 8 ++++---- .../src/screens/settings/AccessScreen.tsx | 6 +++--- .../src/screens/settings/PrivacyScreen.tsx | 4 ++-- .../src/screens/settings/TermsScreen.tsx | 4 ++-- .../src/screens/tutorial/CalendarAssetDemo.tsx | 4 ++-- .../src/screens/tutorial/DayAssetDemo.tsx | 4 ++-- .../src/screens/tutorial/NoteAssetDemo.tsx | 4 ++-- 53 files changed, 183 insertions(+), 164 deletions(-) diff --git a/packages/components/src/components/common/Avatar/Avatar.tsx b/packages/components/src/components/common/Avatar/Avatar.tsx index 1a6c77338..d77297f2f 100644 --- a/packages/components/src/components/common/Avatar/Avatar.tsx +++ b/packages/components/src/components/common/Avatar/Avatar.tsx @@ -9,7 +9,7 @@ import styled from 'styled-components/native' import { Icon } from '../Icon' import { HeartAnimation } from './HeartAnimation' import { useSelector } from '../../../hooks/useSelector' -import * as selectors from '../../../redux/common/selectors/index' +import { commonSelectors } from '../../../redux/common/selectors/index' import moment from 'moment' import { useDisplayText } from '../../context/DisplayTextContext' import { useTodayPrediction } from '../../context/PredictionProvider' @@ -37,11 +37,13 @@ export function Avatar({ const isJumpingToggled = React.useRef(false) const isDancingToggled = React.useRef(false) const randomDance = React.useRef(1) - const selectedAvatar = useSelector(selectors.currentAvatarSelector) + const selectedAvatar = useSelector(commonSelectors.currentAvatarSelector) const [animatedProgress] = React.useState(new Animated.Value(0)) const { onPeriod } = useTodayPrediction() - const cardAnswersToday = useSelector((state) => selectors.cardAnswerSelector(state, moment.utc())) + const cardAnswersToday = useSelector((state) => + commonSelectors.cardAnswerSelector(state, moment.utc()), + ) React.useEffect(() => { const intervalId = setTimeout(hideDisplayText, 3000) return () => { diff --git a/packages/components/src/components/common/CalendarList.tsx b/packages/components/src/components/common/CalendarList.tsx index 325149f7c..8304a7e35 100644 --- a/packages/components/src/components/common/CalendarList.tsx +++ b/packages/components/src/components/common/CalendarList.tsx @@ -4,7 +4,7 @@ import { CalendarList as DefaultCalendarList, LocaleConfig } from 'react-native- import momentTimezone from 'moment-timezone' import { assets } from '../../assets/index' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { calendarTranslations } from '@oky/core' LocaleConfig.locales = { @@ -19,7 +19,7 @@ export function CalendarList({ setInputDay, width = null, }: any) { - const locale = useSelector(selectors.currentLocaleSelector) + const locale = useSelector(commonSelectors.currentLocaleSelector) LocaleConfig.defaultLocale = locale const [markedDates, setMarkedDates] = React.useState({}) const calendarRef = React.useRef() diff --git a/packages/components/src/components/common/DateBadge.tsx b/packages/components/src/components/common/DateBadge.tsx index 0a26432f2..916ab0cd7 100644 --- a/packages/components/src/components/common/DateBadge.tsx +++ b/packages/components/src/components/common/DateBadge.tsx @@ -7,7 +7,7 @@ import { TouchableOpacity } from 'react-native' import _ from 'lodash' import moment from 'moment' import { useSelector } from 'react-redux' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { useTodayPrediction, useActualCurrentStartDateSelector, @@ -69,7 +69,7 @@ export function DateBadge({ dataEntry, style, textStyle = null, showModal, cardV const { id: themeName } = useTheme() const currentCycleInfo = useTodayPrediction() const actualCurrentStartDate = useActualCurrentStartDateSelector() - const hasFuturePredictionActive = useSelector(selectors.isFuturePredictionSelector) + const hasFuturePredictionActive = useSelector(commonSelectors.isFuturePredictionSelector) const source = useStatusForSource( dataEntry, diff --git a/packages/components/src/components/common/DayBadge.tsx b/packages/components/src/components/common/DayBadge.tsx index 30deb93c9..2ca66b712 100644 --- a/packages/components/src/components/common/DayBadge.tsx +++ b/packages/components/src/components/common/DayBadge.tsx @@ -5,7 +5,7 @@ import { TextWithoutTranslation, Text } from './Text' import _ from 'lodash' import moment from 'moment' import { useSelector } from 'react-redux' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { useTodayPrediction, useActualCurrentStartDateSelector, @@ -51,7 +51,7 @@ function useStatusForSource( export const DayBadge = ({ dataEntry, style, fontSizes, cardValues }) => { const currentCycleInfo = useTodayPrediction() - const hasFuturePredictionActive = useSelector(selectors.isFuturePredictionSelector) + const hasFuturePredictionActive = useSelector(commonSelectors.isFuturePredictionSelector) const actualCurrentStartDate = useActualCurrentStartDateSelector() diff --git a/packages/components/src/components/common/LanguageSelect.tsx b/packages/components/src/components/common/LanguageSelect.tsx index 9d457b085..ec1c5c9b1 100644 --- a/packages/components/src/components/common/LanguageSelect.tsx +++ b/packages/components/src/components/common/LanguageSelect.tsx @@ -1,7 +1,7 @@ import React from 'react' import styled from 'styled-components/native' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { commonActions } from '../../redux/common/actions/index' import { useDispatch } from 'react-redux' @@ -13,7 +13,7 @@ import { availableAppLocales } from '@oky/core' export const LanguageSelect = ({ style = null, textStyle = null, onPress = null }) => { const [modalVisible, setModalVisible] = React.useState(false) const [lang, setLang] = React.useState('') - const locale = useSelector(selectors.currentLocaleSelector) + const locale = useSelector(commonSelectors.currentLocaleSelector) const dispatch = useDispatch() return ( <> diff --git a/packages/components/src/components/context/DisplayTextContext.tsx b/packages/components/src/components/context/DisplayTextContext.tsx index dff4b43b4..3c6f755f2 100644 --- a/packages/components/src/components/context/DisplayTextContext.tsx +++ b/packages/components/src/components/context/DisplayTextContext.tsx @@ -3,7 +3,7 @@ import Tts from 'react-native-tts' import _ from 'lodash' import { translate } from '../../i18n' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' interface Props { text: null | string @@ -15,10 +15,10 @@ interface Props { const DisplayTextContext = React.createContext(undefined) export function DisplayTextProvider({ children }) { - const availableText = useSelector(selectors.allAvatarText) + const availableText = useSelector(commonSelectors.allAvatarText) const [text, setText] = React.useState(null) - const hasTtsActive = useSelector(selectors.isTtsActiveSelector) + const hasTtsActive = useSelector(commonSelectors.isTtsActiveSelector) const setDisplayTextRandom = () => { setText(_.sample(availableText).content) diff --git a/packages/components/src/components/context/ThemeContext.tsx b/packages/components/src/components/context/ThemeContext.tsx index 6a1c44fce..686e27b62 100644 --- a/packages/components/src/components/context/ThemeContext.tsx +++ b/packages/components/src/components/context/ThemeContext.tsx @@ -1,12 +1,12 @@ import React from 'react' import { ThemeContext, ThemeProvider as StyledThemeProvider } from 'styled-components' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { themes } from '@oky/core' export function ThemeProvider({ children }) { const themeName = useSelector((state) => state.app.theme) - const locale = useSelector(selectors.currentLocaleSelector) + const locale = useSelector(commonSelectors.currentLocaleSelector) return ( { diff --git a/packages/components/src/redux/common/sagas/analyticsSaga.ts b/packages/components/src/redux/common/sagas/analyticsSaga.ts index e852c1fce..9f4bca907 100644 --- a/packages/components/src/redux/common/sagas/analyticsSaga.ts +++ b/packages/components/src/redux/common/sagas/analyticsSaga.ts @@ -4,7 +4,7 @@ import moment from 'moment' import { fetchNetworkConnectionStatus } from '../../../services/network' import { httpClient } from '../../../services/HttpClient' import { commonActions } from '../actions' -import * as selectors from '../selectors' +import { commonSelectors } from '../selectors' import { ActionTypes } from '../types' const ACTIONS_TO_TRACK: ActionTypes[] = [ @@ -21,7 +21,7 @@ const ACTIONS_TO_TRACK: ActionTypes[] = [ ] function* onTrackAction(action) { - const currentUser = yield select(selectors.currentUserSelector) + const currentUser = yield select(commonSelectors.currentUserSelector) yield put( commonActions.queueEvent({ id: uuidv4(), @@ -40,8 +40,8 @@ function* processEventQueue() { // process queue every minute yield delay(60 * 1000) - const appToken = yield select(selectors.appTokenSelector) - const events = yield select(selectors.allAnalyticsEventsSelector) + const appToken = yield select(commonSelectors.appTokenSelector) + const events = yield select(commonSelectors.allAnalyticsEventsSelector) const isQueueEmpty = events.length === 0 if (isQueueEmpty) { diff --git a/packages/components/src/redux/common/sagas/appSaga.ts b/packages/components/src/redux/common/sagas/appSaga.ts index 6f20e9c62..f82eb1dbf 100644 --- a/packages/components/src/redux/common/sagas/appSaga.ts +++ b/packages/components/src/redux/common/sagas/appSaga.ts @@ -6,7 +6,7 @@ import { extractReducerState } from '../sync' import { ReduxState, exportReducerNames } from '../reducers' import { version as storeVersion } from '../../store' import { commonActions } from '../actions' -import * as selectors from '../selectors' +import { commonSelectors } from '../selectors' import messaging from '@react-native-firebase/messaging' function* syncAppState() { @@ -16,7 +16,7 @@ function* syncAppState() { // process queue every minute yield delay(60 * 1000) - const appToken = yield select(selectors.appTokenSelector) + const appToken = yield select(commonSelectors.appTokenSelector) if (!appToken) { // not logged continue diff --git a/packages/components/src/redux/common/sagas/authSaga.ts b/packages/components/src/redux/common/sagas/authSaga.ts index 7aceb18b8..0d5cf048c 100644 --- a/packages/components/src/redux/common/sagas/authSaga.ts +++ b/packages/components/src/redux/common/sagas/authSaga.ts @@ -6,7 +6,7 @@ import { ExtractActionFromActionType } from '../types' import { httpClient } from '../../../services/HttpClient' import { ReduxState, exportReducerNames } from '../reducers' import { commonActions } from '../actions' -import * as selectors from '../selectors' +import { commonSelectors } from '../selectors' import { navigateAndReset } from '../../../services/navigationService' import { PredictionState } from '../../../prediction' import moment from 'moment' @@ -19,8 +19,8 @@ type Await = T extends Promise ? U : T function* onRehydrate() { const state: ReduxState = yield select() - const appToken = selectors.appTokenSelector(state) - const user = selectors.currentUserSelector(state) + const appToken = commonSelectors.appTokenSelector(state) + const user = commonSelectors.currentUserSelector(state) // convert guest account if (!appToken && user && user.isGuest) { @@ -61,7 +61,7 @@ function* onConvertGuestAccount(action: ExtractActionFromActionType<'CONVERT_GUE function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { const { name, password } = action.payload const stateRedux: ReduxState = yield select() - const localeapp = selectors.currentLocaleSelector(stateRedux) + const localeapp = commonSelectors.currentLocaleSelector(stateRedux) yield commonActions.setLocale(localeapp) try { @@ -224,7 +224,7 @@ function* onCreateAccountSuccess(action: ExtractActionFromActionType<'CREATE_ACC function* onDeleteAccountRequest(action: ExtractActionFromActionType<'DELETE_ACCOUNT_REQUEST'>) { const { setLoading } = action.payload const state: ReduxState = yield select() - const user = selectors.currentUserSelector(state) + const user = commonSelectors.currentUserSelector(state) setLoading(true) try { const { name, password } = action.payload @@ -251,7 +251,7 @@ function* onDeleteAccountRequest(action: ExtractActionFromActionType<'DELETE_ACC } function* onLogoutRequest() { - const isTtsActive = yield select(selectors.isTtsActiveSelector) + const isTtsActive = yield select(commonSelectors.isTtsActiveSelector) if (isTtsActive) { yield call(closeOutTTs) @@ -271,7 +271,7 @@ function* onLogoutRequest() { function* onJourneyCompletion(action: ExtractActionFromActionType<'JOURNEY_COMPLETION'>) { const { data } = action.payload - const currentUser = yield select(selectors.currentUserSelector) + const currentUser = yield select(commonSelectors.currentUserSelector) let periodResult = null if (yield fetchNetworkConnectionStatus()) { try { diff --git a/packages/components/src/redux/common/sagas/contentSaga.ts b/packages/components/src/redux/common/sagas/contentSaga.ts index 2ad34ac7f..7d0db8d76 100644 --- a/packages/components/src/redux/common/sagas/contentSaga.ts +++ b/packages/components/src/redux/common/sagas/contentSaga.ts @@ -11,14 +11,14 @@ import { liveContent as staleContent, } from '@oky/core' import { httpClient } from '../../../services/HttpClient' -import * as selectors from '../selectors' +import { commonSelectors } from '../selectors' import { commonActions } from '../actions' import _ from 'lodash' import messaging from '@react-native-firebase/messaging' import { closeOutTTs } from '../../../services/textToSpeech' function* onRehydrate(action: RehydrateAction) { - const locale = yield select(selectors.currentLocaleSelector) + const locale = yield select(commonSelectors.currentLocaleSelector) const hasPreviousContentFromStorage = action.payload && action.payload.content @@ -41,15 +41,15 @@ function* onRehydrate(action: RehydrateAction) { function* onFetchSurveyContent( action: ExtractActionFromActionType<'FETCH_SURVEY_CONTENT_REQUEST'>, ) { - const locale = yield select(selectors.currentLocaleSelector) - const userID = yield select(selectors.currentUserSelector) + const locale = yield select(commonSelectors.currentLocaleSelector) + const userID = yield select(commonSelectors.currentUserSelector) try { const surveys = yield httpClient.fetchSurveys({ locale, userID, }) - const previousSurveys = yield select(selectors.allSurveys) - const completedSurveys = yield select(selectors.completedSurveys) + const previousSurveys = yield select(commonSelectors.allSurveys) + const completedSurveys = yield select(commonSelectors.completedSurveys) const newSurveyArr = previousSurveys?.length ? previousSurveys : [] surveys.forEach((item) => { const itemExits = _.find(previousSurveys, { id: item.id }) @@ -177,9 +177,9 @@ function* onFetchContentRequest(action: ExtractActionFromActionType<'FETCH_CONTE ) } catch (error) { yield put(commonActions.fetchContentFailure()) - const aboutContent = yield select(selectors.aboutContent) + const aboutContent = yield select(commonSelectors.aboutContent) if (!aboutContent) { - const localeInit = yield select(selectors.currentLocaleSelector) + const localeInit = yield select(commonSelectors.currentLocaleSelector) yield put(commonActions.initStaleContent(staleContent[localeInit])) } } @@ -187,7 +187,7 @@ function* onFetchContentRequest(action: ExtractActionFromActionType<'FETCH_CONTE function* onSetLocale(action: ExtractActionFromActionType<'SET_LOCALE'>) { const { locale } = action.payload - const isTtsActive = yield select(selectors.isTtsActiveSelector) + const isTtsActive = yield select(commonSelectors.isTtsActiveSelector) if (isTtsActive) { // TODO_ALEX why? yield call(closeOutTTs) diff --git a/packages/components/src/redux/common/selectors/index.ts b/packages/components/src/redux/common/selectors/index.ts index 6d37f9edb..54f46f1b2 100644 --- a/packages/components/src/redux/common/selectors/index.ts +++ b/packages/components/src/redux/common/selectors/index.ts @@ -1,5 +1,13 @@ -export * from './analyticsSelectors' -export * from './answerSelectors' -export * from './appSelectors' -export * from './authSelectors' -export * from './contentSelectors' +import * as analyticsSelectors from './analyticsSelectors' +import * as answerSelectors from './answerSelectors' +import * as appSelectors from './appSelectors' +import * as authSelectors from './authSelectors' +import * as contentSelectors from './contentSelectors' + +export const commonSelectors = { + ...analyticsSelectors, + ...answerSelectors, + ...appSelectors, + ...authSelectors, + ...contentSelectors, +} diff --git a/packages/components/src/screens/ArticlesScreen.tsx b/packages/components/src/screens/ArticlesScreen.tsx index 7be187867..4065ace86 100644 --- a/packages/components/src/screens/ArticlesScreen.tsx +++ b/packages/components/src/screens/ArticlesScreen.tsx @@ -4,13 +4,13 @@ import { FlatList } from 'react-native' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/common/selectors' +import { commonSelectors } from '../redux/common/selectors' import { Header } from '../components/common/Header' import { TextWithoutTranslation } from '../components/common/Text' import { useTextToSpeechHook } from '../hooks/useTextToSpeechHook' const ArticleItem = ({ article, index, articles }) => { - const articleObject = useSelector((state) => selectors.articleByIDSelector(state, article)) + const articleObject = useSelector((state) => commonSelectors.articleByIDSelector(state, article)) if (!articleObject) { return null @@ -38,9 +38,9 @@ const ArticleItem = ({ article, index, articles }) => { export function ArticlesScreen({ navigation }) { const subCategory = navigation.getParam('subCategory') const subCategoryObject = useSelector((state) => - selectors.subCategoryByIDSelector(state, subCategory), + commonSelectors.subCategoryByIDSelector(state, subCategory), ) - const allArticlesByIDObject = useSelector(selectors.articlesObjectByIDSelector) + const allArticlesByIDObject = useSelector(commonSelectors.articlesObjectByIDSelector) const articles = subCategoryObject.articles const articlesTextArray = articles.reduce((acc, item) => { const selectedArticle = allArticlesByIDObject[item] diff --git a/packages/components/src/screens/AuthScreen.tsx b/packages/components/src/screens/AuthScreen.tsx index c3b35e5bc..d26af7304 100644 --- a/packages/components/src/screens/AuthScreen.tsx +++ b/packages/components/src/screens/AuthScreen.tsx @@ -13,7 +13,7 @@ import { navigate } from '../services/navigationService' export function AuthScreen() { const [toggled, setToggled] = React.useState(true) // @TODO: LANGUAGES This is commented in case the client wants multiple languages - // const locale = useSelector(selectors.currentLocaleSelector) + // const locale = useSelector(commonSelectors.currentLocaleSelector) return ( diff --git a/packages/components/src/screens/AvatarAndThemeScreen.tsx b/packages/components/src/screens/AvatarAndThemeScreen.tsx index fc9e00a47..591779640 100644 --- a/packages/components/src/screens/AvatarAndThemeScreen.tsx +++ b/packages/components/src/screens/AvatarAndThemeScreen.tsx @@ -11,7 +11,7 @@ import { Header } from '../components/common/Header' import { useTheme } from '../components/context/ThemeContext' import { BackOneScreen, navigate } from '../services/navigationService' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/common/selectors' +import { commonSelectors } from '../redux/common/selectors' import styled from 'styled-components/native' import { Text } from '../components/common/Text' import { ScrollView } from 'react-native-gesture-handler' @@ -21,7 +21,7 @@ export function AvatarAndThemeScreen({ navigation }) { const signingUp = navigation.getParam('signingUp') const newUser = navigation.getParam('newUser') const [loading, setLoading] = React.useState(false) - const selectedAvatar = useSelector(selectors.currentAvatarSelector) + const selectedAvatar = useSelector(commonSelectors.currentAvatarSelector) const dispatch = useDispatch() const { id } = useTheme() diff --git a/packages/components/src/screens/ContactUsScreen.tsx b/packages/components/src/screens/ContactUsScreen.tsx index df86d7e86..506f49ac3 100644 --- a/packages/components/src/screens/ContactUsScreen.tsx +++ b/packages/components/src/screens/ContactUsScreen.tsx @@ -10,7 +10,7 @@ import { TextInput } from '../components/common/TextInput' import { KeyboardAwareAvoidance } from '../components/common/KeyboardAwareAvoidance' import { httpClient } from '../services/HttpClient' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/common/selectors' +import { commonSelectors } from '../redux/common/selectors' import moment from 'moment' import { Text } from '../components/common/Text' import { ThemedModal } from '../components/common/ThemedModal' @@ -23,8 +23,8 @@ const Reasons = ['reason', 'report_bug', 'request_topic', 'Other', 'problem_app' export function ContactUsScreen({ navigation }) { const [email, setEmail] = React.useState('') - const user = useSelector(selectors.currentUserSelector) - const locale = useSelector(selectors.currentLocaleSelector) + const user = useSelector(commonSelectors.currentUserSelector) + const locale = useSelector(commonSelectors.currentLocaleSelector) const [reason, setReason] = React.useState('') const [message, setMessage] = React.useState('') const [notValid, setNotValid] = React.useState(false) diff --git a/packages/components/src/screens/DayScreen.tsx b/packages/components/src/screens/DayScreen.tsx index 8847cb017..1864ce0b0 100644 --- a/packages/components/src/screens/DayScreen.tsx +++ b/packages/components/src/screens/DayScreen.tsx @@ -12,7 +12,7 @@ import { assets } from '../assets' import { usePredictDay } from '../components/context/PredictionProvider' import { ThemedModal } from '../components/common/ThemedModal' import { ColourButtons } from './mainScreen/ColourButtons' -import * as selectors from '../redux/common/selectors' +import { commonSelectors } from '../redux/common/selectors' import { useSelector } from '../hooks/useSelector' import moment from 'moment' @@ -22,7 +22,7 @@ export function DayScreen({ navigation }) { const [isVisible, setIsVisible] = React.useState(false) const { keyboardIsOpen, dismiss } = useKeyboardController() const cardAnswersToday = useSelector((state) => - selectors.verifyPeriodDaySelectorWithDate(state, moment(dataEntry.date)), + commonSelectors.verifyPeriodDaySelectorWithDate(state, moment(dataEntry.date)), ) const goBack = () => { diff --git a/packages/components/src/screens/EditProfileScreen.tsx b/packages/components/src/screens/EditProfileScreen.tsx index 64849efa3..42a9375db 100644 --- a/packages/components/src/screens/EditProfileScreen.tsx +++ b/packages/components/src/screens/EditProfileScreen.tsx @@ -9,7 +9,7 @@ import { SelectBox } from '../components/common/SelectBox' import { DateOfBirthInput } from '../components/common/DateOfBirthInput' import { assets } from '../assets/index' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/common/selectors' +import { commonSelectors } from '../redux/common/selectors' import { commonActions } from '../redux/common/actions' import { useDispatch } from 'react-redux' import { BackOneScreen } from '../services/navigationService' @@ -71,8 +71,8 @@ async function runInSequence(functions) { export function EditProfileScreen() { const dispatch = useDispatch() - const currentUser = useSelector(selectors.currentUserSelector) - const appToken = useSelector(selectors.appTokenSelector) + const currentUser = useSelector(commonSelectors.currentUserSelector) + const appToken = useSelector(commonSelectors.appTokenSelector) const [name, setName] = React.useState(currentUser.name) const [notValid, setNotValid] = React.useState(false) diff --git a/packages/components/src/screens/EncyclopediaScreen.tsx b/packages/components/src/screens/EncyclopediaScreen.tsx index 726d85875..39663f748 100644 --- a/packages/components/src/screens/EncyclopediaScreen.tsx +++ b/packages/components/src/screens/EncyclopediaScreen.tsx @@ -4,7 +4,7 @@ import { ScrollView, Animated } from 'react-native' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/common/selectors' +import { commonSelectors } from '../redux/common/selectors' import { Category } from './encyclopediaScreen/Category' import { SubCategoryCard, VideoSubCategoryCard } from './encyclopediaScreen/SubCategoryCard' import Accordion from 'react-native-collapsible/Accordion' @@ -18,16 +18,16 @@ import analytics from '@react-native-firebase/analytics' import { fetchNetworkConnectionStatus } from '../services/network' export function EncyclopediaScreen({ navigation }) { - const categories = useSelector(selectors.allCategoriesSelector) - const articles = useSelector(selectors.allArticlesSelector) - const subCategories = useSelector(selectors.allSubCategoriesSelector) - const subCategoriesObject = useSelector(selectors.allSubCategoriesObjectSelector) + const categories = useSelector(commonSelectors.allCategoriesSelector) + const articles = useSelector(commonSelectors.allArticlesSelector) + const subCategories = useSelector(commonSelectors.allSubCategoriesSelector) + const subCategoriesObject = useSelector(commonSelectors.allSubCategoriesObjectSelector) const [activeCategories, setActiveCategory] = React.useState([]) const [filteredCategories, setFilteredCategories] = React.useState(categories) const [shownCategories, setShownCategories] = React.useState(categories) const [searching, setSearching] = React.useState(false) const [position] = React.useState(new Animated.Value(0)) - const currentUser = useSelector(selectors.currentUserSelector) + const currentUser = useSelector(commonSelectors.currentUserSelector) const categoryNames = categories.map((item) => item?.name) const [textArray, setTextArray] = React.useState(categoryNames) diff --git a/packages/components/src/screens/FindHelpScreen.tsx b/packages/components/src/screens/FindHelpScreen.tsx index f6b8e8991..d2b100ca7 100644 --- a/packages/components/src/screens/FindHelpScreen.tsx +++ b/packages/components/src/screens/FindHelpScreen.tsx @@ -8,7 +8,7 @@ import { TextWithoutTranslation, Text } from '../components/common/Text' import { SwiperContainer } from '../components/common/SwiperContainer' import { PageContainer } from '../components/layout/PageContainer' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/common/selectors' +import { commonSelectors } from '../redux/common/selectors' import { useTextToSpeechHook } from '../hooks/useTextToSpeechHook' import analytics from '@react-native-firebase/analytics' import { fetchNetworkConnectionStatus } from '../services/network' @@ -18,7 +18,7 @@ const screenHeight = Dimensions.get('screen').height const heightOfCarousel = screenHeight * 0.5 export function FindHelpScreen({ navigation }) { - const helpCenters: any = useSelector(selectors.allHelpCentersForCurrentLocale) + const helpCenters: any = useSelector(commonSelectors.allHelpCentersForCurrentLocale) const [textToSpeak, setTextToSpeak] = React.useState([]) React.useEffect(() => { diff --git a/packages/components/src/screens/MainScreen.tsx b/packages/components/src/screens/MainScreen.tsx index 157fb2ad1..f11a46b4d 100644 --- a/packages/components/src/screens/MainScreen.tsx +++ b/packages/components/src/screens/MainScreen.tsx @@ -23,7 +23,7 @@ import { assets } from '../assets' import { commonActions } from '../redux/common/actions' import { useDispatch } from 'react-redux' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/common/selectors' +import { commonSelectors } from '../redux/common/selectors' import moment from 'moment' import { FlowerButton, FlowerModal } from '../optional/Flower' @@ -44,10 +44,10 @@ const MainScreenContainer = ({ navigation }) => { const theme = useTheme() const todayInfo = useTodayPrediction() const dispatch = useDispatch() - const userID = useSelector(selectors.currentUserSelector).id + const userID = useSelector(commonSelectors.currentUserSelector).id const fullState = useFullState() const history = useHistoryPrediction() - const currentUser = useSelector(selectors.currentUserSelector) + const currentUser = useSelector(commonSelectors.currentUserSelector) // @TODO: careful note here, may be worth the performance increase though May not work with Memo now React.useEffect(() => { @@ -62,10 +62,10 @@ const MainScreenActual = React.memo(() => { const { data, index, isActive, currentIndex, absoluteIndex } = useInfiniteScroll() // TODO_ALEX: DO NOT USE HOOKS LIKE THIS const renamedUseSelector = useSelector - const allCardsData = renamedUseSelector((state) => selectors.allCardAnswersSelector(state)) + const allCardsData = renamedUseSelector((state) => commonSelectors.allCardAnswersSelector(state)) const getCardAnswersValues = (inputDay: any) => { const verifiedPeriodDaysData = renamedUseSelector((state) => - selectors.verifyPeriodDaySelectorWithDate(state, moment(inputDay.date)), + commonSelectors.verifyPeriodDaySelectorWithDate(state, moment(inputDay.date)), ) return verifiedPeriodDaysData } diff --git a/packages/components/src/screens/OnboardingScreen.tsx b/packages/components/src/screens/OnboardingScreen.tsx index ae6e615e6..e9795490d 100644 --- a/packages/components/src/screens/OnboardingScreen.tsx +++ b/packages/components/src/screens/OnboardingScreen.tsx @@ -18,7 +18,7 @@ export function OnboardingScreen() { const [index, setIndex] = React.useState(0) const [isButtonVisible, setIsButtonVisible] = React.useState(false) // @TODO: LANGUAGES This is commented in case the client wants multiple languages - // const region = useSelector(selectors.currentChosenRegionSelector) + // const region = useSelector(commonSelectors.currentChosenRegionSelector) React.useEffect(() => { if (index === 2) { diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index 45bed8837..227fadc5e 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -4,7 +4,7 @@ import { useDispatch } from 'react-redux' import { commonActions } from '../redux/common/actions' import { Text } from '../components/common/Text' import { TextInput } from '../components/common/TextInput' -import * as selectors from '../redux/common/selectors' +import { commonSelectors } from '../redux/common/selectors' import { navigateAndReset } from '../services/navigationService' import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { PageContainer } from '../components/layout/PageContainer' @@ -15,7 +15,7 @@ import _ from 'lodash' export function PasswordRequestScreen() { const dispatch = useDispatch() - const user = useSelector(selectors.currentUserSelector) + const user = useSelector(commonSelectors.currentUserSelector) const [loading, setLoading] = React.useState(false) const [valid, setValid] = React.useState(false) const [passwordError, setPasswordError] = React.useState(false) diff --git a/packages/components/src/screens/ProfileScreen.tsx b/packages/components/src/screens/ProfileScreen.tsx index 90d957b83..8a63678e5 100644 --- a/packages/components/src/screens/ProfileScreen.tsx +++ b/packages/components/src/screens/ProfileScreen.tsx @@ -19,7 +19,7 @@ import { ThemeSelectItem } from './avatarAndTheme/ThemeSelectItem' import { useHistoryPrediction, useTodayPrediction } from '../components/context/PredictionProvider' import { useSelector } from '../hooks/useSelector' import { commonActions } from '../redux/common/actions' -import * as selectors from '../redux/common/selectors' +import { commonSelectors } from '../redux/common/selectors' import { translate } from '../i18n/index' import { IconButton } from '../components/common/buttons/IconButton' import Modal from 'react-native-modal' @@ -29,12 +29,12 @@ import moment from 'moment' export function ProfileScreen({ navigation }) { const History = useHistoryPrediction() - const selectedAvatar = useSelector(selectors.currentAvatarSelector) + const selectedAvatar = useSelector(commonSelectors.currentAvatarSelector) const [isModalVisible, setIsModalVisible] = React.useState(false) const [error, setError] = React.useState(false) const shouldSkip = React.useRef(0) - const currentUser = useSelector(selectors.currentUserSelector) - const errorCode: any = useSelector(selectors.authError) + const currentUser = useSelector(commonSelectors.currentUserSelector) + const errorCode: any = useSelector(commonSelectors.authError) const todayInfo = useTodayPrediction() const { id: theme } = useTheme() const dispatch = useDispatch() diff --git a/packages/components/src/screens/SettingsScreen.tsx b/packages/components/src/screens/SettingsScreen.tsx index c82c8e91b..6b4c8de40 100644 --- a/packages/components/src/screens/SettingsScreen.tsx +++ b/packages/components/src/screens/SettingsScreen.tsx @@ -11,7 +11,7 @@ import { useDispatch } from 'react-redux' import { commonActions } from '../redux/common/actions' import { ConfirmAlert } from '../components/common/ConfirmAlert' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/common/selectors' +import { commonSelectors } from '../redux/common/selectors' import { translate } from '../i18n/index' import { SpinLoader } from '../components/common/SpinLoader' import { settingsScreenText } from '../config' @@ -25,9 +25,9 @@ export function SettingsScreen({ navigation }) { const dispatch = useDispatch() const [loading, setLoading] = React.useState(false) const currentCycleInfo = useTodayPrediction() - const currentUser = useSelector(selectors.currentUserSelector) - const hasTtsActive = useSelector(selectors.isTtsActiveSelector) - const hasFuturePredictionActive = useSelector(selectors.isFuturePredictionSelector) + const currentUser = useSelector(commonSelectors.currentUserSelector) + const hasTtsActive = useSelector(commonSelectors.isTtsActiveSelector) + const hasFuturePredictionActive = useSelector(commonSelectors.isFuturePredictionSelector) useTextToSpeechHook({ navigation, diff --git a/packages/components/src/screens/SplashScreen.tsx b/packages/components/src/screens/SplashScreen.tsx index db68d1378..2fcf5f9e1 100644 --- a/packages/components/src/screens/SplashScreen.tsx +++ b/packages/components/src/screens/SplashScreen.tsx @@ -4,7 +4,7 @@ import { PageContainer } from '../components/layout/PageContainer' import { assets } from '../assets/index' import styled from 'styled-components/native' import { useSelector, useDispatch } from 'react-redux' -import * as selectors from '../redux/common/selectors' +import { commonSelectors } from '../redux/common/selectors' import { commonActions } from '../redux/common/actions' import { navigateAndReset } from '../services/navigationService' import { Animated, Easing } from 'react-native' @@ -18,14 +18,14 @@ import messaging from '@react-native-firebase/messaging' export function SplashScreen() { const dispatch = useDispatch() - const user: any = useSelector(selectors.currentUserSelector) + const user: any = useSelector(commonSelectors.currentUserSelector) const Alert = useAlert() - const locale = useSelector(selectors.currentLocaleSelector) - const hasOpened = useSelector(selectors.hasOpenedSelector) - const currentAppVersion = useSelector(selectors.currentAppVersion) - const currentFirebaseToken = useSelector(selectors.currentFirebaseToken) - const hasPasswordRequestOn = useSelector(selectors.isLoginPasswordActiveSelector) + const locale = useSelector(commonSelectors.currentLocaleSelector) + const hasOpened = useSelector(commonSelectors.hasOpenedSelector) + const currentAppVersion = useSelector(commonSelectors.currentAppVersion) + const currentFirebaseToken = useSelector(commonSelectors.currentFirebaseToken) + const hasPasswordRequestOn = useSelector(commonSelectors.isLoginPasswordActiveSelector) const [animatedValue] = React.useState(new Animated.Value(0)) async function checkForPermanentAlerts() { diff --git a/packages/components/src/screens/TutorialFirstScreen.tsx b/packages/components/src/screens/TutorialFirstScreen.tsx index 5b7da1ca8..e1a99dcd2 100644 --- a/packages/components/src/screens/TutorialFirstScreen.tsx +++ b/packages/components/src/screens/TutorialFirstScreen.tsx @@ -19,7 +19,7 @@ import { ColourButtonsDemo } from './tutorial/ColourButtonsDemo' import { SpinLoader } from '../components/common/SpinLoader' import DeviceInfo from 'react-native-device-info' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/common/selectors' +import { commonSelectors } from '../redux/common/selectors' import moment from 'moment' import Tts from 'react-native-tts' import { translate } from '../i18n' @@ -43,7 +43,7 @@ export function TutorialFirstScreen() { const dispatch = useDispatch() const [completedStep, setCompletedStep] = React.useState(0) - const hasTtsActive = useSelector(selectors.isTtsActiveSelector) + const hasTtsActive = useSelector(commonSelectors.isTtsActiveSelector) const normalizePosition = (percentage, dimension) => { return percentage * dimension - arrowSize / 2 @@ -53,7 +53,7 @@ export function TutorialFirstScreen() { const getCardAnswersValues = (inputDay) => { const cardData = renamedUseSelector((state) => - selectors.verifyPeriodDaySelectorWithDate(state, moment(inputDay.date)), + commonSelectors.verifyPeriodDaySelectorWithDate(state, moment(inputDay.date)), ) return cardData } diff --git a/packages/components/src/screens/TutorialSecondScreen.tsx b/packages/components/src/screens/TutorialSecondScreen.tsx index 0496efda2..ba73c4834 100644 --- a/packages/components/src/screens/TutorialSecondScreen.tsx +++ b/packages/components/src/screens/TutorialSecondScreen.tsx @@ -20,7 +20,7 @@ import { CalendarAssetDemo } from './tutorial/CalendarAssetDemo' import { SpinLoader } from '../components/common/SpinLoader' import { NoteAssetDemo } from './tutorial/NoteAssetDemo' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/common/selectors' +import { commonSelectors } from '../redux/common/selectors' import moment from 'moment' import Tts from 'react-native-tts' import { translate } from '../i18n' @@ -51,7 +51,7 @@ export function TutorialSecondScreen({ navigation }) { const dispatch = useDispatch() // TODO_ALEX: DO NOT USE HOOKS LIKE THIS const renamedUseSelector = useSelector - const hasTtsActive = useSelector(selectors.isTtsActiveSelector) + const hasTtsActive = useSelector(commonSelectors.isTtsActiveSelector) const normalizePosition = (percentage, dimension) => { return percentage * dimension - arrowSize / 2 @@ -375,7 +375,7 @@ export function TutorialSecondScreen({ navigation }) { const getCardAnswersValues = (inputDay) => { const cardData = renamedUseSelector((state) => - selectors.verifyPeriodDaySelectorWithDate(state, moment(inputDay.date)), + commonSelectors.verifyPeriodDaySelectorWithDate(state, moment(inputDay.date)), ) return cardData } diff --git a/packages/components/src/screens/VideosScreen.tsx b/packages/components/src/screens/VideosScreen.tsx index eaa0e69f1..0c75e91d9 100644 --- a/packages/components/src/screens/VideosScreen.tsx +++ b/packages/components/src/screens/VideosScreen.tsx @@ -4,7 +4,7 @@ import { FlatList } from 'react-native' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { useSelector } from '../hooks/useSelector' -import * as selectors from '../redux/common/selectors' +import { commonSelectors } from '../redux/common/selectors' import { Header } from '../components/common/Header' import { TextWithoutTranslation } from '../components/common/Text' import { VideoData } from '../types' @@ -17,7 +17,7 @@ export const VideoItem = ({ videoId: string onSelect: React.Dispatch> }) => { - const videoObject = useSelector((state) => selectors.videoByIDSelector(state, videoId)) + const videoObject = useSelector((state) => commonSelectors.videoByIDSelector(state, videoId)) if (!videoObject) { return null @@ -40,7 +40,7 @@ export const VideoItem = ({ export function VideosScreen({ navigation }) { const categoryId = navigation.getParam('categoryId') - const category = useSelector((state) => selectors.categoryByIDSelector(state, categoryId)) + const category = useSelector((state) => commonSelectors.categoryByIDSelector(state, categoryId)) const videos = category?.videos || [] return ( diff --git a/packages/components/src/screens/authScreen/signUp/AskLocation.tsx b/packages/components/src/screens/authScreen/signUp/AskLocation.tsx index 73acc37fc..7a88b812c 100644 --- a/packages/components/src/screens/authScreen/signUp/AskLocation.tsx +++ b/packages/components/src/screens/authScreen/signUp/AskLocation.tsx @@ -8,13 +8,13 @@ import { GenderSelectItem } from '../../../components/common/GenderSelectItem' import { formHeights } from './FormHeights' import { ModalSearchBox } from '../../../components/common/ModalSearchBox' import { useSelector } from '../../../hooks/useSelector' -import * as selectors from '../../../redux/common/selectors' +import { commonSelectors } from '../../../redux/common/selectors' import { translate } from '../../../i18n' import { FAST_SIGN_UP } from '../../../config' export function AskLocation({ step, createAccount }) { const [{ app: state }, dispatch] = useMultiStepForm() - const lang = useSelector(selectors.currentLocaleSelector) + const lang = useSelector(commonSelectors.currentLocaleSelector) const { country, province, location } = state const [derivedCountry, setDerivedCountry] = React.useState( FAST_SIGN_UP ? { code: 'AF', item: 'Afghanistan' } : null, diff --git a/packages/components/src/screens/dayScreen/DayCarousel.tsx b/packages/components/src/screens/dayScreen/DayCarousel.tsx index 2a8762a2e..f26d63d00 100644 --- a/packages/components/src/screens/dayScreen/DayCarousel.tsx +++ b/packages/components/src/screens/dayScreen/DayCarousel.tsx @@ -9,7 +9,7 @@ import { QuizCard } from './QuizCard' import { DidYouKnowCard } from './DidYouKnowCard' import { SurveyCard } from './SurveyCard' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { translate } from '../../i18n' import { ThemedModal } from '../../components/common/ThemedModal' @@ -27,9 +27,9 @@ export function DayCarousel({ navigation, dataEntry }) { const [tempCardName, setTempCardName] = React.useState(null) const [tempCardAnswer, setTempCardAnswer] = React.useState(null) const viewConfigRef = React.useRef({ viewAreaCoveragePercentThreshold: 50 }) - const userID = useSelector(selectors.currentUserSelector).id - const allSurveys = useSelector(selectors.allSurveys) - const completedSurveys = useSelector(selectors.completedSurveys) + const userID = useSelector(commonSelectors.currentUserSelector).id + const allSurveys = useSelector(commonSelectors.allSurveys) + const completedSurveys = useSelector(commonSelectors.completedSurveys) const newSurveys = allSurveys?.length ? allSurveys[0] : null const cards = { diff --git a/packages/components/src/screens/dayScreen/DayCarouselItem.tsx b/packages/components/src/screens/dayScreen/DayCarouselItem.tsx index d78f25a0d..36b15c2e7 100644 --- a/packages/components/src/screens/dayScreen/DayCarouselItem.tsx +++ b/packages/components/src/screens/dayScreen/DayCarouselItem.tsx @@ -6,7 +6,7 @@ import { assets } from '../../assets/index' import { Text } from '../../components/common/Text' import { EmojiSelector } from '../../components/common/EmojiSelector' import { Icon } from '../../components/common/Icon' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { useSelector } from '../../hooks/useSelector' import { useColor } from '../../hooks/useColor' import { translate } from '../../i18n' @@ -14,7 +14,9 @@ import { translate } from '../../i18n' const deviceWidth = Dimensions.get('window').width export function DayCarouselItem({ content, cardName, dataEntry, onPress, index }) { - const selectedEmojis = useSelector((state) => selectors.cardAnswerSelector(state, dataEntry.date)) + const selectedEmojis = useSelector((state) => + commonSelectors.cardAnswerSelector(state, dataEntry.date), + ) const color = useColor(dataEntry.onPeriod, dataEntry.onFertile) const source = selectedEmojis[cardName] diff --git a/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx b/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx index 03c850443..ba8f6102d 100644 --- a/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx +++ b/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx @@ -4,13 +4,13 @@ import styled from 'styled-components/native' import { TextWithoutTranslation, Text } from '../../components/common/Text' import { useSelector } from '../../hooks/useSelector' import _ from 'lodash' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { TitleText } from '../../components/common/TitleText' const deviceWidth = Dimensions.get('window').width function useDidYouKnow() { - const allDidYouKnows = useSelector(selectors.allDidYouKnowsSelectors) + const allDidYouKnows = useSelector(commonSelectors.allDidYouKnowsSelectors) const randomDidYouKnow = React.useMemo(() => { return _.sample(allDidYouKnows) }, []) diff --git a/packages/components/src/screens/dayScreen/NoteCard.tsx b/packages/components/src/screens/dayScreen/NoteCard.tsx index 2d1b2d79b..ba359f47a 100644 --- a/packages/components/src/screens/dayScreen/NoteCard.tsx +++ b/packages/components/src/screens/dayScreen/NoteCard.tsx @@ -1,7 +1,7 @@ import React from 'react' import { Dimensions } from 'react-native' import styled from 'styled-components/native' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { commonActions } from '../../redux/common/actions' import { useSelector } from '../../hooks/useSelector' import { TextInput } from '../../components/common/TextInput' @@ -17,9 +17,9 @@ const deviceWidth = Dimensions.get('window').width export function NoteCard({ dataEntry }) { const noteObject: any = useSelector((state) => - selectors.notesAnswerSelector(state, dataEntry.date), + commonSelectors.notesAnswerSelector(state, dataEntry.date), ) - const userID = useSelector(selectors.currentUserSelector).id + const userID = useSelector(commonSelectors.currentUserSelector).id const [title, setTitle] = React.useState(noteObject.title || '') const [titlePlaceholder, setTitlePlaceholder] = React.useState('title') const [notesPlaceholder, setNotesPlaceholder] = React.useState('daily_note_description') diff --git a/packages/components/src/screens/dayScreen/QuizCard.tsx b/packages/components/src/screens/dayScreen/QuizCard.tsx index 4eb150468..8eae46ab8 100644 --- a/packages/components/src/screens/dayScreen/QuizCard.tsx +++ b/packages/components/src/screens/dayScreen/QuizCard.tsx @@ -6,16 +6,16 @@ import { EmojiSelector } from '../../components/common/EmojiSelector' import { TitleText } from '../../components/common/TitleText' import { useSelector } from '../../hooks/useSelector' import _ from 'lodash' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { commonActions } from '../../redux/common/actions' import { useDispatch } from 'react-redux' const deviceWidth = Dimensions.get('window').width function useQuiz() { - const unansweredQuizzes = useSelector(selectors.quizzesWithoutAnswersSelector) + const unansweredQuizzes = useSelector(commonSelectors.quizzesWithoutAnswersSelector) - const allQuizzes = useSelector(selectors.allQuizzesSelectors) + const allQuizzes = useSelector(commonSelectors.allQuizzesSelectors) const randomQuiz = React.useMemo(() => { if (_.isEmpty(unansweredQuizzes)) { return _.sample(allQuizzes) @@ -27,9 +27,11 @@ function useQuiz() { export const QuizCard = React.memo<{ dataEntry: any; index: number }>(({ dataEntry, index }) => { const dispatch = useDispatch() - const userID = useSelector(selectors.currentUserSelector).id + const userID = useSelector(commonSelectors.currentUserSelector).id const selectedQuestion = useQuiz() - const answeredQuestion = useSelector((state) => selectors.quizAnswerByDate(state, dataEntry.date)) + const answeredQuestion = useSelector((state) => + commonSelectors.quizAnswerByDate(state, dataEntry.date), + ) const QuizContent = () => { return selectedQuestion.answers.map((item, ind) => { diff --git a/packages/components/src/screens/dayScreen/SurveyCard.tsx b/packages/components/src/screens/dayScreen/SurveyCard.tsx index f165f17cf..b0f298649 100644 --- a/packages/components/src/screens/dayScreen/SurveyCard.tsx +++ b/packages/components/src/screens/dayScreen/SurveyCard.tsx @@ -6,7 +6,7 @@ import { EmojiSelector } from '../../components/common/EmojiSelector' import { TitleText } from '../../components/common/TitleText' import { useSelector } from '../../hooks/useSelector' import _ from 'lodash' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { commonActions } from '../../redux/common/actions' import { useDispatch } from 'react-redux' import { TextInput } from '../../components/common/TextInput' @@ -34,12 +34,12 @@ export const SurveyCard = React.memo<{ const [title, setTitle] = React.useState('') const [titlePlaceholder, setTitlePlaceholder] = React.useState('type_answer_placeholder') const [isSkip, setSkip] = React.useState(null) - const userID = useSelector(selectors.currentUserSelector).id + const userID = useSelector(commonSelectors.currentUserSelector).id const dispatch = useDispatch() const [showThankYouMsg, setThankYouMsg] = React.useState(null) const [selectedIndex, setSelectedIndex] = React.useState(null) - const completedSurveys = useSelector(selectors.completedSurveys) - const allSurveys = useSelector(selectors.allSurveys) + const completedSurveys = useSelector(commonSelectors.completedSurveys) + const allSurveys = useSelector(commonSelectors.allSurveys) const checkUserPermission = (option, optionIndex) => { setSelectedIndex(optionIndex) diff --git a/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx b/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx index 70477b5bc..4378b5156 100644 --- a/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx +++ b/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx @@ -7,7 +7,7 @@ import styled from 'styled-components/native' import { handleCategoriesFilter, handleSearchResult } from './searchFunctions' import { EmojiSelector } from '../../components/common/EmojiSelector' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { translate } from '../../i18n' export const SearchBar = ({ @@ -22,8 +22,8 @@ export const SearchBar = ({ }) => { const [searchStr, setSearchStr] = React.useState('') const [emojiFilter, updateEmojiFilter] = React.useState([]) - const locale = useSelector(selectors.currentLocaleSelector) - const emojiList = useSelector(selectors.allCategoryEmojis) + const locale = useSelector(commonSelectors.currentLocaleSelector) + const emojiList = useSelector(commonSelectors.allCategoryEmojis) return ( <> diff --git a/packages/components/src/screens/journeyScreen/JourneyCard.tsx b/packages/components/src/screens/journeyScreen/JourneyCard.tsx index 429070371..7518d2afd 100644 --- a/packages/components/src/screens/journeyScreen/JourneyCard.tsx +++ b/packages/components/src/screens/journeyScreen/JourneyCard.tsx @@ -6,7 +6,7 @@ import { WheelPickerContent } from '../../components/WheelPickerContent' import { Avatar } from '../../components/common/Avatar/Avatar' import { assets } from '../../assets' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' export function JourneyCard({ question, @@ -29,7 +29,7 @@ export function JourneyCard({ leftButtonTitle = 'i_dont_remember', rightButtonTitle = 'i_remember', }) { - const selectedAvatar = useSelector(selectors.currentAvatarSelector) + const selectedAvatar = useSelector(commonSelectors.currentAvatarSelector) return ( <> {status === 'initial' && ( diff --git a/packages/components/src/screens/mainScreen/Calendar.tsx b/packages/components/src/screens/mainScreen/Calendar.tsx index d2e0a95a3..5409f3bb6 100644 --- a/packages/components/src/screens/mainScreen/Calendar.tsx +++ b/packages/components/src/screens/mainScreen/Calendar.tsx @@ -22,7 +22,7 @@ import { translate } from '../../i18n' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { calendarScreenSpeech } from '../../config' import { useSelector } from 'react-redux' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' const width = Dimensions.get('window').width const height = Dimensions.get('window').height @@ -34,8 +34,10 @@ const startDate = moment().startOf('day').subtract(24, 'months') const endDate = moment().startOf('day').add(12, 'months') export const Calendar = ({ navigation }) => { - const hasFuturePredictionActive = useSelector(selectors.isFuturePredictionSelector) - const verifiedPeriodsData = useSelector((state: any) => selectors.allCardAnswersSelector(state)) + const hasFuturePredictionActive = useSelector(commonSelectors.isFuturePredictionSelector) + const verifiedPeriodsData = useSelector((state: any) => + commonSelectors.allCardAnswersSelector(state), + ) const highlightedDates = useCalculateStatusForDateRange( startDate, endDate, diff --git a/packages/components/src/screens/mainScreen/ColourButtons.tsx b/packages/components/src/screens/mainScreen/ColourButtons.tsx index 39828405a..1edf3d680 100644 --- a/packages/components/src/screens/mainScreen/ColourButtons.tsx +++ b/packages/components/src/screens/mainScreen/ColourButtons.tsx @@ -19,7 +19,7 @@ import { decisionProcessNonPeriod, decisionProcessPeriod } from './predictionLog import { translate } from '../../i18n' import { useDispatch } from 'react-redux' import { commonActions } from '../../redux/common/actions/index' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import analytics from '@react-native-firebase/analytics' import moment from 'moment' import { fetchNetworkConnectionStatus } from '../../services/network' @@ -59,8 +59,8 @@ export function ColourButtons({ const selectedDayInfoEngine = usePredictDay(inputDay) const isActive = useIsActiveSelector() const appDispatch = useDispatch() - const userID = useSelector(selectors.currentUserSelector).id - const currentUser = useSelector(selectors.currentUserSelector) + const userID = useSelector(commonSelectors.currentUserSelector).id + const currentUser = useSelector(commonSelectors.currentUserSelector) const currentCycleInfo = useTodayPrediction() const inputDayStr = moment(inputDay).format('YYYY-MM-DD') const todayStr = moment().format('YYYY-MM-DD') @@ -69,12 +69,12 @@ export function ColourButtons({ const flowerState = useFlowerStateSelector() const cardAnswersToday = useSelector((state) => - selectors.verifyPeriodDaySelectorWithDate(state, moment(inputDayStr)), + commonSelectors.verifyPeriodDaySelectorWithDate(state, moment(inputDayStr)), ) as any const fullState = useFullState() const [addNewCycleHistory, setNewCycleHistory] = React.useState(false) - const hasFuturePredictionActive = useSelector(selectors.isFuturePredictionSelector) + const hasFuturePredictionActive = useSelector(commonSelectors.isFuturePredictionSelector) // TODO_ALEX this useState is redundant const [futurePredictionStatus, setFuturePredictionStatus] = useState(false) diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx index 763b759e5..5df58975b 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx @@ -8,7 +8,7 @@ import { navigate, navigateAndReset } from '../../../services/navigationService' import moment from 'moment' import { useDisplayText } from '../../../components/context/DisplayTextContext' import { useSelector } from '../../../hooks/useSelector' -import * as selectors from '../../../redux/common/selectors' +import { commonSelectors } from '../../../redux/common/selectors' import { SpinLoader } from '../../../components/common/SpinLoader' const screenWidth = Dimensions.get('window').width @@ -28,7 +28,7 @@ export function Carousel({ absoluteIndex, disableInteraction = false, }) { - const isTutorialTwoOn = useSelector(selectors.isTutorialTwoActiveSelector) + const isTutorialTwoOn = useSelector(commonSelectors.isTutorialTwoActiveSelector) const [isVisible, setIsVisible] = React.useState(false) const { setDisplayTextStatic } = useDisplayText() diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx index 4a380a168..1018c2783 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx @@ -8,7 +8,7 @@ import { assets } from '../../../assets/index' import { EmojiSelector } from '../../../components/common/EmojiSelector' import { useSelector } from '../../../hooks/useSelector' import { emojis } from '../../../config' -import * as selectors from '../../../redux/common/selectors' +import { commonSelectors } from '../../../redux/common/selectors' import { useColor } from '../../../hooks/useColor' import styled from 'styled-components/native' import moment from 'moment' @@ -47,7 +47,7 @@ export function CarouselElement({ dataEntry, index, isActive, currentIndex }) { const value = new Value(0) const color = useColor(dataEntry.onPeriod, dataEntry.onFertile) const cardAnswersValues = useSelector((state) => - selectors.cardAnswerSelector(state, moment(dataEntry.date)), + commonSelectors.cardAnswerSelector(state, moment(dataEntry.date)), ) const [isVisible, setIsVisible] = React.useState(false) @@ -73,7 +73,7 @@ export function CarouselElement({ dataEntry, index, isActive, currentIndex }) { }) } const verifiedPeriodDaysData = useSelector((state) => - selectors.verifyPeriodDaySelectorWithDate(state, moment(dataEntry.date)), + commonSelectors.verifyPeriodDaySelectorWithDate(state, moment(dataEntry.date)), ) return ( diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx index 49b47a8a5..cd2466626 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx @@ -7,7 +7,7 @@ import { TouchableOpacity } from 'react-native-gesture-handler' import { assets } from '../../../assets/index' import { translate } from '../../../i18n' import { useSelector } from 'react-redux' -import * as selectors from '../../../redux/common/selectors' +import { commonSelectors } from '../../../redux/common/selectors' import moment from 'moment' import { useTodayPrediction, @@ -95,7 +95,7 @@ export function CircularElement({ state, }) { const currentCycleInfo = useTodayPrediction() - const hasFuturePredictionActive = useSelector(selectors.isFuturePredictionSelector) + const hasFuturePredictionActive = useSelector(commonSelectors.isFuturePredictionSelector) const actualCurrentStartDate = useActualCurrentStartDateSelector() const { id: themeName } = useTheme() const clock = new Clock() diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx index 984b29e90..076b0d365 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx @@ -7,7 +7,7 @@ import { PanGesture } from './PanGesture' import { TouchableOpacity } from 'react-native-gesture-handler' import { ColourButtons } from '../ColourButtons' import { useSelector } from '../../../hooks/useSelector' -import * as selectors from '../../../redux/common/selectors' +import { commonSelectors } from '../../../redux/common/selectors' import { navigateAndReset } from '../../../services/navigationService' import { useCheckDayWarning } from '../../../hooks/usePredictionWarnings' import { ThemedModal } from '../../../components/common/ThemedModal' @@ -44,7 +44,7 @@ export function CircularSelection({ inputRange: [0, data.length], outputRange: [0, -2 * Math.PI], }) - const isTutorialOneOn = useSelector(selectors.isTutorialOneActiveSelector) + const isTutorialOneOn = useSelector(commonSelectors.isTutorialOneActiveSelector) const checkIfWarning = useCheckDayWarning() // automatically close the modal if the wheel start scrolling React.useEffect(() => { @@ -139,7 +139,10 @@ export function CircularSelection({ onPress={() => setIsVisible(false)} selectedDayInfo={data[currentIndex]} cardValues={useSelector((state) => - selectors.verifyPeriodDaySelectorWithDate(state, moment(data[currentIndex].date)), + commonSelectors.verifyPeriodDaySelectorWithDate( + state, + moment(data[currentIndex].date), + ), )} /> diff --git a/packages/components/src/screens/profileScreen/CycleCard.tsx b/packages/components/src/screens/profileScreen/CycleCard.tsx index 806127c8f..dd0107387 100644 --- a/packages/components/src/screens/profileScreen/CycleCard.tsx +++ b/packages/components/src/screens/profileScreen/CycleCard.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components/native' import { Text } from '../../components/common/Text' import { Icon } from '../../components/common/Icon' import { assets } from '../../assets/index' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { EmojiSelector } from '../../components/common/EmojiSelector' import { useSelector } from '../../hooks/useSelector' import { emojis } from '../../config' @@ -13,7 +13,7 @@ const cardNames = ['mood', 'body', 'activity', 'flow'] export const CycleCard = ({ item, cycleNumber }) => { const cardAnswersValues = useSelector((state) => - selectors.mostAnsweredSelector(state, item.cycleStartDate, item.cycleEndDate), + commonSelectors.mostAnsweredSelector(state, item.cycleStartDate, item.cycleEndDate), ) return ( diff --git a/packages/components/src/screens/settings/AboutScreen.tsx b/packages/components/src/screens/settings/AboutScreen.tsx index c698c541d..1c9ed781b 100644 --- a/packages/components/src/screens/settings/AboutScreen.tsx +++ b/packages/components/src/screens/settings/AboutScreen.tsx @@ -5,7 +5,7 @@ import { TextWithoutTranslation } from '../../components/common/Text' import { Header } from '../../components/common/Header' import { Icon } from '../../components/common/Icon' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { aboutScreenText } from '../../config' import { Dimensions } from 'react-native' @@ -13,9 +13,9 @@ import { assets } from '../../assets' const width = Dimensions.get('window').width const imageWidth = width - 30 export const AboutScreen = ({ navigation }) => { - const aboutContent = useSelector(selectors.aboutContent) - const aboutBanner = useSelector(selectors.aboutBanner) - const locale = useSelector(selectors.currentLocaleSelector) + const aboutContent = useSelector(commonSelectors.aboutContent) + const aboutBanner = useSelector(commonSelectors.aboutBanner) + const locale = useSelector(commonSelectors.currentLocaleSelector) const iconSource = aboutBanner ? { uri: aboutBanner } : assets.general.aboutBanner[locale] diff --git a/packages/components/src/screens/settings/AccessScreen.tsx b/packages/components/src/screens/settings/AccessScreen.tsx index 820770e59..feb68f89f 100644 --- a/packages/components/src/screens/settings/AccessScreen.tsx +++ b/packages/components/src/screens/settings/AccessScreen.tsx @@ -5,7 +5,7 @@ import styled from 'styled-components/native' import { ListItem } from './accessScreen/ListItem' import { Header } from '../../components/common/Header' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { commonActions } from '../../redux/common/actions/index' import { useDispatch } from 'react-redux' import { navigateAndReset } from '../../services/navigationService' @@ -19,11 +19,11 @@ import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { acessSettingsScreenText, WEBSITE_URL } from '../../config' export function AccessScreen({ navigation }) { - const locale = useSelector(selectors.currentLocaleSelector) + const locale = useSelector(commonSelectors.currentLocaleSelector) const dispatch = useDispatch() const [loading, setLoading] = React.useState(false) - const privacyContent = useSelector(selectors.privacyContent) + const privacyContent = useSelector(commonSelectors.privacyContent) const speechText = privacyContent.map((item) => item.content) const shareLink = () => { // @TODO: app event diff --git a/packages/components/src/screens/settings/PrivacyScreen.tsx b/packages/components/src/screens/settings/PrivacyScreen.tsx index ede766525..1678662d2 100644 --- a/packages/components/src/screens/settings/PrivacyScreen.tsx +++ b/packages/components/src/screens/settings/PrivacyScreen.tsx @@ -5,14 +5,14 @@ import { Header } from '../../components/common/Header' import { TextWithoutTranslation } from '../../components/common/Text' import { ScrollView, Dimensions } from 'react-native' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { chunk } from 'lodash' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' const width = Dimensions.get('window').width export function PrivacyScreen({ navigation }) { const [page, setPage] = React.useState(0) - const privacyContent = useSelector(selectors.privacyContent) + const privacyContent = useSelector(commonSelectors.privacyContent) const speechText = privacyContent.map((item) => item.content) const content = privacyContent.map((item, ind) => { if (item.type === 'HEADING') { diff --git a/packages/components/src/screens/settings/TermsScreen.tsx b/packages/components/src/screens/settings/TermsScreen.tsx index d4722ac54..2fc24b9ef 100644 --- a/packages/components/src/screens/settings/TermsScreen.tsx +++ b/packages/components/src/screens/settings/TermsScreen.tsx @@ -5,14 +5,14 @@ import { Header } from '../../components/common/Header' import { TextWithoutTranslation } from '../../components/common/Text' import { ScrollView, Dimensions, Platform } from 'react-native' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' import { chunk } from 'lodash' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' const width = Dimensions.get('window').width export function TermsScreen({ navigation }) { const [page, setPage] = React.useState(0) - const termsAndConditions = useSelector(selectors.termsAndConditionsContent) + const termsAndConditions = useSelector(commonSelectors.termsAndConditionsContent) const speechText = termsAndConditions.map((item) => item.content) const content = termsAndConditions.map((item, ind) => { if (item.type === 'HEADING') { diff --git a/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx b/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx index 779e8105a..7d49f77ea 100644 --- a/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx +++ b/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx @@ -3,12 +3,12 @@ import { Dimensions } from 'react-native' import styled from 'styled-components/native' import { assets } from '../../assets/index' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' const deviceWidth = Dimensions.get('window').width const deviceHeight = Dimensions.get('screen').height export function CalendarAssetDemo() { - const locale = useSelector(selectors.currentLocaleSelector) + const locale = useSelector(commonSelectors.currentLocaleSelector) const source = assets.general.calendarStatic[locale] return ( diff --git a/packages/components/src/screens/tutorial/DayAssetDemo.tsx b/packages/components/src/screens/tutorial/DayAssetDemo.tsx index 93eedb6a3..3f3122aca 100644 --- a/packages/components/src/screens/tutorial/DayAssetDemo.tsx +++ b/packages/components/src/screens/tutorial/DayAssetDemo.tsx @@ -9,12 +9,12 @@ import { EmojiSelector } from '../../components/common/EmojiSelector' import { translate } from '../../i18n' import Tts from 'react-native-tts' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' const deviceWidth = Dimensions.get('window').width const deviceHeight = Dimensions.get('window').height export function DayAssetDemo({ step }) { - const hasTtsActive = useSelector(selectors.isTtsActiveSelector) + const hasTtsActive = useSelector(commonSelectors.isTtsActiveSelector) React.useEffect(() => { if (hasTtsActive) { diff --git a/packages/components/src/screens/tutorial/NoteAssetDemo.tsx b/packages/components/src/screens/tutorial/NoteAssetDemo.tsx index f0d5bd1d2..620b3fd95 100644 --- a/packages/components/src/screens/tutorial/NoteAssetDemo.tsx +++ b/packages/components/src/screens/tutorial/NoteAssetDemo.tsx @@ -8,13 +8,13 @@ import { Text } from '../../components/common/Text' import { translate } from '../../i18n' import Tts from 'react-native-tts' import { useSelector } from '../../hooks/useSelector' -import * as selectors from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/common/selectors' const deviceWidth = Dimensions.get('window').width const deviceHeight = Dimensions.get('screen').height export function NoteAssetDemo({ step }) { - const hasTtsActive = useSelector(selectors.isTtsActiveSelector) + const hasTtsActive = useSelector(commonSelectors.isTtsActiveSelector) React.useEffect(() => { if (hasTtsActive) { From b4bdc0d11650e77fab133e5c8c44e7c77dc8b548 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 22 Dec 2023 12:46:22 +0700 Subject: [PATCH 06/83] Rename useSelector --- .../src/components/common/Avatar/Avatar.tsx | 6 +++--- .../src/components/common/CalendarList.tsx | 4 ++-- .../components/src/components/common/DateBadge.tsx | 4 ++-- .../components/src/components/common/DayBadge.tsx | 4 ++-- .../src/components/common/LanguageSelect.tsx | 4 ++-- .../src/components/context/DisplayTextContext.tsx | 6 +++--- .../src/components/context/LocaleContext.tsx | 4 ++-- .../src/components/context/PredictionProvider.tsx | 4 ++-- .../src/components/context/ThemeContext.tsx | 6 +++--- packages/components/src/hooks/useSelector.ts | 4 ---- .../components/src/hooks/useTextToSpeechHook.ts | 4 ++-- .../src/redux/common/useCommonSelector.ts | 4 ++++ packages/components/src/screens/ArticlesScreen.tsx | 10 ++++++---- packages/components/src/screens/AuthScreen.tsx | 2 +- .../src/screens/AvatarAndThemeScreen.tsx | 4 ++-- .../components/src/screens/ContactUsScreen.tsx | 6 +++--- packages/components/src/screens/DayScreen.tsx | 4 ++-- .../components/src/screens/EditProfileScreen.tsx | 6 +++--- .../components/src/screens/EncyclopediaScreen.tsx | 12 ++++++------ packages/components/src/screens/FindHelpScreen.tsx | 4 ++-- packages/components/src/screens/MainScreen.tsx | 8 ++++---- .../components/src/screens/OnboardingScreen.tsx | 2 +- .../src/screens/PasswordRequestScreen.tsx | 4 ++-- packages/components/src/screens/ProfileScreen.tsx | 10 +++++----- packages/components/src/screens/SettingsScreen.tsx | 8 ++++---- packages/components/src/screens/SplashScreen.tsx | 14 +++++++------- .../components/src/screens/TutorialFirstScreen.tsx | 6 +++--- .../src/screens/TutorialSecondScreen.tsx | 6 +++--- packages/components/src/screens/VideosScreen.tsx | 10 +++++++--- .../components/src/screens/authScreen/Login.tsx | 4 ++-- .../src/screens/authScreen/signUp/AskLocation.tsx | 4 ++-- .../src/screens/dayScreen/DayCarousel.tsx | 8 ++++---- .../src/screens/dayScreen/DayCarouselItem.tsx | 4 ++-- .../src/screens/dayScreen/DidYouKnowCard.tsx | 4 ++-- .../components/src/screens/dayScreen/NoteCard.tsx | 6 +++--- .../components/src/screens/dayScreen/QuizCard.tsx | 10 +++++----- .../src/screens/dayScreen/SurveyCard.tsx | 8 ++++---- .../src/screens/encyclopediaScreen/SearchBar.tsx | 6 +++--- .../src/screens/journeyScreen/JourneyCard.tsx | 4 ++-- .../components/src/screens/mainScreen/Calendar.tsx | 6 +++--- .../src/screens/mainScreen/ColourButtons.tsx | 10 +++++----- .../screens/mainScreen/wheelCarousel/Carousel.tsx | 4 ++-- .../mainScreen/wheelCarousel/CarouselElement.tsx | 6 +++--- .../mainScreen/wheelCarousel/CircularElement.tsx | 4 ++-- .../mainScreen/wheelCarousel/CircularSelection.tsx | 6 +++--- .../src/screens/profileScreen/CycleCard.tsx | 4 ++-- .../src/screens/settings/AboutScreen.tsx | 8 ++++---- .../src/screens/settings/AccessScreen.tsx | 6 +++--- .../src/screens/settings/PrivacyScreen.tsx | 4 ++-- .../src/screens/settings/TermsScreen.tsx | 4 ++-- .../src/screens/tutorial/CalendarAssetDemo.tsx | 4 ++-- .../src/screens/tutorial/DayAssetDemo.tsx | 4 ++-- .../src/screens/tutorial/NoteAssetDemo.tsx | 4 ++-- 53 files changed, 154 insertions(+), 148 deletions(-) delete mode 100644 packages/components/src/hooks/useSelector.ts create mode 100644 packages/components/src/redux/common/useCommonSelector.ts diff --git a/packages/components/src/components/common/Avatar/Avatar.tsx b/packages/components/src/components/common/Avatar/Avatar.tsx index d77297f2f..3420da6c9 100644 --- a/packages/components/src/components/common/Avatar/Avatar.tsx +++ b/packages/components/src/components/common/Avatar/Avatar.tsx @@ -8,7 +8,7 @@ import { FloatingQuestion } from './FloatingQuestion' import styled from 'styled-components/native' import { Icon } from '../Icon' import { HeartAnimation } from './HeartAnimation' -import { useSelector } from '../../../hooks/useSelector' +import { useCommonSelector } from '../../../redux/common/useCommonSelector' import { commonSelectors } from '../../../redux/common/selectors/index' import moment from 'moment' import { useDisplayText } from '../../context/DisplayTextContext' @@ -37,11 +37,11 @@ export function Avatar({ const isJumpingToggled = React.useRef(false) const isDancingToggled = React.useRef(false) const randomDance = React.useRef(1) - const selectedAvatar = useSelector(commonSelectors.currentAvatarSelector) + const selectedAvatar = useCommonSelector(commonSelectors.currentAvatarSelector) const [animatedProgress] = React.useState(new Animated.Value(0)) const { onPeriod } = useTodayPrediction() - const cardAnswersToday = useSelector((state) => + const cardAnswersToday = useCommonSelector((state) => commonSelectors.cardAnswerSelector(state, moment.utc()), ) React.useEffect(() => { diff --git a/packages/components/src/components/common/CalendarList.tsx b/packages/components/src/components/common/CalendarList.tsx index 8304a7e35..13fca2310 100644 --- a/packages/components/src/components/common/CalendarList.tsx +++ b/packages/components/src/components/common/CalendarList.tsx @@ -3,7 +3,7 @@ import { Image } from 'react-native' import { CalendarList as DefaultCalendarList, LocaleConfig } from 'react-native-calendars' import momentTimezone from 'moment-timezone' import { assets } from '../../assets/index' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { commonSelectors } from '../../redux/common/selectors' import { calendarTranslations } from '@oky/core' @@ -19,7 +19,7 @@ export function CalendarList({ setInputDay, width = null, }: any) { - const locale = useSelector(commonSelectors.currentLocaleSelector) + const locale = useCommonSelector(commonSelectors.currentLocaleSelector) LocaleConfig.defaultLocale = locale const [markedDates, setMarkedDates] = React.useState({}) const calendarRef = React.useRef() diff --git a/packages/components/src/components/common/DateBadge.tsx b/packages/components/src/components/common/DateBadge.tsx index 916ab0cd7..b0670f375 100644 --- a/packages/components/src/components/common/DateBadge.tsx +++ b/packages/components/src/components/common/DateBadge.tsx @@ -6,7 +6,7 @@ import { translate } from '../../i18n' import { TouchableOpacity } from 'react-native' import _ from 'lodash' import moment from 'moment' -import { useSelector } from 'react-redux' +import { useCommonSelector } from 'react-redux' import { commonSelectors } from '../../redux/common/selectors' import { useTodayPrediction, @@ -69,7 +69,7 @@ export function DateBadge({ dataEntry, style, textStyle = null, showModal, cardV const { id: themeName } = useTheme() const currentCycleInfo = useTodayPrediction() const actualCurrentStartDate = useActualCurrentStartDateSelector() - const hasFuturePredictionActive = useSelector(commonSelectors.isFuturePredictionSelector) + const hasFuturePredictionActive = useCommonSelector(commonSelectors.isFuturePredictionSelector) const source = useStatusForSource( dataEntry, diff --git a/packages/components/src/components/common/DayBadge.tsx b/packages/components/src/components/common/DayBadge.tsx index 2ca66b712..c95a8a920 100644 --- a/packages/components/src/components/common/DayBadge.tsx +++ b/packages/components/src/components/common/DayBadge.tsx @@ -4,7 +4,7 @@ import styled from 'styled-components/native' import { TextWithoutTranslation, Text } from './Text' import _ from 'lodash' import moment from 'moment' -import { useSelector } from 'react-redux' +import { useCommonSelector } from 'react-redux' import { commonSelectors } from '../../redux/common/selectors' import { useTodayPrediction, @@ -51,7 +51,7 @@ function useStatusForSource( export const DayBadge = ({ dataEntry, style, fontSizes, cardValues }) => { const currentCycleInfo = useTodayPrediction() - const hasFuturePredictionActive = useSelector(commonSelectors.isFuturePredictionSelector) + const hasFuturePredictionActive = useCommonSelector(commonSelectors.isFuturePredictionSelector) const actualCurrentStartDate = useActualCurrentStartDateSelector() diff --git a/packages/components/src/components/common/LanguageSelect.tsx b/packages/components/src/components/common/LanguageSelect.tsx index ec1c5c9b1..9cf34cfc5 100644 --- a/packages/components/src/components/common/LanguageSelect.tsx +++ b/packages/components/src/components/common/LanguageSelect.tsx @@ -1,6 +1,6 @@ import React from 'react' import styled from 'styled-components/native' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { commonSelectors } from '../../redux/common/selectors' import { commonActions } from '../../redux/common/actions/index' import { useDispatch } from 'react-redux' @@ -13,7 +13,7 @@ import { availableAppLocales } from '@oky/core' export const LanguageSelect = ({ style = null, textStyle = null, onPress = null }) => { const [modalVisible, setModalVisible] = React.useState(false) const [lang, setLang] = React.useState('') - const locale = useSelector(commonSelectors.currentLocaleSelector) + const locale = useCommonSelector(commonSelectors.currentLocaleSelector) const dispatch = useDispatch() return ( <> diff --git a/packages/components/src/components/context/DisplayTextContext.tsx b/packages/components/src/components/context/DisplayTextContext.tsx index 3c6f755f2..4261f053a 100644 --- a/packages/components/src/components/context/DisplayTextContext.tsx +++ b/packages/components/src/components/context/DisplayTextContext.tsx @@ -2,7 +2,7 @@ import React from 'react' import Tts from 'react-native-tts' import _ from 'lodash' import { translate } from '../../i18n' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { commonSelectors } from '../../redux/common/selectors' interface Props { @@ -15,10 +15,10 @@ interface Props { const DisplayTextContext = React.createContext(undefined) export function DisplayTextProvider({ children }) { - const availableText = useSelector(commonSelectors.allAvatarText) + const availableText = useCommonSelector(commonSelectors.allAvatarText) const [text, setText] = React.useState(null) - const hasTtsActive = useSelector(commonSelectors.isTtsActiveSelector) + const hasTtsActive = useCommonSelector(commonSelectors.isTtsActiveSelector) const setDisplayTextRandom = () => { setText(_.sample(availableText).content) diff --git a/packages/components/src/components/context/LocaleContext.tsx b/packages/components/src/components/context/LocaleContext.tsx index 8365e7a5f..1e4b0bb85 100644 --- a/packages/components/src/components/context/LocaleContext.tsx +++ b/packages/components/src/components/context/LocaleContext.tsx @@ -1,9 +1,9 @@ import React from 'react' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { currentLocale, configureI18n } from '../../i18n' export function LocaleProvider({ children }) { - const locale = useSelector(state => state.app.locale) + const locale = useCommonSelector((state) => state.app.locale) const [syncLocale, setSyncLocale] = React.useState(currentLocale()) React.useLayoutEffect(() => { diff --git a/packages/components/src/components/context/PredictionProvider.tsx b/packages/components/src/components/context/PredictionProvider.tsx index 988a09bba..a26509bd9 100644 --- a/packages/components/src/components/context/PredictionProvider.tsx +++ b/packages/components/src/components/context/PredictionProvider.tsx @@ -3,7 +3,7 @@ import moment, { Moment } from 'moment' import _ from 'lodash' import { PredictionState, PredictionEngine } from '../../prediction' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { useDispatch } from 'react-redux' import { commonActions } from '../../redux/common/actions' @@ -23,7 +23,7 @@ const defaultState = PredictionState.fromData({ export function PredictionProvider({ children }) { const reduxDispatch = useDispatch() - const predictionState = useSelector((state) => state.prediction) + const predictionState = useCommonSelector((state) => state.prediction) const [predictionSnapshots, setPredictionSnapshots] = React.useState([]) const predictionEngine = React.useMemo(() => { diff --git a/packages/components/src/components/context/ThemeContext.tsx b/packages/components/src/components/context/ThemeContext.tsx index 686e27b62..01b099a3c 100644 --- a/packages/components/src/components/context/ThemeContext.tsx +++ b/packages/components/src/components/context/ThemeContext.tsx @@ -1,12 +1,12 @@ import React from 'react' import { ThemeContext, ThemeProvider as StyledThemeProvider } from 'styled-components' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { commonSelectors } from '../../redux/common/selectors' import { themes } from '@oky/core' export function ThemeProvider({ children }) { - const themeName = useSelector((state) => state.app.theme) - const locale = useSelector(commonSelectors.currentLocaleSelector) + const themeName = useCommonSelector((state) => state.app.theme) + const locale = useCommonSelector(commonSelectors.currentLocaleSelector) return ( = useReduxSelector diff --git a/packages/components/src/hooks/useTextToSpeechHook.ts b/packages/components/src/hooks/useTextToSpeechHook.ts index 1f5ef9283..cbcda73cd 100644 --- a/packages/components/src/hooks/useTextToSpeechHook.ts +++ b/packages/components/src/hooks/useTextToSpeechHook.ts @@ -1,10 +1,10 @@ import React from 'react' import { speakArray, clearTTSQueue } from '../services/textToSpeech' -import { useSelector } from './useSelector' +import { useCommonSelector } from '../redux/common/useCommonSelector' import { commonSelectors } from '../redux/common/selectors' export function useTextToSpeechHook({ navigation, text }) { - const hasTtsActive = useSelector(commonSelectors.isTtsActiveSelector) + const hasTtsActive = useCommonSelector(commonSelectors.isTtsActiveSelector) const [shouldSpeak, setShouldSpeak] = React.useState(false) React.useEffect(() => { diff --git a/packages/components/src/redux/common/useCommonSelector.ts b/packages/components/src/redux/common/useCommonSelector.ts new file mode 100644 index 000000000..5be569e1b --- /dev/null +++ b/packages/components/src/redux/common/useCommonSelector.ts @@ -0,0 +1,4 @@ +import { useSelector as useReduxSelector, TypedUseSelectorHook } from 'react-redux' +import { ReduxState } from './reducers' + +export const useCommonSelector: TypedUseSelectorHook = useReduxSelector diff --git a/packages/components/src/screens/ArticlesScreen.tsx b/packages/components/src/screens/ArticlesScreen.tsx index 4065ace86..7f83d8e16 100644 --- a/packages/components/src/screens/ArticlesScreen.tsx +++ b/packages/components/src/screens/ArticlesScreen.tsx @@ -3,14 +3,16 @@ import styled from 'styled-components/native' import { FlatList } from 'react-native' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' -import { useSelector } from '../hooks/useSelector' +import { useCommonSelector } from '../redux/common/useCommonSelector' import { commonSelectors } from '../redux/common/selectors' import { Header } from '../components/common/Header' import { TextWithoutTranslation } from '../components/common/Text' import { useTextToSpeechHook } from '../hooks/useTextToSpeechHook' const ArticleItem = ({ article, index, articles }) => { - const articleObject = useSelector((state) => commonSelectors.articleByIDSelector(state, article)) + const articleObject = useCommonSelector((state) => + commonSelectors.articleByIDSelector(state, article), + ) if (!articleObject) { return null @@ -37,10 +39,10 @@ const ArticleItem = ({ article, index, articles }) => { export function ArticlesScreen({ navigation }) { const subCategory = navigation.getParam('subCategory') - const subCategoryObject = useSelector((state) => + const subCategoryObject = useCommonSelector((state) => commonSelectors.subCategoryByIDSelector(state, subCategory), ) - const allArticlesByIDObject = useSelector(commonSelectors.articlesObjectByIDSelector) + const allArticlesByIDObject = useCommonSelector(commonSelectors.articlesObjectByIDSelector) const articles = subCategoryObject.articles const articlesTextArray = articles.reduce((acc, item) => { const selectedArticle = allArticlesByIDObject[item] diff --git a/packages/components/src/screens/AuthScreen.tsx b/packages/components/src/screens/AuthScreen.tsx index d26af7304..fc8e221c0 100644 --- a/packages/components/src/screens/AuthScreen.tsx +++ b/packages/components/src/screens/AuthScreen.tsx @@ -13,7 +13,7 @@ import { navigate } from '../services/navigationService' export function AuthScreen() { const [toggled, setToggled] = React.useState(true) // @TODO: LANGUAGES This is commented in case the client wants multiple languages - // const locale = useSelector(commonSelectors.currentLocaleSelector) + // const locale = useCommonSelector(commonSelectors.currentLocaleSelector) return ( diff --git a/packages/components/src/screens/AvatarAndThemeScreen.tsx b/packages/components/src/screens/AvatarAndThemeScreen.tsx index 591779640..313cc6f6a 100644 --- a/packages/components/src/screens/AvatarAndThemeScreen.tsx +++ b/packages/components/src/screens/AvatarAndThemeScreen.tsx @@ -10,7 +10,7 @@ import { commonActions } from '../redux/common/actions/index' import { Header } from '../components/common/Header' import { useTheme } from '../components/context/ThemeContext' import { BackOneScreen, navigate } from '../services/navigationService' -import { useSelector } from '../hooks/useSelector' +import { useCommonSelector } from '../redux/common/useCommonSelector' import { commonSelectors } from '../redux/common/selectors' import styled from 'styled-components/native' import { Text } from '../components/common/Text' @@ -21,7 +21,7 @@ export function AvatarAndThemeScreen({ navigation }) { const signingUp = navigation.getParam('signingUp') const newUser = navigation.getParam('newUser') const [loading, setLoading] = React.useState(false) - const selectedAvatar = useSelector(commonSelectors.currentAvatarSelector) + const selectedAvatar = useCommonSelector(commonSelectors.currentAvatarSelector) const dispatch = useDispatch() const { id } = useTheme() diff --git a/packages/components/src/screens/ContactUsScreen.tsx b/packages/components/src/screens/ContactUsScreen.tsx index 506f49ac3..5f844d296 100644 --- a/packages/components/src/screens/ContactUsScreen.tsx +++ b/packages/components/src/screens/ContactUsScreen.tsx @@ -9,7 +9,7 @@ import { PageContainer } from '../components/layout/PageContainer' import { TextInput } from '../components/common/TextInput' import { KeyboardAwareAvoidance } from '../components/common/KeyboardAwareAvoidance' import { httpClient } from '../services/HttpClient' -import { useSelector } from '../hooks/useSelector' +import { useCommonSelector } from '../redux/common/useCommonSelector' import { commonSelectors } from '../redux/common/selectors' import moment from 'moment' import { Text } from '../components/common/Text' @@ -23,8 +23,8 @@ const Reasons = ['reason', 'report_bug', 'request_topic', 'Other', 'problem_app' export function ContactUsScreen({ navigation }) { const [email, setEmail] = React.useState('') - const user = useSelector(commonSelectors.currentUserSelector) - const locale = useSelector(commonSelectors.currentLocaleSelector) + const user = useCommonSelector(commonSelectors.currentUserSelector) + const locale = useCommonSelector(commonSelectors.currentLocaleSelector) const [reason, setReason] = React.useState('') const [message, setMessage] = React.useState('') const [notValid, setNotValid] = React.useState(false) diff --git a/packages/components/src/screens/DayScreen.tsx b/packages/components/src/screens/DayScreen.tsx index 1864ce0b0..b1603e6bd 100644 --- a/packages/components/src/screens/DayScreen.tsx +++ b/packages/components/src/screens/DayScreen.tsx @@ -13,7 +13,7 @@ import { usePredictDay } from '../components/context/PredictionProvider' import { ThemedModal } from '../components/common/ThemedModal' import { ColourButtons } from './mainScreen/ColourButtons' import { commonSelectors } from '../redux/common/selectors' -import { useSelector } from '../hooks/useSelector' +import { useCommonSelector } from '../redux/common/useCommonSelector' import moment from 'moment' export function DayScreen({ navigation }) { @@ -21,7 +21,7 @@ export function DayScreen({ navigation }) { const dataEntry = usePredictDay(temp.date) const [isVisible, setIsVisible] = React.useState(false) const { keyboardIsOpen, dismiss } = useKeyboardController() - const cardAnswersToday = useSelector((state) => + const cardAnswersToday = useCommonSelector((state) => commonSelectors.verifyPeriodDaySelectorWithDate(state, moment(dataEntry.date)), ) diff --git a/packages/components/src/screens/EditProfileScreen.tsx b/packages/components/src/screens/EditProfileScreen.tsx index 42a9375db..84bae3538 100644 --- a/packages/components/src/screens/EditProfileScreen.tsx +++ b/packages/components/src/screens/EditProfileScreen.tsx @@ -8,7 +8,7 @@ import { Icon } from '../components/common/Icon' import { SelectBox } from '../components/common/SelectBox' import { DateOfBirthInput } from '../components/common/DateOfBirthInput' import { assets } from '../assets/index' -import { useSelector } from '../hooks/useSelector' +import { useCommonSelector } from '../redux/common/useCommonSelector' import { commonSelectors } from '../redux/common/selectors' import { commonActions } from '../redux/common/actions' import { useDispatch } from 'react-redux' @@ -71,8 +71,8 @@ async function runInSequence(functions) { export function EditProfileScreen() { const dispatch = useDispatch() - const currentUser = useSelector(commonSelectors.currentUserSelector) - const appToken = useSelector(commonSelectors.appTokenSelector) + const currentUser = useCommonSelector(commonSelectors.currentUserSelector) + const appToken = useCommonSelector(commonSelectors.appTokenSelector) const [name, setName] = React.useState(currentUser.name) const [notValid, setNotValid] = React.useState(false) diff --git a/packages/components/src/screens/EncyclopediaScreen.tsx b/packages/components/src/screens/EncyclopediaScreen.tsx index 39663f748..de445547c 100644 --- a/packages/components/src/screens/EncyclopediaScreen.tsx +++ b/packages/components/src/screens/EncyclopediaScreen.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components/native' import { ScrollView, Animated } from 'react-native' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' -import { useSelector } from '../hooks/useSelector' +import { useCommonSelector } from '../redux/common/useCommonSelector' import { commonSelectors } from '../redux/common/selectors' import { Category } from './encyclopediaScreen/Category' import { SubCategoryCard, VideoSubCategoryCard } from './encyclopediaScreen/SubCategoryCard' @@ -18,16 +18,16 @@ import analytics from '@react-native-firebase/analytics' import { fetchNetworkConnectionStatus } from '../services/network' export function EncyclopediaScreen({ navigation }) { - const categories = useSelector(commonSelectors.allCategoriesSelector) - const articles = useSelector(commonSelectors.allArticlesSelector) - const subCategories = useSelector(commonSelectors.allSubCategoriesSelector) - const subCategoriesObject = useSelector(commonSelectors.allSubCategoriesObjectSelector) + const categories = useCommonSelector(commonSelectors.allCategoriesSelector) + const articles = useCommonSelector(commonSelectors.allArticlesSelector) + const subCategories = useCommonSelector(commonSelectors.allSubCategoriesSelector) + const subCategoriesObject = useCommonSelector(commonSelectors.allSubCategoriesObjectSelector) const [activeCategories, setActiveCategory] = React.useState([]) const [filteredCategories, setFilteredCategories] = React.useState(categories) const [shownCategories, setShownCategories] = React.useState(categories) const [searching, setSearching] = React.useState(false) const [position] = React.useState(new Animated.Value(0)) - const currentUser = useSelector(commonSelectors.currentUserSelector) + const currentUser = useCommonSelector(commonSelectors.currentUserSelector) const categoryNames = categories.map((item) => item?.name) const [textArray, setTextArray] = React.useState(categoryNames) diff --git a/packages/components/src/screens/FindHelpScreen.tsx b/packages/components/src/screens/FindHelpScreen.tsx index d2b100ca7..1878d1791 100644 --- a/packages/components/src/screens/FindHelpScreen.tsx +++ b/packages/components/src/screens/FindHelpScreen.tsx @@ -7,7 +7,7 @@ import { Avatar } from '../components/common/Avatar/Avatar' import { TextWithoutTranslation, Text } from '../components/common/Text' import { SwiperContainer } from '../components/common/SwiperContainer' import { PageContainer } from '../components/layout/PageContainer' -import { useSelector } from '../hooks/useSelector' +import { useCommonSelector } from '../redux/common/useCommonSelector' import { commonSelectors } from '../redux/common/selectors' import { useTextToSpeechHook } from '../hooks/useTextToSpeechHook' import analytics from '@react-native-firebase/analytics' @@ -18,7 +18,7 @@ const screenHeight = Dimensions.get('screen').height const heightOfCarousel = screenHeight * 0.5 export function FindHelpScreen({ navigation }) { - const helpCenters: any = useSelector(commonSelectors.allHelpCentersForCurrentLocale) + const helpCenters: any = useCommonSelector(commonSelectors.allHelpCentersForCurrentLocale) const [textToSpeak, setTextToSpeak] = React.useState([]) React.useEffect(() => { diff --git a/packages/components/src/screens/MainScreen.tsx b/packages/components/src/screens/MainScreen.tsx index f11a46b4d..9baf46199 100644 --- a/packages/components/src/screens/MainScreen.tsx +++ b/packages/components/src/screens/MainScreen.tsx @@ -22,7 +22,7 @@ import { InformationButton } from '../components/common/InformationButton' import { assets } from '../assets' import { commonActions } from '../redux/common/actions' import { useDispatch } from 'react-redux' -import { useSelector } from '../hooks/useSelector' +import { useCommonSelector } from '../redux/common/useCommonSelector' import { commonSelectors } from '../redux/common/selectors' import moment from 'moment' import { FlowerButton, FlowerModal } from '../optional/Flower' @@ -44,10 +44,10 @@ const MainScreenContainer = ({ navigation }) => { const theme = useTheme() const todayInfo = useTodayPrediction() const dispatch = useDispatch() - const userID = useSelector(commonSelectors.currentUserSelector).id + const userID = useCommonSelector(commonSelectors.currentUserSelector).id const fullState = useFullState() const history = useHistoryPrediction() - const currentUser = useSelector(commonSelectors.currentUserSelector) + const currentUser = useCommonSelector(commonSelectors.currentUserSelector) // @TODO: careful note here, may be worth the performance increase though May not work with Memo now React.useEffect(() => { @@ -61,7 +61,7 @@ const MainScreenContainer = ({ navigation }) => { const MainScreenActual = React.memo(() => { const { data, index, isActive, currentIndex, absoluteIndex } = useInfiniteScroll() // TODO_ALEX: DO NOT USE HOOKS LIKE THIS - const renamedUseSelector = useSelector + const renamedUseSelector = useCommonSelector const allCardsData = renamedUseSelector((state) => commonSelectors.allCardAnswersSelector(state)) const getCardAnswersValues = (inputDay: any) => { const verifiedPeriodDaysData = renamedUseSelector((state) => diff --git a/packages/components/src/screens/OnboardingScreen.tsx b/packages/components/src/screens/OnboardingScreen.tsx index e9795490d..84c7a0866 100644 --- a/packages/components/src/screens/OnboardingScreen.tsx +++ b/packages/components/src/screens/OnboardingScreen.tsx @@ -18,7 +18,7 @@ export function OnboardingScreen() { const [index, setIndex] = React.useState(0) const [isButtonVisible, setIsButtonVisible] = React.useState(false) // @TODO: LANGUAGES This is commented in case the client wants multiple languages - // const region = useSelector(commonSelectors.currentChosenRegionSelector) + // const region = useCommonSelector(commonSelectors.currentChosenRegionSelector) React.useEffect(() => { if (index === 2) { diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index 227fadc5e..9039abcf4 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -8,14 +8,14 @@ import { commonSelectors } from '../redux/common/selectors' import { navigateAndReset } from '../services/navigationService' import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { PageContainer } from '../components/layout/PageContainer' -import { useSelector } from '../hooks/useSelector' +import { useCommonSelector } from '../redux/common/useCommonSelector' import { KeyboardAwareAvoidance } from '../components/common/KeyboardAwareAvoidance' import { SpinLoader } from '../components/common/SpinLoader' import _ from 'lodash' export function PasswordRequestScreen() { const dispatch = useDispatch() - const user = useSelector(commonSelectors.currentUserSelector) + const user = useCommonSelector(commonSelectors.currentUserSelector) const [loading, setLoading] = React.useState(false) const [valid, setValid] = React.useState(false) const [passwordError, setPasswordError] = React.useState(false) diff --git a/packages/components/src/screens/ProfileScreen.tsx b/packages/components/src/screens/ProfileScreen.tsx index 8a63678e5..d47d67f61 100644 --- a/packages/components/src/screens/ProfileScreen.tsx +++ b/packages/components/src/screens/ProfileScreen.tsx @@ -17,7 +17,7 @@ import { PrimaryButton } from '../components/common/buttons/PrimaryButton' import { AvatarOption } from './avatarAndTheme/avatarSelect/AvatarOption' import { ThemeSelectItem } from './avatarAndTheme/ThemeSelectItem' import { useHistoryPrediction, useTodayPrediction } from '../components/context/PredictionProvider' -import { useSelector } from '../hooks/useSelector' +import { useCommonSelector } from '../redux/common/useCommonSelector' import { commonActions } from '../redux/common/actions' import { commonSelectors } from '../redux/common/selectors' import { translate } from '../i18n/index' @@ -29,17 +29,17 @@ import moment from 'moment' export function ProfileScreen({ navigation }) { const History = useHistoryPrediction() - const selectedAvatar = useSelector(commonSelectors.currentAvatarSelector) + const selectedAvatar = useCommonSelector(commonSelectors.currentAvatarSelector) const [isModalVisible, setIsModalVisible] = React.useState(false) const [error, setError] = React.useState(false) const shouldSkip = React.useRef(0) - const currentUser = useSelector(commonSelectors.currentUserSelector) - const errorCode: any = useSelector(commonSelectors.authError) + const currentUser = useCommonSelector(commonSelectors.currentUserSelector) + const errorCode: any = useCommonSelector(commonSelectors.authError) const todayInfo = useTodayPrediction() const { id: theme } = useTheme() const dispatch = useDispatch() - const connectAccountCount = useSelector((state) => state.auth.connectAccountAttempts) + const connectAccountCount = useCommonSelector((state) => state.auth.connectAccountAttempts) const dateOfBirth = moment(currentUser.dateOfBirth) useTextToSpeechHook({ diff --git a/packages/components/src/screens/SettingsScreen.tsx b/packages/components/src/screens/SettingsScreen.tsx index 6b4c8de40..d0f969b0a 100644 --- a/packages/components/src/screens/SettingsScreen.tsx +++ b/packages/components/src/screens/SettingsScreen.tsx @@ -10,7 +10,7 @@ import { navigate } from '../services/navigationService' import { useDispatch } from 'react-redux' import { commonActions } from '../redux/common/actions' import { ConfirmAlert } from '../components/common/ConfirmAlert' -import { useSelector } from '../hooks/useSelector' +import { useCommonSelector } from '../redux/common/useCommonSelector' import { commonSelectors } from '../redux/common/selectors' import { translate } from '../i18n/index' import { SpinLoader } from '../components/common/SpinLoader' @@ -25,9 +25,9 @@ export function SettingsScreen({ navigation }) { const dispatch = useDispatch() const [loading, setLoading] = React.useState(false) const currentCycleInfo = useTodayPrediction() - const currentUser = useSelector(commonSelectors.currentUserSelector) - const hasTtsActive = useSelector(commonSelectors.isTtsActiveSelector) - const hasFuturePredictionActive = useSelector(commonSelectors.isFuturePredictionSelector) + const currentUser = useCommonSelector(commonSelectors.currentUserSelector) + const hasTtsActive = useCommonSelector(commonSelectors.isTtsActiveSelector) + const hasFuturePredictionActive = useCommonSelector(commonSelectors.isFuturePredictionSelector) useTextToSpeechHook({ navigation, diff --git a/packages/components/src/screens/SplashScreen.tsx b/packages/components/src/screens/SplashScreen.tsx index 2fcf5f9e1..66fc73e97 100644 --- a/packages/components/src/screens/SplashScreen.tsx +++ b/packages/components/src/screens/SplashScreen.tsx @@ -3,7 +3,7 @@ import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { PageContainer } from '../components/layout/PageContainer' import { assets } from '../assets/index' import styled from 'styled-components/native' -import { useSelector, useDispatch } from 'react-redux' +import { useCommonSelector, useDispatch } from 'react-redux' import { commonSelectors } from '../redux/common/selectors' import { commonActions } from '../redux/common/actions' import { navigateAndReset } from '../services/navigationService' @@ -18,14 +18,14 @@ import messaging from '@react-native-firebase/messaging' export function SplashScreen() { const dispatch = useDispatch() - const user: any = useSelector(commonSelectors.currentUserSelector) + const user: any = useCommonSelector(commonSelectors.currentUserSelector) const Alert = useAlert() - const locale = useSelector(commonSelectors.currentLocaleSelector) - const hasOpened = useSelector(commonSelectors.hasOpenedSelector) - const currentAppVersion = useSelector(commonSelectors.currentAppVersion) - const currentFirebaseToken = useSelector(commonSelectors.currentFirebaseToken) - const hasPasswordRequestOn = useSelector(commonSelectors.isLoginPasswordActiveSelector) + const locale = useCommonSelector(commonSelectors.currentLocaleSelector) + const hasOpened = useCommonSelector(commonSelectors.hasOpenedSelector) + const currentAppVersion = useCommonSelector(commonSelectors.currentAppVersion) + const currentFirebaseToken = useCommonSelector(commonSelectors.currentFirebaseToken) + const hasPasswordRequestOn = useCommonSelector(commonSelectors.isLoginPasswordActiveSelector) const [animatedValue] = React.useState(new Animated.Value(0)) async function checkForPermanentAlerts() { diff --git a/packages/components/src/screens/TutorialFirstScreen.tsx b/packages/components/src/screens/TutorialFirstScreen.tsx index e1a99dcd2..354a95138 100644 --- a/packages/components/src/screens/TutorialFirstScreen.tsx +++ b/packages/components/src/screens/TutorialFirstScreen.tsx @@ -18,7 +18,7 @@ import { assets } from '../assets' import { ColourButtonsDemo } from './tutorial/ColourButtonsDemo' import { SpinLoader } from '../components/common/SpinLoader' import DeviceInfo from 'react-native-device-info' -import { useSelector } from '../hooks/useSelector' +import { useCommonSelector } from '../redux/common/useCommonSelector' import { commonSelectors } from '../redux/common/selectors' import moment from 'moment' import Tts from 'react-native-tts' @@ -43,13 +43,13 @@ export function TutorialFirstScreen() { const dispatch = useDispatch() const [completedStep, setCompletedStep] = React.useState(0) - const hasTtsActive = useSelector(commonSelectors.isTtsActiveSelector) + const hasTtsActive = useCommonSelector(commonSelectors.isTtsActiveSelector) const normalizePosition = (percentage, dimension) => { return percentage * dimension - arrowSize / 2 } // TODO_ALEX: DO NOT USE HOOKS LIKE THIS - const renamedUseSelector = useSelector + const renamedUseSelector = useCommonSelector const getCardAnswersValues = (inputDay) => { const cardData = renamedUseSelector((state) => diff --git a/packages/components/src/screens/TutorialSecondScreen.tsx b/packages/components/src/screens/TutorialSecondScreen.tsx index ba73c4834..fc1e9fb58 100644 --- a/packages/components/src/screens/TutorialSecondScreen.tsx +++ b/packages/components/src/screens/TutorialSecondScreen.tsx @@ -19,7 +19,7 @@ import { DayAssetDemo } from './tutorial/DayAssetDemo' import { CalendarAssetDemo } from './tutorial/CalendarAssetDemo' import { SpinLoader } from '../components/common/SpinLoader' import { NoteAssetDemo } from './tutorial/NoteAssetDemo' -import { useSelector } from '../hooks/useSelector' +import { useCommonSelector } from '../redux/common/useCommonSelector' import { commonSelectors } from '../redux/common/selectors' import moment from 'moment' import Tts from 'react-native-tts' @@ -50,8 +50,8 @@ export function TutorialSecondScreen({ navigation }) { const flag = React.useRef(false) const dispatch = useDispatch() // TODO_ALEX: DO NOT USE HOOKS LIKE THIS - const renamedUseSelector = useSelector - const hasTtsActive = useSelector(commonSelectors.isTtsActiveSelector) + const renamedUseSelector = useCommonSelector + const hasTtsActive = useCommonSelector(commonSelectors.isTtsActiveSelector) const normalizePosition = (percentage, dimension) => { return percentage * dimension - arrowSize / 2 diff --git a/packages/components/src/screens/VideosScreen.tsx b/packages/components/src/screens/VideosScreen.tsx index 0c75e91d9..02da60ee9 100644 --- a/packages/components/src/screens/VideosScreen.tsx +++ b/packages/components/src/screens/VideosScreen.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components/native' import { FlatList } from 'react-native' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' -import { useSelector } from '../hooks/useSelector' +import { useCommonSelector } from '../redux/common/useCommonSelector' import { commonSelectors } from '../redux/common/selectors' import { Header } from '../components/common/Header' import { TextWithoutTranslation } from '../components/common/Text' @@ -17,7 +17,9 @@ export const VideoItem = ({ videoId: string onSelect: React.Dispatch> }) => { - const videoObject = useSelector((state) => commonSelectors.videoByIDSelector(state, videoId)) + const videoObject = useCommonSelector((state) => + commonSelectors.videoByIDSelector(state, videoId), + ) if (!videoObject) { return null @@ -40,7 +42,9 @@ export const VideoItem = ({ export function VideosScreen({ navigation }) { const categoryId = navigation.getParam('categoryId') - const category = useSelector((state) => commonSelectors.categoryByIDSelector(state, categoryId)) + const category = useCommonSelector((state) => + commonSelectors.categoryByIDSelector(state, categoryId), + ) const videos = category?.videos || [] return ( diff --git a/packages/components/src/screens/authScreen/Login.tsx b/packages/components/src/screens/authScreen/Login.tsx index 337388716..95112776f 100644 --- a/packages/components/src/screens/authScreen/Login.tsx +++ b/packages/components/src/screens/authScreen/Login.tsx @@ -4,13 +4,13 @@ import { Text } from '../../components/common/Text' import { TextInput } from '../../components/common/TextInput' import { useDispatch } from 'react-redux' import { commonActions } from '../../redux/common/actions' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { SpinLoader } from '../../components/common/SpinLoader' import _ from 'lodash' export function Login() { const dispatch = useDispatch() - const { error: loginError, isLoggingIn } = useSelector((state) => state.auth) + const { error: loginError, isLoggingIn } = useCommonSelector((state) => state.auth) const [loading, setLoading] = React.useState(false) const [name, setName] = React.useState('') diff --git a/packages/components/src/screens/authScreen/signUp/AskLocation.tsx b/packages/components/src/screens/authScreen/signUp/AskLocation.tsx index 7a88b812c..33b6fa54c 100644 --- a/packages/components/src/screens/authScreen/signUp/AskLocation.tsx +++ b/packages/components/src/screens/authScreen/signUp/AskLocation.tsx @@ -7,14 +7,14 @@ import { Text } from '../../../components/common/Text' import { GenderSelectItem } from '../../../components/common/GenderSelectItem' import { formHeights } from './FormHeights' import { ModalSearchBox } from '../../../components/common/ModalSearchBox' -import { useSelector } from '../../../hooks/useSelector' +import { useCommonSelector } from '../../../redux/common/useCommonSelector' import { commonSelectors } from '../../../redux/common/selectors' import { translate } from '../../../i18n' import { FAST_SIGN_UP } from '../../../config' export function AskLocation({ step, createAccount }) { const [{ app: state }, dispatch] = useMultiStepForm() - const lang = useSelector(commonSelectors.currentLocaleSelector) + const lang = useCommonSelector(commonSelectors.currentLocaleSelector) const { country, province, location } = state const [derivedCountry, setDerivedCountry] = React.useState( FAST_SIGN_UP ? { code: 'AF', item: 'Afghanistan' } : null, diff --git a/packages/components/src/screens/dayScreen/DayCarousel.tsx b/packages/components/src/screens/dayScreen/DayCarousel.tsx index f26d63d00..6ec5fca0d 100644 --- a/packages/components/src/screens/dayScreen/DayCarousel.tsx +++ b/packages/components/src/screens/dayScreen/DayCarousel.tsx @@ -8,7 +8,7 @@ import { NoteCard } from './NoteCard' import { QuizCard } from './QuizCard' import { DidYouKnowCard } from './DidYouKnowCard' import { SurveyCard } from './SurveyCard' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { commonSelectors } from '../../redux/common/selectors' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { translate } from '../../i18n' @@ -27,9 +27,9 @@ export function DayCarousel({ navigation, dataEntry }) { const [tempCardName, setTempCardName] = React.useState(null) const [tempCardAnswer, setTempCardAnswer] = React.useState(null) const viewConfigRef = React.useRef({ viewAreaCoveragePercentThreshold: 50 }) - const userID = useSelector(commonSelectors.currentUserSelector).id - const allSurveys = useSelector(commonSelectors.allSurveys) - const completedSurveys = useSelector(commonSelectors.completedSurveys) + const userID = useCommonSelector(commonSelectors.currentUserSelector).id + const allSurveys = useCommonSelector(commonSelectors.allSurveys) + const completedSurveys = useCommonSelector(commonSelectors.completedSurveys) const newSurveys = allSurveys?.length ? allSurveys[0] : null const cards = { diff --git a/packages/components/src/screens/dayScreen/DayCarouselItem.tsx b/packages/components/src/screens/dayScreen/DayCarouselItem.tsx index 36b15c2e7..eaa8201fd 100644 --- a/packages/components/src/screens/dayScreen/DayCarouselItem.tsx +++ b/packages/components/src/screens/dayScreen/DayCarouselItem.tsx @@ -7,14 +7,14 @@ import { Text } from '../../components/common/Text' import { EmojiSelector } from '../../components/common/EmojiSelector' import { Icon } from '../../components/common/Icon' import { commonSelectors } from '../../redux/common/selectors' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { useColor } from '../../hooks/useColor' import { translate } from '../../i18n' const deviceWidth = Dimensions.get('window').width export function DayCarouselItem({ content, cardName, dataEntry, onPress, index }) { - const selectedEmojis = useSelector((state) => + const selectedEmojis = useCommonSelector((state) => commonSelectors.cardAnswerSelector(state, dataEntry.date), ) diff --git a/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx b/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx index ba8f6102d..1e5d72957 100644 --- a/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx +++ b/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx @@ -2,7 +2,7 @@ import React from 'react' import { Dimensions } from 'react-native' import styled from 'styled-components/native' import { TextWithoutTranslation, Text } from '../../components/common/Text' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import _ from 'lodash' import { commonSelectors } from '../../redux/common/selectors' import { TitleText } from '../../components/common/TitleText' @@ -10,7 +10,7 @@ import { TitleText } from '../../components/common/TitleText' const deviceWidth = Dimensions.get('window').width function useDidYouKnow() { - const allDidYouKnows = useSelector(commonSelectors.allDidYouKnowsSelectors) + const allDidYouKnows = useCommonSelector(commonSelectors.allDidYouKnowsSelectors) const randomDidYouKnow = React.useMemo(() => { return _.sample(allDidYouKnows) }, []) diff --git a/packages/components/src/screens/dayScreen/NoteCard.tsx b/packages/components/src/screens/dayScreen/NoteCard.tsx index ba359f47a..247c48d60 100644 --- a/packages/components/src/screens/dayScreen/NoteCard.tsx +++ b/packages/components/src/screens/dayScreen/NoteCard.tsx @@ -3,7 +3,7 @@ import { Dimensions } from 'react-native' import styled from 'styled-components/native' import { commonSelectors } from '../../redux/common/selectors' import { commonActions } from '../../redux/common/actions' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { TextInput } from '../../components/common/TextInput' import { useDispatch } from 'react-redux' import { BackOneScreen } from '../../services/navigationService' @@ -16,10 +16,10 @@ import { translate } from '../../i18n' const deviceWidth = Dimensions.get('window').width export function NoteCard({ dataEntry }) { - const noteObject: any = useSelector((state) => + const noteObject: any = useCommonSelector((state) => commonSelectors.notesAnswerSelector(state, dataEntry.date), ) - const userID = useSelector(commonSelectors.currentUserSelector).id + const userID = useCommonSelector(commonSelectors.currentUserSelector).id const [title, setTitle] = React.useState(noteObject.title || '') const [titlePlaceholder, setTitlePlaceholder] = React.useState('title') const [notesPlaceholder, setNotesPlaceholder] = React.useState('daily_note_description') diff --git a/packages/components/src/screens/dayScreen/QuizCard.tsx b/packages/components/src/screens/dayScreen/QuizCard.tsx index 8eae46ab8..a222b3ac1 100644 --- a/packages/components/src/screens/dayScreen/QuizCard.tsx +++ b/packages/components/src/screens/dayScreen/QuizCard.tsx @@ -4,7 +4,7 @@ import { Dimensions } from 'react-native' import { TextWithoutTranslation, Text } from '../../components/common/Text' import { EmojiSelector } from '../../components/common/EmojiSelector' import { TitleText } from '../../components/common/TitleText' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import _ from 'lodash' import { commonSelectors } from '../../redux/common/selectors' import { commonActions } from '../../redux/common/actions' @@ -13,9 +13,9 @@ import { useDispatch } from 'react-redux' const deviceWidth = Dimensions.get('window').width function useQuiz() { - const unansweredQuizzes = useSelector(commonSelectors.quizzesWithoutAnswersSelector) + const unansweredQuizzes = useCommonSelector(commonSelectors.quizzesWithoutAnswersSelector) - const allQuizzes = useSelector(commonSelectors.allQuizzesSelectors) + const allQuizzes = useCommonSelector(commonSelectors.allQuizzesSelectors) const randomQuiz = React.useMemo(() => { if (_.isEmpty(unansweredQuizzes)) { return _.sample(allQuizzes) @@ -27,9 +27,9 @@ function useQuiz() { export const QuizCard = React.memo<{ dataEntry: any; index: number }>(({ dataEntry, index }) => { const dispatch = useDispatch() - const userID = useSelector(commonSelectors.currentUserSelector).id + const userID = useCommonSelector(commonSelectors.currentUserSelector).id const selectedQuestion = useQuiz() - const answeredQuestion = useSelector((state) => + const answeredQuestion = useCommonSelector((state) => commonSelectors.quizAnswerByDate(state, dataEntry.date), ) diff --git a/packages/components/src/screens/dayScreen/SurveyCard.tsx b/packages/components/src/screens/dayScreen/SurveyCard.tsx index b0f298649..9f6b6595f 100644 --- a/packages/components/src/screens/dayScreen/SurveyCard.tsx +++ b/packages/components/src/screens/dayScreen/SurveyCard.tsx @@ -4,7 +4,7 @@ import { Dimensions } from 'react-native' import { TextWithoutTranslation, Text } from '../../components/common/Text' import { EmojiSelector } from '../../components/common/EmojiSelector' import { TitleText } from '../../components/common/TitleText' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import _ from 'lodash' import { commonSelectors } from '../../redux/common/selectors' import { commonActions } from '../../redux/common/actions' @@ -34,12 +34,12 @@ export const SurveyCard = React.memo<{ const [title, setTitle] = React.useState('') const [titlePlaceholder, setTitlePlaceholder] = React.useState('type_answer_placeholder') const [isSkip, setSkip] = React.useState(null) - const userID = useSelector(commonSelectors.currentUserSelector).id + const userID = useCommonSelector(commonSelectors.currentUserSelector).id const dispatch = useDispatch() const [showThankYouMsg, setThankYouMsg] = React.useState(null) const [selectedIndex, setSelectedIndex] = React.useState(null) - const completedSurveys = useSelector(commonSelectors.completedSurveys) - const allSurveys = useSelector(commonSelectors.allSurveys) + const completedSurveys = useCommonSelector(commonSelectors.completedSurveys) + const allSurveys = useCommonSelector(commonSelectors.allSurveys) const checkUserPermission = (option, optionIndex) => { setSelectedIndex(optionIndex) diff --git a/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx b/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx index 4378b5156..59962a7df 100644 --- a/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx +++ b/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx @@ -6,7 +6,7 @@ import { IconButton } from '../../components/common/buttons/IconButton' import styled from 'styled-components/native' import { handleCategoriesFilter, handleSearchResult } from './searchFunctions' import { EmojiSelector } from '../../components/common/EmojiSelector' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { commonSelectors } from '../../redux/common/selectors' import { translate } from '../../i18n' @@ -22,8 +22,8 @@ export const SearchBar = ({ }) => { const [searchStr, setSearchStr] = React.useState('') const [emojiFilter, updateEmojiFilter] = React.useState([]) - const locale = useSelector(commonSelectors.currentLocaleSelector) - const emojiList = useSelector(commonSelectors.allCategoryEmojis) + const locale = useCommonSelector(commonSelectors.currentLocaleSelector) + const emojiList = useCommonSelector(commonSelectors.allCategoryEmojis) return ( <> diff --git a/packages/components/src/screens/journeyScreen/JourneyCard.tsx b/packages/components/src/screens/journeyScreen/JourneyCard.tsx index 7518d2afd..90b1661b9 100644 --- a/packages/components/src/screens/journeyScreen/JourneyCard.tsx +++ b/packages/components/src/screens/journeyScreen/JourneyCard.tsx @@ -5,7 +5,7 @@ import { CalendarCardContent } from './CalendarCardContent' import { WheelPickerContent } from '../../components/WheelPickerContent' import { Avatar } from '../../components/common/Avatar/Avatar' import { assets } from '../../assets' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { commonSelectors } from '../../redux/common/selectors' export function JourneyCard({ @@ -29,7 +29,7 @@ export function JourneyCard({ leftButtonTitle = 'i_dont_remember', rightButtonTitle = 'i_remember', }) { - const selectedAvatar = useSelector(commonSelectors.currentAvatarSelector) + const selectedAvatar = useCommonSelector(commonSelectors.currentAvatarSelector) return ( <> {status === 'initial' && ( diff --git a/packages/components/src/screens/mainScreen/Calendar.tsx b/packages/components/src/screens/mainScreen/Calendar.tsx index 5409f3bb6..498004207 100644 --- a/packages/components/src/screens/mainScreen/Calendar.tsx +++ b/packages/components/src/screens/mainScreen/Calendar.tsx @@ -21,7 +21,7 @@ import { assets } from '../../assets' import { translate } from '../../i18n' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { calendarScreenSpeech } from '../../config' -import { useSelector } from 'react-redux' +import { useCommonSelector } from 'react-redux' import { commonSelectors } from '../../redux/common/selectors' const width = Dimensions.get('window').width @@ -34,8 +34,8 @@ const startDate = moment().startOf('day').subtract(24, 'months') const endDate = moment().startOf('day').add(12, 'months') export const Calendar = ({ navigation }) => { - const hasFuturePredictionActive = useSelector(commonSelectors.isFuturePredictionSelector) - const verifiedPeriodsData = useSelector((state: any) => + const hasFuturePredictionActive = useCommonSelector(commonSelectors.isFuturePredictionSelector) + const verifiedPeriodsData = useCommonSelector((state: any) => commonSelectors.allCardAnswersSelector(state), ) const highlightedDates = useCalculateStatusForDateRange( diff --git a/packages/components/src/screens/mainScreen/ColourButtons.tsx b/packages/components/src/screens/mainScreen/ColourButtons.tsx index 1edf3d680..4fcc5b9d4 100644 --- a/packages/components/src/screens/mainScreen/ColourButtons.tsx +++ b/packages/components/src/screens/mainScreen/ColourButtons.tsx @@ -24,7 +24,7 @@ import analytics from '@react-native-firebase/analytics' import moment from 'moment' import { fetchNetworkConnectionStatus } from '../../services/network' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { incrementFlowerProgress, useFlowerStateSelector, FlowerModal } from '../../optional/Flower' const minBufferBetweenCycles = 2 @@ -59,8 +59,8 @@ export function ColourButtons({ const selectedDayInfoEngine = usePredictDay(inputDay) const isActive = useIsActiveSelector() const appDispatch = useDispatch() - const userID = useSelector(commonSelectors.currentUserSelector).id - const currentUser = useSelector(commonSelectors.currentUserSelector) + const userID = useCommonSelector(commonSelectors.currentUserSelector).id + const currentUser = useCommonSelector(commonSelectors.currentUserSelector) const currentCycleInfo = useTodayPrediction() const inputDayStr = moment(inputDay).format('YYYY-MM-DD') const todayStr = moment().format('YYYY-MM-DD') @@ -68,13 +68,13 @@ export function ColourButtons({ const [isFlowerVisible, setFlowerVisible] = React.useState(false) const flowerState = useFlowerStateSelector() - const cardAnswersToday = useSelector((state) => + const cardAnswersToday = useCommonSelector((state) => commonSelectors.verifyPeriodDaySelectorWithDate(state, moment(inputDayStr)), ) as any const fullState = useFullState() const [addNewCycleHistory, setNewCycleHistory] = React.useState(false) - const hasFuturePredictionActive = useSelector(commonSelectors.isFuturePredictionSelector) + const hasFuturePredictionActive = useCommonSelector(commonSelectors.isFuturePredictionSelector) // TODO_ALEX this useState is redundant const [futurePredictionStatus, setFuturePredictionStatus] = useState(false) diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx index 5df58975b..5b66a850e 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx @@ -7,7 +7,7 @@ import { TouchableOpacity } from 'react-native-gesture-handler' import { navigate, navigateAndReset } from '../../../services/navigationService' import moment from 'moment' import { useDisplayText } from '../../../components/context/DisplayTextContext' -import { useSelector } from '../../../hooks/useSelector' +import { useCommonSelector } from '../../../redux/common/useCommonSelector' import { commonSelectors } from '../../../redux/common/selectors' import { SpinLoader } from '../../../components/common/SpinLoader' @@ -28,7 +28,7 @@ export function Carousel({ absoluteIndex, disableInteraction = false, }) { - const isTutorialTwoOn = useSelector(commonSelectors.isTutorialTwoActiveSelector) + const isTutorialTwoOn = useCommonSelector(commonSelectors.isTutorialTwoActiveSelector) const [isVisible, setIsVisible] = React.useState(false) const { setDisplayTextStatic } = useDisplayText() diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx index 1018c2783..2acdd1725 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx @@ -6,7 +6,7 @@ import { DayBadge } from '../../../components/common/DayBadge' import { DateBadge } from '../../../components/common/DateBadge' import { assets } from '../../../assets/index' import { EmojiSelector } from '../../../components/common/EmojiSelector' -import { useSelector } from '../../../hooks/useSelector' +import { useCommonSelector } from '../../../redux/common/useCommonSelector' import { emojis } from '../../../config' import { commonSelectors } from '../../../redux/common/selectors' import { useColor } from '../../../hooks/useColor' @@ -46,7 +46,7 @@ export function CarouselElement({ dataEntry, index, isActive, currentIndex }) { const clock = new Clock() const value = new Value(0) const color = useColor(dataEntry.onPeriod, dataEntry.onFertile) - const cardAnswersValues = useSelector((state) => + const cardAnswersValues = useCommonSelector((state) => commonSelectors.cardAnswerSelector(state, moment(dataEntry.date)), ) @@ -72,7 +72,7 @@ export function CarouselElement({ dataEntry, index, isActive, currentIndex }) { navigateAndReset('TutorialFirstStack', null) }) } - const verifiedPeriodDaysData = useSelector((state) => + const verifiedPeriodDaysData = useCommonSelector((state) => commonSelectors.verifyPeriodDaySelectorWithDate(state, moment(dataEntry.date)), ) diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx index cd2466626..48d0f8f35 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx @@ -6,7 +6,7 @@ import { useTheme } from '../../../components/context/ThemeContext' import { TouchableOpacity } from 'react-native-gesture-handler' import { assets } from '../../../assets/index' import { translate } from '../../../i18n' -import { useSelector } from 'react-redux' +import { useCommonSelector } from 'react-redux' import { commonSelectors } from '../../../redux/common/selectors' import moment from 'moment' import { @@ -95,7 +95,7 @@ export function CircularElement({ state, }) { const currentCycleInfo = useTodayPrediction() - const hasFuturePredictionActive = useSelector(commonSelectors.isFuturePredictionSelector) + const hasFuturePredictionActive = useCommonSelector(commonSelectors.isFuturePredictionSelector) const actualCurrentStartDate = useActualCurrentStartDateSelector() const { id: themeName } = useTheme() const clock = new Clock() diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx index 076b0d365..5db53a350 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx @@ -6,7 +6,7 @@ import { CircularElement } from './CircularElement' import { PanGesture } from './PanGesture' import { TouchableOpacity } from 'react-native-gesture-handler' import { ColourButtons } from '../ColourButtons' -import { useSelector } from '../../../hooks/useSelector' +import { useCommonSelector } from '../../../redux/common/useCommonSelector' import { commonSelectors } from '../../../redux/common/selectors' import { navigateAndReset } from '../../../services/navigationService' import { useCheckDayWarning } from '../../../hooks/usePredictionWarnings' @@ -44,7 +44,7 @@ export function CircularSelection({ inputRange: [0, data.length], outputRange: [0, -2 * Math.PI], }) - const isTutorialOneOn = useSelector(commonSelectors.isTutorialOneActiveSelector) + const isTutorialOneOn = useCommonSelector(commonSelectors.isTutorialOneActiveSelector) const checkIfWarning = useCheckDayWarning() // automatically close the modal if the wheel start scrolling React.useEffect(() => { @@ -138,7 +138,7 @@ export function CircularSelection({ isCalendar={false} onPress={() => setIsVisible(false)} selectedDayInfo={data[currentIndex]} - cardValues={useSelector((state) => + cardValues={useCommonSelector((state) => commonSelectors.verifyPeriodDaySelectorWithDate( state, moment(data[currentIndex].date), diff --git a/packages/components/src/screens/profileScreen/CycleCard.tsx b/packages/components/src/screens/profileScreen/CycleCard.tsx index dd0107387..b6d48aa39 100644 --- a/packages/components/src/screens/profileScreen/CycleCard.tsx +++ b/packages/components/src/screens/profileScreen/CycleCard.tsx @@ -5,14 +5,14 @@ import { Icon } from '../../components/common/Icon' import { assets } from '../../assets/index' import { commonSelectors } from '../../redux/common/selectors' import { EmojiSelector } from '../../components/common/EmojiSelector' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { emojis } from '../../config' import { translate } from '../../i18n' const cardNames = ['mood', 'body', 'activity', 'flow'] export const CycleCard = ({ item, cycleNumber }) => { - const cardAnswersValues = useSelector((state) => + const cardAnswersValues = useCommonSelector((state) => commonSelectors.mostAnsweredSelector(state, item.cycleStartDate, item.cycleEndDate), ) diff --git a/packages/components/src/screens/settings/AboutScreen.tsx b/packages/components/src/screens/settings/AboutScreen.tsx index 1c9ed781b..6b18bb624 100644 --- a/packages/components/src/screens/settings/AboutScreen.tsx +++ b/packages/components/src/screens/settings/AboutScreen.tsx @@ -4,7 +4,7 @@ import { BackgroundTheme } from '../../components/layout/BackgroundTheme' import { TextWithoutTranslation } from '../../components/common/Text' import { Header } from '../../components/common/Header' import { Icon } from '../../components/common/Icon' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { commonSelectors } from '../../redux/common/selectors' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { aboutScreenText } from '../../config' @@ -13,9 +13,9 @@ import { assets } from '../../assets' const width = Dimensions.get('window').width const imageWidth = width - 30 export const AboutScreen = ({ navigation }) => { - const aboutContent = useSelector(commonSelectors.aboutContent) - const aboutBanner = useSelector(commonSelectors.aboutBanner) - const locale = useSelector(commonSelectors.currentLocaleSelector) + const aboutContent = useCommonSelector(commonSelectors.aboutContent) + const aboutBanner = useCommonSelector(commonSelectors.aboutBanner) + const locale = useCommonSelector(commonSelectors.currentLocaleSelector) const iconSource = aboutBanner ? { uri: aboutBanner } : assets.general.aboutBanner[locale] diff --git a/packages/components/src/screens/settings/AccessScreen.tsx b/packages/components/src/screens/settings/AccessScreen.tsx index feb68f89f..3f8a468e4 100644 --- a/packages/components/src/screens/settings/AccessScreen.tsx +++ b/packages/components/src/screens/settings/AccessScreen.tsx @@ -4,7 +4,7 @@ import { BackgroundTheme } from '../../components/layout/BackgroundTheme' import styled from 'styled-components/native' import { ListItem } from './accessScreen/ListItem' import { Header } from '../../components/common/Header' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { commonSelectors } from '../../redux/common/selectors' import { commonActions } from '../../redux/common/actions/index' import { useDispatch } from 'react-redux' @@ -19,11 +19,11 @@ import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { acessSettingsScreenText, WEBSITE_URL } from '../../config' export function AccessScreen({ navigation }) { - const locale = useSelector(commonSelectors.currentLocaleSelector) + const locale = useCommonSelector(commonSelectors.currentLocaleSelector) const dispatch = useDispatch() const [loading, setLoading] = React.useState(false) - const privacyContent = useSelector(commonSelectors.privacyContent) + const privacyContent = useCommonSelector(commonSelectors.privacyContent) const speechText = privacyContent.map((item) => item.content) const shareLink = () => { // @TODO: app event diff --git a/packages/components/src/screens/settings/PrivacyScreen.tsx b/packages/components/src/screens/settings/PrivacyScreen.tsx index 1678662d2..1ce4e8a32 100644 --- a/packages/components/src/screens/settings/PrivacyScreen.tsx +++ b/packages/components/src/screens/settings/PrivacyScreen.tsx @@ -4,7 +4,7 @@ import { BackgroundTheme } from '../../components/layout/BackgroundTheme' import { Header } from '../../components/common/Header' import { TextWithoutTranslation } from '../../components/common/Text' import { ScrollView, Dimensions } from 'react-native' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { commonSelectors } from '../../redux/common/selectors' import { chunk } from 'lodash' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' @@ -12,7 +12,7 @@ import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' const width = Dimensions.get('window').width export function PrivacyScreen({ navigation }) { const [page, setPage] = React.useState(0) - const privacyContent = useSelector(commonSelectors.privacyContent) + const privacyContent = useCommonSelector(commonSelectors.privacyContent) const speechText = privacyContent.map((item) => item.content) const content = privacyContent.map((item, ind) => { if (item.type === 'HEADING') { diff --git a/packages/components/src/screens/settings/TermsScreen.tsx b/packages/components/src/screens/settings/TermsScreen.tsx index 2fc24b9ef..f5f66238d 100644 --- a/packages/components/src/screens/settings/TermsScreen.tsx +++ b/packages/components/src/screens/settings/TermsScreen.tsx @@ -4,7 +4,7 @@ import { BackgroundTheme } from '../../components/layout/BackgroundTheme' import { Header } from '../../components/common/Header' import { TextWithoutTranslation } from '../../components/common/Text' import { ScrollView, Dimensions, Platform } from 'react-native' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { commonSelectors } from '../../redux/common/selectors' import { chunk } from 'lodash' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' @@ -12,7 +12,7 @@ import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' const width = Dimensions.get('window').width export function TermsScreen({ navigation }) { const [page, setPage] = React.useState(0) - const termsAndConditions = useSelector(commonSelectors.termsAndConditionsContent) + const termsAndConditions = useCommonSelector(commonSelectors.termsAndConditionsContent) const speechText = termsAndConditions.map((item) => item.content) const content = termsAndConditions.map((item, ind) => { if (item.type === 'HEADING') { diff --git a/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx b/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx index 7d49f77ea..4f17e1d60 100644 --- a/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx +++ b/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx @@ -2,13 +2,13 @@ import React from 'react' import { Dimensions } from 'react-native' import styled from 'styled-components/native' import { assets } from '../../assets/index' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { commonSelectors } from '../../redux/common/selectors' const deviceWidth = Dimensions.get('window').width const deviceHeight = Dimensions.get('screen').height export function CalendarAssetDemo() { - const locale = useSelector(commonSelectors.currentLocaleSelector) + const locale = useCommonSelector(commonSelectors.currentLocaleSelector) const source = assets.general.calendarStatic[locale] return ( diff --git a/packages/components/src/screens/tutorial/DayAssetDemo.tsx b/packages/components/src/screens/tutorial/DayAssetDemo.tsx index 3f3122aca..6b0ad820f 100644 --- a/packages/components/src/screens/tutorial/DayAssetDemo.tsx +++ b/packages/components/src/screens/tutorial/DayAssetDemo.tsx @@ -8,13 +8,13 @@ import { Icon } from '../../components/common/Icon' import { EmojiSelector } from '../../components/common/EmojiSelector' import { translate } from '../../i18n' import Tts from 'react-native-tts' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { commonSelectors } from '../../redux/common/selectors' const deviceWidth = Dimensions.get('window').width const deviceHeight = Dimensions.get('window').height export function DayAssetDemo({ step }) { - const hasTtsActive = useSelector(commonSelectors.isTtsActiveSelector) + const hasTtsActive = useCommonSelector(commonSelectors.isTtsActiveSelector) React.useEffect(() => { if (hasTtsActive) { diff --git a/packages/components/src/screens/tutorial/NoteAssetDemo.tsx b/packages/components/src/screens/tutorial/NoteAssetDemo.tsx index 620b3fd95..8f79b8174 100644 --- a/packages/components/src/screens/tutorial/NoteAssetDemo.tsx +++ b/packages/components/src/screens/tutorial/NoteAssetDemo.tsx @@ -7,14 +7,14 @@ import { TextInput } from '../../components/common/TextInput' import { Text } from '../../components/common/Text' import { translate } from '../../i18n' import Tts from 'react-native-tts' -import { useSelector } from '../../hooks/useSelector' +import { useCommonSelector } from '../../redux/common/useCommonSelector' import { commonSelectors } from '../../redux/common/selectors' const deviceWidth = Dimensions.get('window').width const deviceHeight = Dimensions.get('screen').height export function NoteAssetDemo({ step }) { - const hasTtsActive = useSelector(commonSelectors.isTtsActiveSelector) + const hasTtsActive = useCommonSelector(commonSelectors.isTtsActiveSelector) React.useEffect(() => { if (hasTtsActive) { From 0325f483b2b71eb84980ded10d71506070ec4d44 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 22 Dec 2023 12:48:13 +0700 Subject: [PATCH 07/83] Move redux helps --- .../components/src/redux/common/actions/analyticsActions.ts | 2 +- packages/components/src/redux/common/actions/answerActions.ts | 2 +- packages/components/src/redux/common/actions/appActions.ts | 2 +- packages/components/src/redux/common/actions/authActions.ts | 2 +- packages/components/src/redux/common/actions/contentActions.ts | 2 +- .../components/src/redux/common/actions/predictionActions.ts | 2 +- packages/components/src/redux/{common => }/helpers/index.ts | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename packages/components/src/redux/{common => }/helpers/index.ts (87%) diff --git a/packages/components/src/redux/common/actions/analyticsActions.ts b/packages/components/src/redux/common/actions/analyticsActions.ts index d7126885e..f25f25c0e 100644 --- a/packages/components/src/redux/common/actions/analyticsActions.ts +++ b/packages/components/src/redux/common/actions/analyticsActions.ts @@ -1,4 +1,4 @@ -import { createAction } from '../helpers' +import { createAction } from '../../helpers' export function queueEvent({ id, diff --git a/packages/components/src/redux/common/actions/answerActions.ts b/packages/components/src/redux/common/actions/answerActions.ts index adb5a7be1..177335c11 100644 --- a/packages/components/src/redux/common/actions/answerActions.ts +++ b/packages/components/src/redux/common/actions/answerActions.ts @@ -1,4 +1,4 @@ -import { createAction } from '../helpers' +import { createAction } from '../../helpers' import { Moment } from 'moment' import { CardName, DailyCard } from '../../../types' import { AnswerForUserState } from '../reducers/answerReducer' diff --git a/packages/components/src/redux/common/actions/appActions.ts b/packages/components/src/redux/common/actions/appActions.ts index b48add7f4..0a7918ecf 100644 --- a/packages/components/src/redux/common/actions/appActions.ts +++ b/packages/components/src/redux/common/actions/appActions.ts @@ -1,5 +1,5 @@ import { AvatarName, ThemeName } from '@oky/core' -import { createAction } from '../helpers' +import { createAction } from '../../helpers' export function setTheme(theme: ThemeName) { return createAction('SET_THEME', { theme }) diff --git a/packages/components/src/redux/common/actions/authActions.ts b/packages/components/src/redux/common/actions/authActions.ts index 15d1ed1e5..6d3c15eb8 100644 --- a/packages/components/src/redux/common/actions/authActions.ts +++ b/packages/components/src/redux/common/actions/authActions.ts @@ -1,4 +1,4 @@ -import { createAction } from '../helpers' +import { createAction } from '../../helpers' export function loginRequest({ name, password }) { return createAction('LOGIN_REQUEST', { name, password }) diff --git a/packages/components/src/redux/common/actions/contentActions.ts b/packages/components/src/redux/common/actions/contentActions.ts index 7cfd0dacf..dd9199406 100644 --- a/packages/components/src/redux/common/actions/contentActions.ts +++ b/packages/components/src/redux/common/actions/contentActions.ts @@ -1,4 +1,4 @@ -import { createAction } from '../helpers' +import { createAction } from '../../helpers' import { Articles, Categories, diff --git a/packages/components/src/redux/common/actions/predictionActions.ts b/packages/components/src/redux/common/actions/predictionActions.ts index a96bced4d..50390c70b 100644 --- a/packages/components/src/redux/common/actions/predictionActions.ts +++ b/packages/components/src/redux/common/actions/predictionActions.ts @@ -1,4 +1,4 @@ -import { createAction } from '../helpers' +import { createAction } from '../../helpers' import { PredictionState } from '../../../prediction' import { Moment } from 'moment' diff --git a/packages/components/src/redux/common/helpers/index.ts b/packages/components/src/redux/helpers/index.ts similarity index 87% rename from packages/components/src/redux/common/helpers/index.ts rename to packages/components/src/redux/helpers/index.ts index 8e18f509d..a38bf6698 100644 --- a/packages/components/src/redux/common/helpers/index.ts +++ b/packages/components/src/redux/helpers/index.ts @@ -1,4 +1,4 @@ -import { Action } from '../types/types' +import { Action } from '../common/types/types' export function createAction(type: T): Action From 3b332c4661d8cecfc4e8f90c62c128b7320c34a2 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 22 Dec 2023 12:54:36 +0700 Subject: [PATCH 08/83] Rename common redux types --- .../redux/common/reducers/analyticsReducer.ts | 4 +- .../redux/common/reducers/answerReducer.ts | 19 ++++---- .../src/redux/common/reducers/appReducer.ts | 4 +- .../src/redux/common/reducers/authReducer.ts | 7 ++- .../redux/common/reducers/contentReducer.ts | 4 +- .../src/redux/common/reducers/index.ts | 6 +-- .../common/reducers/predictionReducer.ts | 4 +- .../src/redux/common/sagas/appSaga.ts | 4 +- .../src/redux/common/sagas/authSaga.ts | 8 ++-- .../common/selectors/analyticsSelectors.ts | 6 +-- .../redux/common/selectors/answerSelectors.ts | 30 +++++++----- .../redux/common/selectors/appSelectors.ts | 37 +++++++------- .../redux/common/selectors/authSelectors.ts | 10 ++-- .../common/selectors/contentSelectors.ts | 48 ++++++++++--------- .../src/redux/common/types/index.ts | 8 ++-- .../src/redux/common/useCommonSelector.ts | 4 +- .../components/src/redux/helpers/index.ts | 2 +- .../src/redux/{common/types => }/types.ts | 0 .../wheelCarousel/CircularSelection.tsx | 4 +- 19 files changed, 111 insertions(+), 98 deletions(-) rename packages/components/src/redux/{common/types => }/types.ts (100%) diff --git a/packages/components/src/redux/common/reducers/analyticsReducer.ts b/packages/components/src/redux/common/reducers/analyticsReducer.ts index 9d1636359..2172160a8 100644 --- a/packages/components/src/redux/common/reducers/analyticsReducer.ts +++ b/packages/components/src/redux/common/reducers/analyticsReducer.ts @@ -1,4 +1,4 @@ -import { Actions } from '../types' +import { CommonActions } from '../types' export type AnalyticsState = Array<{ id: string @@ -9,7 +9,7 @@ export type AnalyticsState = Array<{ const initialState: AnalyticsState = [] -export function analyticsReducer(state = initialState, action: Actions): AnalyticsState { +export function analyticsReducer(state = initialState, action: CommonActions): AnalyticsState { switch (action.type) { case 'QUEUE_EVENT': return state.concat({ diff --git a/packages/components/src/redux/common/reducers/answerReducer.ts b/packages/components/src/redux/common/reducers/answerReducer.ts index b9339d10b..80843488c 100644 --- a/packages/components/src/redux/common/reducers/answerReducer.ts +++ b/packages/components/src/redux/common/reducers/answerReducer.ts @@ -1,4 +1,4 @@ -import { Actions } from '../types' +import { CommonActions } from '../types' import { combineReducers } from 'redux' import { toShortISO } from '../../../services/dateUtils' import { DailyCard } from '../../../types' @@ -47,7 +47,7 @@ export interface AnswerState { [userId: string]: AnswerForUserState } -function surveysReducer(state = {}, action: Actions): AnswerForUserState['surveys'] { +function surveysReducer(state = {}, action: CommonActions): AnswerForUserState['surveys'] { if (action.type === 'ANSWER_SURVEY') { return { ...state, @@ -65,7 +65,7 @@ function surveysReducer(state = {}, action: Actions): AnswerForUserState['survey return state } -function quizzesReducer(state = {}, action: Actions): AnswerForUserState['quizzes'] { +function quizzesReducer(state = {}, action: CommonActions): AnswerForUserState['quizzes'] { if (action.type === 'ANSWER_QUIZ') { return { ...state, @@ -85,7 +85,7 @@ function quizzesReducer(state = {}, action: Actions): AnswerForUserState['quizze return state } -function cardsReducer(state = {}, action: Actions): AnswerForUserState['cards'] { +function cardsReducer(state = {}, action: CommonActions): AnswerForUserState['cards'] { if (action.type === 'ANSWER_DAILY_CARD') { const keyCard = toShortISO(action.payload.utcDateTime) let answersToInsert = [] @@ -125,7 +125,10 @@ function cardsReducer(state = {}, action: Actions): AnswerForUserState['cards'] } return state } -function periodVerifyReducer(state = {}, action: Actions): AnswerForUserState['verifiedDates'] { +function periodVerifyReducer( + state = {}, + action: CommonActions, +): AnswerForUserState['verifiedDates'] { if (action.type === 'ANSWER_VERIFY_DATES') { const keyCard = toShortISO(action.payload.utcDateTime) const answersToInsert = [] @@ -141,7 +144,7 @@ function periodVerifyReducer(state = {}, action: Actions): AnswerForUserState['v return state } -function notesReducer(state = {}, action: Actions): AnswerForUserState['notes'] { +function notesReducer(state = {}, action: CommonActions): AnswerForUserState['notes'] { if (action.type === 'ANSWER_NOTES_CARD') { const keyCard = toShortISO(action.payload.utcDateTime) return { @@ -156,7 +159,7 @@ function notesReducer(state = {}, action: Actions): AnswerForUserState['notes'] return state } -const answerForUserReducer = combineReducers({ +const answerForUserReducer = combineReducers({ surveys: surveysReducer, quizzes: quizzesReducer, cards: cardsReducer, @@ -164,7 +167,7 @@ const answerForUserReducer = combineReducers({ verifiedDates: periodVerifyReducer, }) -export function answerReducer(state: AnswerState = {}, action: Actions): AnswerState { +export function answerReducer(state: AnswerState = {}, action: CommonActions): AnswerState { // TODO_ALEX: survey if (action.type === 'ANSWER_SURVEY') { return { diff --git a/packages/components/src/redux/common/reducers/appReducer.ts b/packages/components/src/redux/common/reducers/appReducer.ts index d5049e205..1a57b56d5 100644 --- a/packages/components/src/redux/common/reducers/appReducer.ts +++ b/packages/components/src/redux/common/reducers/appReducer.ts @@ -1,5 +1,5 @@ import _ from 'lodash' -import { Actions } from '../types' +import { CommonActions } from '../types' import { currentLocale } from '../../../i18n' import DeviceInfo from 'react-native-device-info' import { AvatarName, ThemeName, defaultAvatar, defaultTheme } from '@oky/core' @@ -44,7 +44,7 @@ const initialState: AppState = { predicted_periods: [], } -export function appReducer(state = initialState, action: Actions): AppState { +export function appReducer(state = initialState, action: CommonActions): AppState { switch (action.type) { case 'SET_THEME': return { diff --git a/packages/components/src/redux/common/reducers/authReducer.ts b/packages/components/src/redux/common/reducers/authReducer.ts index e933d4fda..62da89589 100644 --- a/packages/components/src/redux/common/reducers/authReducer.ts +++ b/packages/components/src/redux/common/reducers/authReducer.ts @@ -1,6 +1,6 @@ import { REHYDRATE, RehydrateAction } from 'redux-persist' import _ from 'lodash' -import { Actions } from '../types/index' +import { CommonActions } from '../types/index' export interface User { id: string @@ -36,7 +36,10 @@ const initialState: AuthState = { user: null, } -export function authReducer(state = initialState, action: Actions | RehydrateAction): AuthState { +export function authReducer( + state = initialState, + action: CommonActions | RehydrateAction, +): AuthState { switch (action.type) { case REHYDRATE: return { diff --git a/packages/components/src/redux/common/reducers/contentReducer.ts b/packages/components/src/redux/common/reducers/contentReducer.ts index 9bdfeb16f..5f1892786 100644 --- a/packages/components/src/redux/common/reducers/contentReducer.ts +++ b/packages/components/src/redux/common/reducers/contentReducer.ts @@ -15,7 +15,7 @@ import { CompletedSurveys, Videos, } from '../../../types' -import { Actions } from '../types/index' +import { CommonActions } from '../types/index' export interface ContentState { timeFetched?: number @@ -89,7 +89,7 @@ const initialState: ContentState = { }, } -export function contentReducer(state = initialState, action: Actions): ContentState { +export function contentReducer(state = initialState, action: CommonActions): ContentState { switch (action.type) { case 'INIT_STALE_CONTENT': return { diff --git a/packages/components/src/redux/common/reducers/index.ts b/packages/components/src/redux/common/reducers/index.ts index 376de497a..cf3c79afa 100644 --- a/packages/components/src/redux/common/reducers/index.ts +++ b/packages/components/src/redux/common/reducers/index.ts @@ -1,7 +1,7 @@ import _ from 'lodash' import { combineReducers } from 'redux' import { syncReducers } from '../sync' -import { Actions } from '../types' +import { CommonActions } from '../types' import { analyticsReducer } from './analyticsReducer' import { answerReducer } from './answerReducer' @@ -27,7 +27,7 @@ const reducer = combineReducers( ), ) -export function rootReducer(state, action: Actions) { +export function commonRootReducer(state, action: CommonActions) { switch (action.type) { case 'LOGOUT': // @ts-ignore @@ -38,4 +38,4 @@ export function rootReducer(state, action: Actions) { } } -export type ReduxState = ReturnType +export type CommonReduxState = ReturnType diff --git a/packages/components/src/redux/common/reducers/predictionReducer.ts b/packages/components/src/redux/common/reducers/predictionReducer.ts index 1113c8bec..3dfeb95bb 100644 --- a/packages/components/src/redux/common/reducers/predictionReducer.ts +++ b/packages/components/src/redux/common/reducers/predictionReducer.ts @@ -1,13 +1,13 @@ import _ from 'lodash' import { PredictionSerializableState } from '../../../prediction' -import { Actions } from '../types/index' +import { CommonActions } from '../types/index' export type PredictionState = PredictionSerializableState | null const initialState: PredictionState = null -export function predictionReducer(state = initialState, action: Actions): PredictionState { +export function predictionReducer(state = initialState, action: CommonActions): PredictionState { switch (action.type) { case 'SET_PREDICTION_ENGINE_STATE': return action.payload.predictionState.toJSON() diff --git a/packages/components/src/redux/common/sagas/appSaga.ts b/packages/components/src/redux/common/sagas/appSaga.ts index f82eb1dbf..6f67271dc 100644 --- a/packages/components/src/redux/common/sagas/appSaga.ts +++ b/packages/components/src/redux/common/sagas/appSaga.ts @@ -3,7 +3,7 @@ import { all, delay, fork, put, select, takeLatest } from 'redux-saga/effects' import { httpClient } from '../../../services/HttpClient' import { fetchNetworkConnectionStatus } from '../../../services/network' import { extractReducerState } from '../sync' -import { ReduxState, exportReducerNames } from '../reducers' +import { CommonReduxState, exportReducerNames } from '../reducers' import { version as storeVersion } from '../../store' import { commonActions } from '../actions' import { commonSelectors } from '../selectors' @@ -22,7 +22,7 @@ function* syncAppState() { continue } - const state: ReduxState = yield select() + const state: CommonReduxState = yield select() const appState = extractReducerState(state, exportReducerNames) if (_.isEqual(appState, lastAppState)) { diff --git a/packages/components/src/redux/common/sagas/authSaga.ts b/packages/components/src/redux/common/sagas/authSaga.ts index 0d5cf048c..2e69e73ce 100644 --- a/packages/components/src/redux/common/sagas/authSaga.ts +++ b/packages/components/src/redux/common/sagas/authSaga.ts @@ -4,7 +4,7 @@ import { Alert } from 'react-native' import { v4 as uuidv4 } from 'uuid' import { ExtractActionFromActionType } from '../types' import { httpClient } from '../../../services/HttpClient' -import { ReduxState, exportReducerNames } from '../reducers' +import { CommonReduxState, exportReducerNames } from '../reducers' import { commonActions } from '../actions' import { commonSelectors } from '../selectors' import { navigateAndReset } from '../../../services/navigationService' @@ -17,7 +17,7 @@ import { fetchNetworkConnectionStatus } from '../../../services/network' type Await = T extends Promise ? U : T function* onRehydrate() { - const state: ReduxState = yield select() + const state: CommonReduxState = yield select() const appToken = commonSelectors.appTokenSelector(state) const user = commonSelectors.currentUserSelector(state) @@ -60,7 +60,7 @@ function* onConvertGuestAccount(action: ExtractActionFromActionType<'CONVERT_GUE function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { const { name, password } = action.payload - const stateRedux: ReduxState = yield select() + const stateRedux: CommonReduxState = yield select() const localeapp = commonSelectors.currentLocaleSelector(stateRedux) yield commonActions.setLocale(localeapp) @@ -223,7 +223,7 @@ function* onCreateAccountSuccess(action: ExtractActionFromActionType<'CREATE_ACC } function* onDeleteAccountRequest(action: ExtractActionFromActionType<'DELETE_ACCOUNT_REQUEST'>) { const { setLoading } = action.payload - const state: ReduxState = yield select() + const state: CommonReduxState = yield select() const user = commonSelectors.currentUserSelector(state) setLoading(true) try { diff --git a/packages/components/src/redux/common/selectors/analyticsSelectors.ts b/packages/components/src/redux/common/selectors/analyticsSelectors.ts index 9780d5425..10fc2dedc 100644 --- a/packages/components/src/redux/common/selectors/analyticsSelectors.ts +++ b/packages/components/src/redux/common/selectors/analyticsSelectors.ts @@ -1,5 +1,5 @@ -import { ReduxState } from '../reducers' +import { CommonReduxState } from '../reducers' -const s = (state: ReduxState) => state.analytics +const s = (state: CommonReduxState) => state.analytics -export const allAnalyticsEventsSelector = (state: ReduxState) => s(state) +export const allAnalyticsEventsSelector = (state: CommonReduxState) => s(state) diff --git a/packages/components/src/redux/common/selectors/answerSelectors.ts b/packages/components/src/redux/common/selectors/answerSelectors.ts index e73b9e696..f5988383c 100644 --- a/packages/components/src/redux/common/selectors/answerSelectors.ts +++ b/packages/components/src/redux/common/selectors/answerSelectors.ts @@ -3,26 +3,26 @@ import { allQuizzesSelectors } from './contentSelectors' import { Moment } from 'moment' import { toShortISO } from '../../../services/dateUtils' import _ from 'lodash' -import { ReduxState } from '../reducers' +import { CommonReduxState } from '../reducers' -const s = (state: ReduxState) => state.answer +const s = (state: CommonReduxState) => state.answer -export const surveyHasAnswerSelector = (state: ReduxState, id: string) => { +export const surveyHasAnswerSelector = (state: CommonReduxState, id: string) => { if (!s(state)[state.auth.user.id]) return false return id in s(state)[state.auth.user.id].surveys } -// export const surveysWithoutAnswersSelector = (state: ReduxState) => { +// export const surveysWithoutAnswersSelector = (state: CommonReduxState) => { // return allSurveysSelectors(state).filter(({ id }) => !surveyHasAnswerSelector(state, id)) // } -export const quizHasAnswerSelector = (state: ReduxState, id: string) => { +export const quizHasAnswerSelector = (state: CommonReduxState, id: string) => { if (!s(state)[state.auth.user.id]) return false return id in s(state)[state.auth.user.id].quizzes } // Had a type error here had to add any to avoid -export const quizAnswerByDate: any = (state: ReduxState, date: Moment) => { +export const quizAnswerByDate: any = (state: CommonReduxState, date: Moment) => { if (!s(state)[state.auth.user.id]) return null return Object.values(s(state)[state.auth.user.id].quizzes).filter( ({ utcDateTime }) => utcDateTime === date.toISOString(), @@ -30,23 +30,23 @@ export const quizAnswerByDate: any = (state: ReduxState, date: Moment) => { } // Had a type error here had to add any to avoid -export const surveyAnswerByDate: any = (state: ReduxState, date: Moment) => { +export const surveyAnswerByDate: any = (state: CommonReduxState, date: Moment) => { if (!s(state)[state.auth.user.id]) return null return Object.values(s(state)[state.auth.user.id].surveys).filter( ({ utcDateTime }) => utcDateTime === date.toISOString(), )[0] } -export const quizzesWithoutAnswersSelector = (state: ReduxState) => { +export const quizzesWithoutAnswersSelector = (state: CommonReduxState) => { return allQuizzesSelectors(state).filter(({ id }) => !quizHasAnswerSelector(state, id)) } -export const cardAnswerSelector = (state: ReduxState, date: Moment) => { +export const cardAnswerSelector = (state: CommonReduxState, date: Moment) => { if (!state.auth.user) return {} // for the use case on info screen where there is no authed user if (!s(state)[state.auth.user.id]) return {} return s(state)[state.auth.user.id]?.cards[toShortISO(date)] || {} } -export const verifyPeriodDaySelectorWithDate = (state: ReduxState, date: Moment) => { +export const verifyPeriodDaySelectorWithDate = (state: CommonReduxState, date: Moment) => { if (!state.auth.user) return {} // for the use case on info screen where there is no authed user if (!s(state)[state.auth.user.id]) return {} if (s(state)[state.auth.user.id]?.verifiedDates) { @@ -55,18 +55,22 @@ export const verifyPeriodDaySelectorWithDate = (state: ReduxState, date: Moment) return {} // return s(state)[state.auth.user.id]?.verifiedDates[toShortISO(date)] || {} } -export const allCardAnswersSelector = (state: ReduxState) => { +export const allCardAnswersSelector = (state: CommonReduxState) => { if (!state.auth.user) return {} // for the use case on info screen where there is no authed user if (!s(state)[state.auth.user.id]) return {} return s(state)[state.auth.user.id]?.verifiedDates || {} } -export const notesAnswerSelector = (state: ReduxState, date: Moment) => { +export const notesAnswerSelector = (state: CommonReduxState, date: Moment) => { if (!s(state)[state.auth.user.id]) return {} return s(state)[state.auth.user.id].notes[toShortISO(date)] || {} } -export const mostAnsweredSelector = (state: ReduxState, startDate: Moment, endDate: Moment) => { +export const mostAnsweredSelector = ( + state: CommonReduxState, + startDate: Moment, + endDate: Moment, +) => { if (!s(state)[state.auth.user.id]) return {} const dates = Object.keys(s(state)[state.auth.user.id].cards) const filteredDates = dates.filter((item) => { diff --git a/packages/components/src/redux/common/selectors/appSelectors.ts b/packages/components/src/redux/common/selectors/appSelectors.ts index e0ec924bb..3d58ecbb9 100644 --- a/packages/components/src/redux/common/selectors/appSelectors.ts +++ b/packages/components/src/redux/common/selectors/appSelectors.ts @@ -1,34 +1,35 @@ -import { ReduxState } from '../reducers' +import { CommonReduxState } from '../reducers' -const s = (state: ReduxState) => state.app -const predictionS = (state: ReduxState) => state.prediction -export const currentLocaleSelector = (state: ReduxState) => s(state).locale +const s = (state: CommonReduxState) => state.app +const predictionS = (state: CommonReduxState) => state.prediction +export const currentLocaleSelector = (state: CommonReduxState) => s(state).locale -export const currentChosenRegionSelector = (state: ReduxState) => s(state).chosenRegion +export const currentChosenRegionSelector = (state: CommonReduxState) => s(state).chosenRegion -export const currentThemeSelector = (state: ReduxState) => s(state).theme +export const currentThemeSelector = (state: CommonReduxState) => s(state).theme -export const currentAvatarSelector = (state: ReduxState) => s(state).avatar +export const currentAvatarSelector = (state: CommonReduxState) => s(state).avatar -export const hasOpenedSelector = (state: ReduxState) => s(state).hasOpened +export const hasOpenedSelector = (state: CommonReduxState) => s(state).hasOpened -export const isTutorialOneActiveSelector = (state: ReduxState) => s(state).isTutorialOneActive +export const isTutorialOneActiveSelector = (state: CommonReduxState) => s(state).isTutorialOneActive -export const isTutorialTwoActiveSelector = (state: ReduxState) => s(state).isTutorialTwoActive +export const isTutorialTwoActiveSelector = (state: CommonReduxState) => s(state).isTutorialTwoActive -export const isTtsActiveSelector = (state: ReduxState) => s(state).isTtsActive +export const isTtsActiveSelector = (state: CommonReduxState) => s(state).isTtsActive -export const isLoginPasswordActiveSelector = (state: ReduxState) => s(state).isLoginPasswordActive +export const isLoginPasswordActiveSelector = (state: CommonReduxState) => + s(state).isLoginPasswordActive -export const currentAppVersion = (state: ReduxState) => s(state).appVersionName +export const currentAppVersion = (state: CommonReduxState) => s(state).appVersionName -export const currentFirebaseToken = (state: ReduxState) => s(state).firebaseToken +export const currentFirebaseToken = (state: CommonReduxState) => s(state).firebaseToken -export const userVerifiedDates = (state: ReduxState) => s(state).verifiedDates +export const userVerifiedDates = (state: CommonReduxState) => s(state).verifiedDates // Smart precition selectors -export const isFuturePredictionSelector = (state: ReduxState) => predictionS(state) -export const isFuturePredictionActiveSelector = (state: ReduxState) => +export const isFuturePredictionSelector = (state: CommonReduxState) => predictionS(state) +export const isFuturePredictionActiveSelector = (state: CommonReduxState) => predictionS(state)?.futurePredictionStatus -// export const smartPredictedPeriods = (state: ReduxState) => s(state).predicted_periods +// export const smartPredictedPeriods = (state: CommonReduxState) => s(state).predicted_periods diff --git a/packages/components/src/redux/common/selectors/authSelectors.ts b/packages/components/src/redux/common/selectors/authSelectors.ts index baae80918..3f288f37e 100644 --- a/packages/components/src/redux/common/selectors/authSelectors.ts +++ b/packages/components/src/redux/common/selectors/authSelectors.ts @@ -1,9 +1,9 @@ -import { ReduxState } from '../reducers' +import { CommonReduxState } from '../reducers' -const s = (state: ReduxState) => state.auth +const s = (state: CommonReduxState) => state.auth -export const appTokenSelector = (state: ReduxState) => s(state).appToken +export const appTokenSelector = (state: CommonReduxState) => s(state).appToken -export const authError = (state: ReduxState) => s(state).error +export const authError = (state: CommonReduxState) => s(state).error -export const currentUserSelector = (state: ReduxState) => s(state).user +export const currentUserSelector = (state: CommonReduxState) => s(state).user diff --git a/packages/components/src/redux/common/selectors/contentSelectors.ts b/packages/components/src/redux/common/selectors/contentSelectors.ts index 034038718..613902b40 100644 --- a/packages/components/src/redux/common/selectors/contentSelectors.ts +++ b/packages/components/src/redux/common/selectors/contentSelectors.ts @@ -1,29 +1,29 @@ import _ from 'lodash' -import { ReduxState } from '../reducers' +import { CommonReduxState } from '../reducers' -const s = (state: ReduxState) => state.content +const s = (state: CommonReduxState) => state.content -export const allArticlesSelector = (state: ReduxState) => +export const allArticlesSelector = (state: CommonReduxState) => s(state).articles.allIds.map((id) => s(state).articles.byId[id]) -export const allVideosSelector = (state: ReduxState) => { +export const allVideosSelector = (state: CommonReduxState) => { if (!s(state)?.videos?.allIds || !s(state)?.videos?.byId) return [] return s(state).videos.allIds.map((id) => s(state).videos.byId[id]) } -export const articleByIDSelector = (state: ReduxState, id) => s(state).articles.byId[id] -export const videoByIDSelector = (state: ReduxState, id) => s(state)?.videos?.byId[id] +export const articleByIDSelector = (state: CommonReduxState, id) => s(state).articles.byId[id] +export const videoByIDSelector = (state: CommonReduxState, id) => s(state)?.videos?.byId[id] -export const articlesObjectByIDSelector = (state: ReduxState) => s(state).articles.byId +export const articlesObjectByIDSelector = (state: CommonReduxState) => s(state).articles.byId // @ts-ignore -export const allHelpCentersForCurrentLocale: any = (state: ReduxState) => +export const allHelpCentersForCurrentLocale: any = (state: CommonReduxState) => s(state).helpCenters.filter((item) => item.lang === state.app.locale) -export const allCategoriesSelector = (state: ReduxState) => +export const allCategoriesSelector = (state: CommonReduxState) => s(state).categories.allIds.map((id) => s(state).categories.byId[id]) -export const allCategoryEmojis = (state: ReduxState) => { +export const allCategoryEmojis = (state: CommonReduxState) => { const categories = allCategoriesSelector(state) return categories.map((item) => { @@ -31,30 +31,32 @@ export const allCategoryEmojis = (state: ReduxState) => { }) } -export const allSubCategoriesSelector = (state: ReduxState) => +export const allSubCategoriesSelector = (state: CommonReduxState) => s(state).subCategories.allIds.map((id) => s(state).subCategories.byId[id]) -export const allSubCategoriesObjectSelector = (state: ReduxState) => s(state).subCategories.byId +export const allSubCategoriesObjectSelector = (state: CommonReduxState) => + s(state).subCategories.byId -export const categoryByIDSelector = (state: ReduxState, id) => s(state).categories.byId[id] +export const categoryByIDSelector = (state: CommonReduxState, id) => s(state).categories.byId[id] -export const subCategoryByIDSelector = (state: ReduxState, id) => s(state).subCategories.byId[id] +export const subCategoryByIDSelector = (state: CommonReduxState, id) => + s(state).subCategories.byId[id] -export const allAvatarText = (state: ReduxState) => s(state).avatarMessages +export const allAvatarText = (state: CommonReduxState) => s(state).avatarMessages -export const privacyContent = (state: ReduxState) => s(state).privacyPolicy +export const privacyContent = (state: CommonReduxState) => s(state).privacyPolicy -export const termsAndConditionsContent = (state: ReduxState) => s(state).termsAndConditions +export const termsAndConditionsContent = (state: CommonReduxState) => s(state).termsAndConditions -export const aboutContent = (state: ReduxState) => s(state).about +export const aboutContent = (state: CommonReduxState) => s(state).about -export const allSurveys = (state: ReduxState) => s(state).allSurveys +export const allSurveys = (state: CommonReduxState) => s(state).allSurveys -export const completedSurveys = (state: ReduxState) => s(state).completedSurveys +export const completedSurveys = (state: CommonReduxState) => s(state).completedSurveys -export const aboutBanner = (state: ReduxState) => s(state).aboutBanner +export const aboutBanner = (state: CommonReduxState) => s(state).aboutBanner -export const allQuizzesSelectors = (state: ReduxState) => { +export const allQuizzesSelectors = (state: CommonReduxState) => { // TODO: FIXME const isUserYoungerThan15 = true // moment() @@ -86,7 +88,7 @@ export const allQuizzesSelectors = (state: ReduxState) => { return filteredArray } -export const allDidYouKnowsSelectors = (state: ReduxState) => { +export const allDidYouKnowsSelectors = (state: CommonReduxState) => { // TODO_ALEX: FIXME const isUserYoungerThan15 = true // moment() diff --git a/packages/components/src/redux/common/types/index.ts b/packages/components/src/redux/common/types/index.ts index d16edabc8..8b3dd1e6d 100644 --- a/packages/components/src/redux/common/types/index.ts +++ b/packages/components/src/redux/common/types/index.ts @@ -1,11 +1,11 @@ import { commonActions } from '../actions' -import { ActionsUnion, ActionsOfType } from './types' +import { ActionsUnion, ActionsOfType } from '../../types' -export type Actions = ActionsUnion +export type CommonActions = ActionsUnion -export type ActionTypes = Actions[keyof Actions] +export type CommonActionTypes = CommonActions[keyof CommonActions] export type ExtractActionFromActionType = ActionsOfType< - Actions, + CommonActions, ActionType > diff --git a/packages/components/src/redux/common/useCommonSelector.ts b/packages/components/src/redux/common/useCommonSelector.ts index 5be569e1b..2ac7a2cfa 100644 --- a/packages/components/src/redux/common/useCommonSelector.ts +++ b/packages/components/src/redux/common/useCommonSelector.ts @@ -1,4 +1,4 @@ import { useSelector as useReduxSelector, TypedUseSelectorHook } from 'react-redux' -import { ReduxState } from './reducers' +import { CommonReduxState } from './reducers' -export const useCommonSelector: TypedUseSelectorHook = useReduxSelector +export const useCommonSelector: TypedUseSelectorHook = useReduxSelector diff --git a/packages/components/src/redux/helpers/index.ts b/packages/components/src/redux/helpers/index.ts index a38bf6698..6925d1fe1 100644 --- a/packages/components/src/redux/helpers/index.ts +++ b/packages/components/src/redux/helpers/index.ts @@ -1,4 +1,4 @@ -import { Action } from '../common/types/types' +import { Action } from '../types' export function createAction(type: T): Action diff --git a/packages/components/src/redux/common/types/types.ts b/packages/components/src/redux/types.ts similarity index 100% rename from packages/components/src/redux/common/types/types.ts rename to packages/components/src/redux/types.ts diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx index 5db53a350..54f17d24e 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx @@ -13,9 +13,9 @@ import { useCheckDayWarning } from '../../../hooks/usePredictionWarnings' import { ThemedModal } from '../../../components/common/ThemedModal' import { SpinLoader } from '../../../components/common/SpinLoader' import moment from 'moment' -import { ReduxState } from '../../../redux/common/reducers' +import { CommonReduxState } from '../../../redux/common/reducers' -const reduxState = (state: ReduxState) => state +const reduxState = (state: CommonReduxState) => state const { interpolate } = Animated const height = 0.55 * Dimensions.get('window').height From 30a5b68ac141a7fd2ada8607de52684443920e18 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 22 Dec 2023 13:01:29 +0700 Subject: [PATCH 09/83] Move redux helpers --- packages/components/src/redux/{helpers/index.ts => helpers.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename packages/components/src/redux/{helpers/index.ts => helpers.ts} (90%) diff --git a/packages/components/src/redux/helpers/index.ts b/packages/components/src/redux/helpers.ts similarity index 90% rename from packages/components/src/redux/helpers/index.ts rename to packages/components/src/redux/helpers.ts index 6925d1fe1..2a8ecf41c 100644 --- a/packages/components/src/redux/helpers/index.ts +++ b/packages/components/src/redux/helpers.ts @@ -1,4 +1,4 @@ -import { Action } from '../types' +import { Action } from './types' export function createAction(type: T): Action From de554d32710eab4dece6fe2eb9f5570e460a6204 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 22 Dec 2023 13:11:53 +0700 Subject: [PATCH 10/83] Add secure store code --- .../redux/secure/actions/analyticsActions.ts | 19 ++ .../src/redux/secure/actions/answerActions.ts | 92 +++++ .../src/redux/secure/actions/appActions.ts | 67 ++++ .../src/redux/secure/actions/authActions.ts | 198 +++++++++++ .../redux/secure/actions/contentActions.ts | 80 +++++ .../src/redux/secure/actions/index.ts | 15 + .../secure/actions/newPredictionAction.ts | 20 ++ .../redux/secure/actions/predictionActions.ts | 41 +++ .../redux/secure/reducers/analyticsReducer.ts | 28 ++ .../redux/secure/reducers/answerReducer.ts | 204 +++++++++++ .../src/redux/secure/reducers/appReducer.ts | 118 +++++++ .../src/redux/secure/reducers/authReducer.ts | 155 +++++++++ .../redux/secure/reducers/contentReducer.ts | 145 ++++++++ .../src/redux/secure/reducers/index.ts | 41 +++ .../secure/reducers/predictionReducer.ts | 36 ++ .../src/redux/secure/sagas/analyticsSaga.ts | 68 ++++ .../src/redux/secure/sagas/appSaga.ts | 67 ++++ .../src/redux/secure/sagas/authSaga.ts | 318 ++++++++++++++++++ .../src/redux/secure/sagas/contentSaga.ts | 214 ++++++++++++ .../src/redux/secure/sagas/index.ts | 17 + .../redux/secure/sagas/smartPredictionSaga.ts | 51 +++ .../secure/selectors/analyticsSelectors.ts | 5 + .../redux/secure/selectors/answerSelectors.ts | 137 ++++++++ .../redux/secure/selectors/appSelectors.ts | 35 ++ .../redux/secure/selectors/authSelectors.ts | 9 + .../secure/selectors/contentSelectors.ts | 113 +++++++ .../src/redux/secure/selectors/index.ts | 13 + .../components/src/redux/secure/sync/index.ts | 42 +++ .../src/redux/secure/types/index.ts | 11 + .../src/redux/secure/useSecureSelector.ts | 4 + 30 files changed, 2363 insertions(+) create mode 100644 packages/components/src/redux/secure/actions/analyticsActions.ts create mode 100644 packages/components/src/redux/secure/actions/answerActions.ts create mode 100644 packages/components/src/redux/secure/actions/appActions.ts create mode 100644 packages/components/src/redux/secure/actions/authActions.ts create mode 100644 packages/components/src/redux/secure/actions/contentActions.ts create mode 100644 packages/components/src/redux/secure/actions/index.ts create mode 100644 packages/components/src/redux/secure/actions/newPredictionAction.ts create mode 100644 packages/components/src/redux/secure/actions/predictionActions.ts create mode 100644 packages/components/src/redux/secure/reducers/analyticsReducer.ts create mode 100644 packages/components/src/redux/secure/reducers/answerReducer.ts create mode 100644 packages/components/src/redux/secure/reducers/appReducer.ts create mode 100644 packages/components/src/redux/secure/reducers/authReducer.ts create mode 100644 packages/components/src/redux/secure/reducers/contentReducer.ts create mode 100644 packages/components/src/redux/secure/reducers/index.ts create mode 100644 packages/components/src/redux/secure/reducers/predictionReducer.ts create mode 100644 packages/components/src/redux/secure/sagas/analyticsSaga.ts create mode 100644 packages/components/src/redux/secure/sagas/appSaga.ts create mode 100644 packages/components/src/redux/secure/sagas/authSaga.ts create mode 100644 packages/components/src/redux/secure/sagas/contentSaga.ts create mode 100644 packages/components/src/redux/secure/sagas/index.ts create mode 100644 packages/components/src/redux/secure/sagas/smartPredictionSaga.ts create mode 100644 packages/components/src/redux/secure/selectors/analyticsSelectors.ts create mode 100644 packages/components/src/redux/secure/selectors/answerSelectors.ts create mode 100644 packages/components/src/redux/secure/selectors/appSelectors.ts create mode 100644 packages/components/src/redux/secure/selectors/authSelectors.ts create mode 100644 packages/components/src/redux/secure/selectors/contentSelectors.ts create mode 100644 packages/components/src/redux/secure/selectors/index.ts create mode 100644 packages/components/src/redux/secure/sync/index.ts create mode 100644 packages/components/src/redux/secure/types/index.ts create mode 100644 packages/components/src/redux/secure/useSecureSelector.ts diff --git a/packages/components/src/redux/secure/actions/analyticsActions.ts b/packages/components/src/redux/secure/actions/analyticsActions.ts new file mode 100644 index 000000000..f25f25c0e --- /dev/null +++ b/packages/components/src/redux/secure/actions/analyticsActions.ts @@ -0,0 +1,19 @@ +import { createAction } from '../../helpers' + +export function queueEvent({ + id, + type, + payload, + metadata, +}: { + id: string + type: string + payload: any + metadata: any +}) { + return createAction('QUEUE_EVENT', { id, type, payload, metadata }) +} + +export function resetQueue() { + return createAction('RESET_QUEUE') +} diff --git a/packages/components/src/redux/secure/actions/answerActions.ts b/packages/components/src/redux/secure/actions/answerActions.ts new file mode 100644 index 000000000..177335c11 --- /dev/null +++ b/packages/components/src/redux/secure/actions/answerActions.ts @@ -0,0 +1,92 @@ +import { createAction } from '../../helpers' +import { Moment } from 'moment' +import { CardName, DailyCard } from '../../../types' +import { AnswerForUserState } from '../reducers/answerReducer' +import { User } from '../reducers/authReducer' + +export function answerSurvey(payload: { + id: string + user_id: string + isCompleted: boolean + isSurveyAnswered: boolean + questions: any + utcDateTime: Moment +}) { + return createAction('ANSWER_SURVEY', payload) +} + +export function answerQuiz(payload: { + id: string + question: string + answerID: number + emoji: string + answer: string + isCorrect: boolean + response: string + userID: string + utcDateTime: Moment +}) { + return createAction('ANSWER_QUIZ', payload) +} + +export function answerDailyCard({ + cardName, + answer, + userID, + utcDateTime, + mutuallyExclusive = false, + periodDay = false, +}: { + cardName: T + answer: DailyCard[T] + userID: string + utcDateTime: Moment + mutuallyExclusive: boolean + periodDay: boolean +}) { + return createAction('ANSWER_DAILY_CARD', { + cardName, + answer, + userID, + utcDateTime, + mutuallyExclusive, + periodDay, + }) +} + +export function answerVerifyDates({ + userID, + utcDateTime, + periodDay = false, +}: { + userID: string + utcDateTime: Moment + periodDay: boolean +}) { + return createAction('ANSWER_VERIFY_DATES', { + userID, + utcDateTime, + periodDay, + }) +} + +export function answerNotesCard(payload: { + title: string + notes: string + userID: string + utcDateTime: Moment +}) { + return createAction('ANSWER_NOTES_CARD', payload) +} + +export function shareApp() { + return createAction('SHARE_APP') +} + +export function migrateAnswerData(payload: { + userId: User['id'] + key: string + data: AnswerForUserState +}) { + return createAction('MIGRATE_ANSWER_DATA', payload) +} diff --git a/packages/components/src/redux/secure/actions/appActions.ts b/packages/components/src/redux/secure/actions/appActions.ts new file mode 100644 index 000000000..0a7918ecf --- /dev/null +++ b/packages/components/src/redux/secure/actions/appActions.ts @@ -0,0 +1,67 @@ +import { AvatarName, ThemeName } from '@oky/core' +import { createAction } from '../../helpers' + +export function setTheme(theme: ThemeName) { + return createAction('SET_THEME', { theme }) +} + +export function setAvatar(avatar: AvatarName) { + return createAction('SET_AVATAR', { avatar }) +} + +export function setLocale(locale: string) { + return createAction('SET_LOCALE', { locale }) +} + +export function setUpdatedVersion() { + return createAction('SET_UPDATED_VERSION') +} + +export function requestStoreFirebaseKey() { + return createAction('REQUEST_STORE_FIREBASE_KEY') +} + +export function storeFirebaseKey(firebaseToken: string) { + return createAction('STORE_FIREBASE_KEY', { firebaseToken }) +} + +export function setChosenRegion(region: string) { + return createAction('SET_CHOSEN_REGION', { region }) +} + +export function setHasOpened(hasOpened: boolean) { + return createAction('SET_HAS_OPENED', { hasOpened }) +} + +export function setTutorialOneActive(isTutorialActive: boolean) { + return createAction('SET_TUTORIAL_ONE_ACTIVE', { isTutorialActive }) +} + +export function setTutorialTwoActive(isTutorialActive: boolean) { + return createAction('SET_TUTORIAL_TWO_ACTIVE', { isTutorialActive }) +} + +export function setLoginPassword(isLoginPasswordActive: boolean) { + return createAction('SET_LOGIN_PASSWORD_ACTIVE', { isLoginPasswordActive }) +} + +export function setTtsActive(isTtsActive: boolean) { + return createAction('SET_TTS_ACTIVE', { isTtsActive }) +} + +export function setFuturePredictionActive(isFuturePredictionActive: boolean) { + return createAction('SET_FUTURE_PREDICTION_ACTIVE', { isFuturePredictionActive }) +} + +export function refreshStore(appState: any) { + return createAction('REFRESH_STORE', appState) +} + +export function syncStore() { + return createAction('SYNC_STORE') +} + +// MARK: For verified dates by user +export function verifyPeriodDayByUser(date: any) { + return createAction('VERIFY_PERIOD_DAY', { date }) +} diff --git a/packages/components/src/redux/secure/actions/authActions.ts b/packages/components/src/redux/secure/actions/authActions.ts new file mode 100644 index 000000000..6d3c15eb8 --- /dev/null +++ b/packages/components/src/redux/secure/actions/authActions.ts @@ -0,0 +1,198 @@ +import { createAction } from '../../helpers' + +export function loginRequest({ name, password }) { + return createAction('LOGIN_REQUEST', { name, password }) +} + +export function loginSuccess({ + appToken, + user: { + id, + name, + dateOfBirth, + gender, + location, + country, + province, + password, + secretQuestion, + secretAnswer, + }, +}) { + return createAction('LOGIN_SUCCESS', { + appToken, + user: { + id, + name, + dateOfBirth, + gender, + location, + country, + province, + password, + secretQuestion, + secretAnswer, + }, + }) +} + +export function loginSuccessAsGuestAccount({ + id, + name, + dateOfBirth, + gender, + location, + country, + province, + password, + secretQuestion, + secretAnswer, +}) { + return createAction('LOGIN_SUCCESS_AS_GUEST_ACCOUNT', { + id, + name, + dateOfBirth, + gender, + location, + country, + province, + password, + secretQuestion, + secretAnswer, + }) +} + +export function loginFailure({ error }) { + return createAction('LOGIN_FAILURE', { error }) +} + +export function logoutRequest() { + return createAction('LOGOUT_REQUEST') +} + +export function logout() { + return createAction('LOGOUT') +} + +export function createAccountRequest({ + id = null, + name, + dateOfBirth, + gender, + location, + country, + province, + password, + secretQuestion, + secretAnswer, +}) { + return createAction('CREATE_ACCOUNT_REQUEST', { + id, + name, + dateOfBirth, + gender, + location, + country, + province, + password, + secretQuestion, + secretAnswer, + }) +} +export function deleteAccountRequest({ name, password, setLoading }) { + return createAction('DELETE_ACCOUNT_REQUEST', { + name, + password, + setLoading, + }) +} + +export function createAccountSuccess({ + appToken, + user: { + id, + name, + password, + dateOfBirth, + gender, + location, + country, + province, + secretQuestion, + secretAnswer, + }, +}) { + return createAction('CREATE_ACCOUNT_SUCCESS', { + appToken, + user: { + id, + name, + dateOfBirth, + gender, + location, + country, + province, + password, + secretQuestion, + secretAnswer, + }, + }) +} + +export function createAccountFailure() { + return createAction('CREATE_ACCOUNT_FAILURE') +} + +export function convertGuestAccount({ + id, + name, + dateOfBirth, + gender, + location, + country, + province, + password, + secretQuestion, + secretAnswer, +}) { + return createAction('CONVERT_GUEST_ACCOUNT', { + id, + name, + password, + dateOfBirth, + gender, + location, + country, + province, + secretQuestion, + secretAnswer, + }) +} + +export function editUser({ + name = null, + dateOfBirth = null, + gender = null, + location = null, + password = null, + secretQuestion = null, + secretAnswer = null, +}) { + return createAction('EDIT_USER', { + name, + dateOfBirth, + gender, + location, + password, + secretQuestion, + secretAnswer, + }) +} + +export function journeyCompletion({ data = null }) { + return createAction('JOURNEY_COMPLETION', { data }) +} + +export function setAuthError({ error }) { + return createAction('SET_AUTH_ERROR', { error }) +} diff --git a/packages/components/src/redux/secure/actions/contentActions.ts b/packages/components/src/redux/secure/actions/contentActions.ts new file mode 100644 index 000000000..dd9199406 --- /dev/null +++ b/packages/components/src/redux/secure/actions/contentActions.ts @@ -0,0 +1,80 @@ +import { createAction } from '../../helpers' +import { + Articles, + Categories, + SubCategories, + Surveys, + Quizzes, + DidYouKnows, + HelpCenters, + AvatarMessages, + PrivacyPolicy, + TermsAndConditions, + About, + AllSurveys, + CompletedSurveys, + Videos, +} from '../../../types' + +export function initStaleContent(payload: { + articles: Articles + avatarMessages: AvatarMessages + categories: Categories + subCategories: SubCategories + quizzes: Quizzes + didYouKnows: DidYouKnows + helpCenters: HelpCenters + privacyPolicy: PrivacyPolicy + termsAndConditions: TermsAndConditions + about: About + aboutBanner: string +}) { + return createAction('INIT_STALE_CONTENT', payload) +} + +export function fetchSurveyContentRequest(userID: string) { + return createAction('FETCH_SURVEY_CONTENT_REQUEST', { userID }) +} + +export function fetchSurveyContentSuccess({ surveys }: { surveys: Surveys }) { + return createAction('FETCH_SURVEY_CONTENT_SUCCESS', { + surveys, + }) +} +export function updateAllSurveyContent(allSurveys: AllSurveys) { + return createAction('UPDATE_ALL_SURVEYS_CONTENT', { + allSurveys, + }) +} +export function updateCompletedSurveys(completedSurveys: CompletedSurveys) { + return createAction('UPDATE_COMPLETED_SURVEYS', { + completedSurveys, + }) +} + +export function fetchContentRequest(locale: string) { + return createAction('FETCH_CONTENT_REQUEST', { locale }) +} + +export function fetchContentSuccess(payload: { + timeFetched: number + articles: Articles + videos: Videos + avatarMessages: AvatarMessages + categories: Categories + subCategories: SubCategories + quizzes: Quizzes + didYouKnows: DidYouKnows + helpCenters: HelpCenters + privacyPolicy: PrivacyPolicy + termsAndConditions: TermsAndConditions + about: About + aboutBanner?: string + aboutBannerTimestamp?: number +}) { + return createAction('FETCH_CONTENT_SUCCESS', payload) +} + +export function fetchContentFailure() { + return createAction('FETCH_CONTENT_FAILURE') +} diff --git a/packages/components/src/redux/secure/actions/index.ts b/packages/components/src/redux/secure/actions/index.ts new file mode 100644 index 000000000..ccfc15dd9 --- /dev/null +++ b/packages/components/src/redux/secure/actions/index.ts @@ -0,0 +1,15 @@ +import * as analyticsActions from './analyticsActions' +import * as answerActions from './answerActions' +import * as appActions from './appActions' +import * as authActions from './authActions' +import * as contentActions from './contentActions' +import * as predictionActions from './predictionActions' + +export const secureActions = { + ...analyticsActions, + ...answerActions, + ...appActions, + ...authActions, + ...contentActions, + ...predictionActions, +} diff --git a/packages/components/src/redux/secure/actions/newPredictionAction.ts b/packages/components/src/redux/secure/actions/newPredictionAction.ts new file mode 100644 index 000000000..610897a46 --- /dev/null +++ b/packages/components/src/redux/secure/actions/newPredictionAction.ts @@ -0,0 +1,20 @@ +// import { createAction } from '../helpers' + +// export function smartPredictionRequest({ cycle_lengths, period_lengths, age }) { +// return createAction('SMART_PREDICTION_REQUEST', { cycle_lengths, period_lengths, age }) +// } + +// export function smartPredictionSuccess({ predicted_cycles, predicted_periods }) { +// return createAction('SMART_PREDICTION_SUCCESS', { +// predicted_cycles, +// predicted_periods, +// }) +// } + +// export function smartPredictionFailure({ error }) { +// return createAction('SMART_PREDICTION_FAILURE', { error }) +// } + +// export function smartPredictionUpdate({ predicted_cycles, predicted_periods }) { +// return createAction('UPDATE_SMART_PREDICTION', { predicted_cycles, predicted_periods }) +// } diff --git a/packages/components/src/redux/secure/actions/predictionActions.ts b/packages/components/src/redux/secure/actions/predictionActions.ts new file mode 100644 index 000000000..50390c70b --- /dev/null +++ b/packages/components/src/redux/secure/actions/predictionActions.ts @@ -0,0 +1,41 @@ +import { createAction } from '../../helpers' +import { PredictionState } from '../../../prediction' +import { Moment } from 'moment' + +export function setPredictionEngineState(predictionState: PredictionState) { + return createAction('SET_PREDICTION_ENGINE_STATE', { predictionState }) +} + +export function smartPredictionRequest({ + cycle_lengths, + period_lengths, + age, + predictionFullState, + futurePredictionStatus, +}) { + return createAction('SMART_PREDICTION_REQUEST', { + cycle_lengths, + period_lengths, + age, + predictionFullState, + futurePredictionStatus, + }) +} + +export function updateActualCurrentStartDate() { + return createAction('SET_ACTUAL_STARTDATE') +} + +export function setSmartPredictionFailure({ error }) { + return createAction('SMART_PREDICTION_FAILURE', { error }) +} + +export function adjustPrediction(action) { + return createAction('ADJUST_PREDICTION', action) +} +export function updateFuturePrediction(isFuturePredictionActive: boolean, currentStartDate: any) { + return createAction('SET_FUTURE_PREDICTION_STATE_ACTIVE', { + isFuturePredictionActive, + currentStartDate, + }) +} diff --git a/packages/components/src/redux/secure/reducers/analyticsReducer.ts b/packages/components/src/redux/secure/reducers/analyticsReducer.ts new file mode 100644 index 000000000..5201ab777 --- /dev/null +++ b/packages/components/src/redux/secure/reducers/analyticsReducer.ts @@ -0,0 +1,28 @@ +import { SecureActions } from '../types' + +export type AnalyticsState = Array<{ + id: string + type: string + payload: any + metadata: any +}> + +const initialState: AnalyticsState = [] + +export function analyticsReducer(state = initialState, action: SecureActions): AnalyticsState { + switch (action.type) { + case 'QUEUE_EVENT': + return state.concat({ + id: action.payload.id, + type: action.payload.type, + payload: action.payload.payload, + metadata: action.payload.metadata, + }) + + case 'RESET_QUEUE': + return initialState + + default: + return state + } +} diff --git a/packages/components/src/redux/secure/reducers/answerReducer.ts b/packages/components/src/redux/secure/reducers/answerReducer.ts new file mode 100644 index 000000000..edc0334dc --- /dev/null +++ b/packages/components/src/redux/secure/reducers/answerReducer.ts @@ -0,0 +1,204 @@ +import { SecureActions } from '../types' +import { combineReducers } from 'redux' +import { toShortISO } from '../../../services/dateUtils' +import { DailyCard } from '../../../types' + +export interface AnswerForUserState { + surveys: { + [id: string]: { + id: string + user_id: string + isCompleted: boolean + isSurveyAnswered: boolean + questions: any + utcDateTime: string + inProgress: boolean + } + } + quizzes: { + [id: string]: { + id: string + question: string + emoji: string + answer: string + isCorrect: boolean + response: string + utcDateTime: string + } + } + cards: { + [utcShortISO: string]: DailyCard + } + verifiedDates: { + [utcShortISO: string]: { + periodDay: null | boolean + utcDateTime: string + } + } + notes: { + [utcShortISO: string]: { + notes: string + utcDateTime: string + } + } +} + +export interface AnswerState { + [userId: string]: AnswerForUserState +} + +function surveysReducer(state = {}, action: SecureActions): AnswerForUserState['surveys'] { + if (action.type === 'ANSWER_SURVEY') { + return { + ...state, + [action.payload.id]: { + id: action.payload.id, + user_id: action.payload.user_id, + isCompleted: action.payload.isCompleted, + isSurveyAnswered: action.payload.isSurveyAnswered, + questions: action.payload.questions, + utcDateTime: action.payload.utcDateTime.toISOString(), + }, + } + } + + return state +} + +function quizzesReducer(state = {}, action: SecureActions): AnswerForUserState['quizzes'] { + if (action.type === 'ANSWER_QUIZ') { + return { + ...state, + [action.payload.id]: { + id: action.payload.id, + question: action.payload.question, + answerID: action.payload.answerID, + emoji: action.payload.emoji, + answer: action.payload.answer, + isCorrect: action.payload.isCorrect, + response: action.payload.response, + utcDateTime: action.payload.utcDateTime.toISOString(), + }, + } + } + + return state +} + +function cardsReducer(state = {}, action: SecureActions): AnswerForUserState['cards'] { + if (action.type === 'ANSWER_DAILY_CARD') { + const keyCard = toShortISO(action.payload.utcDateTime) + let answersToInsert = [] + // Added as a way of handling multiple selections and to account for the initial release of single selections (Painful, I know) + + if ( + state[keyCard] !== undefined && + state[keyCard][action.payload.cardName] && + !action.payload.mutuallyExclusive && + action.payload.cardName !== 'periodDay' + ) { + if (typeof state[keyCard][action.payload.cardName] === 'string') { + // This is to account for old data that used to just be a string and now we need to have multiple + // we put that string as part of an array before concatenating the new answers + answersToInsert = [state[keyCard][action.payload.cardName]].concat(action.payload.answer) + } else { + if (state[keyCard][action.payload.cardName].includes(action.payload.answer)) { + // Remove if already contained (toggle ability) + answersToInsert = state[keyCard][action.payload.cardName].filter( + (item) => item !== action.payload.answer, + ) + } else { + answersToInsert = state[keyCard][action.payload.cardName].concat(action.payload.answer) + } + } + } else { + answersToInsert = [action.payload.answer] + } + + return { + ...state, + [keyCard]: { + ...(state[keyCard] || {}), + [action.payload.cardName]: answersToInsert, + }, + } + } + return state +} +function periodVerifyReducer( + state = {}, + action: SecureActions, +): AnswerForUserState['verifiedDates'] { + if (action.type === 'ANSWER_VERIFY_DATES') { + const keyCard = toShortISO(action.payload.utcDateTime) + const answersToInsert = [] + // Added as a way of handling multiple selections and to account for the initial release of single selections (Painful, I know) + return { + ...state, + [keyCard]: { + ...(state[keyCard] || {}), + periodDay: action.payload.periodDay, + }, + } + } + return state +} + +function notesReducer(state = {}, action: SecureActions): AnswerForUserState['notes'] { + if (action.type === 'ANSWER_NOTES_CARD') { + const keyCard = toShortISO(action.payload.utcDateTime) + return { + ...state, + [keyCard]: { + title: action.payload.title, + notes: action.payload.notes, + utcDateTime: action.payload.utcDateTime, + }, + } + } + return state +} + +const answerForUserReducer = combineReducers({ + surveys: surveysReducer, + quizzes: quizzesReducer, + cards: cardsReducer, + notes: notesReducer, + verifiedDates: periodVerifyReducer, +}) + +export function answerReducer(state: AnswerState = {}, action: SecureActions): AnswerState { + // TODO_ALEX: survey + if (action.type === 'ANSWER_SURVEY') { + return { + ...state, + // [action.payload.user_id]: answerForUserReducer(state[action.payload.userID], action), + } + } + if (action.type === 'ANSWER_QUIZ') { + return { + ...state, + [action.payload.userID]: answerForUserReducer(state[action.payload.userID], action), + } + } + if (action.type === 'ANSWER_DAILY_CARD') { + return { + ...state, + [action.payload.userID]: answerForUserReducer(state[action.payload.userID], action), + } + } + if (action.type === 'ANSWER_VERIFY_DATES') { + return { + ...state, + [action.payload.userID]: answerForUserReducer(state[action.payload.userID], action), + } + } + if (action.type === 'ANSWER_NOTES_CARD') { + return { + ...state, + [action.payload.userID]: answerForUserReducer(state[action.payload.userID], action), + } + } + + return state +} diff --git a/packages/components/src/redux/secure/reducers/appReducer.ts b/packages/components/src/redux/secure/reducers/appReducer.ts new file mode 100644 index 000000000..5ae9ded03 --- /dev/null +++ b/packages/components/src/redux/secure/reducers/appReducer.ts @@ -0,0 +1,118 @@ +import _ from 'lodash' +import { SecureActions } from '../types' +import { currentLocale } from '../../../i18n' +import DeviceInfo from 'react-native-device-info' +import { AvatarName, ThemeName, defaultAvatar, defaultTheme } from '@oky/core' + +export interface AppState { + appLocale: string + locale: string + chosenRegion: string + appVersionName: string + appVersionCode: string + firebaseToken: string + hasOpened: boolean + isTutorialOneActive: boolean + isTutorialTwoActive: boolean + isLoginPasswordActive: boolean + isTtsActive: boolean + isFuturePredictionActive: boolean + theme: ThemeName + avatar: AvatarName + verifiedDates: any + predicted_cycles: any + predicted_periods: any +} + +const initialState: AppState = { + appVersionName: DeviceInfo.getVersion(), + appVersionCode: DeviceInfo.getBuildNumber(), + firebaseToken: null, + appLocale: currentLocale(), + locale: currentLocale(), + chosenRegion: 'en', // @TODO: PENAL CODE change to currentLocale() if no penal code // @TODO: LANGUAGES This is commented in case the client wants multiple languages + hasOpened: false, + isTutorialOneActive: true, + isTutorialTwoActive: true, + isLoginPasswordActive: true, + isTtsActive: false, + isFuturePredictionActive: true, + theme: defaultTheme, + avatar: defaultAvatar, + verifiedDates: [], + predicted_cycles: [], + predicted_periods: [], +} + +export function appReducer(state = initialState, action: SecureActions): AppState { + switch (action.type) { + case 'SET_THEME': + return { + ...state, + theme: action.payload.theme, + } + case 'SET_AVATAR': + return { + ...state, + avatar: action.payload.avatar, + } + case 'SET_UPDATED_VERSION': + return { + ...state, + appVersionName: DeviceInfo.getVersion(), + appVersionCode: DeviceInfo.getBuildNumber(), + } + case 'STORE_FIREBASE_KEY': + return { + ...state, + firebaseToken: action.payload.firebaseToken, + } + case 'SET_LOCALE': + return { + ...state, + locale: action.payload.locale, + } + case 'SET_CHOSEN_REGION': + return { + ...state, + chosenRegion: action.payload.region, + } + case 'SET_HAS_OPENED': + return { + ...state, + hasOpened: action.payload.hasOpened, + } + case 'SET_TUTORIAL_ONE_ACTIVE': + return { + ...state, + isTutorialOneActive: action.payload.isTutorialActive, + } + case 'SET_TUTORIAL_TWO_ACTIVE': + return { + ...state, + isTutorialTwoActive: action.payload.isTutorialActive, + } + case 'SET_LOGIN_PASSWORD_ACTIVE': + return { + ...state, + isLoginPasswordActive: action.payload.isLoginPasswordActive, + } + case 'SET_TTS_ACTIVE': + return { + ...state, + isTtsActive: action.payload.isTtsActive, + } + case 'SET_FUTURE_PREDICTION_ACTIVE': + return { + ...state, + isFuturePredictionActive: action.payload.isFuturePredictionActive, + } + case 'VERIFY_PERIOD_DAY': + return { + ...state, + verifiedDates: action.payload.date, + } + default: + return state + } +} diff --git a/packages/components/src/redux/secure/reducers/authReducer.ts b/packages/components/src/redux/secure/reducers/authReducer.ts new file mode 100644 index 000000000..f0332b2d8 --- /dev/null +++ b/packages/components/src/redux/secure/reducers/authReducer.ts @@ -0,0 +1,155 @@ +import { REHYDRATE, RehydrateAction } from 'redux-persist' +import _ from 'lodash' +import { SecureActions } from '../types/index' + +export interface User { + id: string + name: string + dateOfBirth: string + gender: string + location: string + country: string + province: string + password: string + secretQuestion: string + secretAnswer: string + isGuest: boolean +} + +export interface AuthState { + appToken: string | null + error: string | null + isCreatingAccount: boolean + isLoggingIn: boolean + loginFailedCount: number + connectAccountAttempts: number + user: User | null +} + +const initialState: AuthState = { + appToken: null, + error: null, + isCreatingAccount: false, + isLoggingIn: false, + loginFailedCount: 0, + connectAccountAttempts: 0, + user: null, +} + +export function authReducer( + state = initialState, + action: SecureActions | RehydrateAction, +): AuthState { + switch (action.type) { + case REHYDRATE: + return { + ...(action.payload && action.payload.auth), + // reset state when store is re-hydrated + ..._.pick(initialState, ['error', 'isLoggingIn', 'loginFailedCount', 'isCreatingAccount']), + } + + case 'LOGIN_REQUEST': + return { + ...state, + error: null, + isLoggingIn: true, + } + + case 'LOGIN_SUCCESS': + return { + ...state, + appToken: action.payload.appToken, + error: null, + isLoggingIn: false, + loginFailedCount: 0, + connectAccountAttempts: 0, + user: { + id: action.payload.user.id, + name: action.payload.user.name, + dateOfBirth: action.payload.user.dateOfBirth, + gender: action.payload.user.gender, + location: action.payload.user.location, + country: action.payload.user.country, + province: action.payload.user.province, + password: action.payload.user.password, + secretQuestion: action.payload.user.secretQuestion, + secretAnswer: action.payload.user.secretAnswer, + isGuest: false, + }, + } + + case 'LOGIN_SUCCESS_AS_GUEST_ACCOUNT': + return { + ...state, + appToken: null, + isLoggingIn: false, + loginFailedCount: 0, + user: { + id: action.payload.id, + name: action.payload.name, + dateOfBirth: action.payload.dateOfBirth, + gender: action.payload.gender, + location: action.payload.location, + country: action.payload.country, + province: action.payload.province, + password: action.payload.password, + secretQuestion: action.payload.secretQuestion, + secretAnswer: action.payload.secretAnswer, + isGuest: true, + }, + } + + case 'LOGIN_FAILURE': + return { + ...state, + appToken: null, + loginFailedCount: state.loginFailedCount + 1, + error: action.payload.error, + isLoggingIn: false, + user: null, + } + + case 'LOGOUT': + return { + ...state, + appToken: null, + isLoggingIn: false, + user: null, + } + + case 'SET_AUTH_ERROR': + return { + ...state, + error: action.payload.error, + } + + case 'CREATE_ACCOUNT_REQUEST': + return { + ...state, + isCreatingAccount: true, + error: null, + } + + case 'CREATE_ACCOUNT_SUCCESS': + return { + ...state, + isCreatingAccount: false, + } + + case 'CREATE_ACCOUNT_FAILURE': + return { + ...state, + connectAccountAttempts: state.connectAccountAttempts + 1, + isCreatingAccount: false, + } + + case 'EDIT_USER': + return { + ...state, + user: { ...state.user, ..._.omitBy(action.payload, _.isNil) }, + } + + default: + return state + } +} diff --git a/packages/components/src/redux/secure/reducers/contentReducer.ts b/packages/components/src/redux/secure/reducers/contentReducer.ts new file mode 100644 index 000000000..7814ebfb6 --- /dev/null +++ b/packages/components/src/redux/secure/reducers/contentReducer.ts @@ -0,0 +1,145 @@ +import _ from 'lodash' +import { + Articles, + Categories, + SubCategories, + Surveys, + Quizzes, + DidYouKnows, + HelpCenters, + AvatarMessages, + PrivacyPolicy, + TermsAndConditions, + About, + AllSurveys, + CompletedSurveys, + Videos, +} from '../../../types' +import { SecureActions } from '../types/index' + +export interface ContentState { + timeFetched?: number + aboutBannerTimestamp?: number + articles: Articles + categories: Categories + subCategories: SubCategories + surveys: Surveys + quizzes: Quizzes + didYouKnows: DidYouKnows + helpCenters: HelpCenters + avatarMessages: AvatarMessages + privacyPolicy: PrivacyPolicy + termsAndConditions: TermsAndConditions + about: About + aboutBanner: string + allSurveys: AllSurveys + completedSurveys: CompletedSurveys + videos?: Videos +} + +const initialState: ContentState = { + timeFetched: undefined, + aboutBannerTimestamp: undefined, + articles: { + byId: {}, + allIds: [], + }, + categories: { + byId: {}, + allIds: [], + }, + subCategories: { + byId: {}, + allIds: [], + }, + surveys: { + date_created: '', + id: 'string', + isAgeRestricted: false, + is_multiple: true, + lang: 'string', + live: true, + option1: 'string', + option2: 'string', + option3: 'string', + option4: 'string', + option5: 'string', + question: 'string', + questions: [], + }, + allSurveys: [], + completedSurveys: [], + quizzes: { + byId: {}, + allIds: [], + }, + didYouKnows: { + byId: {}, + allIds: [], + }, + helpCenters: [], + avatarMessages: [], + privacyPolicy: [], + termsAndConditions: [], + about: [], + aboutBanner: '', + videos: { + byId: {}, + allIds: [], + }, +} + +export function contentReducer(state = initialState, action: SecureActions): ContentState { + switch (action.type) { + case 'INIT_STALE_CONTENT': + return { + ...state, + ...action.payload, + } + + case 'FETCH_CONTENT_SUCCESS': { + const shouldUpdateBanner = action.payload.aboutBanner !== undefined + + return { + ...state, + timeFetched: action.payload.timeFetched, + articles: action.payload.articles, + videos: action.payload.videos, + categories: action.payload.categories, + subCategories: action.payload.subCategories, + quizzes: action.payload.quizzes, + didYouKnows: action.payload.didYouKnows, + helpCenters: action.payload.helpCenters, + avatarMessages: action.payload.avatarMessages, + privacyPolicy: action.payload.privacyPolicy, + termsAndConditions: action.payload.termsAndConditions, + about: action.payload.about, + aboutBanner: shouldUpdateBanner ? action.payload.aboutBanner : state.aboutBanner, + aboutBannerTimestamp: shouldUpdateBanner + ? action.payload.aboutBannerTimestamp + : state.aboutBannerTimestamp, + } + } + + case 'FETCH_SURVEY_CONTENT_SUCCESS': + return { + ...state, + surveys: action.payload.surveys, + } + + case 'UPDATE_ALL_SURVEYS_CONTENT': + return { + ...state, + allSurveys: action.payload.allSurveys, + } + + case 'UPDATE_COMPLETED_SURVEYS': + return { + ...state, + completedSurveys: action.payload.completedSurveys, + } + + default: + return state + } +} diff --git a/packages/components/src/redux/secure/reducers/index.ts b/packages/components/src/redux/secure/reducers/index.ts new file mode 100644 index 000000000..48861588d --- /dev/null +++ b/packages/components/src/redux/secure/reducers/index.ts @@ -0,0 +1,41 @@ +import _ from 'lodash' +import { combineReducers } from 'redux' +import { syncReducers } from '../sync' +import { SecureActions } from '../types' + +import { analyticsReducer } from './analyticsReducer' +import { answerReducer } from './answerReducer' +import { appReducer } from './appReducer' +import { authReducer } from './authReducer' +import { contentReducer } from './contentReducer' +import { predictionReducer } from './predictionReducer' + +export const exportReducerNames = ['app', 'prediction'] + +const reducer = combineReducers( + syncReducers( + { + analytics: analyticsReducer, + answer: answerReducer, + app: appReducer, + auth: authReducer, + content: contentReducer, + prediction: predictionReducer, + // flower: flowerReducer, TODO: Flower state should be saved per user + }, + exportReducerNames, + ), +) + +export function secureRootReducer(state, action: SecureActions) { + switch (action.type) { + case 'LOGOUT': + // @ts-ignore + return reducer(_.pick(state, 'app', 'content', 'answer'), action) + + default: + return reducer(state, action) + } +} + +export type SecureReduxState = ReturnType diff --git a/packages/components/src/redux/secure/reducers/predictionReducer.ts b/packages/components/src/redux/secure/reducers/predictionReducer.ts new file mode 100644 index 000000000..ab9637e49 --- /dev/null +++ b/packages/components/src/redux/secure/reducers/predictionReducer.ts @@ -0,0 +1,36 @@ +import _ from 'lodash' +import { PredictionSerializableState } from '../../../prediction' + +import { SecureActions } from '../types/index' + +export type PredictionState = PredictionSerializableState | null + +const initialState: PredictionState = null + +export function predictionReducer(state = initialState, action: SecureActions): PredictionState { + switch (action.type) { + case 'SET_PREDICTION_ENGINE_STATE': + return action.payload.predictionState.toJSON() + + case 'SMART_PREDICTION_FAILURE': + return { + ...state, + } + case 'SET_FUTURE_PREDICTION_STATE_ACTIVE': + return { + ...state, + futurePredictionStatus: action.payload.isFuturePredictionActive, + actualCurrentStartDate: !action.payload.isFuturePredictionActive + ? action.payload.currentStartDate + : null, + } + + case 'SET_ACTUAL_STARTDATE': + return { + ...state, + actualCurrentStartDate: null, + } + default: + return state + } +} diff --git a/packages/components/src/redux/secure/sagas/analyticsSaga.ts b/packages/components/src/redux/secure/sagas/analyticsSaga.ts new file mode 100644 index 000000000..98d1d6c9b --- /dev/null +++ b/packages/components/src/redux/secure/sagas/analyticsSaga.ts @@ -0,0 +1,68 @@ +import { all, delay, fork, put, select, takeLatest } from 'redux-saga/effects' +import { v4 as uuidv4 } from 'uuid' +import moment from 'moment' +import { fetchNetworkConnectionStatus } from '../../../services/network' +import { httpClient } from '../../../services/HttpClient' +import { secureActions } from '../actions' +import { secureSelectors } from '../selectors' +import { ActionTypes } from '../types' + +const ACTIONS_TO_TRACK: ActionTypes[] = [ + // app + 'SET_THEME', + 'SET_LOCALE', + // answers + 'ANSWER_SURVEY', + 'ANSWER_QUIZ', + 'SHARE_APP', + // 'ANSWER_DAILY_CARD', // removed for privacy + // prediction + 'ADJUST_PREDICTION', +] + +function* onTrackAction(action) { + const currentUser = yield select(secureSelectors.currentUserSelector) + yield put( + secureActions.queueEvent({ + id: uuidv4(), + type: action.type, + payload: action.payload || {}, + metadata: { + date: moment.utc(), + user: currentUser && currentUser.id ? currentUser.id : null, + }, + }), + ) +} + +function* processEventQueue() { + while (true) { + // process queue every minute + yield delay(60 * 1000) + + const appToken = yield select(secureSelectors.appTokenSelector) + const events = yield select(secureSelectors.allAnalyticsEventsSelector) + + const isQueueEmpty = events.length === 0 + if (isQueueEmpty) { + // nothing to send + continue + } + + if (!(yield fetchNetworkConnectionStatus())) { + // no internet connection + continue + } + + try { + yield httpClient.appendEvents({ events, appToken }) + yield put(secureActions.resetQueue()) + } catch (err) { + // ignore error, we'll try later + } + } +} + +export function* analyticsSaga() { + yield all([fork(processEventQueue), takeLatest(ACTIONS_TO_TRACK, onTrackAction)]) +} diff --git a/packages/components/src/redux/secure/sagas/appSaga.ts b/packages/components/src/redux/secure/sagas/appSaga.ts new file mode 100644 index 000000000..c2349ba23 --- /dev/null +++ b/packages/components/src/redux/secure/sagas/appSaga.ts @@ -0,0 +1,67 @@ +import _ from 'lodash' +import { all, delay, fork, put, select, takeLatest } from 'redux-saga/effects' +import { httpClient } from '../../../services/HttpClient' +import { fetchNetworkConnectionStatus } from '../../../services/network' +import { extractReducerState } from '../sync' +import { SecureReduxState, exportReducerNames } from '../reducers' +import { version as storeVersion } from '../../store' +import { secureActions } from '../actions' +import { secureSelectors } from '../selectors' +import messaging from '@react-native-firebase/messaging' + +function* syncAppState() { + let lastAppState + + while (true) { + // process queue every minute + yield delay(60 * 1000) + + const appToken = yield select(secureSelectors.appTokenSelector) + if (!appToken) { + // not logged + continue + } + + const state: SecureReduxState = yield select() + const appState = extractReducerState(state, exportReducerNames) + + if (_.isEqual(appState, lastAppState)) { + // bailout, nothing changed from last sync + continue + } + + if (!(yield fetchNetworkConnectionStatus())) { + // no internet connection + continue + } + + try { + yield httpClient.replaceStore({ + storeVersion, + appState, + appToken, + }) + + const temp = yield put(secureActions.syncStore()) + + lastAppState = appState + } catch (err) { + // ignore error, we'll try later + } + } +} + +function* onRequestStoreFirebaseKey() { + if (yield fetchNetworkConnectionStatus()) { + // no internet connection + const firebaseToken = yield messaging().getToken() + yield put(secureActions.storeFirebaseKey(firebaseToken)) + } +} + +export function* appSaga() { + yield all([ + fork(syncAppState), + takeLatest('REQUEST_STORE_FIREBASE_KEY', onRequestStoreFirebaseKey), + ]) +} diff --git a/packages/components/src/redux/secure/sagas/authSaga.ts b/packages/components/src/redux/secure/sagas/authSaga.ts new file mode 100644 index 000000000..7f7e93c4c --- /dev/null +++ b/packages/components/src/redux/secure/sagas/authSaga.ts @@ -0,0 +1,318 @@ +import { all, call, put, select, takeLatest, delay } from 'redux-saga/effects' +import { REHYDRATE } from 'redux-persist' +import { Alert } from 'react-native' +import { v4 as uuidv4 } from 'uuid' +import { ExtractActionFromActionType } from '../types' +import { httpClient } from '../../../services/HttpClient' +import { SecureReduxState, exportReducerNames } from '../reducers' +import { secureActions } from '../actions' +import { secureSelectors } from '../selectors' +import { navigateAndReset } from '../../../services/navigationService' +import { PredictionState } from '../../../prediction' +import moment from 'moment' +import { closeOutTTs } from '../../../services/textToSpeech' +import { fetchNetworkConnectionStatus } from '../../../services/network' + +// unwrap promise +type Await = T extends Promise ? U : T + +function* onRehydrate() { + const state: SecureReduxState = yield select() + + const appToken = secureSelectors.appTokenSelector(state) + const user = secureSelectors.currentUserSelector(state) + + // convert guest account + if (!appToken && user && user.isGuest) { + yield put(secureActions.convertGuestAccount(user)) + } +} + +function* onConvertGuestAccount(action: ExtractActionFromActionType<'CONVERT_GUEST_ACCOUNT'>) { + const { + id, + name, + dateOfBirth, + gender, + location, + country, + province, + password, + secretAnswer, + secretQuestion, + } = action.payload + + yield put( + secureActions.createAccountRequest({ + id, + name, + dateOfBirth, + gender, + location, + country, + province, + password, + secretAnswer, + secretQuestion, + }), + ) +} + +function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { + const { name, password } = action.payload + const stateRedux: SecureReduxState = yield select() + const localeapp = secureSelectors.currentLocaleSelector(stateRedux) + yield secureActions.setLocale(localeapp) + + try { + const { + appToken, + user, + store, + }: Await> = yield httpClient.login({ + name, + password, + }) + + yield put( + secureActions.loginSuccess({ + appToken, + user: { + id: user.id, + name, + dateOfBirth: user.dateOfBirth, + gender: user.gender, + location: user.location, + country: user.country, + province: user.province, + secretQuestion: user.secretQuestion, + secretAnswer: user.secretAnswer, + password, + }, + }), + ) + + if (store && store.storeVersion && store.appState) { + const newAppState = exportReducerNames.reduce((state, reducerName) => { + if (store.appState[reducerName]) { + return { + ...state, + [reducerName]: store.appState[reducerName], + app: { ...store.appState.app, appLocale: localeapp, locale: localeapp }, + } + } + + return state + }, {}) + + // @TODO: execute migration based on storeVersion + yield put(secureActions.refreshStore(newAppState)) + } + + yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones + yield call(navigateAndReset, 'MainStack', null) + } catch (error) { + let errorMessage = 'request_fail' + if (error && error.response && error.response.data) { + if (error.response.data.name === 'BadRequestError') { + errorMessage = 'login_failed' + } + if (error.response.data.name !== 'BadRequestError') { + errorMessage = error.response.data.message + } + } + yield put( + secureActions.loginFailure({ + error: errorMessage, + }), + ) + } +} + +function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACCOUNT_REQUEST'>) { + const { + id, + name, + dateOfBirth, + gender, + location, + country, + province, + password, + secretAnswer, + secretQuestion, + } = action.payload + try { + const { appToken, user }: Await> = yield httpClient.signup( + { + name, + password, + dateOfBirth, + gender, + location, + country, + province, + secretAnswer, + secretQuestion, + preferredId: id || null, + }, + ) + if (!appToken || !user || !user.id) { + throw new Error(`Invalid data`) + } + + yield put( + secureActions.createAccountSuccess({ + appToken, + user: { + id: user.id, + name, + dateOfBirth: user.dateOfBirth, + gender: user.gender, + location: user.location, + country: user.country, + province: user.province, + secretQuestion: user.secretQuestion, + secretAnswer: user.secretAnswer, + password, + }, + }), + ) + } catch (error) { + const errorStatusCode = + error && error.response && error.response.status ? error.response.status : null // to check various error codes and respond accordingly + yield put(secureActions.setAuthError({ error: errorStatusCode })) + yield put(secureActions.createAccountFailure()) + + yield put( + secureActions.loginSuccessAsGuestAccount({ + id: id || uuidv4(), + name, + dateOfBirth, + gender, + location, + country, + province, + password, + secretAnswer, + secretQuestion, + }), + ) + } +} + +function* onCreateAccountSuccess(action: ExtractActionFromActionType<'CREATE_ACCOUNT_SUCCESS'>) { + const { appToken, user } = action.payload + yield put( + secureActions.loginSuccess({ + appToken, + user: { + id: user.id, + name: user.name, + dateOfBirth: user.dateOfBirth, + gender: user.gender, + location: user.location, + country: user.country, + province: user.province, + password: user.password, + secretQuestion: user.secretQuestion, + secretAnswer: user.secretAnswer, + }, + }), + ) +} +function* onDeleteAccountRequest(action: ExtractActionFromActionType<'DELETE_ACCOUNT_REQUEST'>) { + const { setLoading } = action.payload + const state: SecureReduxState = yield select() + const user = secureSelectors.currentUserSelector(state) + setLoading(true) + try { + const { name, password } = action.payload + yield httpClient.deleteUserFromPassword({ + name, + password, + }) + yield put(secureActions.updateAllSurveyContent([])) // TODO_ALEX + yield put(secureActions.updateCompletedSurveys([])) // TODO_ALEX + yield put( + secureActions.fetchSurveyContentSuccess({ + surveys: null, + }), // TODO_ALEX + ) + yield call(navigateAndReset, 'LoginStack', null) + + if (user) { + yield put(secureActions.logout()) + } + } catch (err) { + setLoading(false) + Alert.alert('Error', 'Unable to delete the account') + } +} + +function* onLogoutRequest() { + const isTtsActive = yield select(secureSelectors.isTtsActiveSelector) + + if (isTtsActive) { + yield call(closeOutTTs) + yield put(secureActions.setTtsActive(false)) + yield put(secureActions.verifyPeriodDayByUser([])) // TODO_ALEX: survey + } + yield put(secureActions.updateAllSurveyContent([])) // TODO_ALEX: survey + yield put( + secureActions.fetchSurveyContentSuccess({ + surveys: null, + }), + ) + yield put(secureActions.updateCompletedSurveys([])) // TODO_ALEX: survey + yield call(navigateAndReset, 'LoginStack', null) + yield put(secureActions.logout()) +} + +function* onJourneyCompletion(action: ExtractActionFromActionType<'JOURNEY_COMPLETION'>) { + const { data } = action.payload + const currentUser = yield select(secureSelectors.currentUserSelector) + let periodResult = null + if (yield fetchNetworkConnectionStatus()) { + try { + periodResult = yield httpClient.getPeriodCycles({ + age: moment().diff(moment(currentUser.dateOfBirth), 'years'), + period_lengths: [0, 0, 0, 0, 0, 0, 0, 0, 0, data[2].answer + 1], + cycle_lengths: [0, 0, 0, 0, 0, 0, 0, 0, 0, (data[3].answer + 1) * 7 + data[2].answer + 1], + }) + } catch (error) { + // console.log( error); + } + } + const stateToSet = PredictionState.fromData({ + isActive: data[0].answer === 'Yes' ? true : false, + startDate: moment(data[1].answer, 'DD-MMM-YYYY'), + periodLength: data[2].answer + 1, + cycleLength: (data[3].answer + 1) * 7 + data[2].answer + 1, + smaCycleLength: periodResult + ? periodResult.predicted_cycles[0] + : (data[3].answer + 1) * 7 + data[2].answer + 1, + smaPeriodLength: periodResult ? periodResult.predicted_periods[0] : data[2].answer + 1, + history: [], + }) + + yield put(secureActions.setPredictionEngineState(stateToSet)) + yield put(secureActions.updateFuturePrediction(true, null)) + yield put(secureActions.setTutorialOneActive(true)) + yield put(secureActions.setTutorialTwoActive(true)) + yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones + yield call(navigateAndReset, 'MainStack', null) +} + +export function* authSaga() { + yield all([ + takeLatest(REHYDRATE, onRehydrate), + takeLatest('LOGOUT_REQUEST', onLogoutRequest), + takeLatest('LOGIN_REQUEST', onLoginRequest), + takeLatest('DELETE_ACCOUNT_REQUEST', onDeleteAccountRequest), + takeLatest('CREATE_ACCOUNT_REQUEST', onCreateAccountRequest), + takeLatest('CREATE_ACCOUNT_SUCCESS', onCreateAccountSuccess), + takeLatest('CONVERT_GUEST_ACCOUNT', onConvertGuestAccount), + takeLatest('JOURNEY_COMPLETION', onJourneyCompletion), + ]) +} diff --git a/packages/components/src/redux/secure/sagas/contentSaga.ts b/packages/components/src/redux/secure/sagas/contentSaga.ts new file mode 100644 index 000000000..686b40945 --- /dev/null +++ b/packages/components/src/redux/secure/sagas/contentSaga.ts @@ -0,0 +1,214 @@ +import { all, call, put, select, takeLatest } from 'redux-saga/effects' +import { RehydrateAction, REHYDRATE } from 'redux-persist' +import { ExtractActionFromActionType } from '../types' +import { + fromEncyclopedia, + fromQuizzes, + fromDidYouKnows, + fromSurveys, + fromHelpCenters, + fromAvatarMessages, + liveContent as staleContent, +} from '@oky/core' +import { httpClient } from '../../../services/HttpClient' +import { secureSelectors } from '../selectors' +import { secureActions } from '../actions' +import _ from 'lodash' +import messaging from '@react-native-firebase/messaging' +import { closeOutTTs } from '../../../services/textToSpeech' + +function* onRehydrate(action: RehydrateAction) { + const locale = yield select(secureSelectors.currentLocaleSelector) + + const hasPreviousContentFromStorage = action.payload && action.payload.content + + if (!hasPreviousContentFromStorage) { + yield put(secureActions.initStaleContent(staleContent[locale])) + } + + const now = new Date().getTime() + // TODO_ALEX what time interval should we use? + const fetchInterval = 0 // 1000 * 60 * 60 * 24 // 24 hours + const timeFetched = action.payload && action.payload.content?.timeFetched + const shouldFetch = !timeFetched || timeFetched + fetchInterval < now + + if (shouldFetch) { + yield put(secureActions.fetchContentRequest(locale)) + } +} + +// TODO_ALEX: survey +function* onFetchSurveyContent( + action: ExtractActionFromActionType<'FETCH_SURVEY_CONTENT_REQUEST'>, +) { + const locale = yield select(secureSelectors.currentLocaleSelector) + const userID = yield select(secureSelectors.currentUserSelector) + try { + const surveys = yield httpClient.fetchSurveys({ + locale, + userID, + }) + const previousSurveys = yield select(secureSelectors.allSurveys) + const completedSurveys = yield select(secureSelectors.completedSurveys) + const newSurveyArr = previousSurveys?.length ? previousSurveys : [] + surveys.forEach((item) => { + const itemExits = _.find(previousSurveys, { id: item.id }) + if (!itemExits) { + newSurveyArr.push(item) + } + }) + const finalArr = [] + newSurveyArr.forEach((item) => { + const itemExits = _.find(completedSurveys, { id: item.id }) + if (!itemExits) { + finalArr.push(item) + } + }) + + yield put(secureActions.updateAllSurveyContent(finalArr)) + } catch (error) { + // + } +} + +function* onFetchContentRequest(action: ExtractActionFromActionType<'FETCH_CONTENT_REQUEST'>) { + const { locale } = action.payload + + function* fetchEncyclopedia() { + const encyclopediaResponse = yield httpClient.fetchEncyclopedia({ locale }) + const videosResponse = yield httpClient.fetchVideos({ locale }) + return fromEncyclopedia({ encyclopediaResponse, videosResponse }) + } + + function* fetchPrivacyPolicy() { + const privacyPolicy = yield httpClient.fetchPrivacyPolicy({ + locale, + }) + return privacyPolicy + } + + function* fetchTermsAndConditions() { + const termsAndConditions = yield httpClient.fetchTermsAndConditions({ + locale, + }) + return termsAndConditions + } + + function* fetchAbout() { + const about = yield httpClient.fetchAbout({ + locale, + }) + return about + } + + function* fetchAboutBannerConditional() { + const timestamp = yield select((s) => s.content.aboutBannerTimestamp) + const aboutBanner = yield httpClient.fetchAboutBannerConditional({ + locale, + timestamp, + }) + return aboutBanner + } + + function* fetchHelpCenters() { + const helpCenterResponse = yield httpClient.fetchHelpCenters({ + locale, + }) + return fromHelpCenters(helpCenterResponse) + } + + function* fetchQuizzes() { + const quizzesResponse = yield httpClient.fetchQuizzes({ + locale, + }) + return fromQuizzes(quizzesResponse) + } + + function* fetchDidYouKnows() { + const didYouKnows = yield httpClient.fetchDidYouKnows({ + locale, + }) + return fromDidYouKnows(didYouKnows) + } + + function* fetchAvatarMessages() { + const avatarMessages = yield httpClient.fetchAvatarMessages({ + locale, + }) + return fromAvatarMessages(avatarMessages) + } + + try { + const { articles, categories, subCategories, videos } = yield fetchEncyclopedia() + const { quizzes } = yield fetchQuizzes() + const { didYouKnows } = yield fetchDidYouKnows() + const { helpCenters } = yield fetchHelpCenters() + const { avatarMessages } = yield fetchAvatarMessages() + const privacyPolicy = yield fetchPrivacyPolicy() + const termsAndConditions = yield fetchTermsAndConditions() + const about = yield fetchAbout() + const aboutBannerData = yield fetchAboutBannerConditional() + + yield put( + secureActions.fetchContentSuccess({ + timeFetched: new Date().getTime(), + articles: _.isEmpty(articles.allIds) ? staleContent[locale].articles : articles, + videos: _.isEmpty(videos.allIds) ? staleContent[locale].videos : videos, + categories: _.isEmpty(categories.allIds) ? staleContent[locale].categories : categories, + subCategories: _.isEmpty(subCategories.allIds) + ? staleContent[locale].subCategories + : subCategories, + quizzes: _.isEmpty(quizzes.allIds) ? staleContent[locale].quizzes : quizzes, + didYouKnows: _.isEmpty(didYouKnows.allIds) ? staleContent[locale].didYouKnows : didYouKnows, + helpCenters: _.isEmpty(helpCenters) ? staleContent[locale].helpCenters : helpCenters, + avatarMessages: _.isEmpty(avatarMessages) + ? staleContent[locale].avatarMessages + : avatarMessages, + privacyPolicy: _.isEmpty(privacyPolicy) + ? staleContent[locale].privacyPolicy + : privacyPolicy, + termsAndConditions: _.isEmpty(termsAndConditions) + ? staleContent[locale].termsAndConditions + : termsAndConditions, + about: _.isEmpty(about) ? staleContent[locale].about : about, + aboutBanner: aboutBannerData?.aboutBanner, + aboutBannerTimestamp: aboutBannerData?.aboutBannerTimestamp, + }), + ) + } catch (error) { + yield put(secureActions.fetchContentFailure()) + const aboutContent = yield select(secureSelectors.aboutContent) + if (!aboutContent) { + const localeInit = yield select(secureSelectors.currentLocaleSelector) + yield put(secureActions.initStaleContent(staleContent[localeInit])) + } + } +} + +function* onSetLocale(action: ExtractActionFromActionType<'SET_LOCALE'>) { + const { locale } = action.payload + const isTtsActive = yield select(secureSelectors.isTtsActiveSelector) + if (isTtsActive) { + // TODO_ALEX why? + yield call(closeOutTTs) + yield put(secureActions.setTtsActive(false)) + } + // unsubscribe from topic + // TODO_ALEX: use locales from submodule + messaging().unsubscribeFromTopic('oky_en_notifications') + messaging().unsubscribeFromTopic('oky_id_notifications') + messaging().unsubscribeFromTopic('oky_mn_notifications') + messaging().subscribeToTopic(`oky_${locale}_notifications`) + yield put(secureActions.initStaleContent(staleContent[locale])) + + yield put(secureActions.fetchContentRequest(locale)) +} + +export function* contentSaga() { + yield all([ + takeLatest(REHYDRATE, onRehydrate), + takeLatest('SET_LOCALE', onSetLocale), + takeLatest('FETCH_CONTENT_REQUEST', onFetchContentRequest), + takeLatest('FETCH_SURVEY_CONTENT_REQUEST', onFetchSurveyContent), + ]) +} diff --git a/packages/components/src/redux/secure/sagas/index.ts b/packages/components/src/redux/secure/sagas/index.ts new file mode 100644 index 000000000..9617fcd84 --- /dev/null +++ b/packages/components/src/redux/secure/sagas/index.ts @@ -0,0 +1,17 @@ +import { all, fork } from 'redux-saga/effects' + +import { analyticsSaga } from './analyticsSaga' +import { appSaga } from './appSaga' +import { authSaga } from './authSaga' +import { contentSaga } from './contentSaga' +import { smartPredictionbSaga } from './smartPredictionSaga' + +export function* rootSaga() { + yield all([ + fork(analyticsSaga), + fork(appSaga), + fork(authSaga), + fork(contentSaga), + fork(smartPredictionbSaga), + ]) +} diff --git a/packages/components/src/redux/secure/sagas/smartPredictionSaga.ts b/packages/components/src/redux/secure/sagas/smartPredictionSaga.ts new file mode 100644 index 000000000..e4d622ea0 --- /dev/null +++ b/packages/components/src/redux/secure/sagas/smartPredictionSaga.ts @@ -0,0 +1,51 @@ +import { all, put, takeLatest } from 'redux-saga/effects' +import { ExtractActionFromActionType } from '../types' + +import { httpClient } from '../../../services/HttpClient' + +import { secureActions } from '../actions' +import _ from 'lodash' +import { PredictionState } from '../../../prediction' + +function* onFetchUpdatedPredictedCycles( + action: ExtractActionFromActionType<'SMART_PREDICTION_REQUEST'>, +) { + try { + const { + age, + period_lengths, + cycle_lengths, + predictionFullState, + futurePredictionStatus, + } = action.payload + let predictionResult = null + predictionResult = yield httpClient.getPeriodCycles({ + age, + period_lengths, + cycle_lengths, + }) + const stateToSet = PredictionState.fromData({ + isActive: predictionFullState.isActive, + startDate: predictionFullState.currentCycle.startDate, + periodLength: predictionFullState.currentCycle.periodLength, + cycleLength: predictionFullState.currentCycle.cycleLength, + smaCycleLength: predictionResult.predicted_cycles[0], + smaPeriodLength: predictionResult.predicted_periods[0], + history: predictionFullState.history, + actualCurrentStartDate: predictionFullState.currentCycle, + }) + yield put(secureActions.setPredictionEngineState(stateToSet)) + yield put( + secureActions.updateFuturePrediction( + futurePredictionStatus, + predictionFullState.currentCycle, + ), + ) + } catch (error) { + yield put(secureActions.setSmartPredictionFailure(error)) + } +} + +export function* smartPredictionbSaga() { + yield all([takeLatest('SMART_PREDICTION_REQUEST', onFetchUpdatedPredictedCycles)]) +} diff --git a/packages/components/src/redux/secure/selectors/analyticsSelectors.ts b/packages/components/src/redux/secure/selectors/analyticsSelectors.ts new file mode 100644 index 000000000..2c15cae38 --- /dev/null +++ b/packages/components/src/redux/secure/selectors/analyticsSelectors.ts @@ -0,0 +1,5 @@ +import { SecureReduxState } from '../reducers' + +const s = (state: SecureReduxState) => state.analytics + +export const allAnalyticsEventsSelector = (state: SecureReduxState) => s(state) diff --git a/packages/components/src/redux/secure/selectors/answerSelectors.ts b/packages/components/src/redux/secure/selectors/answerSelectors.ts new file mode 100644 index 000000000..c636eb26c --- /dev/null +++ b/packages/components/src/redux/secure/selectors/answerSelectors.ts @@ -0,0 +1,137 @@ +import { allQuizzesSelectors } from './contentSelectors' + +import { Moment } from 'moment' +import { toShortISO } from '../../../services/dateUtils' +import _ from 'lodash' +import { SecureReduxState } from '../reducers' + +const s = (state: SecureReduxState) => state.answer + +export const surveyHasAnswerSelector = (state: SecureReduxState, id: string) => { + if (!s(state)[state.auth.user.id]) return false + return id in s(state)[state.auth.user.id].surveys +} + +// export const surveysWithoutAnswersSelector = (state: SecureReduxState) => { +// return allSurveysSelectors(state).filter(({ id }) => !surveyHasAnswerSelector(state, id)) +// } + +export const quizHasAnswerSelector = (state: SecureReduxState, id: string) => { + if (!s(state)[state.auth.user.id]) return false + return id in s(state)[state.auth.user.id].quizzes +} + +// Had a type error here had to add any to avoid +export const quizAnswerByDate: any = (state: SecureReduxState, date: Moment) => { + if (!s(state)[state.auth.user.id]) return null + return Object.values(s(state)[state.auth.user.id].quizzes).filter( + ({ utcDateTime }) => utcDateTime === date.toISOString(), + )[0] +} + +// Had a type error here had to add any to avoid +export const surveyAnswerByDate: any = (state: SecureReduxState, date: Moment) => { + if (!s(state)[state.auth.user.id]) return null + return Object.values(s(state)[state.auth.user.id].surveys).filter( + ({ utcDateTime }) => utcDateTime === date.toISOString(), + )[0] +} + +export const quizzesWithoutAnswersSelector = (state: SecureReduxState) => { + return allQuizzesSelectors(state).filter(({ id }) => !quizHasAnswerSelector(state, id)) +} + +export const cardAnswerSelector = (state: SecureReduxState, date: Moment) => { + if (!state.auth.user) return {} // for the use case on info screen where there is no authed user + if (!s(state)[state.auth.user.id]) return {} + return s(state)[state.auth.user.id]?.cards[toShortISO(date)] || {} +} +export const verifyPeriodDaySelectorWithDate = (state: SecureReduxState, date: Moment) => { + if (!state.auth.user) return {} // for the use case on info screen where there is no authed user + if (!s(state)[state.auth.user.id]) return {} + if (s(state)[state.auth.user.id]?.verifiedDates) { + return s(state)[state.auth.user.id]?.verifiedDates[toShortISO(date)] + } + return {} + // return s(state)[state.auth.user.id]?.verifiedDates[toShortISO(date)] || {} +} +export const allCardAnswersSelector = (state: SecureReduxState) => { + if (!state.auth.user) return {} // for the use case on info screen where there is no authed user + if (!s(state)[state.auth.user.id]) return {} + return s(state)[state.auth.user.id]?.verifiedDates || {} +} + +export const notesAnswerSelector = (state: SecureReduxState, date: Moment) => { + if (!s(state)[state.auth.user.id]) return {} + return s(state)[state.auth.user.id].notes[toShortISO(date)] || {} +} + +export const mostAnsweredSelector = ( + state: SecureReduxState, + startDate: Moment, + endDate: Moment, +) => { + if (!s(state)[state.auth.user.id]) return {} + const dates = Object.keys(s(state)[state.auth.user.id].cards) + const filteredDates = dates.filter((item) => { + return ( + parseInt(item, 10) > parseInt(startDate.format('YYYYMMDD'), 10) && + parseInt(item, 10) <= parseInt(endDate.format('YYYYMMDD'), 10) + ) + }) + + // This creates an array of all the selected moods (now that there are multiple) + const moodsInDateRange = filteredDates.reduce((acc, filteredDate) => { + return acc.concat(s(state)[state.auth.user.id].cards[filteredDate].mood) + }, []) + + // This counts occurrences of each item + const moodCountedObject = _.countBy(moodsInDateRange, (mood) => mood) + + const bodyInDateRange = filteredDates.reduce((acc, filteredDate) => { + return acc.concat(s(state)[state.auth.user.id].cards[filteredDate].body) + }, []) + + const bodyCountedObject = _.countBy(bodyInDateRange, (body) => body) + + const activityInDateRange = filteredDates.reduce((acc, filteredDate) => { + return acc.concat(s(state)[state.auth.user.id].cards[filteredDate].activity) + }, []) + + const activityCountedObject = _.countBy(activityInDateRange, (activity) => activity) + + const flowInDateRange = filteredDates.reduce((acc, filteredDate) => { + return acc.concat(s(state)[state.auth.user.id].cards[filteredDate].flow) + }, []) + + const flowCountedObject = _.countBy(flowInDateRange, (flow) => flow) + + delete moodCountedObject.undefined + delete bodyCountedObject.undefined + delete activityCountedObject.undefined + delete flowCountedObject.undefined + + const highestMood = Object.keys(moodCountedObject).reduce( + (a, b) => (moodCountedObject[a] > moodCountedObject[b] ? a : b), + null, + ) + const highestBody = Object.keys(bodyCountedObject).reduce( + (a, b) => (bodyCountedObject[a] > bodyCountedObject[b] ? a : b), + null, + ) + const highestActivity = Object.keys(activityCountedObject).reduce( + (a, b) => (activityCountedObject[a] > activityCountedObject[b] ? a : b), + null, + ) + const highestFlow = Object.keys(flowCountedObject).reduce( + (a, b) => (flowCountedObject[a] > flowCountedObject[b] ? a : b), + null, + ) + + return { + mood: highestMood, + body: highestBody, + activity: highestActivity, + flow: highestFlow, + } +} diff --git a/packages/components/src/redux/secure/selectors/appSelectors.ts b/packages/components/src/redux/secure/selectors/appSelectors.ts new file mode 100644 index 000000000..bfac95510 --- /dev/null +++ b/packages/components/src/redux/secure/selectors/appSelectors.ts @@ -0,0 +1,35 @@ +import { SecureReduxState } from '../reducers' + +const s = (state: SecureReduxState) => state.app +const predictionS = (state: SecureReduxState) => state.prediction +export const currentLocaleSelector = (state: SecureReduxState) => s(state).locale + +export const currentChosenRegionSelector = (state: SecureReduxState) => s(state).chosenRegion + +export const currentThemeSelector = (state: SecureReduxState) => s(state).theme + +export const currentAvatarSelector = (state: SecureReduxState) => s(state).avatar + +export const hasOpenedSelector = (state: SecureReduxState) => s(state).hasOpened + +export const isTutorialOneActiveSelector = (state: SecureReduxState) => s(state).isTutorialOneActive + +export const isTutorialTwoActiveSelector = (state: SecureReduxState) => s(state).isTutorialTwoActive + +export const isTtsActiveSelector = (state: SecureReduxState) => s(state).isTtsActive + +export const isLoginPasswordActiveSelector = (state: SecureReduxState) => + s(state).isLoginPasswordActive + +export const currentAppVersion = (state: SecureReduxState) => s(state).appVersionName + +export const currentFirebaseToken = (state: SecureReduxState) => s(state).firebaseToken + +export const userVerifiedDates = (state: SecureReduxState) => s(state).verifiedDates + +// Smart precition selectors +export const isFuturePredictionSelector = (state: SecureReduxState) => predictionS(state) +export const isFuturePredictionActiveSelector = (state: SecureReduxState) => + predictionS(state)?.futurePredictionStatus + +// export const smartPredictedPeriods = (state: SecureReduxState) => s(state).predicted_periods diff --git a/packages/components/src/redux/secure/selectors/authSelectors.ts b/packages/components/src/redux/secure/selectors/authSelectors.ts new file mode 100644 index 000000000..f0e339d2a --- /dev/null +++ b/packages/components/src/redux/secure/selectors/authSelectors.ts @@ -0,0 +1,9 @@ +import { SecureReduxState } from '../reducers' + +const s = (state: SecureReduxState) => state.auth + +export const appTokenSelector = (state: SecureReduxState) => s(state).appToken + +export const authError = (state: SecureReduxState) => s(state).error + +export const currentUserSelector = (state: SecureReduxState) => s(state).user diff --git a/packages/components/src/redux/secure/selectors/contentSelectors.ts b/packages/components/src/redux/secure/selectors/contentSelectors.ts new file mode 100644 index 000000000..094d0d392 --- /dev/null +++ b/packages/components/src/redux/secure/selectors/contentSelectors.ts @@ -0,0 +1,113 @@ +import _ from 'lodash' +import { SecureReduxState } from '../reducers' + +const s = (state: SecureReduxState) => state.content + +export const allArticlesSelector = (state: SecureReduxState) => + s(state).articles.allIds.map((id) => s(state).articles.byId[id]) + +export const allVideosSelector = (state: SecureReduxState) => { + if (!s(state)?.videos?.allIds || !s(state)?.videos?.byId) return [] + return s(state).videos.allIds.map((id) => s(state).videos.byId[id]) +} + +export const articleByIDSelector = (state: SecureReduxState, id) => s(state).articles.byId[id] +export const videoByIDSelector = (state: SecureReduxState, id) => s(state)?.videos?.byId[id] + +export const articlesObjectByIDSelector = (state: SecureReduxState) => s(state).articles.byId + +// @ts-ignore +export const allHelpCentersForCurrentLocale: any = (state: SecureReduxState) => + s(state).helpCenters.filter((item) => item.lang === state.app.locale) + +export const allCategoriesSelector = (state: SecureReduxState) => + s(state).categories.allIds.map((id) => s(state).categories.byId[id]) + +export const allCategoryEmojis = (state: SecureReduxState) => { + const categories = allCategoriesSelector(state) + + return categories.map((item) => { + return { tag: item.tags.primary.name, emoji: item.tags.primary.emoji } + }) +} + +export const allSubCategoriesSelector = (state: SecureReduxState) => + s(state).subCategories.allIds.map((id) => s(state).subCategories.byId[id]) + +export const allSubCategoriesObjectSelector = (state: SecureReduxState) => + s(state).subCategories.byId + +export const categoryByIDSelector = (state: SecureReduxState, id) => s(state).categories.byId[id] + +export const subCategoryByIDSelector = (state: SecureReduxState, id) => + s(state).subCategories.byId[id] + +export const allAvatarText = (state: SecureReduxState) => s(state).avatarMessages + +export const privacyContent = (state: SecureReduxState) => s(state).privacyPolicy + +export const termsAndConditionsContent = (state: SecureReduxState) => s(state).termsAndConditions + +export const aboutContent = (state: SecureReduxState) => s(state).about + +export const allSurveys = (state: SecureReduxState) => s(state).allSurveys + +export const completedSurveys = (state: SecureReduxState) => s(state).completedSurveys + +export const aboutBanner = (state: SecureReduxState) => s(state).aboutBanner + +export const allQuizzesSelectors = (state: SecureReduxState) => { + // TODO: FIXME + const isUserYoungerThan15 = true + // moment() + // .utc() + // .diff(state.auth.user.dateOfBirth) < 15 + const tempArr = [] + const filteredArray = s(state).quizzes.allIds.reduce((acc, id) => { + if ( + (!s(state).quizzes.byId[id]?.isAgeRestricted && isUserYoungerThan15) || + !isUserYoungerThan15 + ) { + tempArr.push(s(state).quizzes.byId[id]) + } + if ( + (!s(state).quizzes.byId[id].isAgeRestricted && isUserYoungerThan15) || + !isUserYoungerThan15 + ) { + acc.push(s(state).quizzes.byId[id]) + } + return acc + }, []) + + // In the extreme event of all content being age restricted return the first quiz/ did you know instead of crashing the app + + if (_.isEmpty(filteredArray)) { + return [s(state).quizzes.byId[s(state).quizzes.allIds[0]]] + } + + return filteredArray +} + +export const allDidYouKnowsSelectors = (state: SecureReduxState) => { + // TODO_ALEX: FIXME + const isUserYoungerThan15 = true + // moment() + // .utc() + // .diff(state.auth.user.dateOfBirth) < 15 + const filteredArray = s(state).didYouKnows.allIds.reduce((acc, id) => { + if ( + (!s(state).didYouKnows.byId[id]?.isAgeRestricted && isUserYoungerThan15) || + !isUserYoungerThan15 + ) { + acc.push(s(state).didYouKnows.byId[id]) + } + return acc + }, []) + + // In the extreme event of all content being age restricted return the first quiz/ did you know instead of crashing the app + if (_.isEmpty(filteredArray)) { + return [s(state).didYouKnows.byId[s(state).didYouKnows.allIds[0]]] + } + + return filteredArray +} diff --git a/packages/components/src/redux/secure/selectors/index.ts b/packages/components/src/redux/secure/selectors/index.ts new file mode 100644 index 000000000..3620d791d --- /dev/null +++ b/packages/components/src/redux/secure/selectors/index.ts @@ -0,0 +1,13 @@ +import * as analyticsSelectors from './analyticsSelectors' +import * as answerSelectors from './answerSelectors' +import * as appSelectors from './appSelectors' +import * as authSelectors from './authSelectors' +import * as contentSelectors from './contentSelectors' + +export const secureSelectors = { + ...analyticsSelectors, + ...answerSelectors, + ...appSelectors, + ...authSelectors, + ...contentSelectors, +} diff --git a/packages/components/src/redux/secure/sync/index.ts b/packages/components/src/redux/secure/sync/index.ts new file mode 100644 index 000000000..2e81c498c --- /dev/null +++ b/packages/components/src/redux/secure/sync/index.ts @@ -0,0 +1,42 @@ +import _ from 'lodash' +import { Action, Reducer, ReducersMapObject } from 'redux' + +function syncReducerFactory(innerReducer: Reducer, reducerName: string): Reducer { + return (state, action) => { + if (action.type === 'REFRESH_STORE' && action.payload[reducerName]) { + return { + ...state, + ...action.payload[reducerName], + } + } + + return innerReducer(state, action) + } +} + +export function syncReducers( + reducers: ReducersMapObject, + reducerNames: string[], +) { + const reducerKeys = Object.keys(reducers) + const reducersWrappers = reducerNames.reduce((prev, reducerName) => { + if (!reducerKeys.includes(reducerName)) { + return prev + } + + const reducer = reducers[reducerName] + return { + ...prev, + [reducerName]: syncReducerFactory(reducer, reducerName), + } + }, {}) + + return { + ...reducers, + ...reducersWrappers, + } +} + +export function extractReducerState(state, reducerNames: string[]) { + return _.pick(state, ...reducerNames) +} diff --git a/packages/components/src/redux/secure/types/index.ts b/packages/components/src/redux/secure/types/index.ts new file mode 100644 index 000000000..bf232373f --- /dev/null +++ b/packages/components/src/redux/secure/types/index.ts @@ -0,0 +1,11 @@ +import { secureActions } from '../actions' +import { ActionsUnion, ActionsOfType } from '../../types' + +export type SecureActions = ActionsUnion + +export type SecureActionTypes = SecureActions[keyof SecureActions] + +export type ExtractActionFromActionType = ActionsOfType< + SecureActions, + ActionType +> diff --git a/packages/components/src/redux/secure/useSecureSelector.ts b/packages/components/src/redux/secure/useSecureSelector.ts new file mode 100644 index 000000000..48a131f28 --- /dev/null +++ b/packages/components/src/redux/secure/useSecureSelector.ts @@ -0,0 +1,4 @@ +import { useSelector as useReduxSelector, TypedUseSelectorHook } from 'react-redux' +import { SecureReduxState } from './reducers' + +export const useSecureSelector: TypedUseSelectorHook = useReduxSelector From fb968fee2f4e273159b7de0578aca4723586ec1d Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 22 Dec 2023 13:12:13 +0700 Subject: [PATCH 11/83] Rename commonRootSaga --- packages/components/src/redux/common/sagas/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/redux/common/sagas/index.ts b/packages/components/src/redux/common/sagas/index.ts index 9617fcd84..43d5b5a85 100644 --- a/packages/components/src/redux/common/sagas/index.ts +++ b/packages/components/src/redux/common/sagas/index.ts @@ -6,7 +6,7 @@ import { authSaga } from './authSaga' import { contentSaga } from './contentSaga' import { smartPredictionbSaga } from './smartPredictionSaga' -export function* rootSaga() { +export function* commonRootSaga() { yield all([ fork(analyticsSaga), fork(appSaga), From 29c0af8b1877f84f745c5ddb5d3cdeee50d3e955 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 22 Dec 2023 13:12:28 +0700 Subject: [PATCH 12/83] Move commonStore config --- packages/components/src/components/App.tsx | 13 ++----------- packages/components/src/redux/common/commonStore.ts | 13 +++++++++++++ 2 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 packages/components/src/redux/common/commonStore.ts diff --git a/packages/components/src/components/App.tsx b/packages/components/src/components/App.tsx index a6bcf6192..bf9f7fa98 100644 --- a/packages/components/src/components/App.tsx +++ b/packages/components/src/components/App.tsx @@ -9,16 +9,7 @@ import { SafeAreaView } from 'react-navigation' import SplashScreen from 'react-native-splash-screen' import { Platform } from 'react-native' import Orientation from 'react-native-orientation-locker' -import { config } from '../redux/config' -import { rootReducer } from '../redux/common/reducers' -import { rootSaga } from '../redux/common/sagas' - -const { persistor, store } = configureStore({ - key: 'primary', - secretKey: config.REDUX_ENCRYPT_KEY, - rootReducer, - rootSaga, -}) +import { commonPersistor, commonStore } from '../redux/common/commonStore' export default function App() { React.useEffect(() => { @@ -36,7 +27,7 @@ export default function App() { }, []) return ( - + Date: Fri, 22 Dec 2023 13:47:01 +0700 Subject: [PATCH 13/83] Fix misc imports --- packages/components/src/components/common/DateBadge.tsx | 2 +- packages/components/src/components/common/DayBadge.tsx | 2 +- packages/components/src/redux/common/sagas/analyticsSaga.ts | 4 ++-- packages/components/src/redux/secure/sagas/analyticsSaga.ts | 4 ++-- packages/components/src/screens/SplashScreen.tsx | 3 ++- packages/components/src/screens/mainScreen/Calendar.tsx | 2 +- .../src/screens/mainScreen/wheelCarousel/CircularElement.tsx | 2 +- 7 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/components/src/components/common/DateBadge.tsx b/packages/components/src/components/common/DateBadge.tsx index b0670f375..635cb9ccb 100644 --- a/packages/components/src/components/common/DateBadge.tsx +++ b/packages/components/src/components/common/DateBadge.tsx @@ -6,12 +6,12 @@ import { translate } from '../../i18n' import { TouchableOpacity } from 'react-native' import _ from 'lodash' import moment from 'moment' -import { useCommonSelector } from 'react-redux' import { commonSelectors } from '../../redux/common/selectors' import { useTodayPrediction, useActualCurrentStartDateSelector, } from '../../components/context/PredictionProvider' +import { useCommonSelector } from '../../redux/common/useCommonSelector' function checkForVerifiedDay(cardValues) { if (_.has(cardValues, 'periodDay')) { diff --git a/packages/components/src/components/common/DayBadge.tsx b/packages/components/src/components/common/DayBadge.tsx index c95a8a920..a049fe350 100644 --- a/packages/components/src/components/common/DayBadge.tsx +++ b/packages/components/src/components/common/DayBadge.tsx @@ -4,12 +4,12 @@ import styled from 'styled-components/native' import { TextWithoutTranslation, Text } from './Text' import _ from 'lodash' import moment from 'moment' -import { useCommonSelector } from 'react-redux' import { commonSelectors } from '../../redux/common/selectors' import { useTodayPrediction, useActualCurrentStartDateSelector, } from '../../components/context/PredictionProvider' +import { useCommonSelector } from '../../redux/common/useCommonSelector' function checkForVerifiedDay(cardValues) { if (_.has(cardValues, 'periodDay')) { diff --git a/packages/components/src/redux/common/sagas/analyticsSaga.ts b/packages/components/src/redux/common/sagas/analyticsSaga.ts index 9f4bca907..cb61d7f13 100644 --- a/packages/components/src/redux/common/sagas/analyticsSaga.ts +++ b/packages/components/src/redux/common/sagas/analyticsSaga.ts @@ -5,9 +5,9 @@ import { fetchNetworkConnectionStatus } from '../../../services/network' import { httpClient } from '../../../services/HttpClient' import { commonActions } from '../actions' import { commonSelectors } from '../selectors' -import { ActionTypes } from '../types' +import { CommonActionTypes } from '../types' -const ACTIONS_TO_TRACK: ActionTypes[] = [ +const ACTIONS_TO_TRACK: CommonActionTypes[] = [ // app 'SET_THEME', 'SET_LOCALE', diff --git a/packages/components/src/redux/secure/sagas/analyticsSaga.ts b/packages/components/src/redux/secure/sagas/analyticsSaga.ts index 98d1d6c9b..63c4977cf 100644 --- a/packages/components/src/redux/secure/sagas/analyticsSaga.ts +++ b/packages/components/src/redux/secure/sagas/analyticsSaga.ts @@ -5,9 +5,9 @@ import { fetchNetworkConnectionStatus } from '../../../services/network' import { httpClient } from '../../../services/HttpClient' import { secureActions } from '../actions' import { secureSelectors } from '../selectors' -import { ActionTypes } from '../types' +import { SecureActionTypes } from '../types' -const ACTIONS_TO_TRACK: ActionTypes[] = [ +const ACTIONS_TO_TRACK: SecureActionTypes[] = [ // app 'SET_THEME', 'SET_LOCALE', diff --git a/packages/components/src/screens/SplashScreen.tsx b/packages/components/src/screens/SplashScreen.tsx index 66fc73e97..61d94aaa3 100644 --- a/packages/components/src/screens/SplashScreen.tsx +++ b/packages/components/src/screens/SplashScreen.tsx @@ -3,7 +3,7 @@ import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { PageContainer } from '../components/layout/PageContainer' import { assets } from '../assets/index' import styled from 'styled-components/native' -import { useCommonSelector, useDispatch } from 'react-redux' +import { useDispatch } from 'react-redux' import { commonSelectors } from '../redux/common/selectors' import { commonActions } from '../redux/common/actions' import { navigateAndReset } from '../services/navigationService' @@ -15,6 +15,7 @@ import { useAlert } from '../components/context/AlertContext' import { httpClient } from '../services/HttpClient' import { fetchNetworkConnectionStatus } from '../services/network' import messaging from '@react-native-firebase/messaging' +import { useCommonSelector } from '../redux/common/useCommonSelector' export function SplashScreen() { const dispatch = useDispatch() diff --git a/packages/components/src/screens/mainScreen/Calendar.tsx b/packages/components/src/screens/mainScreen/Calendar.tsx index 498004207..c0a3333d7 100644 --- a/packages/components/src/screens/mainScreen/Calendar.tsx +++ b/packages/components/src/screens/mainScreen/Calendar.tsx @@ -21,8 +21,8 @@ import { assets } from '../../assets' import { translate } from '../../i18n' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { calendarScreenSpeech } from '../../config' -import { useCommonSelector } from 'react-redux' import { commonSelectors } from '../../redux/common/selectors' +import { useCommonSelector } from '../../redux/common/useCommonSelector' const width = Dimensions.get('window').width const height = Dimensions.get('window').height diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx index 48d0f8f35..593995520 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx @@ -6,7 +6,6 @@ import { useTheme } from '../../../components/context/ThemeContext' import { TouchableOpacity } from 'react-native-gesture-handler' import { assets } from '../../../assets/index' import { translate } from '../../../i18n' -import { useCommonSelector } from 'react-redux' import { commonSelectors } from '../../../redux/common/selectors' import moment from 'moment' import { @@ -14,6 +13,7 @@ import { useActualCurrentStartDateSelector, } from '../../../components/context/PredictionProvider' import _ from 'lodash' +import { useCommonSelector } from '../../../redux/common/useCommonSelector' const { Value, From 8e3077609c541624cf232270b1a643077e9077bf Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 22 Dec 2023 14:56:47 +0700 Subject: [PATCH 14/83] Rename secureRootSaga --- packages/components/src/redux/secure/sagas/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/redux/secure/sagas/index.ts b/packages/components/src/redux/secure/sagas/index.ts index 9617fcd84..9d3739975 100644 --- a/packages/components/src/redux/secure/sagas/index.ts +++ b/packages/components/src/redux/secure/sagas/index.ts @@ -6,7 +6,7 @@ import { authSaga } from './authSaga' import { contentSaga } from './contentSaga' import { smartPredictionbSaga } from './smartPredictionSaga' -export function* rootSaga() { +export function* secureRootSaga() { yield all([ fork(analyticsSaga), fork(appSaga), From ac772e170a6bcc10fdae9aa4a3ec1b27aa6e7b11 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 22 Dec 2023 15:40:15 +0700 Subject: [PATCH 15/83] Add StoreCoordinator --- packages/components/src/components/App.tsx | 4 +- .../components/src/components/AppProvider.tsx | 33 ++++---- .../components/src/redux/StoreCoordinator.tsx | 78 +++++++++++++++++++ 3 files changed, 94 insertions(+), 21 deletions(-) create mode 100644 packages/components/src/redux/StoreCoordinator.tsx diff --git a/packages/components/src/components/App.tsx b/packages/components/src/components/App.tsx index bf9f7fa98..c96135402 100644 --- a/packages/components/src/components/App.tsx +++ b/packages/components/src/components/App.tsx @@ -2,14 +2,12 @@ import 'react-native-get-random-values' // Required for uuid package import React from 'react' import { AppProvider } from './AppProvider' import AppNavigator from '../navigators/AppNavigator' -import { configureStore } from '../redux/store' import { setTopLevelNavigator } from '../services/navigationService' import { notificationListener } from '../services/notifications' import { SafeAreaView } from 'react-navigation' import SplashScreen from 'react-native-splash-screen' import { Platform } from 'react-native' import Orientation from 'react-native-orientation-locker' -import { commonPersistor, commonStore } from '../redux/common/commonStore' export default function App() { React.useEffect(() => { @@ -27,7 +25,7 @@ export default function App() { }, []) return ( - + ( - - - - - - - - {children} - - - - - - - +export const AppProvider = ({ children }) => ( + + + + + + + {children} + + + + + + ) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx new file mode 100644 index 000000000..bed7751e5 --- /dev/null +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -0,0 +1,78 @@ +import React from 'react' +import { Provider as ReduxProvider } from 'react-redux' +import { PersistGate } from 'redux-persist/integration/react' +import { configureStore } from './store' +import { config } from './config' +import { commonRootReducer } from './common/reducers' +import { commonRootSaga } from './common/sagas' +import { secureRootReducer } from './secure/reducers' +import { secureRootSaga } from './secure/sagas' + +interface Keys { + key: string + secretKey: string +} + +interface Context { + switchStore: (keys: Keys) => void + switchToCommonStore: () => void +} + +const StoreCoordinatorContext = React.createContext({ + switchStore: () => { + // + }, + switchToCommonStore: () => { + // + }, +}) + +const commonStore = configureStore({ + key: 'primary', + secretKey: config.REDUX_ENCRYPT_KEY, + rootReducer: commonRootReducer, + rootSaga: commonRootSaga, +}) + +export function StoreCoordinator({ children }) { + const [{ persistor, store }, setStore] = React.useState(commonStore) + + const switchToCommonStore = () => { + setStore(commonStore) + } + + const switchStore = ({ key, secretKey }: Keys) => { + setStore( + configureStore({ + key, + secretKey, + rootReducer: secureRootReducer, + rootSaga: secureRootSaga, + }), + ) + } + + return ( + + + + {children} + + + + ) +} + +export function useStoreCoordinator() { + const storeCoordinatorContext = React.useContext(StoreCoordinatorContext) + if (storeCoordinatorContext === undefined) { + throw new Error(`useStoreCoordinator must be used within a StoreCoordinator`) + } + + return storeCoordinatorContext +} From af6f3d159b160e994d4a42ef1c5a2a6efc53c302 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 22 Dec 2023 16:51:59 +0700 Subject: [PATCH 16/83] Prevent crash on store change --- packages/components/src/config/speech.ts | 47 ++++++++++--------- .../components/src/screens/MainScreen.tsx | 2 +- .../components/src/screens/ProfileScreen.tsx | 3 +- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/packages/components/src/config/speech.ts b/packages/components/src/config/speech.ts index 386e51336..6304d747a 100644 --- a/packages/components/src/config/speech.ts +++ b/packages/components/src/config/speech.ts @@ -80,28 +80,31 @@ export const profileScreenSpeech = ({ dateOfBirth, selectedAvatar, theme, -}) => [ - translate('arrow_button'), - translate('profile'), - translate('name'), - currentUser.name, - translate('age'), - translate(dateOfBirth.format('MMM')) + ' ' + dateOfBirth.format('YYYY'), - translate('gender'), - translate(currentUser.gender), - translate('green_btn_with_two_arrows'), - translate('location'), - translate(currentUser.location), - translate('green_btn_with_two_arrows'), - translate('cycle_length'), - todayInfo.cycleLength.toString() + translate('days'), - translate('period_length'), - todayInfo?.periodLength?.toString() + translate('days'), - // translate(`selected_avatar`), - translate(selectedAvatar), - // translate('selected_theme'), - translate(theme), -] +}) => { + if (!currentUser) return [translate('arrow_button'), translate('profile')] + return [ + translate('arrow_button'), + translate('profile'), + translate('name'), + currentUser.name, + translate('age'), + translate(dateOfBirth.format('MMM')) + ' ' + dateOfBirth.format('YYYY'), + translate('gender'), + translate(currentUser.gender), + translate('green_btn_with_two_arrows'), + translate('location'), + translate(currentUser.location), + translate('green_btn_with_two_arrows'), + translate('cycle_length'), + todayInfo.cycleLength.toString() + translate('days'), + translate('period_length'), + todayInfo?.periodLength?.toString() + translate('days'), + // translate(`selected_avatar`), + translate(selectedAvatar), + // translate('selected_theme'), + translate(theme), + ] +} export const settingsScreenText = ({ hasTtsActive }) => [ translate('settings'), diff --git a/packages/components/src/screens/MainScreen.tsx b/packages/components/src/screens/MainScreen.tsx index 9baf46199..c6ac9d1cf 100644 --- a/packages/components/src/screens/MainScreen.tsx +++ b/packages/components/src/screens/MainScreen.tsx @@ -44,7 +44,7 @@ const MainScreenContainer = ({ navigation }) => { const theme = useTheme() const todayInfo = useTodayPrediction() const dispatch = useDispatch() - const userID = useCommonSelector(commonSelectors.currentUserSelector).id + const userID = useCommonSelector(commonSelectors.currentUserSelector)?.id const fullState = useFullState() const history = useHistoryPrediction() const currentUser = useCommonSelector(commonSelectors.currentUserSelector) diff --git a/packages/components/src/screens/ProfileScreen.tsx b/packages/components/src/screens/ProfileScreen.tsx index d47d67f61..df9d87b4f 100644 --- a/packages/components/src/screens/ProfileScreen.tsx +++ b/packages/components/src/screens/ProfileScreen.tsx @@ -12,7 +12,6 @@ import { useTheme } from '../components/context/ThemeContext' import { CycleCard } from './profileScreen/CycleCard' import { FlatList } from 'react-native' import { navigate } from '../services/navigationService' -import { toAge } from '../services/dateUtils' import { PrimaryButton } from '../components/common/buttons/PrimaryButton' import { AvatarOption } from './avatarAndTheme/avatarSelect/AvatarOption' import { ThemeSelectItem } from './avatarAndTheme/ThemeSelectItem' @@ -40,7 +39,7 @@ export function ProfileScreen({ navigation }) { const dispatch = useDispatch() const connectAccountCount = useCommonSelector((state) => state.auth.connectAccountAttempts) - const dateOfBirth = moment(currentUser.dateOfBirth) + const dateOfBirth = moment(currentUser?.dateOfBirth) useTextToSpeechHook({ navigation, From dcdbbda7dfb5e378ef281bb1d9eeb1bf989a35a8 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Wed, 27 Dec 2023 13:02:50 +0700 Subject: [PATCH 17/83] Add sha256 hashing --- packages/components/package.json | 3 ++- packages/components/src/services/hash.ts | 5 +++++ yarn.lock | 5 +++++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 packages/components/src/services/hash.ts diff --git a/packages/components/package.json b/packages/components/package.json index 60bbea4d3..44dfe2f47 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -44,7 +44,8 @@ "tslint-react": "^4.0.0", "typescript": "4.7.4", "uuid": "9.0.0", - "react-native-get-random-values": "1.9.0" + "react-native-get-random-values": "1.9.0", + "js-sha256": "0.10.1" }, "devDependencies": { "@types/lodash": "^4.14.136", diff --git a/packages/components/src/services/hash.ts b/packages/components/src/services/hash.ts new file mode 100644 index 000000000..407086058 --- /dev/null +++ b/packages/components/src/services/hash.ts @@ -0,0 +1,5 @@ +import { sha256 } from 'js-sha256' + +export const hash = (str: string) => { + return sha256(str) +} diff --git a/yarn.lock b/yarn.lock index f5f711213..8bd72babe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7126,6 +7126,11 @@ jose@^4.14.6: resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.4.tgz#02a9a763803e3872cf55f29ecef0dfdcc218cc03" integrity sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ== +js-sha256@0.10.1: + version "0.10.1" + resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.10.1.tgz#b40104ba1368e823fdd5f41b66b104b15a0da60d" + integrity sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" From 127889a5d2c791166e0b0e30a2c5f705a28fcec6 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Thu, 28 Dec 2023 13:40:51 +0700 Subject: [PATCH 18/83] Multiple offline users WIP --- .../src/redux/common/actions/authActions.ts | 4 +- .../redux/common/reducers/accessReducer.ts | 56 +++++++++++++++++++ .../src/redux/common/reducers/authReducer.ts | 2 +- .../src/redux/common/reducers/index.ts | 4 +- .../src/redux/common/sagas/authSaga.ts | 46 ++++++++++++++- 5 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 packages/components/src/redux/common/reducers/accessReducer.ts diff --git a/packages/components/src/redux/common/actions/authActions.ts b/packages/components/src/redux/common/actions/authActions.ts index 6d3c15eb8..b7bf6d743 100644 --- a/packages/components/src/redux/common/actions/authActions.ts +++ b/packages/components/src/redux/common/actions/authActions.ts @@ -36,7 +36,7 @@ export function loginSuccess({ }) } -export function loginSuccessAsGuestAccount({ +export function createGuestAccountSuccess({ id, name, dateOfBirth, @@ -48,7 +48,7 @@ export function loginSuccessAsGuestAccount({ secretQuestion, secretAnswer, }) { - return createAction('LOGIN_SUCCESS_AS_GUEST_ACCOUNT', { + return createAction('CREATE_GUEST_ACCOUNT_SUCCESS', { id, name, dateOfBirth, diff --git a/packages/components/src/redux/common/reducers/accessReducer.ts b/packages/components/src/redux/common/reducers/accessReducer.ts new file mode 100644 index 000000000..746b4a5f9 --- /dev/null +++ b/packages/components/src/redux/common/reducers/accessReducer.ts @@ -0,0 +1,56 @@ +import { hash } from '../../../services/hash' +import { CommonActions } from '../types' +import { v4 as uuidv4 } from 'uuid' + +export interface AccessState { + credentials: { + [usernameHash: string]: { + passwordSalt: string + } + } + lastLoggedInUsername?: string +} + +const initialState: AccessState = { + credentials: {}, + lastLoggedInUsername: undefined, +} + +export function accessReducer(state = initialState, action: CommonActions): AccessState { + switch (action.type) { + case 'CREATE_ACCOUNT_SUCCESS': { + const usernameHash = hash(action.payload.user.name) + const passwordSalt = uuidv4() + + return { + ...state, + lastLoggedInUsername: action.payload.user.name, + credentials: { + ...state.credentials, + [usernameHash]: { + passwordSalt, + }, + }, + } + } + + case 'CREATE_GUEST_ACCOUNT_SUCCESS': { + const usernameHash = hash(action.payload.name) + const passwordSalt = uuidv4() + + return { + ...state, + lastLoggedInUsername: action.payload.name, + credentials: { + ...state.credentials, + [usernameHash]: { + passwordSalt, + }, + }, + } + } + + default: + return state + } +} diff --git a/packages/components/src/redux/common/reducers/authReducer.ts b/packages/components/src/redux/common/reducers/authReducer.ts index 62da89589..df4a61c7f 100644 --- a/packages/components/src/redux/common/reducers/authReducer.ts +++ b/packages/components/src/redux/common/reducers/authReducer.ts @@ -78,7 +78,7 @@ export function authReducer( }, } - case 'LOGIN_SUCCESS_AS_GUEST_ACCOUNT': + case 'CREATE_GUEST_ACCOUNT_SUCCESS': return { ...state, appToken: null, diff --git a/packages/components/src/redux/common/reducers/index.ts b/packages/components/src/redux/common/reducers/index.ts index cf3c79afa..0742a9507 100644 --- a/packages/components/src/redux/common/reducers/index.ts +++ b/packages/components/src/redux/common/reducers/index.ts @@ -9,12 +9,14 @@ import { appReducer } from './appReducer' import { authReducer } from './authReducer' import { contentReducer } from './contentReducer' import { predictionReducer } from './predictionReducer' +import { accessReducer } from './accessReducer' export const exportReducerNames = ['app', 'prediction'] const reducer = combineReducers( syncReducers( { + access: accessReducer, analytics: analyticsReducer, answer: answerReducer, app: appReducer, @@ -31,7 +33,7 @@ export function commonRootReducer(state, action: CommonActions) { switch (action.type) { case 'LOGOUT': // @ts-ignore - return reducer(_.pick(state, 'app', 'content', 'answer'), action) + return reducer(_.pick(state, 'app', 'content', 'answer', 'access'), action) default: return reducer(state, action) diff --git a/packages/components/src/redux/common/sagas/authSaga.ts b/packages/components/src/redux/common/sagas/authSaga.ts index 2e69e73ce..0aaad385e 100644 --- a/packages/components/src/redux/common/sagas/authSaga.ts +++ b/packages/components/src/redux/common/sagas/authSaga.ts @@ -12,6 +12,7 @@ import { PredictionState } from '../../../prediction' import moment from 'moment' import { closeOutTTs } from '../../../services/textToSpeech' import { fetchNetworkConnectionStatus } from '../../../services/network' +import { hash } from '../../../services/hash' // unwrap promise type Await = T extends Promise ? U : T @@ -121,6 +122,37 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { errorMessage = error.response.data.message } } + + // Attempt offline login + const usernameHash = hash(name) + const credentials = yield select((s) => s.access.credentials) + const credential = credentials[usernameHash] + + if (credential) { + // ??????????????? + // Switch stores here ? + // Change boolean to trigger switch in coordinator ? + // Somehow need to pass the keys to the coordinator though + // yield put( + // commonActions.loginOfflineSuccess({ + // appToken: null, + // user: { + // id: user.id, + // name, + // dateOfBirth: user.dateOfBirth, + // gender: user.gender, + // location: user.location, + // country: user.country, + // province: user.province, + // secretQuestion: user.secretQuestion, + // secretAnswer: user.secretAnswer, + // password, + // }, + // }), + // ) + // return + } + yield put( commonActions.loginFailure({ error: errorMessage, @@ -184,8 +216,19 @@ function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACC yield put(commonActions.setAuthError({ error: errorStatusCode })) yield put(commonActions.createAccountFailure()) + // Check username is not already taken + const usernameHash = hash(name) + const credentials = yield select((s) => s.access.credentials) + const credential = credentials[usernameHash] + + if (credential) { + // username already taken + // yield put(secureActions.setAuthError({ error: errorStatusCode })) + return + } + yield put( - commonActions.loginSuccessAsGuestAccount({ + commonActions.createGuestAccountSuccess({ id: id || uuidv4(), name, dateOfBirth, @@ -221,6 +264,7 @@ function* onCreateAccountSuccess(action: ExtractActionFromActionType<'CREATE_ACC }), ) } + function* onDeleteAccountRequest(action: ExtractActionFromActionType<'DELETE_ACCOUNT_REQUEST'>) { const { setLoading } = action.payload const state: CommonReduxState = yield select() From 6dd0b7e8fcbcb251e6d8530f7c8ac2777445877a Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 29 Dec 2023 13:20:46 +0700 Subject: [PATCH 19/83] Store coordination POC --- .../components/src/redux/StoreCoordinator.tsx | 54 +++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index bed7751e5..b0f557661 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -5,8 +5,7 @@ import { configureStore } from './store' import { config } from './config' import { commonRootReducer } from './common/reducers' import { commonRootSaga } from './common/sagas' -import { secureRootReducer } from './secure/reducers' -import { secureRootSaga } from './secure/sagas' +import { REHYDRATE } from 'redux-persist' interface Keys { key: string @@ -37,6 +36,12 @@ const commonStore = configureStore({ export function StoreCoordinator({ children }) { const [{ persistor, store }, setStore] = React.useState(commonStore) + const hasTimedOut = React.useRef(false) + + const [state, setState] = React.useState(undefined) + const [shouldSwitch, setShouldSwitch] = React.useState(false) + const [shouldMigrate, setShouldMigrate] = React.useState(false) + const switchToCommonStore = () => { setStore(commonStore) } @@ -46,12 +51,53 @@ export function StoreCoordinator({ children }) { configureStore({ key, secretKey, - rootReducer: secureRootReducer, - rootSaga: secureRootSaga, + rootReducer: commonRootReducer, + rootSaga: commonRootSaga, }), ) } + // ===== Step 1: Get the current state ===== // + React.useEffect(() => { + if (hasTimedOut.current) { + return + } + + hasTimedOut.current = true + + setTimeout(() => { + const commonState = store.getState() + setState(commonState) + setShouldSwitch(true) + }, 20000) + }) + + // ===== Step 2: Switch stores ===== // + React.useEffect(() => { + if (!shouldSwitch) { + return + } + + switchStore({ + key: 'test6', + secretKey: 'test6', + }) + + setShouldMigrate(true) + }, [shouldSwitch]) + + // ===== Step 3: Migrate state ===== // + React.useEffect(() => { + if (!shouldMigrate) { + return + } + + store.dispatch({ type: REHYDRATE, payload: state }) + + setShouldSwitch(false) + setShouldMigrate(false) + }, [shouldMigrate]) + return ( Date: Fri, 29 Dec 2023 14:18:17 +0700 Subject: [PATCH 20/83] Switch store on key change POC --- .../components/src/redux/StoreCoordinator.tsx | 32 +++++++++++++------ .../src/redux/common/actions/authActions.ts | 4 +++ .../redux/common/reducers/accessReducer.ts | 14 ++++++++ 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index b0f557661..1f71d97d4 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -6,6 +6,7 @@ import { config } from './config' import { commonRootReducer } from './common/reducers' import { commonRootSaga } from './common/sagas' import { REHYDRATE } from 'redux-persist' +import { commonActions } from './common/actions' interface Keys { key: string @@ -39,6 +40,7 @@ export function StoreCoordinator({ children }) { const hasTimedOut = React.useRef(false) const [state, setState] = React.useState(undefined) + const [keys, setKeys] = React.useState(undefined) const [shouldSwitch, setShouldSwitch] = React.useState(false) const [shouldMigrate, setShouldMigrate] = React.useState(false) @@ -57,7 +59,6 @@ export function StoreCoordinator({ children }) { ) } - // ===== Step 1: Get the current state ===== // React.useEffect(() => { if (hasTimedOut.current) { return @@ -66,23 +67,35 @@ export function StoreCoordinator({ children }) { hasTimedOut.current = true setTimeout(() => { - const commonState = store.getState() - setState(commonState) - setShouldSwitch(true) + store.dispatch(commonActions.setStoreKeys({ key: 'test', secretKey: 'test' })) }, 20000) }) + // ===== Step 1: Detect key change ===== // + React.useEffect(() => { + const unsubscribe = store.subscribe(() => { + const commonState = store.getState() + // TODO: + // @ts-ignore + const currentKeys = commonState.access.keys + + if (currentKeys && currentKeys !== keys) { + setKeys(currentKeys) + setState(commonState) + setShouldSwitch(true) + } + }) + + return () => unsubscribe() + }, [store]) + // ===== Step 2: Switch stores ===== // React.useEffect(() => { if (!shouldSwitch) { return } - switchStore({ - key: 'test6', - secretKey: 'test6', - }) - + switchStore(keys) setShouldMigrate(true) }, [shouldSwitch]) @@ -96,6 +109,7 @@ export function StoreCoordinator({ children }) { setShouldSwitch(false) setShouldMigrate(false) + setState(undefined) }, [shouldMigrate]) return ( diff --git a/packages/components/src/redux/common/actions/authActions.ts b/packages/components/src/redux/common/actions/authActions.ts index b7bf6d743..32997b353 100644 --- a/packages/components/src/redux/common/actions/authActions.ts +++ b/packages/components/src/redux/common/actions/authActions.ts @@ -36,6 +36,10 @@ export function loginSuccess({ }) } +export function setStoreKeys(payload: { key: string; secretKey: string }) { + return createAction('SET_STORE_KEYS', payload) +} + export function createGuestAccountSuccess({ id, name, diff --git a/packages/components/src/redux/common/reducers/accessReducer.ts b/packages/components/src/redux/common/reducers/accessReducer.ts index 746b4a5f9..e413a7935 100644 --- a/packages/components/src/redux/common/reducers/accessReducer.ts +++ b/packages/components/src/redux/common/reducers/accessReducer.ts @@ -1,6 +1,7 @@ import { hash } from '../../../services/hash' import { CommonActions } from '../types' import { v4 as uuidv4 } from 'uuid' +import _ from 'lodash' export interface AccessState { credentials: { @@ -9,11 +10,18 @@ export interface AccessState { } } lastLoggedInUsername?: string + keys: + | { + key: string + secretKey: string + } + | undefined } const initialState: AccessState = { credentials: {}, lastLoggedInUsername: undefined, + keys: undefined, } export function accessReducer(state = initialState, action: CommonActions): AccessState { @@ -50,6 +58,12 @@ export function accessReducer(state = initialState, action: CommonActions): Acce } } + case 'SET_STORE_KEYS': + return { + ...state, + keys: action.payload, + } + default: return state } From 4908afa77318ea8c036eede31e7143dbb9ba4a93 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 1 Jan 2024 20:26:12 +0800 Subject: [PATCH 21/83] Trigger store change via PasswordRequestScreen --- .../components/src/redux/StoreCoordinator.tsx | 19 +++---------- .../redux/common/reducers/accessReducer.ts | 13 --------- .../src/redux/common/reducers/index.ts | 2 ++ .../src/redux/common/reducers/keysReducer.ts | 28 +++++++++++++++++++ packages/components/src/redux/store.ts | 2 +- .../components/src/screens/MainScreen.tsx | 1 + .../src/screens/PasswordRequestScreen.tsx | 11 ++++++++ 7 files changed, 47 insertions(+), 29 deletions(-) create mode 100644 packages/components/src/redux/common/reducers/keysReducer.ts diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index 1f71d97d4..2889573ee 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -37,8 +37,6 @@ const commonStore = configureStore({ export function StoreCoordinator({ children }) { const [{ persistor, store }, setStore] = React.useState(commonStore) - const hasTimedOut = React.useRef(false) - const [state, setState] = React.useState(undefined) const [keys, setKeys] = React.useState(undefined) const [shouldSwitch, setShouldSwitch] = React.useState(false) @@ -49,6 +47,9 @@ export function StoreCoordinator({ children }) { } const switchStore = ({ key, secretKey }: Keys) => { + if (!key || !secretKey) { + return + } setStore( configureStore({ key, @@ -59,25 +60,13 @@ export function StoreCoordinator({ children }) { ) } - React.useEffect(() => { - if (hasTimedOut.current) { - return - } - - hasTimedOut.current = true - - setTimeout(() => { - store.dispatch(commonActions.setStoreKeys({ key: 'test', secretKey: 'test' })) - }, 20000) - }) - // ===== Step 1: Detect key change ===== // React.useEffect(() => { const unsubscribe = store.subscribe(() => { const commonState = store.getState() // TODO: // @ts-ignore - const currentKeys = commonState.access.keys + const currentKeys = commonState?.keys.keys if (currentKeys && currentKeys !== keys) { setKeys(currentKeys) diff --git a/packages/components/src/redux/common/reducers/accessReducer.ts b/packages/components/src/redux/common/reducers/accessReducer.ts index e413a7935..90840c512 100644 --- a/packages/components/src/redux/common/reducers/accessReducer.ts +++ b/packages/components/src/redux/common/reducers/accessReducer.ts @@ -10,18 +10,11 @@ export interface AccessState { } } lastLoggedInUsername?: string - keys: - | { - key: string - secretKey: string - } - | undefined } const initialState: AccessState = { credentials: {}, lastLoggedInUsername: undefined, - keys: undefined, } export function accessReducer(state = initialState, action: CommonActions): AccessState { @@ -58,12 +51,6 @@ export function accessReducer(state = initialState, action: CommonActions): Acce } } - case 'SET_STORE_KEYS': - return { - ...state, - keys: action.payload, - } - default: return state } diff --git a/packages/components/src/redux/common/reducers/index.ts b/packages/components/src/redux/common/reducers/index.ts index 0742a9507..61bd2eac2 100644 --- a/packages/components/src/redux/common/reducers/index.ts +++ b/packages/components/src/redux/common/reducers/index.ts @@ -10,12 +10,14 @@ import { authReducer } from './authReducer' import { contentReducer } from './contentReducer' import { predictionReducer } from './predictionReducer' import { accessReducer } from './accessReducer' +import { keysReducer } from './keysReducer' export const exportReducerNames = ['app', 'prediction'] const reducer = combineReducers( syncReducers( { + keys: keysReducer, access: accessReducer, analytics: analyticsReducer, answer: answerReducer, diff --git a/packages/components/src/redux/common/reducers/keysReducer.ts b/packages/components/src/redux/common/reducers/keysReducer.ts new file mode 100644 index 000000000..204074386 --- /dev/null +++ b/packages/components/src/redux/common/reducers/keysReducer.ts @@ -0,0 +1,28 @@ +import { CommonActions } from '../types' +import _ from 'lodash' + +export interface KeysState { + keys: + | { + key: string + secretKey: string + } + | undefined +} + +const initialState: KeysState = { + keys: undefined, +} + +export function keysReducer(state = initialState, action: CommonActions): KeysState { + switch (action.type) { + case 'SET_STORE_KEYS': + return { + ...state, + keys: action.payload, + } + + default: + return state + } +} diff --git a/packages/components/src/redux/store.ts b/packages/components/src/redux/store.ts index b6492f9ee..a46967008 100644 --- a/packages/components/src/redux/store.ts +++ b/packages/components/src/redux/store.ts @@ -25,7 +25,7 @@ export function configureStore({ key, secretKey, rootReducer, rootSaga }) { storage, timeout: 10000, throttle: 500, - blacklist: [], + blacklist: ['keys'], transforms: [encryptor], } diff --git a/packages/components/src/screens/MainScreen.tsx b/packages/components/src/screens/MainScreen.tsx index c6ac9d1cf..696bb42e1 100644 --- a/packages/components/src/screens/MainScreen.tsx +++ b/packages/components/src/screens/MainScreen.tsx @@ -54,6 +54,7 @@ const MainScreenContainer = ({ navigation }) => { dispatch(commonActions.fetchSurveyContentRequest(userID)) }, []) + // TODO: Cant use hook like this? useRandomText({ navigation }) return } diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index 9039abcf4..d93be8abe 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -12,6 +12,7 @@ import { useCommonSelector } from '../redux/common/useCommonSelector' import { KeyboardAwareAvoidance } from '../components/common/KeyboardAwareAvoidance' import { SpinLoader } from '../components/common/SpinLoader' import _ from 'lodash' +import { hash } from '../services/hash' export function PasswordRequestScreen() { const dispatch = useDispatch() @@ -68,6 +69,16 @@ export function PasswordRequestScreen() { requestAnimationFrame(() => { navigateAndReset('MainStack', null) }) + + const keys = { + key: hash(user.name), + secretKey: hash(trimmedPassword), + } + + // TODO_ALEX Consider moving to MainScreen for safer transition + setTimeout(() => { + dispatch(commonActions.setStoreKeys(keys)) + }, 1000) } else if (trimmedPassword === user.password && name !== user.name) { setLoading(false) setPasswordError(false) From d39ad4a1139e852f3fd12b6a848479960755305d Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 1 Jan 2024 20:57:46 +0800 Subject: [PATCH 22/83] Switch stores on login success --- .../src/redux/common/reducers/accessReducer.ts | 5 +++++ packages/components/src/redux/common/sagas/authSaga.ts | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/packages/components/src/redux/common/reducers/accessReducer.ts b/packages/components/src/redux/common/reducers/accessReducer.ts index 90840c512..b030b504e 100644 --- a/packages/components/src/redux/common/reducers/accessReducer.ts +++ b/packages/components/src/redux/common/reducers/accessReducer.ts @@ -51,6 +51,11 @@ export function accessReducer(state = initialState, action: CommonActions): Acce } } + case 'LOGIN_SUCCESS': { + // TODO_ALEX Can just update keys here instead of via saga ? + return state + } + default: return state } diff --git a/packages/components/src/redux/common/sagas/authSaga.ts b/packages/components/src/redux/common/sagas/authSaga.ts index 0aaad385e..cf8b58876 100644 --- a/packages/components/src/redux/common/sagas/authSaga.ts +++ b/packages/components/src/redux/common/sagas/authSaga.ts @@ -161,6 +161,15 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { } } +function* onLoginSuccess(action: ExtractActionFromActionType<'LOGIN_SUCCESS'>) { + const keys = { + key: hash(action.payload.user.name), + secretKey: hash(action.payload.user.password), + } + + yield put(commonActions.setStoreKeys(keys)) +} + function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACCOUNT_REQUEST'>) { const { id, @@ -353,6 +362,7 @@ export function* authSaga() { takeLatest(REHYDRATE, onRehydrate), takeLatest('LOGOUT_REQUEST', onLogoutRequest), takeLatest('LOGIN_REQUEST', onLoginRequest), + takeLatest('LOGIN_SUCCESS', onLoginSuccess), takeLatest('DELETE_ACCOUNT_REQUEST', onDeleteAccountRequest), takeLatest('CREATE_ACCOUNT_REQUEST', onCreateAccountRequest), takeLatest('CREATE_ACCOUNT_SUCCESS', onCreateAccountSuccess), From 3e027a2ee796d39ea2a67dba8786dc3793bd1851 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 1 Jan 2024 21:41:07 +0800 Subject: [PATCH 23/83] Use salt when hashing password --- .../components/src/redux/common/sagas/authSaga.ts | 13 +++++++++++-- .../src/screens/PasswordRequestScreen.tsx | 12 ++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/components/src/redux/common/sagas/authSaga.ts b/packages/components/src/redux/common/sagas/authSaga.ts index cf8b58876..e23370111 100644 --- a/packages/components/src/redux/common/sagas/authSaga.ts +++ b/packages/components/src/redux/common/sagas/authSaga.ts @@ -162,9 +162,18 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { } function* onLoginSuccess(action: ExtractActionFromActionType<'LOGIN_SUCCESS'>) { + const credentials = yield select((s) => s.access.credentials) + const usernameHash = hash(action.payload.user.name) + const salt = credentials[usernameHash]?.passwordSalt + + if (!salt) { + // TODO_ALEX ??? + // Cant just return because they could have online account from another device that they're logging in to + } + const keys = { - key: hash(action.payload.user.name), - secretKey: hash(action.payload.user.password), + key: usernameHash, + secretKey: hash(action.payload.user.password + salt), } yield put(commonActions.setStoreKeys(keys)) diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index d93be8abe..21ebaa2e5 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -17,6 +17,7 @@ import { hash } from '../services/hash' export function PasswordRequestScreen() { const dispatch = useDispatch() const user = useCommonSelector(commonSelectors.currentUserSelector) + const credentials = useCommonSelector((s) => s.access.credentials) const [loading, setLoading] = React.useState(false) const [valid, setValid] = React.useState(false) const [passwordError, setPasswordError] = React.useState(false) @@ -24,6 +25,13 @@ export function PasswordRequestScreen() { const [name, setName] = React.useState(user.name) const [password, setPassword] = React.useState('') + const usernameHash = hash(user.name) + const salt = credentials[usernameHash]?.passwordSalt + + if (!salt) { + // TODO_ALEX: handle this case, navigate away? + } + return ( @@ -71,8 +79,8 @@ export function PasswordRequestScreen() { }) const keys = { - key: hash(user.name), - secretKey: hash(trimmedPassword), + key: usernameHash, + secretKey: hash(trimmedPassword + salt), } // TODO_ALEX Consider moving to MainScreen for safer transition From 47d8f5fec7f437129747ad5528d0db21bfa5c2d8 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Tue, 2 Jan 2024 16:25:59 +0800 Subject: [PATCH 24/83] Remove redundant 'secure' store logic --- .../redux/secure/actions/analyticsActions.ts | 19 -- .../src/redux/secure/actions/answerActions.ts | 92 ----- .../src/redux/secure/actions/appActions.ts | 67 ---- .../src/redux/secure/actions/authActions.ts | 198 ----------- .../redux/secure/actions/contentActions.ts | 80 ----- .../src/redux/secure/actions/index.ts | 15 - .../secure/actions/newPredictionAction.ts | 20 -- .../redux/secure/actions/predictionActions.ts | 41 --- .../redux/secure/reducers/analyticsReducer.ts | 28 -- .../redux/secure/reducers/answerReducer.ts | 204 ----------- .../src/redux/secure/reducers/appReducer.ts | 118 ------- .../src/redux/secure/reducers/authReducer.ts | 155 --------- .../redux/secure/reducers/contentReducer.ts | 145 -------- .../src/redux/secure/reducers/index.ts | 41 --- .../secure/reducers/predictionReducer.ts | 36 -- .../src/redux/secure/sagas/analyticsSaga.ts | 68 ---- .../src/redux/secure/sagas/appSaga.ts | 67 ---- .../src/redux/secure/sagas/authSaga.ts | 318 ------------------ .../src/redux/secure/sagas/contentSaga.ts | 214 ------------ .../src/redux/secure/sagas/index.ts | 17 - .../redux/secure/sagas/smartPredictionSaga.ts | 51 --- .../secure/selectors/analyticsSelectors.ts | 5 - .../redux/secure/selectors/answerSelectors.ts | 137 -------- .../redux/secure/selectors/appSelectors.ts | 35 -- .../redux/secure/selectors/authSelectors.ts | 9 - .../secure/selectors/contentSelectors.ts | 113 ------- .../src/redux/secure/selectors/index.ts | 13 - .../components/src/redux/secure/sync/index.ts | 42 --- .../src/redux/secure/types/index.ts | 11 - .../src/redux/secure/useSecureSelector.ts | 4 - 30 files changed, 2363 deletions(-) delete mode 100644 packages/components/src/redux/secure/actions/analyticsActions.ts delete mode 100644 packages/components/src/redux/secure/actions/answerActions.ts delete mode 100644 packages/components/src/redux/secure/actions/appActions.ts delete mode 100644 packages/components/src/redux/secure/actions/authActions.ts delete mode 100644 packages/components/src/redux/secure/actions/contentActions.ts delete mode 100644 packages/components/src/redux/secure/actions/index.ts delete mode 100644 packages/components/src/redux/secure/actions/newPredictionAction.ts delete mode 100644 packages/components/src/redux/secure/actions/predictionActions.ts delete mode 100644 packages/components/src/redux/secure/reducers/analyticsReducer.ts delete mode 100644 packages/components/src/redux/secure/reducers/answerReducer.ts delete mode 100644 packages/components/src/redux/secure/reducers/appReducer.ts delete mode 100644 packages/components/src/redux/secure/reducers/authReducer.ts delete mode 100644 packages/components/src/redux/secure/reducers/contentReducer.ts delete mode 100644 packages/components/src/redux/secure/reducers/index.ts delete mode 100644 packages/components/src/redux/secure/reducers/predictionReducer.ts delete mode 100644 packages/components/src/redux/secure/sagas/analyticsSaga.ts delete mode 100644 packages/components/src/redux/secure/sagas/appSaga.ts delete mode 100644 packages/components/src/redux/secure/sagas/authSaga.ts delete mode 100644 packages/components/src/redux/secure/sagas/contentSaga.ts delete mode 100644 packages/components/src/redux/secure/sagas/index.ts delete mode 100644 packages/components/src/redux/secure/sagas/smartPredictionSaga.ts delete mode 100644 packages/components/src/redux/secure/selectors/analyticsSelectors.ts delete mode 100644 packages/components/src/redux/secure/selectors/answerSelectors.ts delete mode 100644 packages/components/src/redux/secure/selectors/appSelectors.ts delete mode 100644 packages/components/src/redux/secure/selectors/authSelectors.ts delete mode 100644 packages/components/src/redux/secure/selectors/contentSelectors.ts delete mode 100644 packages/components/src/redux/secure/selectors/index.ts delete mode 100644 packages/components/src/redux/secure/sync/index.ts delete mode 100644 packages/components/src/redux/secure/types/index.ts delete mode 100644 packages/components/src/redux/secure/useSecureSelector.ts diff --git a/packages/components/src/redux/secure/actions/analyticsActions.ts b/packages/components/src/redux/secure/actions/analyticsActions.ts deleted file mode 100644 index f25f25c0e..000000000 --- a/packages/components/src/redux/secure/actions/analyticsActions.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { createAction } from '../../helpers' - -export function queueEvent({ - id, - type, - payload, - metadata, -}: { - id: string - type: string - payload: any - metadata: any -}) { - return createAction('QUEUE_EVENT', { id, type, payload, metadata }) -} - -export function resetQueue() { - return createAction('RESET_QUEUE') -} diff --git a/packages/components/src/redux/secure/actions/answerActions.ts b/packages/components/src/redux/secure/actions/answerActions.ts deleted file mode 100644 index 177335c11..000000000 --- a/packages/components/src/redux/secure/actions/answerActions.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { createAction } from '../../helpers' -import { Moment } from 'moment' -import { CardName, DailyCard } from '../../../types' -import { AnswerForUserState } from '../reducers/answerReducer' -import { User } from '../reducers/authReducer' - -export function answerSurvey(payload: { - id: string - user_id: string - isCompleted: boolean - isSurveyAnswered: boolean - questions: any - utcDateTime: Moment -}) { - return createAction('ANSWER_SURVEY', payload) -} - -export function answerQuiz(payload: { - id: string - question: string - answerID: number - emoji: string - answer: string - isCorrect: boolean - response: string - userID: string - utcDateTime: Moment -}) { - return createAction('ANSWER_QUIZ', payload) -} - -export function answerDailyCard({ - cardName, - answer, - userID, - utcDateTime, - mutuallyExclusive = false, - periodDay = false, -}: { - cardName: T - answer: DailyCard[T] - userID: string - utcDateTime: Moment - mutuallyExclusive: boolean - periodDay: boolean -}) { - return createAction('ANSWER_DAILY_CARD', { - cardName, - answer, - userID, - utcDateTime, - mutuallyExclusive, - periodDay, - }) -} - -export function answerVerifyDates({ - userID, - utcDateTime, - periodDay = false, -}: { - userID: string - utcDateTime: Moment - periodDay: boolean -}) { - return createAction('ANSWER_VERIFY_DATES', { - userID, - utcDateTime, - periodDay, - }) -} - -export function answerNotesCard(payload: { - title: string - notes: string - userID: string - utcDateTime: Moment -}) { - return createAction('ANSWER_NOTES_CARD', payload) -} - -export function shareApp() { - return createAction('SHARE_APP') -} - -export function migrateAnswerData(payload: { - userId: User['id'] - key: string - data: AnswerForUserState -}) { - return createAction('MIGRATE_ANSWER_DATA', payload) -} diff --git a/packages/components/src/redux/secure/actions/appActions.ts b/packages/components/src/redux/secure/actions/appActions.ts deleted file mode 100644 index 0a7918ecf..000000000 --- a/packages/components/src/redux/secure/actions/appActions.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { AvatarName, ThemeName } from '@oky/core' -import { createAction } from '../../helpers' - -export function setTheme(theme: ThemeName) { - return createAction('SET_THEME', { theme }) -} - -export function setAvatar(avatar: AvatarName) { - return createAction('SET_AVATAR', { avatar }) -} - -export function setLocale(locale: string) { - return createAction('SET_LOCALE', { locale }) -} - -export function setUpdatedVersion() { - return createAction('SET_UPDATED_VERSION') -} - -export function requestStoreFirebaseKey() { - return createAction('REQUEST_STORE_FIREBASE_KEY') -} - -export function storeFirebaseKey(firebaseToken: string) { - return createAction('STORE_FIREBASE_KEY', { firebaseToken }) -} - -export function setChosenRegion(region: string) { - return createAction('SET_CHOSEN_REGION', { region }) -} - -export function setHasOpened(hasOpened: boolean) { - return createAction('SET_HAS_OPENED', { hasOpened }) -} - -export function setTutorialOneActive(isTutorialActive: boolean) { - return createAction('SET_TUTORIAL_ONE_ACTIVE', { isTutorialActive }) -} - -export function setTutorialTwoActive(isTutorialActive: boolean) { - return createAction('SET_TUTORIAL_TWO_ACTIVE', { isTutorialActive }) -} - -export function setLoginPassword(isLoginPasswordActive: boolean) { - return createAction('SET_LOGIN_PASSWORD_ACTIVE', { isLoginPasswordActive }) -} - -export function setTtsActive(isTtsActive: boolean) { - return createAction('SET_TTS_ACTIVE', { isTtsActive }) -} - -export function setFuturePredictionActive(isFuturePredictionActive: boolean) { - return createAction('SET_FUTURE_PREDICTION_ACTIVE', { isFuturePredictionActive }) -} - -export function refreshStore(appState: any) { - return createAction('REFRESH_STORE', appState) -} - -export function syncStore() { - return createAction('SYNC_STORE') -} - -// MARK: For verified dates by user -export function verifyPeriodDayByUser(date: any) { - return createAction('VERIFY_PERIOD_DAY', { date }) -} diff --git a/packages/components/src/redux/secure/actions/authActions.ts b/packages/components/src/redux/secure/actions/authActions.ts deleted file mode 100644 index 6d3c15eb8..000000000 --- a/packages/components/src/redux/secure/actions/authActions.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { createAction } from '../../helpers' - -export function loginRequest({ name, password }) { - return createAction('LOGIN_REQUEST', { name, password }) -} - -export function loginSuccess({ - appToken, - user: { - id, - name, - dateOfBirth, - gender, - location, - country, - province, - password, - secretQuestion, - secretAnswer, - }, -}) { - return createAction('LOGIN_SUCCESS', { - appToken, - user: { - id, - name, - dateOfBirth, - gender, - location, - country, - province, - password, - secretQuestion, - secretAnswer, - }, - }) -} - -export function loginSuccessAsGuestAccount({ - id, - name, - dateOfBirth, - gender, - location, - country, - province, - password, - secretQuestion, - secretAnswer, -}) { - return createAction('LOGIN_SUCCESS_AS_GUEST_ACCOUNT', { - id, - name, - dateOfBirth, - gender, - location, - country, - province, - password, - secretQuestion, - secretAnswer, - }) -} - -export function loginFailure({ error }) { - return createAction('LOGIN_FAILURE', { error }) -} - -export function logoutRequest() { - return createAction('LOGOUT_REQUEST') -} - -export function logout() { - return createAction('LOGOUT') -} - -export function createAccountRequest({ - id = null, - name, - dateOfBirth, - gender, - location, - country, - province, - password, - secretQuestion, - secretAnswer, -}) { - return createAction('CREATE_ACCOUNT_REQUEST', { - id, - name, - dateOfBirth, - gender, - location, - country, - province, - password, - secretQuestion, - secretAnswer, - }) -} -export function deleteAccountRequest({ name, password, setLoading }) { - return createAction('DELETE_ACCOUNT_REQUEST', { - name, - password, - setLoading, - }) -} - -export function createAccountSuccess({ - appToken, - user: { - id, - name, - password, - dateOfBirth, - gender, - location, - country, - province, - secretQuestion, - secretAnswer, - }, -}) { - return createAction('CREATE_ACCOUNT_SUCCESS', { - appToken, - user: { - id, - name, - dateOfBirth, - gender, - location, - country, - province, - password, - secretQuestion, - secretAnswer, - }, - }) -} - -export function createAccountFailure() { - return createAction('CREATE_ACCOUNT_FAILURE') -} - -export function convertGuestAccount({ - id, - name, - dateOfBirth, - gender, - location, - country, - province, - password, - secretQuestion, - secretAnswer, -}) { - return createAction('CONVERT_GUEST_ACCOUNT', { - id, - name, - password, - dateOfBirth, - gender, - location, - country, - province, - secretQuestion, - secretAnswer, - }) -} - -export function editUser({ - name = null, - dateOfBirth = null, - gender = null, - location = null, - password = null, - secretQuestion = null, - secretAnswer = null, -}) { - return createAction('EDIT_USER', { - name, - dateOfBirth, - gender, - location, - password, - secretQuestion, - secretAnswer, - }) -} - -export function journeyCompletion({ data = null }) { - return createAction('JOURNEY_COMPLETION', { data }) -} - -export function setAuthError({ error }) { - return createAction('SET_AUTH_ERROR', { error }) -} diff --git a/packages/components/src/redux/secure/actions/contentActions.ts b/packages/components/src/redux/secure/actions/contentActions.ts deleted file mode 100644 index dd9199406..000000000 --- a/packages/components/src/redux/secure/actions/contentActions.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { createAction } from '../../helpers' -import { - Articles, - Categories, - SubCategories, - Surveys, - Quizzes, - DidYouKnows, - HelpCenters, - AvatarMessages, - PrivacyPolicy, - TermsAndConditions, - About, - AllSurveys, - CompletedSurveys, - Videos, -} from '../../../types' - -export function initStaleContent(payload: { - articles: Articles - avatarMessages: AvatarMessages - categories: Categories - subCategories: SubCategories - quizzes: Quizzes - didYouKnows: DidYouKnows - helpCenters: HelpCenters - privacyPolicy: PrivacyPolicy - termsAndConditions: TermsAndConditions - about: About - aboutBanner: string -}) { - return createAction('INIT_STALE_CONTENT', payload) -} - -export function fetchSurveyContentRequest(userID: string) { - return createAction('FETCH_SURVEY_CONTENT_REQUEST', { userID }) -} - -export function fetchSurveyContentSuccess({ surveys }: { surveys: Surveys }) { - return createAction('FETCH_SURVEY_CONTENT_SUCCESS', { - surveys, - }) -} -export function updateAllSurveyContent(allSurveys: AllSurveys) { - return createAction('UPDATE_ALL_SURVEYS_CONTENT', { - allSurveys, - }) -} -export function updateCompletedSurveys(completedSurveys: CompletedSurveys) { - return createAction('UPDATE_COMPLETED_SURVEYS', { - completedSurveys, - }) -} - -export function fetchContentRequest(locale: string) { - return createAction('FETCH_CONTENT_REQUEST', { locale }) -} - -export function fetchContentSuccess(payload: { - timeFetched: number - articles: Articles - videos: Videos - avatarMessages: AvatarMessages - categories: Categories - subCategories: SubCategories - quizzes: Quizzes - didYouKnows: DidYouKnows - helpCenters: HelpCenters - privacyPolicy: PrivacyPolicy - termsAndConditions: TermsAndConditions - about: About - aboutBanner?: string - aboutBannerTimestamp?: number -}) { - return createAction('FETCH_CONTENT_SUCCESS', payload) -} - -export function fetchContentFailure() { - return createAction('FETCH_CONTENT_FAILURE') -} diff --git a/packages/components/src/redux/secure/actions/index.ts b/packages/components/src/redux/secure/actions/index.ts deleted file mode 100644 index ccfc15dd9..000000000 --- a/packages/components/src/redux/secure/actions/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import * as analyticsActions from './analyticsActions' -import * as answerActions from './answerActions' -import * as appActions from './appActions' -import * as authActions from './authActions' -import * as contentActions from './contentActions' -import * as predictionActions from './predictionActions' - -export const secureActions = { - ...analyticsActions, - ...answerActions, - ...appActions, - ...authActions, - ...contentActions, - ...predictionActions, -} diff --git a/packages/components/src/redux/secure/actions/newPredictionAction.ts b/packages/components/src/redux/secure/actions/newPredictionAction.ts deleted file mode 100644 index 610897a46..000000000 --- a/packages/components/src/redux/secure/actions/newPredictionAction.ts +++ /dev/null @@ -1,20 +0,0 @@ -// import { createAction } from '../helpers' - -// export function smartPredictionRequest({ cycle_lengths, period_lengths, age }) { -// return createAction('SMART_PREDICTION_REQUEST', { cycle_lengths, period_lengths, age }) -// } - -// export function smartPredictionSuccess({ predicted_cycles, predicted_periods }) { -// return createAction('SMART_PREDICTION_SUCCESS', { -// predicted_cycles, -// predicted_periods, -// }) -// } - -// export function smartPredictionFailure({ error }) { -// return createAction('SMART_PREDICTION_FAILURE', { error }) -// } - -// export function smartPredictionUpdate({ predicted_cycles, predicted_periods }) { -// return createAction('UPDATE_SMART_PREDICTION', { predicted_cycles, predicted_periods }) -// } diff --git a/packages/components/src/redux/secure/actions/predictionActions.ts b/packages/components/src/redux/secure/actions/predictionActions.ts deleted file mode 100644 index 50390c70b..000000000 --- a/packages/components/src/redux/secure/actions/predictionActions.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { createAction } from '../../helpers' -import { PredictionState } from '../../../prediction' -import { Moment } from 'moment' - -export function setPredictionEngineState(predictionState: PredictionState) { - return createAction('SET_PREDICTION_ENGINE_STATE', { predictionState }) -} - -export function smartPredictionRequest({ - cycle_lengths, - period_lengths, - age, - predictionFullState, - futurePredictionStatus, -}) { - return createAction('SMART_PREDICTION_REQUEST', { - cycle_lengths, - period_lengths, - age, - predictionFullState, - futurePredictionStatus, - }) -} - -export function updateActualCurrentStartDate() { - return createAction('SET_ACTUAL_STARTDATE') -} - -export function setSmartPredictionFailure({ error }) { - return createAction('SMART_PREDICTION_FAILURE', { error }) -} - -export function adjustPrediction(action) { - return createAction('ADJUST_PREDICTION', action) -} -export function updateFuturePrediction(isFuturePredictionActive: boolean, currentStartDate: any) { - return createAction('SET_FUTURE_PREDICTION_STATE_ACTIVE', { - isFuturePredictionActive, - currentStartDate, - }) -} diff --git a/packages/components/src/redux/secure/reducers/analyticsReducer.ts b/packages/components/src/redux/secure/reducers/analyticsReducer.ts deleted file mode 100644 index 5201ab777..000000000 --- a/packages/components/src/redux/secure/reducers/analyticsReducer.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { SecureActions } from '../types' - -export type AnalyticsState = Array<{ - id: string - type: string - payload: any - metadata: any -}> - -const initialState: AnalyticsState = [] - -export function analyticsReducer(state = initialState, action: SecureActions): AnalyticsState { - switch (action.type) { - case 'QUEUE_EVENT': - return state.concat({ - id: action.payload.id, - type: action.payload.type, - payload: action.payload.payload, - metadata: action.payload.metadata, - }) - - case 'RESET_QUEUE': - return initialState - - default: - return state - } -} diff --git a/packages/components/src/redux/secure/reducers/answerReducer.ts b/packages/components/src/redux/secure/reducers/answerReducer.ts deleted file mode 100644 index edc0334dc..000000000 --- a/packages/components/src/redux/secure/reducers/answerReducer.ts +++ /dev/null @@ -1,204 +0,0 @@ -import { SecureActions } from '../types' -import { combineReducers } from 'redux' -import { toShortISO } from '../../../services/dateUtils' -import { DailyCard } from '../../../types' - -export interface AnswerForUserState { - surveys: { - [id: string]: { - id: string - user_id: string - isCompleted: boolean - isSurveyAnswered: boolean - questions: any - utcDateTime: string - inProgress: boolean - } - } - quizzes: { - [id: string]: { - id: string - question: string - emoji: string - answer: string - isCorrect: boolean - response: string - utcDateTime: string - } - } - cards: { - [utcShortISO: string]: DailyCard - } - verifiedDates: { - [utcShortISO: string]: { - periodDay: null | boolean - utcDateTime: string - } - } - notes: { - [utcShortISO: string]: { - notes: string - utcDateTime: string - } - } -} - -export interface AnswerState { - [userId: string]: AnswerForUserState -} - -function surveysReducer(state = {}, action: SecureActions): AnswerForUserState['surveys'] { - if (action.type === 'ANSWER_SURVEY') { - return { - ...state, - [action.payload.id]: { - id: action.payload.id, - user_id: action.payload.user_id, - isCompleted: action.payload.isCompleted, - isSurveyAnswered: action.payload.isSurveyAnswered, - questions: action.payload.questions, - utcDateTime: action.payload.utcDateTime.toISOString(), - }, - } - } - - return state -} - -function quizzesReducer(state = {}, action: SecureActions): AnswerForUserState['quizzes'] { - if (action.type === 'ANSWER_QUIZ') { - return { - ...state, - [action.payload.id]: { - id: action.payload.id, - question: action.payload.question, - answerID: action.payload.answerID, - emoji: action.payload.emoji, - answer: action.payload.answer, - isCorrect: action.payload.isCorrect, - response: action.payload.response, - utcDateTime: action.payload.utcDateTime.toISOString(), - }, - } - } - - return state -} - -function cardsReducer(state = {}, action: SecureActions): AnswerForUserState['cards'] { - if (action.type === 'ANSWER_DAILY_CARD') { - const keyCard = toShortISO(action.payload.utcDateTime) - let answersToInsert = [] - // Added as a way of handling multiple selections and to account for the initial release of single selections (Painful, I know) - - if ( - state[keyCard] !== undefined && - state[keyCard][action.payload.cardName] && - !action.payload.mutuallyExclusive && - action.payload.cardName !== 'periodDay' - ) { - if (typeof state[keyCard][action.payload.cardName] === 'string') { - // This is to account for old data that used to just be a string and now we need to have multiple - // we put that string as part of an array before concatenating the new answers - answersToInsert = [state[keyCard][action.payload.cardName]].concat(action.payload.answer) - } else { - if (state[keyCard][action.payload.cardName].includes(action.payload.answer)) { - // Remove if already contained (toggle ability) - answersToInsert = state[keyCard][action.payload.cardName].filter( - (item) => item !== action.payload.answer, - ) - } else { - answersToInsert = state[keyCard][action.payload.cardName].concat(action.payload.answer) - } - } - } else { - answersToInsert = [action.payload.answer] - } - - return { - ...state, - [keyCard]: { - ...(state[keyCard] || {}), - [action.payload.cardName]: answersToInsert, - }, - } - } - return state -} -function periodVerifyReducer( - state = {}, - action: SecureActions, -): AnswerForUserState['verifiedDates'] { - if (action.type === 'ANSWER_VERIFY_DATES') { - const keyCard = toShortISO(action.payload.utcDateTime) - const answersToInsert = [] - // Added as a way of handling multiple selections and to account for the initial release of single selections (Painful, I know) - return { - ...state, - [keyCard]: { - ...(state[keyCard] || {}), - periodDay: action.payload.periodDay, - }, - } - } - return state -} - -function notesReducer(state = {}, action: SecureActions): AnswerForUserState['notes'] { - if (action.type === 'ANSWER_NOTES_CARD') { - const keyCard = toShortISO(action.payload.utcDateTime) - return { - ...state, - [keyCard]: { - title: action.payload.title, - notes: action.payload.notes, - utcDateTime: action.payload.utcDateTime, - }, - } - } - return state -} - -const answerForUserReducer = combineReducers({ - surveys: surveysReducer, - quizzes: quizzesReducer, - cards: cardsReducer, - notes: notesReducer, - verifiedDates: periodVerifyReducer, -}) - -export function answerReducer(state: AnswerState = {}, action: SecureActions): AnswerState { - // TODO_ALEX: survey - if (action.type === 'ANSWER_SURVEY') { - return { - ...state, - // [action.payload.user_id]: answerForUserReducer(state[action.payload.userID], action), - } - } - if (action.type === 'ANSWER_QUIZ') { - return { - ...state, - [action.payload.userID]: answerForUserReducer(state[action.payload.userID], action), - } - } - if (action.type === 'ANSWER_DAILY_CARD') { - return { - ...state, - [action.payload.userID]: answerForUserReducer(state[action.payload.userID], action), - } - } - if (action.type === 'ANSWER_VERIFY_DATES') { - return { - ...state, - [action.payload.userID]: answerForUserReducer(state[action.payload.userID], action), - } - } - if (action.type === 'ANSWER_NOTES_CARD') { - return { - ...state, - [action.payload.userID]: answerForUserReducer(state[action.payload.userID], action), - } - } - - return state -} diff --git a/packages/components/src/redux/secure/reducers/appReducer.ts b/packages/components/src/redux/secure/reducers/appReducer.ts deleted file mode 100644 index 5ae9ded03..000000000 --- a/packages/components/src/redux/secure/reducers/appReducer.ts +++ /dev/null @@ -1,118 +0,0 @@ -import _ from 'lodash' -import { SecureActions } from '../types' -import { currentLocale } from '../../../i18n' -import DeviceInfo from 'react-native-device-info' -import { AvatarName, ThemeName, defaultAvatar, defaultTheme } from '@oky/core' - -export interface AppState { - appLocale: string - locale: string - chosenRegion: string - appVersionName: string - appVersionCode: string - firebaseToken: string - hasOpened: boolean - isTutorialOneActive: boolean - isTutorialTwoActive: boolean - isLoginPasswordActive: boolean - isTtsActive: boolean - isFuturePredictionActive: boolean - theme: ThemeName - avatar: AvatarName - verifiedDates: any - predicted_cycles: any - predicted_periods: any -} - -const initialState: AppState = { - appVersionName: DeviceInfo.getVersion(), - appVersionCode: DeviceInfo.getBuildNumber(), - firebaseToken: null, - appLocale: currentLocale(), - locale: currentLocale(), - chosenRegion: 'en', // @TODO: PENAL CODE change to currentLocale() if no penal code // @TODO: LANGUAGES This is commented in case the client wants multiple languages - hasOpened: false, - isTutorialOneActive: true, - isTutorialTwoActive: true, - isLoginPasswordActive: true, - isTtsActive: false, - isFuturePredictionActive: true, - theme: defaultTheme, - avatar: defaultAvatar, - verifiedDates: [], - predicted_cycles: [], - predicted_periods: [], -} - -export function appReducer(state = initialState, action: SecureActions): AppState { - switch (action.type) { - case 'SET_THEME': - return { - ...state, - theme: action.payload.theme, - } - case 'SET_AVATAR': - return { - ...state, - avatar: action.payload.avatar, - } - case 'SET_UPDATED_VERSION': - return { - ...state, - appVersionName: DeviceInfo.getVersion(), - appVersionCode: DeviceInfo.getBuildNumber(), - } - case 'STORE_FIREBASE_KEY': - return { - ...state, - firebaseToken: action.payload.firebaseToken, - } - case 'SET_LOCALE': - return { - ...state, - locale: action.payload.locale, - } - case 'SET_CHOSEN_REGION': - return { - ...state, - chosenRegion: action.payload.region, - } - case 'SET_HAS_OPENED': - return { - ...state, - hasOpened: action.payload.hasOpened, - } - case 'SET_TUTORIAL_ONE_ACTIVE': - return { - ...state, - isTutorialOneActive: action.payload.isTutorialActive, - } - case 'SET_TUTORIAL_TWO_ACTIVE': - return { - ...state, - isTutorialTwoActive: action.payload.isTutorialActive, - } - case 'SET_LOGIN_PASSWORD_ACTIVE': - return { - ...state, - isLoginPasswordActive: action.payload.isLoginPasswordActive, - } - case 'SET_TTS_ACTIVE': - return { - ...state, - isTtsActive: action.payload.isTtsActive, - } - case 'SET_FUTURE_PREDICTION_ACTIVE': - return { - ...state, - isFuturePredictionActive: action.payload.isFuturePredictionActive, - } - case 'VERIFY_PERIOD_DAY': - return { - ...state, - verifiedDates: action.payload.date, - } - default: - return state - } -} diff --git a/packages/components/src/redux/secure/reducers/authReducer.ts b/packages/components/src/redux/secure/reducers/authReducer.ts deleted file mode 100644 index f0332b2d8..000000000 --- a/packages/components/src/redux/secure/reducers/authReducer.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { REHYDRATE, RehydrateAction } from 'redux-persist' -import _ from 'lodash' -import { SecureActions } from '../types/index' - -export interface User { - id: string - name: string - dateOfBirth: string - gender: string - location: string - country: string - province: string - password: string - secretQuestion: string - secretAnswer: string - isGuest: boolean -} - -export interface AuthState { - appToken: string | null - error: string | null - isCreatingAccount: boolean - isLoggingIn: boolean - loginFailedCount: number - connectAccountAttempts: number - user: User | null -} - -const initialState: AuthState = { - appToken: null, - error: null, - isCreatingAccount: false, - isLoggingIn: false, - loginFailedCount: 0, - connectAccountAttempts: 0, - user: null, -} - -export function authReducer( - state = initialState, - action: SecureActions | RehydrateAction, -): AuthState { - switch (action.type) { - case REHYDRATE: - return { - ...(action.payload && action.payload.auth), - // reset state when store is re-hydrated - ..._.pick(initialState, ['error', 'isLoggingIn', 'loginFailedCount', 'isCreatingAccount']), - } - - case 'LOGIN_REQUEST': - return { - ...state, - error: null, - isLoggingIn: true, - } - - case 'LOGIN_SUCCESS': - return { - ...state, - appToken: action.payload.appToken, - error: null, - isLoggingIn: false, - loginFailedCount: 0, - connectAccountAttempts: 0, - user: { - id: action.payload.user.id, - name: action.payload.user.name, - dateOfBirth: action.payload.user.dateOfBirth, - gender: action.payload.user.gender, - location: action.payload.user.location, - country: action.payload.user.country, - province: action.payload.user.province, - password: action.payload.user.password, - secretQuestion: action.payload.user.secretQuestion, - secretAnswer: action.payload.user.secretAnswer, - isGuest: false, - }, - } - - case 'LOGIN_SUCCESS_AS_GUEST_ACCOUNT': - return { - ...state, - appToken: null, - isLoggingIn: false, - loginFailedCount: 0, - user: { - id: action.payload.id, - name: action.payload.name, - dateOfBirth: action.payload.dateOfBirth, - gender: action.payload.gender, - location: action.payload.location, - country: action.payload.country, - province: action.payload.province, - password: action.payload.password, - secretQuestion: action.payload.secretQuestion, - secretAnswer: action.payload.secretAnswer, - isGuest: true, - }, - } - - case 'LOGIN_FAILURE': - return { - ...state, - appToken: null, - loginFailedCount: state.loginFailedCount + 1, - error: action.payload.error, - isLoggingIn: false, - user: null, - } - - case 'LOGOUT': - return { - ...state, - appToken: null, - isLoggingIn: false, - user: null, - } - - case 'SET_AUTH_ERROR': - return { - ...state, - error: action.payload.error, - } - - case 'CREATE_ACCOUNT_REQUEST': - return { - ...state, - isCreatingAccount: true, - error: null, - } - - case 'CREATE_ACCOUNT_SUCCESS': - return { - ...state, - isCreatingAccount: false, - } - - case 'CREATE_ACCOUNT_FAILURE': - return { - ...state, - connectAccountAttempts: state.connectAccountAttempts + 1, - isCreatingAccount: false, - } - - case 'EDIT_USER': - return { - ...state, - user: { ...state.user, ..._.omitBy(action.payload, _.isNil) }, - } - - default: - return state - } -} diff --git a/packages/components/src/redux/secure/reducers/contentReducer.ts b/packages/components/src/redux/secure/reducers/contentReducer.ts deleted file mode 100644 index 7814ebfb6..000000000 --- a/packages/components/src/redux/secure/reducers/contentReducer.ts +++ /dev/null @@ -1,145 +0,0 @@ -import _ from 'lodash' -import { - Articles, - Categories, - SubCategories, - Surveys, - Quizzes, - DidYouKnows, - HelpCenters, - AvatarMessages, - PrivacyPolicy, - TermsAndConditions, - About, - AllSurveys, - CompletedSurveys, - Videos, -} from '../../../types' -import { SecureActions } from '../types/index' - -export interface ContentState { - timeFetched?: number - aboutBannerTimestamp?: number - articles: Articles - categories: Categories - subCategories: SubCategories - surveys: Surveys - quizzes: Quizzes - didYouKnows: DidYouKnows - helpCenters: HelpCenters - avatarMessages: AvatarMessages - privacyPolicy: PrivacyPolicy - termsAndConditions: TermsAndConditions - about: About - aboutBanner: string - allSurveys: AllSurveys - completedSurveys: CompletedSurveys - videos?: Videos -} - -const initialState: ContentState = { - timeFetched: undefined, - aboutBannerTimestamp: undefined, - articles: { - byId: {}, - allIds: [], - }, - categories: { - byId: {}, - allIds: [], - }, - subCategories: { - byId: {}, - allIds: [], - }, - surveys: { - date_created: '', - id: 'string', - isAgeRestricted: false, - is_multiple: true, - lang: 'string', - live: true, - option1: 'string', - option2: 'string', - option3: 'string', - option4: 'string', - option5: 'string', - question: 'string', - questions: [], - }, - allSurveys: [], - completedSurveys: [], - quizzes: { - byId: {}, - allIds: [], - }, - didYouKnows: { - byId: {}, - allIds: [], - }, - helpCenters: [], - avatarMessages: [], - privacyPolicy: [], - termsAndConditions: [], - about: [], - aboutBanner: '', - videos: { - byId: {}, - allIds: [], - }, -} - -export function contentReducer(state = initialState, action: SecureActions): ContentState { - switch (action.type) { - case 'INIT_STALE_CONTENT': - return { - ...state, - ...action.payload, - } - - case 'FETCH_CONTENT_SUCCESS': { - const shouldUpdateBanner = action.payload.aboutBanner !== undefined - - return { - ...state, - timeFetched: action.payload.timeFetched, - articles: action.payload.articles, - videos: action.payload.videos, - categories: action.payload.categories, - subCategories: action.payload.subCategories, - quizzes: action.payload.quizzes, - didYouKnows: action.payload.didYouKnows, - helpCenters: action.payload.helpCenters, - avatarMessages: action.payload.avatarMessages, - privacyPolicy: action.payload.privacyPolicy, - termsAndConditions: action.payload.termsAndConditions, - about: action.payload.about, - aboutBanner: shouldUpdateBanner ? action.payload.aboutBanner : state.aboutBanner, - aboutBannerTimestamp: shouldUpdateBanner - ? action.payload.aboutBannerTimestamp - : state.aboutBannerTimestamp, - } - } - - case 'FETCH_SURVEY_CONTENT_SUCCESS': - return { - ...state, - surveys: action.payload.surveys, - } - - case 'UPDATE_ALL_SURVEYS_CONTENT': - return { - ...state, - allSurveys: action.payload.allSurveys, - } - - case 'UPDATE_COMPLETED_SURVEYS': - return { - ...state, - completedSurveys: action.payload.completedSurveys, - } - - default: - return state - } -} diff --git a/packages/components/src/redux/secure/reducers/index.ts b/packages/components/src/redux/secure/reducers/index.ts deleted file mode 100644 index 48861588d..000000000 --- a/packages/components/src/redux/secure/reducers/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -import _ from 'lodash' -import { combineReducers } from 'redux' -import { syncReducers } from '../sync' -import { SecureActions } from '../types' - -import { analyticsReducer } from './analyticsReducer' -import { answerReducer } from './answerReducer' -import { appReducer } from './appReducer' -import { authReducer } from './authReducer' -import { contentReducer } from './contentReducer' -import { predictionReducer } from './predictionReducer' - -export const exportReducerNames = ['app', 'prediction'] - -const reducer = combineReducers( - syncReducers( - { - analytics: analyticsReducer, - answer: answerReducer, - app: appReducer, - auth: authReducer, - content: contentReducer, - prediction: predictionReducer, - // flower: flowerReducer, TODO: Flower state should be saved per user - }, - exportReducerNames, - ), -) - -export function secureRootReducer(state, action: SecureActions) { - switch (action.type) { - case 'LOGOUT': - // @ts-ignore - return reducer(_.pick(state, 'app', 'content', 'answer'), action) - - default: - return reducer(state, action) - } -} - -export type SecureReduxState = ReturnType diff --git a/packages/components/src/redux/secure/reducers/predictionReducer.ts b/packages/components/src/redux/secure/reducers/predictionReducer.ts deleted file mode 100644 index ab9637e49..000000000 --- a/packages/components/src/redux/secure/reducers/predictionReducer.ts +++ /dev/null @@ -1,36 +0,0 @@ -import _ from 'lodash' -import { PredictionSerializableState } from '../../../prediction' - -import { SecureActions } from '../types/index' - -export type PredictionState = PredictionSerializableState | null - -const initialState: PredictionState = null - -export function predictionReducer(state = initialState, action: SecureActions): PredictionState { - switch (action.type) { - case 'SET_PREDICTION_ENGINE_STATE': - return action.payload.predictionState.toJSON() - - case 'SMART_PREDICTION_FAILURE': - return { - ...state, - } - case 'SET_FUTURE_PREDICTION_STATE_ACTIVE': - return { - ...state, - futurePredictionStatus: action.payload.isFuturePredictionActive, - actualCurrentStartDate: !action.payload.isFuturePredictionActive - ? action.payload.currentStartDate - : null, - } - - case 'SET_ACTUAL_STARTDATE': - return { - ...state, - actualCurrentStartDate: null, - } - default: - return state - } -} diff --git a/packages/components/src/redux/secure/sagas/analyticsSaga.ts b/packages/components/src/redux/secure/sagas/analyticsSaga.ts deleted file mode 100644 index 63c4977cf..000000000 --- a/packages/components/src/redux/secure/sagas/analyticsSaga.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { all, delay, fork, put, select, takeLatest } from 'redux-saga/effects' -import { v4 as uuidv4 } from 'uuid' -import moment from 'moment' -import { fetchNetworkConnectionStatus } from '../../../services/network' -import { httpClient } from '../../../services/HttpClient' -import { secureActions } from '../actions' -import { secureSelectors } from '../selectors' -import { SecureActionTypes } from '../types' - -const ACTIONS_TO_TRACK: SecureActionTypes[] = [ - // app - 'SET_THEME', - 'SET_LOCALE', - // answers - 'ANSWER_SURVEY', - 'ANSWER_QUIZ', - 'SHARE_APP', - // 'ANSWER_DAILY_CARD', // removed for privacy - // prediction - 'ADJUST_PREDICTION', -] - -function* onTrackAction(action) { - const currentUser = yield select(secureSelectors.currentUserSelector) - yield put( - secureActions.queueEvent({ - id: uuidv4(), - type: action.type, - payload: action.payload || {}, - metadata: { - date: moment.utc(), - user: currentUser && currentUser.id ? currentUser.id : null, - }, - }), - ) -} - -function* processEventQueue() { - while (true) { - // process queue every minute - yield delay(60 * 1000) - - const appToken = yield select(secureSelectors.appTokenSelector) - const events = yield select(secureSelectors.allAnalyticsEventsSelector) - - const isQueueEmpty = events.length === 0 - if (isQueueEmpty) { - // nothing to send - continue - } - - if (!(yield fetchNetworkConnectionStatus())) { - // no internet connection - continue - } - - try { - yield httpClient.appendEvents({ events, appToken }) - yield put(secureActions.resetQueue()) - } catch (err) { - // ignore error, we'll try later - } - } -} - -export function* analyticsSaga() { - yield all([fork(processEventQueue), takeLatest(ACTIONS_TO_TRACK, onTrackAction)]) -} diff --git a/packages/components/src/redux/secure/sagas/appSaga.ts b/packages/components/src/redux/secure/sagas/appSaga.ts deleted file mode 100644 index c2349ba23..000000000 --- a/packages/components/src/redux/secure/sagas/appSaga.ts +++ /dev/null @@ -1,67 +0,0 @@ -import _ from 'lodash' -import { all, delay, fork, put, select, takeLatest } from 'redux-saga/effects' -import { httpClient } from '../../../services/HttpClient' -import { fetchNetworkConnectionStatus } from '../../../services/network' -import { extractReducerState } from '../sync' -import { SecureReduxState, exportReducerNames } from '../reducers' -import { version as storeVersion } from '../../store' -import { secureActions } from '../actions' -import { secureSelectors } from '../selectors' -import messaging from '@react-native-firebase/messaging' - -function* syncAppState() { - let lastAppState - - while (true) { - // process queue every minute - yield delay(60 * 1000) - - const appToken = yield select(secureSelectors.appTokenSelector) - if (!appToken) { - // not logged - continue - } - - const state: SecureReduxState = yield select() - const appState = extractReducerState(state, exportReducerNames) - - if (_.isEqual(appState, lastAppState)) { - // bailout, nothing changed from last sync - continue - } - - if (!(yield fetchNetworkConnectionStatus())) { - // no internet connection - continue - } - - try { - yield httpClient.replaceStore({ - storeVersion, - appState, - appToken, - }) - - const temp = yield put(secureActions.syncStore()) - - lastAppState = appState - } catch (err) { - // ignore error, we'll try later - } - } -} - -function* onRequestStoreFirebaseKey() { - if (yield fetchNetworkConnectionStatus()) { - // no internet connection - const firebaseToken = yield messaging().getToken() - yield put(secureActions.storeFirebaseKey(firebaseToken)) - } -} - -export function* appSaga() { - yield all([ - fork(syncAppState), - takeLatest('REQUEST_STORE_FIREBASE_KEY', onRequestStoreFirebaseKey), - ]) -} diff --git a/packages/components/src/redux/secure/sagas/authSaga.ts b/packages/components/src/redux/secure/sagas/authSaga.ts deleted file mode 100644 index 7f7e93c4c..000000000 --- a/packages/components/src/redux/secure/sagas/authSaga.ts +++ /dev/null @@ -1,318 +0,0 @@ -import { all, call, put, select, takeLatest, delay } from 'redux-saga/effects' -import { REHYDRATE } from 'redux-persist' -import { Alert } from 'react-native' -import { v4 as uuidv4 } from 'uuid' -import { ExtractActionFromActionType } from '../types' -import { httpClient } from '../../../services/HttpClient' -import { SecureReduxState, exportReducerNames } from '../reducers' -import { secureActions } from '../actions' -import { secureSelectors } from '../selectors' -import { navigateAndReset } from '../../../services/navigationService' -import { PredictionState } from '../../../prediction' -import moment from 'moment' -import { closeOutTTs } from '../../../services/textToSpeech' -import { fetchNetworkConnectionStatus } from '../../../services/network' - -// unwrap promise -type Await = T extends Promise ? U : T - -function* onRehydrate() { - const state: SecureReduxState = yield select() - - const appToken = secureSelectors.appTokenSelector(state) - const user = secureSelectors.currentUserSelector(state) - - // convert guest account - if (!appToken && user && user.isGuest) { - yield put(secureActions.convertGuestAccount(user)) - } -} - -function* onConvertGuestAccount(action: ExtractActionFromActionType<'CONVERT_GUEST_ACCOUNT'>) { - const { - id, - name, - dateOfBirth, - gender, - location, - country, - province, - password, - secretAnswer, - secretQuestion, - } = action.payload - - yield put( - secureActions.createAccountRequest({ - id, - name, - dateOfBirth, - gender, - location, - country, - province, - password, - secretAnswer, - secretQuestion, - }), - ) -} - -function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { - const { name, password } = action.payload - const stateRedux: SecureReduxState = yield select() - const localeapp = secureSelectors.currentLocaleSelector(stateRedux) - yield secureActions.setLocale(localeapp) - - try { - const { - appToken, - user, - store, - }: Await> = yield httpClient.login({ - name, - password, - }) - - yield put( - secureActions.loginSuccess({ - appToken, - user: { - id: user.id, - name, - dateOfBirth: user.dateOfBirth, - gender: user.gender, - location: user.location, - country: user.country, - province: user.province, - secretQuestion: user.secretQuestion, - secretAnswer: user.secretAnswer, - password, - }, - }), - ) - - if (store && store.storeVersion && store.appState) { - const newAppState = exportReducerNames.reduce((state, reducerName) => { - if (store.appState[reducerName]) { - return { - ...state, - [reducerName]: store.appState[reducerName], - app: { ...store.appState.app, appLocale: localeapp, locale: localeapp }, - } - } - - return state - }, {}) - - // @TODO: execute migration based on storeVersion - yield put(secureActions.refreshStore(newAppState)) - } - - yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones - yield call(navigateAndReset, 'MainStack', null) - } catch (error) { - let errorMessage = 'request_fail' - if (error && error.response && error.response.data) { - if (error.response.data.name === 'BadRequestError') { - errorMessage = 'login_failed' - } - if (error.response.data.name !== 'BadRequestError') { - errorMessage = error.response.data.message - } - } - yield put( - secureActions.loginFailure({ - error: errorMessage, - }), - ) - } -} - -function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACCOUNT_REQUEST'>) { - const { - id, - name, - dateOfBirth, - gender, - location, - country, - province, - password, - secretAnswer, - secretQuestion, - } = action.payload - try { - const { appToken, user }: Await> = yield httpClient.signup( - { - name, - password, - dateOfBirth, - gender, - location, - country, - province, - secretAnswer, - secretQuestion, - preferredId: id || null, - }, - ) - if (!appToken || !user || !user.id) { - throw new Error(`Invalid data`) - } - - yield put( - secureActions.createAccountSuccess({ - appToken, - user: { - id: user.id, - name, - dateOfBirth: user.dateOfBirth, - gender: user.gender, - location: user.location, - country: user.country, - province: user.province, - secretQuestion: user.secretQuestion, - secretAnswer: user.secretAnswer, - password, - }, - }), - ) - } catch (error) { - const errorStatusCode = - error && error.response && error.response.status ? error.response.status : null // to check various error codes and respond accordingly - yield put(secureActions.setAuthError({ error: errorStatusCode })) - yield put(secureActions.createAccountFailure()) - - yield put( - secureActions.loginSuccessAsGuestAccount({ - id: id || uuidv4(), - name, - dateOfBirth, - gender, - location, - country, - province, - password, - secretAnswer, - secretQuestion, - }), - ) - } -} - -function* onCreateAccountSuccess(action: ExtractActionFromActionType<'CREATE_ACCOUNT_SUCCESS'>) { - const { appToken, user } = action.payload - yield put( - secureActions.loginSuccess({ - appToken, - user: { - id: user.id, - name: user.name, - dateOfBirth: user.dateOfBirth, - gender: user.gender, - location: user.location, - country: user.country, - province: user.province, - password: user.password, - secretQuestion: user.secretQuestion, - secretAnswer: user.secretAnswer, - }, - }), - ) -} -function* onDeleteAccountRequest(action: ExtractActionFromActionType<'DELETE_ACCOUNT_REQUEST'>) { - const { setLoading } = action.payload - const state: SecureReduxState = yield select() - const user = secureSelectors.currentUserSelector(state) - setLoading(true) - try { - const { name, password } = action.payload - yield httpClient.deleteUserFromPassword({ - name, - password, - }) - yield put(secureActions.updateAllSurveyContent([])) // TODO_ALEX - yield put(secureActions.updateCompletedSurveys([])) // TODO_ALEX - yield put( - secureActions.fetchSurveyContentSuccess({ - surveys: null, - }), // TODO_ALEX - ) - yield call(navigateAndReset, 'LoginStack', null) - - if (user) { - yield put(secureActions.logout()) - } - } catch (err) { - setLoading(false) - Alert.alert('Error', 'Unable to delete the account') - } -} - -function* onLogoutRequest() { - const isTtsActive = yield select(secureSelectors.isTtsActiveSelector) - - if (isTtsActive) { - yield call(closeOutTTs) - yield put(secureActions.setTtsActive(false)) - yield put(secureActions.verifyPeriodDayByUser([])) // TODO_ALEX: survey - } - yield put(secureActions.updateAllSurveyContent([])) // TODO_ALEX: survey - yield put( - secureActions.fetchSurveyContentSuccess({ - surveys: null, - }), - ) - yield put(secureActions.updateCompletedSurveys([])) // TODO_ALEX: survey - yield call(navigateAndReset, 'LoginStack', null) - yield put(secureActions.logout()) -} - -function* onJourneyCompletion(action: ExtractActionFromActionType<'JOURNEY_COMPLETION'>) { - const { data } = action.payload - const currentUser = yield select(secureSelectors.currentUserSelector) - let periodResult = null - if (yield fetchNetworkConnectionStatus()) { - try { - periodResult = yield httpClient.getPeriodCycles({ - age: moment().diff(moment(currentUser.dateOfBirth), 'years'), - period_lengths: [0, 0, 0, 0, 0, 0, 0, 0, 0, data[2].answer + 1], - cycle_lengths: [0, 0, 0, 0, 0, 0, 0, 0, 0, (data[3].answer + 1) * 7 + data[2].answer + 1], - }) - } catch (error) { - // console.log( error); - } - } - const stateToSet = PredictionState.fromData({ - isActive: data[0].answer === 'Yes' ? true : false, - startDate: moment(data[1].answer, 'DD-MMM-YYYY'), - periodLength: data[2].answer + 1, - cycleLength: (data[3].answer + 1) * 7 + data[2].answer + 1, - smaCycleLength: periodResult - ? periodResult.predicted_cycles[0] - : (data[3].answer + 1) * 7 + data[2].answer + 1, - smaPeriodLength: periodResult ? periodResult.predicted_periods[0] : data[2].answer + 1, - history: [], - }) - - yield put(secureActions.setPredictionEngineState(stateToSet)) - yield put(secureActions.updateFuturePrediction(true, null)) - yield put(secureActions.setTutorialOneActive(true)) - yield put(secureActions.setTutorialTwoActive(true)) - yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones - yield call(navigateAndReset, 'MainStack', null) -} - -export function* authSaga() { - yield all([ - takeLatest(REHYDRATE, onRehydrate), - takeLatest('LOGOUT_REQUEST', onLogoutRequest), - takeLatest('LOGIN_REQUEST', onLoginRequest), - takeLatest('DELETE_ACCOUNT_REQUEST', onDeleteAccountRequest), - takeLatest('CREATE_ACCOUNT_REQUEST', onCreateAccountRequest), - takeLatest('CREATE_ACCOUNT_SUCCESS', onCreateAccountSuccess), - takeLatest('CONVERT_GUEST_ACCOUNT', onConvertGuestAccount), - takeLatest('JOURNEY_COMPLETION', onJourneyCompletion), - ]) -} diff --git a/packages/components/src/redux/secure/sagas/contentSaga.ts b/packages/components/src/redux/secure/sagas/contentSaga.ts deleted file mode 100644 index 686b40945..000000000 --- a/packages/components/src/redux/secure/sagas/contentSaga.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { all, call, put, select, takeLatest } from 'redux-saga/effects' -import { RehydrateAction, REHYDRATE } from 'redux-persist' -import { ExtractActionFromActionType } from '../types' -import { - fromEncyclopedia, - fromQuizzes, - fromDidYouKnows, - fromSurveys, - fromHelpCenters, - fromAvatarMessages, - liveContent as staleContent, -} from '@oky/core' -import { httpClient } from '../../../services/HttpClient' -import { secureSelectors } from '../selectors' -import { secureActions } from '../actions' -import _ from 'lodash' -import messaging from '@react-native-firebase/messaging' -import { closeOutTTs } from '../../../services/textToSpeech' - -function* onRehydrate(action: RehydrateAction) { - const locale = yield select(secureSelectors.currentLocaleSelector) - - const hasPreviousContentFromStorage = action.payload && action.payload.content - - if (!hasPreviousContentFromStorage) { - yield put(secureActions.initStaleContent(staleContent[locale])) - } - - const now = new Date().getTime() - // TODO_ALEX what time interval should we use? - const fetchInterval = 0 // 1000 * 60 * 60 * 24 // 24 hours - const timeFetched = action.payload && action.payload.content?.timeFetched - const shouldFetch = !timeFetched || timeFetched + fetchInterval < now - - if (shouldFetch) { - yield put(secureActions.fetchContentRequest(locale)) - } -} - -// TODO_ALEX: survey -function* onFetchSurveyContent( - action: ExtractActionFromActionType<'FETCH_SURVEY_CONTENT_REQUEST'>, -) { - const locale = yield select(secureSelectors.currentLocaleSelector) - const userID = yield select(secureSelectors.currentUserSelector) - try { - const surveys = yield httpClient.fetchSurveys({ - locale, - userID, - }) - const previousSurveys = yield select(secureSelectors.allSurveys) - const completedSurveys = yield select(secureSelectors.completedSurveys) - const newSurveyArr = previousSurveys?.length ? previousSurveys : [] - surveys.forEach((item) => { - const itemExits = _.find(previousSurveys, { id: item.id }) - if (!itemExits) { - newSurveyArr.push(item) - } - }) - const finalArr = [] - newSurveyArr.forEach((item) => { - const itemExits = _.find(completedSurveys, { id: item.id }) - if (!itemExits) { - finalArr.push(item) - } - }) - - yield put(secureActions.updateAllSurveyContent(finalArr)) - } catch (error) { - // - } -} - -function* onFetchContentRequest(action: ExtractActionFromActionType<'FETCH_CONTENT_REQUEST'>) { - const { locale } = action.payload - - function* fetchEncyclopedia() { - const encyclopediaResponse = yield httpClient.fetchEncyclopedia({ locale }) - const videosResponse = yield httpClient.fetchVideos({ locale }) - return fromEncyclopedia({ encyclopediaResponse, videosResponse }) - } - - function* fetchPrivacyPolicy() { - const privacyPolicy = yield httpClient.fetchPrivacyPolicy({ - locale, - }) - return privacyPolicy - } - - function* fetchTermsAndConditions() { - const termsAndConditions = yield httpClient.fetchTermsAndConditions({ - locale, - }) - return termsAndConditions - } - - function* fetchAbout() { - const about = yield httpClient.fetchAbout({ - locale, - }) - return about - } - - function* fetchAboutBannerConditional() { - const timestamp = yield select((s) => s.content.aboutBannerTimestamp) - const aboutBanner = yield httpClient.fetchAboutBannerConditional({ - locale, - timestamp, - }) - return aboutBanner - } - - function* fetchHelpCenters() { - const helpCenterResponse = yield httpClient.fetchHelpCenters({ - locale, - }) - return fromHelpCenters(helpCenterResponse) - } - - function* fetchQuizzes() { - const quizzesResponse = yield httpClient.fetchQuizzes({ - locale, - }) - return fromQuizzes(quizzesResponse) - } - - function* fetchDidYouKnows() { - const didYouKnows = yield httpClient.fetchDidYouKnows({ - locale, - }) - return fromDidYouKnows(didYouKnows) - } - - function* fetchAvatarMessages() { - const avatarMessages = yield httpClient.fetchAvatarMessages({ - locale, - }) - return fromAvatarMessages(avatarMessages) - } - - try { - const { articles, categories, subCategories, videos } = yield fetchEncyclopedia() - const { quizzes } = yield fetchQuizzes() - const { didYouKnows } = yield fetchDidYouKnows() - const { helpCenters } = yield fetchHelpCenters() - const { avatarMessages } = yield fetchAvatarMessages() - const privacyPolicy = yield fetchPrivacyPolicy() - const termsAndConditions = yield fetchTermsAndConditions() - const about = yield fetchAbout() - const aboutBannerData = yield fetchAboutBannerConditional() - - yield put( - secureActions.fetchContentSuccess({ - timeFetched: new Date().getTime(), - articles: _.isEmpty(articles.allIds) ? staleContent[locale].articles : articles, - videos: _.isEmpty(videos.allIds) ? staleContent[locale].videos : videos, - categories: _.isEmpty(categories.allIds) ? staleContent[locale].categories : categories, - subCategories: _.isEmpty(subCategories.allIds) - ? staleContent[locale].subCategories - : subCategories, - quizzes: _.isEmpty(quizzes.allIds) ? staleContent[locale].quizzes : quizzes, - didYouKnows: _.isEmpty(didYouKnows.allIds) ? staleContent[locale].didYouKnows : didYouKnows, - helpCenters: _.isEmpty(helpCenters) ? staleContent[locale].helpCenters : helpCenters, - avatarMessages: _.isEmpty(avatarMessages) - ? staleContent[locale].avatarMessages - : avatarMessages, - privacyPolicy: _.isEmpty(privacyPolicy) - ? staleContent[locale].privacyPolicy - : privacyPolicy, - termsAndConditions: _.isEmpty(termsAndConditions) - ? staleContent[locale].termsAndConditions - : termsAndConditions, - about: _.isEmpty(about) ? staleContent[locale].about : about, - aboutBanner: aboutBannerData?.aboutBanner, - aboutBannerTimestamp: aboutBannerData?.aboutBannerTimestamp, - }), - ) - } catch (error) { - yield put(secureActions.fetchContentFailure()) - const aboutContent = yield select(secureSelectors.aboutContent) - if (!aboutContent) { - const localeInit = yield select(secureSelectors.currentLocaleSelector) - yield put(secureActions.initStaleContent(staleContent[localeInit])) - } - } -} - -function* onSetLocale(action: ExtractActionFromActionType<'SET_LOCALE'>) { - const { locale } = action.payload - const isTtsActive = yield select(secureSelectors.isTtsActiveSelector) - if (isTtsActive) { - // TODO_ALEX why? - yield call(closeOutTTs) - yield put(secureActions.setTtsActive(false)) - } - // unsubscribe from topic - // TODO_ALEX: use locales from submodule - messaging().unsubscribeFromTopic('oky_en_notifications') - messaging().unsubscribeFromTopic('oky_id_notifications') - messaging().unsubscribeFromTopic('oky_mn_notifications') - messaging().subscribeToTopic(`oky_${locale}_notifications`) - yield put(secureActions.initStaleContent(staleContent[locale])) - - yield put(secureActions.fetchContentRequest(locale)) -} - -export function* contentSaga() { - yield all([ - takeLatest(REHYDRATE, onRehydrate), - takeLatest('SET_LOCALE', onSetLocale), - takeLatest('FETCH_CONTENT_REQUEST', onFetchContentRequest), - takeLatest('FETCH_SURVEY_CONTENT_REQUEST', onFetchSurveyContent), - ]) -} diff --git a/packages/components/src/redux/secure/sagas/index.ts b/packages/components/src/redux/secure/sagas/index.ts deleted file mode 100644 index 9d3739975..000000000 --- a/packages/components/src/redux/secure/sagas/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { all, fork } from 'redux-saga/effects' - -import { analyticsSaga } from './analyticsSaga' -import { appSaga } from './appSaga' -import { authSaga } from './authSaga' -import { contentSaga } from './contentSaga' -import { smartPredictionbSaga } from './smartPredictionSaga' - -export function* secureRootSaga() { - yield all([ - fork(analyticsSaga), - fork(appSaga), - fork(authSaga), - fork(contentSaga), - fork(smartPredictionbSaga), - ]) -} diff --git a/packages/components/src/redux/secure/sagas/smartPredictionSaga.ts b/packages/components/src/redux/secure/sagas/smartPredictionSaga.ts deleted file mode 100644 index e4d622ea0..000000000 --- a/packages/components/src/redux/secure/sagas/smartPredictionSaga.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { all, put, takeLatest } from 'redux-saga/effects' -import { ExtractActionFromActionType } from '../types' - -import { httpClient } from '../../../services/HttpClient' - -import { secureActions } from '../actions' -import _ from 'lodash' -import { PredictionState } from '../../../prediction' - -function* onFetchUpdatedPredictedCycles( - action: ExtractActionFromActionType<'SMART_PREDICTION_REQUEST'>, -) { - try { - const { - age, - period_lengths, - cycle_lengths, - predictionFullState, - futurePredictionStatus, - } = action.payload - let predictionResult = null - predictionResult = yield httpClient.getPeriodCycles({ - age, - period_lengths, - cycle_lengths, - }) - const stateToSet = PredictionState.fromData({ - isActive: predictionFullState.isActive, - startDate: predictionFullState.currentCycle.startDate, - periodLength: predictionFullState.currentCycle.periodLength, - cycleLength: predictionFullState.currentCycle.cycleLength, - smaCycleLength: predictionResult.predicted_cycles[0], - smaPeriodLength: predictionResult.predicted_periods[0], - history: predictionFullState.history, - actualCurrentStartDate: predictionFullState.currentCycle, - }) - yield put(secureActions.setPredictionEngineState(stateToSet)) - yield put( - secureActions.updateFuturePrediction( - futurePredictionStatus, - predictionFullState.currentCycle, - ), - ) - } catch (error) { - yield put(secureActions.setSmartPredictionFailure(error)) - } -} - -export function* smartPredictionbSaga() { - yield all([takeLatest('SMART_PREDICTION_REQUEST', onFetchUpdatedPredictedCycles)]) -} diff --git a/packages/components/src/redux/secure/selectors/analyticsSelectors.ts b/packages/components/src/redux/secure/selectors/analyticsSelectors.ts deleted file mode 100644 index 2c15cae38..000000000 --- a/packages/components/src/redux/secure/selectors/analyticsSelectors.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { SecureReduxState } from '../reducers' - -const s = (state: SecureReduxState) => state.analytics - -export const allAnalyticsEventsSelector = (state: SecureReduxState) => s(state) diff --git a/packages/components/src/redux/secure/selectors/answerSelectors.ts b/packages/components/src/redux/secure/selectors/answerSelectors.ts deleted file mode 100644 index c636eb26c..000000000 --- a/packages/components/src/redux/secure/selectors/answerSelectors.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { allQuizzesSelectors } from './contentSelectors' - -import { Moment } from 'moment' -import { toShortISO } from '../../../services/dateUtils' -import _ from 'lodash' -import { SecureReduxState } from '../reducers' - -const s = (state: SecureReduxState) => state.answer - -export const surveyHasAnswerSelector = (state: SecureReduxState, id: string) => { - if (!s(state)[state.auth.user.id]) return false - return id in s(state)[state.auth.user.id].surveys -} - -// export const surveysWithoutAnswersSelector = (state: SecureReduxState) => { -// return allSurveysSelectors(state).filter(({ id }) => !surveyHasAnswerSelector(state, id)) -// } - -export const quizHasAnswerSelector = (state: SecureReduxState, id: string) => { - if (!s(state)[state.auth.user.id]) return false - return id in s(state)[state.auth.user.id].quizzes -} - -// Had a type error here had to add any to avoid -export const quizAnswerByDate: any = (state: SecureReduxState, date: Moment) => { - if (!s(state)[state.auth.user.id]) return null - return Object.values(s(state)[state.auth.user.id].quizzes).filter( - ({ utcDateTime }) => utcDateTime === date.toISOString(), - )[0] -} - -// Had a type error here had to add any to avoid -export const surveyAnswerByDate: any = (state: SecureReduxState, date: Moment) => { - if (!s(state)[state.auth.user.id]) return null - return Object.values(s(state)[state.auth.user.id].surveys).filter( - ({ utcDateTime }) => utcDateTime === date.toISOString(), - )[0] -} - -export const quizzesWithoutAnswersSelector = (state: SecureReduxState) => { - return allQuizzesSelectors(state).filter(({ id }) => !quizHasAnswerSelector(state, id)) -} - -export const cardAnswerSelector = (state: SecureReduxState, date: Moment) => { - if (!state.auth.user) return {} // for the use case on info screen where there is no authed user - if (!s(state)[state.auth.user.id]) return {} - return s(state)[state.auth.user.id]?.cards[toShortISO(date)] || {} -} -export const verifyPeriodDaySelectorWithDate = (state: SecureReduxState, date: Moment) => { - if (!state.auth.user) return {} // for the use case on info screen where there is no authed user - if (!s(state)[state.auth.user.id]) return {} - if (s(state)[state.auth.user.id]?.verifiedDates) { - return s(state)[state.auth.user.id]?.verifiedDates[toShortISO(date)] - } - return {} - // return s(state)[state.auth.user.id]?.verifiedDates[toShortISO(date)] || {} -} -export const allCardAnswersSelector = (state: SecureReduxState) => { - if (!state.auth.user) return {} // for the use case on info screen where there is no authed user - if (!s(state)[state.auth.user.id]) return {} - return s(state)[state.auth.user.id]?.verifiedDates || {} -} - -export const notesAnswerSelector = (state: SecureReduxState, date: Moment) => { - if (!s(state)[state.auth.user.id]) return {} - return s(state)[state.auth.user.id].notes[toShortISO(date)] || {} -} - -export const mostAnsweredSelector = ( - state: SecureReduxState, - startDate: Moment, - endDate: Moment, -) => { - if (!s(state)[state.auth.user.id]) return {} - const dates = Object.keys(s(state)[state.auth.user.id].cards) - const filteredDates = dates.filter((item) => { - return ( - parseInt(item, 10) > parseInt(startDate.format('YYYYMMDD'), 10) && - parseInt(item, 10) <= parseInt(endDate.format('YYYYMMDD'), 10) - ) - }) - - // This creates an array of all the selected moods (now that there are multiple) - const moodsInDateRange = filteredDates.reduce((acc, filteredDate) => { - return acc.concat(s(state)[state.auth.user.id].cards[filteredDate].mood) - }, []) - - // This counts occurrences of each item - const moodCountedObject = _.countBy(moodsInDateRange, (mood) => mood) - - const bodyInDateRange = filteredDates.reduce((acc, filteredDate) => { - return acc.concat(s(state)[state.auth.user.id].cards[filteredDate].body) - }, []) - - const bodyCountedObject = _.countBy(bodyInDateRange, (body) => body) - - const activityInDateRange = filteredDates.reduce((acc, filteredDate) => { - return acc.concat(s(state)[state.auth.user.id].cards[filteredDate].activity) - }, []) - - const activityCountedObject = _.countBy(activityInDateRange, (activity) => activity) - - const flowInDateRange = filteredDates.reduce((acc, filteredDate) => { - return acc.concat(s(state)[state.auth.user.id].cards[filteredDate].flow) - }, []) - - const flowCountedObject = _.countBy(flowInDateRange, (flow) => flow) - - delete moodCountedObject.undefined - delete bodyCountedObject.undefined - delete activityCountedObject.undefined - delete flowCountedObject.undefined - - const highestMood = Object.keys(moodCountedObject).reduce( - (a, b) => (moodCountedObject[a] > moodCountedObject[b] ? a : b), - null, - ) - const highestBody = Object.keys(bodyCountedObject).reduce( - (a, b) => (bodyCountedObject[a] > bodyCountedObject[b] ? a : b), - null, - ) - const highestActivity = Object.keys(activityCountedObject).reduce( - (a, b) => (activityCountedObject[a] > activityCountedObject[b] ? a : b), - null, - ) - const highestFlow = Object.keys(flowCountedObject).reduce( - (a, b) => (flowCountedObject[a] > flowCountedObject[b] ? a : b), - null, - ) - - return { - mood: highestMood, - body: highestBody, - activity: highestActivity, - flow: highestFlow, - } -} diff --git a/packages/components/src/redux/secure/selectors/appSelectors.ts b/packages/components/src/redux/secure/selectors/appSelectors.ts deleted file mode 100644 index bfac95510..000000000 --- a/packages/components/src/redux/secure/selectors/appSelectors.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { SecureReduxState } from '../reducers' - -const s = (state: SecureReduxState) => state.app -const predictionS = (state: SecureReduxState) => state.prediction -export const currentLocaleSelector = (state: SecureReduxState) => s(state).locale - -export const currentChosenRegionSelector = (state: SecureReduxState) => s(state).chosenRegion - -export const currentThemeSelector = (state: SecureReduxState) => s(state).theme - -export const currentAvatarSelector = (state: SecureReduxState) => s(state).avatar - -export const hasOpenedSelector = (state: SecureReduxState) => s(state).hasOpened - -export const isTutorialOneActiveSelector = (state: SecureReduxState) => s(state).isTutorialOneActive - -export const isTutorialTwoActiveSelector = (state: SecureReduxState) => s(state).isTutorialTwoActive - -export const isTtsActiveSelector = (state: SecureReduxState) => s(state).isTtsActive - -export const isLoginPasswordActiveSelector = (state: SecureReduxState) => - s(state).isLoginPasswordActive - -export const currentAppVersion = (state: SecureReduxState) => s(state).appVersionName - -export const currentFirebaseToken = (state: SecureReduxState) => s(state).firebaseToken - -export const userVerifiedDates = (state: SecureReduxState) => s(state).verifiedDates - -// Smart precition selectors -export const isFuturePredictionSelector = (state: SecureReduxState) => predictionS(state) -export const isFuturePredictionActiveSelector = (state: SecureReduxState) => - predictionS(state)?.futurePredictionStatus - -// export const smartPredictedPeriods = (state: SecureReduxState) => s(state).predicted_periods diff --git a/packages/components/src/redux/secure/selectors/authSelectors.ts b/packages/components/src/redux/secure/selectors/authSelectors.ts deleted file mode 100644 index f0e339d2a..000000000 --- a/packages/components/src/redux/secure/selectors/authSelectors.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { SecureReduxState } from '../reducers' - -const s = (state: SecureReduxState) => state.auth - -export const appTokenSelector = (state: SecureReduxState) => s(state).appToken - -export const authError = (state: SecureReduxState) => s(state).error - -export const currentUserSelector = (state: SecureReduxState) => s(state).user diff --git a/packages/components/src/redux/secure/selectors/contentSelectors.ts b/packages/components/src/redux/secure/selectors/contentSelectors.ts deleted file mode 100644 index 094d0d392..000000000 --- a/packages/components/src/redux/secure/selectors/contentSelectors.ts +++ /dev/null @@ -1,113 +0,0 @@ -import _ from 'lodash' -import { SecureReduxState } from '../reducers' - -const s = (state: SecureReduxState) => state.content - -export const allArticlesSelector = (state: SecureReduxState) => - s(state).articles.allIds.map((id) => s(state).articles.byId[id]) - -export const allVideosSelector = (state: SecureReduxState) => { - if (!s(state)?.videos?.allIds || !s(state)?.videos?.byId) return [] - return s(state).videos.allIds.map((id) => s(state).videos.byId[id]) -} - -export const articleByIDSelector = (state: SecureReduxState, id) => s(state).articles.byId[id] -export const videoByIDSelector = (state: SecureReduxState, id) => s(state)?.videos?.byId[id] - -export const articlesObjectByIDSelector = (state: SecureReduxState) => s(state).articles.byId - -// @ts-ignore -export const allHelpCentersForCurrentLocale: any = (state: SecureReduxState) => - s(state).helpCenters.filter((item) => item.lang === state.app.locale) - -export const allCategoriesSelector = (state: SecureReduxState) => - s(state).categories.allIds.map((id) => s(state).categories.byId[id]) - -export const allCategoryEmojis = (state: SecureReduxState) => { - const categories = allCategoriesSelector(state) - - return categories.map((item) => { - return { tag: item.tags.primary.name, emoji: item.tags.primary.emoji } - }) -} - -export const allSubCategoriesSelector = (state: SecureReduxState) => - s(state).subCategories.allIds.map((id) => s(state).subCategories.byId[id]) - -export const allSubCategoriesObjectSelector = (state: SecureReduxState) => - s(state).subCategories.byId - -export const categoryByIDSelector = (state: SecureReduxState, id) => s(state).categories.byId[id] - -export const subCategoryByIDSelector = (state: SecureReduxState, id) => - s(state).subCategories.byId[id] - -export const allAvatarText = (state: SecureReduxState) => s(state).avatarMessages - -export const privacyContent = (state: SecureReduxState) => s(state).privacyPolicy - -export const termsAndConditionsContent = (state: SecureReduxState) => s(state).termsAndConditions - -export const aboutContent = (state: SecureReduxState) => s(state).about - -export const allSurveys = (state: SecureReduxState) => s(state).allSurveys - -export const completedSurveys = (state: SecureReduxState) => s(state).completedSurveys - -export const aboutBanner = (state: SecureReduxState) => s(state).aboutBanner - -export const allQuizzesSelectors = (state: SecureReduxState) => { - // TODO: FIXME - const isUserYoungerThan15 = true - // moment() - // .utc() - // .diff(state.auth.user.dateOfBirth) < 15 - const tempArr = [] - const filteredArray = s(state).quizzes.allIds.reduce((acc, id) => { - if ( - (!s(state).quizzes.byId[id]?.isAgeRestricted && isUserYoungerThan15) || - !isUserYoungerThan15 - ) { - tempArr.push(s(state).quizzes.byId[id]) - } - if ( - (!s(state).quizzes.byId[id].isAgeRestricted && isUserYoungerThan15) || - !isUserYoungerThan15 - ) { - acc.push(s(state).quizzes.byId[id]) - } - return acc - }, []) - - // In the extreme event of all content being age restricted return the first quiz/ did you know instead of crashing the app - - if (_.isEmpty(filteredArray)) { - return [s(state).quizzes.byId[s(state).quizzes.allIds[0]]] - } - - return filteredArray -} - -export const allDidYouKnowsSelectors = (state: SecureReduxState) => { - // TODO_ALEX: FIXME - const isUserYoungerThan15 = true - // moment() - // .utc() - // .diff(state.auth.user.dateOfBirth) < 15 - const filteredArray = s(state).didYouKnows.allIds.reduce((acc, id) => { - if ( - (!s(state).didYouKnows.byId[id]?.isAgeRestricted && isUserYoungerThan15) || - !isUserYoungerThan15 - ) { - acc.push(s(state).didYouKnows.byId[id]) - } - return acc - }, []) - - // In the extreme event of all content being age restricted return the first quiz/ did you know instead of crashing the app - if (_.isEmpty(filteredArray)) { - return [s(state).didYouKnows.byId[s(state).didYouKnows.allIds[0]]] - } - - return filteredArray -} diff --git a/packages/components/src/redux/secure/selectors/index.ts b/packages/components/src/redux/secure/selectors/index.ts deleted file mode 100644 index 3620d791d..000000000 --- a/packages/components/src/redux/secure/selectors/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import * as analyticsSelectors from './analyticsSelectors' -import * as answerSelectors from './answerSelectors' -import * as appSelectors from './appSelectors' -import * as authSelectors from './authSelectors' -import * as contentSelectors from './contentSelectors' - -export const secureSelectors = { - ...analyticsSelectors, - ...answerSelectors, - ...appSelectors, - ...authSelectors, - ...contentSelectors, -} diff --git a/packages/components/src/redux/secure/sync/index.ts b/packages/components/src/redux/secure/sync/index.ts deleted file mode 100644 index 2e81c498c..000000000 --- a/packages/components/src/redux/secure/sync/index.ts +++ /dev/null @@ -1,42 +0,0 @@ -import _ from 'lodash' -import { Action, Reducer, ReducersMapObject } from 'redux' - -function syncReducerFactory(innerReducer: Reducer, reducerName: string): Reducer { - return (state, action) => { - if (action.type === 'REFRESH_STORE' && action.payload[reducerName]) { - return { - ...state, - ...action.payload[reducerName], - } - } - - return innerReducer(state, action) - } -} - -export function syncReducers( - reducers: ReducersMapObject, - reducerNames: string[], -) { - const reducerKeys = Object.keys(reducers) - const reducersWrappers = reducerNames.reduce((prev, reducerName) => { - if (!reducerKeys.includes(reducerName)) { - return prev - } - - const reducer = reducers[reducerName] - return { - ...prev, - [reducerName]: syncReducerFactory(reducer, reducerName), - } - }, {}) - - return { - ...reducers, - ...reducersWrappers, - } -} - -export function extractReducerState(state, reducerNames: string[]) { - return _.pick(state, ...reducerNames) -} diff --git a/packages/components/src/redux/secure/types/index.ts b/packages/components/src/redux/secure/types/index.ts deleted file mode 100644 index bf232373f..000000000 --- a/packages/components/src/redux/secure/types/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { secureActions } from '../actions' -import { ActionsUnion, ActionsOfType } from '../../types' - -export type SecureActions = ActionsUnion - -export type SecureActionTypes = SecureActions[keyof SecureActions] - -export type ExtractActionFromActionType = ActionsOfType< - SecureActions, - ActionType -> diff --git a/packages/components/src/redux/secure/useSecureSelector.ts b/packages/components/src/redux/secure/useSecureSelector.ts deleted file mode 100644 index 48a131f28..000000000 --- a/packages/components/src/redux/secure/useSecureSelector.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { useSelector as useReduxSelector, TypedUseSelectorHook } from 'react-redux' -import { SecureReduxState } from './reducers' - -export const useSecureSelector: TypedUseSelectorHook = useReduxSelector From 1c2ae0bda818543a229cf0dd7b6d4da744a62db0 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Tue, 2 Jan 2024 16:50:43 +0800 Subject: [PATCH 25/83] Remove unnecessary /common folder --- .../__tests__/redux/actions/appActions.test.ts | 2 +- .../__tests__/redux/reducers/authReducer.test.ts | 4 ++-- .../src/components/common/Avatar/Avatar.tsx | 4 ++-- .../src/components/common/CalendarList.tsx | 4 ++-- .../components/src/components/common/DateBadge.tsx | 4 ++-- .../components/src/components/common/DayBadge.tsx | 4 ++-- .../src/components/common/LanguageSelect.tsx | 6 +++--- .../src/components/context/DisplayTextContext.tsx | 4 ++-- .../src/components/context/LocaleContext.tsx | 2 +- .../src/components/context/PredictionProvider.tsx | 4 ++-- .../src/components/context/ThemeContext.tsx | 4 ++-- .../components/src/hooks/useTextToSpeechHook.ts | 4 ++-- packages/components/src/redux/StoreCoordinator.tsx | 6 +++--- .../redux/{common => }/actions/analyticsActions.ts | 2 +- .../src/redux/{common => }/actions/answerActions.ts | 4 ++-- .../src/redux/{common => }/actions/appActions.ts | 2 +- .../src/redux/{common => }/actions/authActions.ts | 2 +- .../redux/{common => }/actions/contentActions.ts | 4 ++-- .../src/redux/{common => }/actions/index.ts | 0 .../{common => }/actions/newPredictionAction.ts | 0 .../redux/{common => }/actions/predictionActions.ts | 4 ++-- packages/components/src/redux/common/commonStore.ts | 13 ------------- packages/components/src/redux/helpers.ts | 2 +- .../redux/{common => }/reducers/accessReducer.ts | 2 +- .../redux/{common => }/reducers/analyticsReducer.ts | 0 .../redux/{common => }/reducers/answerReducer.ts | 4 ++-- .../src/redux/{common => }/reducers/appReducer.ts | 2 +- .../src/redux/{common => }/reducers/authReducer.ts | 0 .../redux/{common => }/reducers/contentReducer.ts | 2 +- .../src/redux/{common => }/reducers/index.ts | 0 .../src/redux/{common => }/reducers/keysReducer.ts | 0 .../{common => }/reducers/predictionReducer.ts | 2 +- .../src/redux/{common => }/sagas/analyticsSaga.ts | 4 ++-- .../src/redux/{common => }/sagas/appSaga.ts | 6 +++--- .../src/redux/{common => }/sagas/authSaga.ts | 12 ++++++------ .../src/redux/{common => }/sagas/contentSaga.ts | 4 ++-- .../src/redux/{common => }/sagas/index.ts | 0 .../redux/{common => }/sagas/smartPredictionSaga.ts | 4 ++-- .../{common => }/selectors/analyticsSelectors.ts | 0 .../redux/{common => }/selectors/answerSelectors.ts | 2 +- .../redux/{common => }/selectors/appSelectors.ts | 0 .../redux/{common => }/selectors/authSelectors.ts | 0 .../{common => }/selectors/contentSelectors.ts | 0 .../src/redux/{common => }/selectors/index.ts | 0 .../components/src/redux/{common => }/sync/index.ts | 0 .../src/redux/{common => }/types/index.ts | 2 +- packages/components/src/redux/{ => types}/types.ts | 0 .../src/redux/{common => }/useCommonSelector.ts | 0 packages/components/src/screens/ArticlesScreen.tsx | 4 ++-- .../components/src/screens/AvatarAndThemeScreen.tsx | 6 +++--- packages/components/src/screens/ContactUsScreen.tsx | 4 ++-- packages/components/src/screens/DayScreen.tsx | 4 ++-- .../components/src/screens/EditProfileScreen.tsx | 6 +++--- .../components/src/screens/EncyclopediaScreen.tsx | 4 ++-- packages/components/src/screens/FindHelpScreen.tsx | 4 ++-- packages/components/src/screens/MainScreen.tsx | 6 +++--- .../components/src/screens/OnboardingScreen.tsx | 2 +- .../src/screens/PasswordRequestScreen.tsx | 6 +++--- packages/components/src/screens/ProfileScreen.tsx | 6 +++--- packages/components/src/screens/SettingsScreen.tsx | 6 +++--- packages/components/src/screens/SplashScreen.tsx | 6 +++--- .../components/src/screens/TutorialFirstScreen.tsx | 6 +++--- .../components/src/screens/TutorialSecondScreen.tsx | 6 +++--- packages/components/src/screens/VideosScreen.tsx | 4 ++-- .../components/src/screens/authScreen/Login.tsx | 4 ++-- .../components/src/screens/authScreen/SignUp.tsx | 2 +- .../src/screens/authScreen/signUp/AskLocation.tsx | 4 ++-- .../src/screens/dayScreen/DayCarousel.tsx | 6 +++--- .../src/screens/dayScreen/DayCarouselItem.tsx | 4 ++-- .../src/screens/dayScreen/DidYouKnowCard.tsx | 4 ++-- .../components/src/screens/dayScreen/NoteCard.tsx | 6 +++--- .../components/src/screens/dayScreen/QuizCard.tsx | 6 +++--- .../components/src/screens/dayScreen/SurveyCard.tsx | 6 +++--- .../src/screens/encyclopediaScreen/SearchBar.tsx | 4 ++-- .../src/screens/journeyScreen/FinalJourneyCard.tsx | 2 +- .../src/screens/journeyScreen/JourneyCard.tsx | 4 ++-- .../components/src/screens/mainScreen/Calendar.tsx | 4 ++-- .../src/screens/mainScreen/ColourButtons.tsx | 6 +++--- .../screens/mainScreen/wheelCarousel/Carousel.tsx | 4 ++-- .../mainScreen/wheelCarousel/CarouselElement.tsx | 4 ++-- .../mainScreen/wheelCarousel/CircularElement.tsx | 4 ++-- .../mainScreen/wheelCarousel/CircularSelection.tsx | 6 +++--- .../src/screens/profileScreen/CycleCard.tsx | 4 ++-- .../components/src/screens/settings/AboutScreen.tsx | 4 ++-- .../src/screens/settings/AccessScreen.tsx | 6 +++--- .../src/screens/settings/PrivacyScreen.tsx | 4 ++-- .../components/src/screens/settings/TermsScreen.tsx | 4 ++-- .../src/screens/tutorial/CalendarAssetDemo.tsx | 4 ++-- .../src/screens/tutorial/DayAssetDemo.tsx | 4 ++-- .../src/screens/tutorial/NoteAssetDemo.tsx | 4 ++-- 90 files changed, 156 insertions(+), 169 deletions(-) rename packages/components/src/redux/{common => }/actions/analyticsActions.ts (86%) rename packages/components/src/redux/{common => }/actions/answerActions.ts (94%) rename packages/components/src/redux/{common => }/actions/appActions.ts (97%) rename packages/components/src/redux/{common => }/actions/authActions.ts (98%) rename packages/components/src/redux/{common => }/actions/contentActions.ts (96%) rename packages/components/src/redux/{common => }/actions/index.ts (100%) rename packages/components/src/redux/{common => }/actions/newPredictionAction.ts (100%) rename packages/components/src/redux/{common => }/actions/predictionActions.ts (91%) delete mode 100644 packages/components/src/redux/common/commonStore.ts rename packages/components/src/redux/{common => }/reducers/accessReducer.ts (96%) rename packages/components/src/redux/{common => }/reducers/analyticsReducer.ts (100%) rename packages/components/src/redux/{common => }/reducers/answerReducer.ts (98%) rename packages/components/src/redux/{common => }/reducers/appReducer.ts (98%) rename packages/components/src/redux/{common => }/reducers/authReducer.ts (100%) rename packages/components/src/redux/{common => }/reducers/contentReducer.ts (99%) rename packages/components/src/redux/{common => }/reducers/index.ts (100%) rename packages/components/src/redux/{common => }/reducers/keysReducer.ts (100%) rename packages/components/src/redux/{common => }/reducers/predictionReducer.ts (93%) rename packages/components/src/redux/{common => }/sagas/analyticsSaga.ts (92%) rename packages/components/src/redux/{common => }/sagas/appSaga.ts (89%) rename packages/components/src/redux/{common => }/sagas/authSaga.ts (96%) rename packages/components/src/redux/{common => }/sagas/contentSaga.ts (98%) rename packages/components/src/redux/{common => }/sagas/index.ts (100%) rename packages/components/src/redux/{common => }/sagas/smartPredictionSaga.ts (93%) rename packages/components/src/redux/{common => }/selectors/analyticsSelectors.ts (100%) rename packages/components/src/redux/{common => }/selectors/answerSelectors.ts (98%) rename packages/components/src/redux/{common => }/selectors/appSelectors.ts (100%) rename packages/components/src/redux/{common => }/selectors/authSelectors.ts (100%) rename packages/components/src/redux/{common => }/selectors/contentSelectors.ts (100%) rename packages/components/src/redux/{common => }/selectors/index.ts (100%) rename packages/components/src/redux/{common => }/sync/index.ts (100%) rename packages/components/src/redux/{common => }/types/index.ts (83%) rename packages/components/src/redux/{ => types}/types.ts (100%) rename packages/components/src/redux/{common => }/useCommonSelector.ts (100%) diff --git a/packages/components/__tests__/redux/actions/appActions.test.ts b/packages/components/__tests__/redux/actions/appActions.test.ts index 8f951bc71..c21231924 100644 --- a/packages/components/__tests__/redux/actions/appActions.test.ts +++ b/packages/components/__tests__/redux/actions/appActions.test.ts @@ -1,7 +1,7 @@ import configureStore from 'redux-mock-store' import _ from 'lodash' -import { commonActions } from '../../../src/redux/common/actions' +import { commonActions } from '../../../src/redux/actions' const middleWares = [] const mockStore = configureStore(middleWares) diff --git a/packages/components/__tests__/redux/reducers/authReducer.test.ts b/packages/components/__tests__/redux/reducers/authReducer.test.ts index d207ddc5a..a3303a0c5 100644 --- a/packages/components/__tests__/redux/reducers/authReducer.test.ts +++ b/packages/components/__tests__/redux/reducers/authReducer.test.ts @@ -3,8 +3,8 @@ import moment from 'moment' import configureStore from 'redux-mock-store' import _ from 'lodash' -import { commonActions } from '../../../src/redux/common/actions' -import { authReducer } from '../../../src/redux/common/reducers/authReducer' +import { commonActions } from '../../../src/redux/actions' +import { authReducer } from '../../../src/redux/reducers/authReducer' const middleWares = [] const mockStore = configureStore(middleWares) diff --git a/packages/components/src/components/common/Avatar/Avatar.tsx b/packages/components/src/components/common/Avatar/Avatar.tsx index 3420da6c9..9d47b2bcf 100644 --- a/packages/components/src/components/common/Avatar/Avatar.tsx +++ b/packages/components/src/components/common/Avatar/Avatar.tsx @@ -8,8 +8,8 @@ import { FloatingQuestion } from './FloatingQuestion' import styled from 'styled-components/native' import { Icon } from '../Icon' import { HeartAnimation } from './HeartAnimation' -import { useCommonSelector } from '../../../redux/common/useCommonSelector' -import { commonSelectors } from '../../../redux/common/selectors/index' +import { useCommonSelector } from '../../../redux/useCommonSelector' +import { commonSelectors } from '../../../redux/selectors/index' import moment from 'moment' import { useDisplayText } from '../../context/DisplayTextContext' import { useTodayPrediction } from '../../context/PredictionProvider' diff --git a/packages/components/src/components/common/CalendarList.tsx b/packages/components/src/components/common/CalendarList.tsx index 13fca2310..553086f01 100644 --- a/packages/components/src/components/common/CalendarList.tsx +++ b/packages/components/src/components/common/CalendarList.tsx @@ -3,8 +3,8 @@ import { Image } from 'react-native' import { CalendarList as DefaultCalendarList, LocaleConfig } from 'react-native-calendars' import momentTimezone from 'moment-timezone' import { assets } from '../../assets/index' -import { useCommonSelector } from '../../redux/common/useCommonSelector' -import { commonSelectors } from '../../redux/common/selectors' +import { useCommonSelector } from '../../redux/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' import { calendarTranslations } from '@oky/core' LocaleConfig.locales = { diff --git a/packages/components/src/components/common/DateBadge.tsx b/packages/components/src/components/common/DateBadge.tsx index 635cb9ccb..a618ac4c8 100644 --- a/packages/components/src/components/common/DateBadge.tsx +++ b/packages/components/src/components/common/DateBadge.tsx @@ -6,12 +6,12 @@ import { translate } from '../../i18n' import { TouchableOpacity } from 'react-native' import _ from 'lodash' import moment from 'moment' -import { commonSelectors } from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/selectors' import { useTodayPrediction, useActualCurrentStartDateSelector, } from '../../components/context/PredictionProvider' -import { useCommonSelector } from '../../redux/common/useCommonSelector' +import { useCommonSelector } from '../../redux/useCommonSelector' function checkForVerifiedDay(cardValues) { if (_.has(cardValues, 'periodDay')) { diff --git a/packages/components/src/components/common/DayBadge.tsx b/packages/components/src/components/common/DayBadge.tsx index a049fe350..5d0e0a1ea 100644 --- a/packages/components/src/components/common/DayBadge.tsx +++ b/packages/components/src/components/common/DayBadge.tsx @@ -4,12 +4,12 @@ import styled from 'styled-components/native' import { TextWithoutTranslation, Text } from './Text' import _ from 'lodash' import moment from 'moment' -import { commonSelectors } from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/selectors' import { useTodayPrediction, useActualCurrentStartDateSelector, } from '../../components/context/PredictionProvider' -import { useCommonSelector } from '../../redux/common/useCommonSelector' +import { useCommonSelector } from '../../redux/useCommonSelector' function checkForVerifiedDay(cardValues) { if (_.has(cardValues, 'periodDay')) { diff --git a/packages/components/src/components/common/LanguageSelect.tsx b/packages/components/src/components/common/LanguageSelect.tsx index 9cf34cfc5..e5de49835 100644 --- a/packages/components/src/components/common/LanguageSelect.tsx +++ b/packages/components/src/components/common/LanguageSelect.tsx @@ -1,8 +1,8 @@ import React from 'react' import styled from 'styled-components/native' -import { useCommonSelector } from '../../redux/common/useCommonSelector' -import { commonSelectors } from '../../redux/common/selectors' -import { commonActions } from '../../redux/common/actions/index' +import { useCommonSelector } from '../../redux/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' +import { commonActions } from '../../redux/actions/index' import { useDispatch } from 'react-redux' import { PrimaryButton } from './buttons/PrimaryButton' diff --git a/packages/components/src/components/context/DisplayTextContext.tsx b/packages/components/src/components/context/DisplayTextContext.tsx index 4261f053a..81cda0323 100644 --- a/packages/components/src/components/context/DisplayTextContext.tsx +++ b/packages/components/src/components/context/DisplayTextContext.tsx @@ -2,8 +2,8 @@ import React from 'react' import Tts from 'react-native-tts' import _ from 'lodash' import { translate } from '../../i18n' -import { useCommonSelector } from '../../redux/common/useCommonSelector' -import { commonSelectors } from '../../redux/common/selectors' +import { useCommonSelector } from '../../redux/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' interface Props { text: null | string diff --git a/packages/components/src/components/context/LocaleContext.tsx b/packages/components/src/components/context/LocaleContext.tsx index 1e4b0bb85..ef34e5ed8 100644 --- a/packages/components/src/components/context/LocaleContext.tsx +++ b/packages/components/src/components/context/LocaleContext.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { useCommonSelector } from '../../redux/common/useCommonSelector' +import { useCommonSelector } from '../../redux/useCommonSelector' import { currentLocale, configureI18n } from '../../i18n' export function LocaleProvider({ children }) { diff --git a/packages/components/src/components/context/PredictionProvider.tsx b/packages/components/src/components/context/PredictionProvider.tsx index a26509bd9..d84b57a18 100644 --- a/packages/components/src/components/context/PredictionProvider.tsx +++ b/packages/components/src/components/context/PredictionProvider.tsx @@ -3,9 +3,9 @@ import moment, { Moment } from 'moment' import _ from 'lodash' import { PredictionState, PredictionEngine } from '../../prediction' -import { useCommonSelector } from '../../redux/common/useCommonSelector' +import { useCommonSelector } from '../../redux/useCommonSelector' import { useDispatch } from 'react-redux' -import { commonActions } from '../../redux/common/actions' +import { commonActions } from '../../redux/actions' type PredictionDispatch = typeof PredictionEngine.prototype.userInputDispatch diff --git a/packages/components/src/components/context/ThemeContext.tsx b/packages/components/src/components/context/ThemeContext.tsx index 01b099a3c..dd4696a45 100644 --- a/packages/components/src/components/context/ThemeContext.tsx +++ b/packages/components/src/components/context/ThemeContext.tsx @@ -1,7 +1,7 @@ import React from 'react' import { ThemeContext, ThemeProvider as StyledThemeProvider } from 'styled-components' -import { useCommonSelector } from '../../redux/common/useCommonSelector' -import { commonSelectors } from '../../redux/common/selectors' +import { useCommonSelector } from '../../redux/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' import { themes } from '@oky/core' export function ThemeProvider({ children }) { diff --git a/packages/components/src/hooks/useTextToSpeechHook.ts b/packages/components/src/hooks/useTextToSpeechHook.ts index cbcda73cd..013a806d3 100644 --- a/packages/components/src/hooks/useTextToSpeechHook.ts +++ b/packages/components/src/hooks/useTextToSpeechHook.ts @@ -1,7 +1,7 @@ import React from 'react' import { speakArray, clearTTSQueue } from '../services/textToSpeech' -import { useCommonSelector } from '../redux/common/useCommonSelector' -import { commonSelectors } from '../redux/common/selectors' +import { useCommonSelector } from '../redux/useCommonSelector' +import { commonSelectors } from '../redux/selectors' export function useTextToSpeechHook({ navigation, text }) { const hasTtsActive = useCommonSelector(commonSelectors.isTtsActiveSelector) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index 2889573ee..1e412af1a 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -3,10 +3,10 @@ import { Provider as ReduxProvider } from 'react-redux' import { PersistGate } from 'redux-persist/integration/react' import { configureStore } from './store' import { config } from './config' -import { commonRootReducer } from './common/reducers' -import { commonRootSaga } from './common/sagas' +import { commonRootReducer } from './reducers' +import { commonRootSaga } from './sagas' import { REHYDRATE } from 'redux-persist' -import { commonActions } from './common/actions' +import { commonActions } from './actions' interface Keys { key: string diff --git a/packages/components/src/redux/common/actions/analyticsActions.ts b/packages/components/src/redux/actions/analyticsActions.ts similarity index 86% rename from packages/components/src/redux/common/actions/analyticsActions.ts rename to packages/components/src/redux/actions/analyticsActions.ts index f25f25c0e..d7126885e 100644 --- a/packages/components/src/redux/common/actions/analyticsActions.ts +++ b/packages/components/src/redux/actions/analyticsActions.ts @@ -1,4 +1,4 @@ -import { createAction } from '../../helpers' +import { createAction } from '../helpers' export function queueEvent({ id, diff --git a/packages/components/src/redux/common/actions/answerActions.ts b/packages/components/src/redux/actions/answerActions.ts similarity index 94% rename from packages/components/src/redux/common/actions/answerActions.ts rename to packages/components/src/redux/actions/answerActions.ts index 177335c11..9332dbee0 100644 --- a/packages/components/src/redux/common/actions/answerActions.ts +++ b/packages/components/src/redux/actions/answerActions.ts @@ -1,6 +1,6 @@ -import { createAction } from '../../helpers' +import { createAction } from '../helpers' import { Moment } from 'moment' -import { CardName, DailyCard } from '../../../types' +import { CardName, DailyCard } from '../../types' import { AnswerForUserState } from '../reducers/answerReducer' import { User } from '../reducers/authReducer' diff --git a/packages/components/src/redux/common/actions/appActions.ts b/packages/components/src/redux/actions/appActions.ts similarity index 97% rename from packages/components/src/redux/common/actions/appActions.ts rename to packages/components/src/redux/actions/appActions.ts index 0a7918ecf..b48add7f4 100644 --- a/packages/components/src/redux/common/actions/appActions.ts +++ b/packages/components/src/redux/actions/appActions.ts @@ -1,5 +1,5 @@ import { AvatarName, ThemeName } from '@oky/core' -import { createAction } from '../../helpers' +import { createAction } from '../helpers' export function setTheme(theme: ThemeName) { return createAction('SET_THEME', { theme }) diff --git a/packages/components/src/redux/common/actions/authActions.ts b/packages/components/src/redux/actions/authActions.ts similarity index 98% rename from packages/components/src/redux/common/actions/authActions.ts rename to packages/components/src/redux/actions/authActions.ts index 32997b353..f8d395d2c 100644 --- a/packages/components/src/redux/common/actions/authActions.ts +++ b/packages/components/src/redux/actions/authActions.ts @@ -1,4 +1,4 @@ -import { createAction } from '../../helpers' +import { createAction } from '../helpers' export function loginRequest({ name, password }) { return createAction('LOGIN_REQUEST', { name, password }) diff --git a/packages/components/src/redux/common/actions/contentActions.ts b/packages/components/src/redux/actions/contentActions.ts similarity index 96% rename from packages/components/src/redux/common/actions/contentActions.ts rename to packages/components/src/redux/actions/contentActions.ts index dd9199406..d804b760f 100644 --- a/packages/components/src/redux/common/actions/contentActions.ts +++ b/packages/components/src/redux/actions/contentActions.ts @@ -1,4 +1,4 @@ -import { createAction } from '../../helpers' +import { createAction } from '../helpers' import { Articles, Categories, @@ -14,7 +14,7 @@ import { AllSurveys, CompletedSurveys, Videos, -} from '../../../types' +} from '../../types' export function initStaleContent(payload: { articles: Articles diff --git a/packages/components/src/redux/common/actions/index.ts b/packages/components/src/redux/actions/index.ts similarity index 100% rename from packages/components/src/redux/common/actions/index.ts rename to packages/components/src/redux/actions/index.ts diff --git a/packages/components/src/redux/common/actions/newPredictionAction.ts b/packages/components/src/redux/actions/newPredictionAction.ts similarity index 100% rename from packages/components/src/redux/common/actions/newPredictionAction.ts rename to packages/components/src/redux/actions/newPredictionAction.ts diff --git a/packages/components/src/redux/common/actions/predictionActions.ts b/packages/components/src/redux/actions/predictionActions.ts similarity index 91% rename from packages/components/src/redux/common/actions/predictionActions.ts rename to packages/components/src/redux/actions/predictionActions.ts index 50390c70b..bb0c12427 100644 --- a/packages/components/src/redux/common/actions/predictionActions.ts +++ b/packages/components/src/redux/actions/predictionActions.ts @@ -1,5 +1,5 @@ -import { createAction } from '../../helpers' -import { PredictionState } from '../../../prediction' +import { createAction } from '../helpers' +import { PredictionState } from '../../prediction' import { Moment } from 'moment' export function setPredictionEngineState(predictionState: PredictionState) { diff --git a/packages/components/src/redux/common/commonStore.ts b/packages/components/src/redux/common/commonStore.ts deleted file mode 100644 index ef86a6880..000000000 --- a/packages/components/src/redux/common/commonStore.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { config } from '../config' -import { configureStore } from '../store' -import { commonRootReducer } from './reducers' -import { commonRootSaga } from './sagas' - -const { persistor, store } = configureStore({ - key: 'primary', - secretKey: config.REDUX_ENCRYPT_KEY, - rootReducer: commonRootReducer, - rootSaga: commonRootSaga, -}) - -export { store as commonStore, persistor as commonPersistor } diff --git a/packages/components/src/redux/helpers.ts b/packages/components/src/redux/helpers.ts index 2a8ecf41c..31b264f78 100644 --- a/packages/components/src/redux/helpers.ts +++ b/packages/components/src/redux/helpers.ts @@ -1,4 +1,4 @@ -import { Action } from './types' +import { Action } from './types/types' export function createAction(type: T): Action diff --git a/packages/components/src/redux/common/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts similarity index 96% rename from packages/components/src/redux/common/reducers/accessReducer.ts rename to packages/components/src/redux/reducers/accessReducer.ts index b030b504e..cb10679ac 100644 --- a/packages/components/src/redux/common/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -1,4 +1,4 @@ -import { hash } from '../../../services/hash' +import { hash } from '../../services/hash' import { CommonActions } from '../types' import { v4 as uuidv4 } from 'uuid' import _ from 'lodash' diff --git a/packages/components/src/redux/common/reducers/analyticsReducer.ts b/packages/components/src/redux/reducers/analyticsReducer.ts similarity index 100% rename from packages/components/src/redux/common/reducers/analyticsReducer.ts rename to packages/components/src/redux/reducers/analyticsReducer.ts diff --git a/packages/components/src/redux/common/reducers/answerReducer.ts b/packages/components/src/redux/reducers/answerReducer.ts similarity index 98% rename from packages/components/src/redux/common/reducers/answerReducer.ts rename to packages/components/src/redux/reducers/answerReducer.ts index 80843488c..cd5291b31 100644 --- a/packages/components/src/redux/common/reducers/answerReducer.ts +++ b/packages/components/src/redux/reducers/answerReducer.ts @@ -1,7 +1,7 @@ import { CommonActions } from '../types' import { combineReducers } from 'redux' -import { toShortISO } from '../../../services/dateUtils' -import { DailyCard } from '../../../types' +import { toShortISO } from '../../services/dateUtils' +import { DailyCard } from '../../types' export interface AnswerForUserState { surveys: { diff --git a/packages/components/src/redux/common/reducers/appReducer.ts b/packages/components/src/redux/reducers/appReducer.ts similarity index 98% rename from packages/components/src/redux/common/reducers/appReducer.ts rename to packages/components/src/redux/reducers/appReducer.ts index 1a57b56d5..b03f816f0 100644 --- a/packages/components/src/redux/common/reducers/appReducer.ts +++ b/packages/components/src/redux/reducers/appReducer.ts @@ -1,6 +1,6 @@ import _ from 'lodash' import { CommonActions } from '../types' -import { currentLocale } from '../../../i18n' +import { currentLocale } from '../../i18n' import DeviceInfo from 'react-native-device-info' import { AvatarName, ThemeName, defaultAvatar, defaultTheme } from '@oky/core' diff --git a/packages/components/src/redux/common/reducers/authReducer.ts b/packages/components/src/redux/reducers/authReducer.ts similarity index 100% rename from packages/components/src/redux/common/reducers/authReducer.ts rename to packages/components/src/redux/reducers/authReducer.ts diff --git a/packages/components/src/redux/common/reducers/contentReducer.ts b/packages/components/src/redux/reducers/contentReducer.ts similarity index 99% rename from packages/components/src/redux/common/reducers/contentReducer.ts rename to packages/components/src/redux/reducers/contentReducer.ts index 5f1892786..e26bfb614 100644 --- a/packages/components/src/redux/common/reducers/contentReducer.ts +++ b/packages/components/src/redux/reducers/contentReducer.ts @@ -14,7 +14,7 @@ import { AllSurveys, CompletedSurveys, Videos, -} from '../../../types' +} from '../../types' import { CommonActions } from '../types/index' export interface ContentState { diff --git a/packages/components/src/redux/common/reducers/index.ts b/packages/components/src/redux/reducers/index.ts similarity index 100% rename from packages/components/src/redux/common/reducers/index.ts rename to packages/components/src/redux/reducers/index.ts diff --git a/packages/components/src/redux/common/reducers/keysReducer.ts b/packages/components/src/redux/reducers/keysReducer.ts similarity index 100% rename from packages/components/src/redux/common/reducers/keysReducer.ts rename to packages/components/src/redux/reducers/keysReducer.ts diff --git a/packages/components/src/redux/common/reducers/predictionReducer.ts b/packages/components/src/redux/reducers/predictionReducer.ts similarity index 93% rename from packages/components/src/redux/common/reducers/predictionReducer.ts rename to packages/components/src/redux/reducers/predictionReducer.ts index 3dfeb95bb..228526366 100644 --- a/packages/components/src/redux/common/reducers/predictionReducer.ts +++ b/packages/components/src/redux/reducers/predictionReducer.ts @@ -1,5 +1,5 @@ import _ from 'lodash' -import { PredictionSerializableState } from '../../../prediction' +import { PredictionSerializableState } from '../../prediction' import { CommonActions } from '../types/index' diff --git a/packages/components/src/redux/common/sagas/analyticsSaga.ts b/packages/components/src/redux/sagas/analyticsSaga.ts similarity index 92% rename from packages/components/src/redux/common/sagas/analyticsSaga.ts rename to packages/components/src/redux/sagas/analyticsSaga.ts index cb61d7f13..992cf92f2 100644 --- a/packages/components/src/redux/common/sagas/analyticsSaga.ts +++ b/packages/components/src/redux/sagas/analyticsSaga.ts @@ -1,8 +1,8 @@ import { all, delay, fork, put, select, takeLatest } from 'redux-saga/effects' import { v4 as uuidv4 } from 'uuid' import moment from 'moment' -import { fetchNetworkConnectionStatus } from '../../../services/network' -import { httpClient } from '../../../services/HttpClient' +import { fetchNetworkConnectionStatus } from '../../services/network' +import { httpClient } from '../../services/HttpClient' import { commonActions } from '../actions' import { commonSelectors } from '../selectors' import { CommonActionTypes } from '../types' diff --git a/packages/components/src/redux/common/sagas/appSaga.ts b/packages/components/src/redux/sagas/appSaga.ts similarity index 89% rename from packages/components/src/redux/common/sagas/appSaga.ts rename to packages/components/src/redux/sagas/appSaga.ts index 6f67271dc..e2605e2d9 100644 --- a/packages/components/src/redux/common/sagas/appSaga.ts +++ b/packages/components/src/redux/sagas/appSaga.ts @@ -1,10 +1,10 @@ import _ from 'lodash' import { all, delay, fork, put, select, takeLatest } from 'redux-saga/effects' -import { httpClient } from '../../../services/HttpClient' -import { fetchNetworkConnectionStatus } from '../../../services/network' +import { httpClient } from '../../services/HttpClient' +import { fetchNetworkConnectionStatus } from '../../services/network' import { extractReducerState } from '../sync' import { CommonReduxState, exportReducerNames } from '../reducers' -import { version as storeVersion } from '../../store' +import { version as storeVersion } from '../store' import { commonActions } from '../actions' import { commonSelectors } from '../selectors' import messaging from '@react-native-firebase/messaging' diff --git a/packages/components/src/redux/common/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts similarity index 96% rename from packages/components/src/redux/common/sagas/authSaga.ts rename to packages/components/src/redux/sagas/authSaga.ts index e23370111..b7f2fe4f2 100644 --- a/packages/components/src/redux/common/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -3,16 +3,16 @@ import { REHYDRATE } from 'redux-persist' import { Alert } from 'react-native' import { v4 as uuidv4 } from 'uuid' import { ExtractActionFromActionType } from '../types' -import { httpClient } from '../../../services/HttpClient' +import { httpClient } from '../../services/HttpClient' import { CommonReduxState, exportReducerNames } from '../reducers' import { commonActions } from '../actions' import { commonSelectors } from '../selectors' -import { navigateAndReset } from '../../../services/navigationService' -import { PredictionState } from '../../../prediction' +import { navigateAndReset } from '../../services/navigationService' +import { PredictionState } from '../../prediction' import moment from 'moment' -import { closeOutTTs } from '../../../services/textToSpeech' -import { fetchNetworkConnectionStatus } from '../../../services/network' -import { hash } from '../../../services/hash' +import { closeOutTTs } from '../../services/textToSpeech' +import { fetchNetworkConnectionStatus } from '../../services/network' +import { hash } from '../../services/hash' // unwrap promise type Await = T extends Promise ? U : T diff --git a/packages/components/src/redux/common/sagas/contentSaga.ts b/packages/components/src/redux/sagas/contentSaga.ts similarity index 98% rename from packages/components/src/redux/common/sagas/contentSaga.ts rename to packages/components/src/redux/sagas/contentSaga.ts index 7d0db8d76..00f786a83 100644 --- a/packages/components/src/redux/common/sagas/contentSaga.ts +++ b/packages/components/src/redux/sagas/contentSaga.ts @@ -10,12 +10,12 @@ import { fromAvatarMessages, liveContent as staleContent, } from '@oky/core' -import { httpClient } from '../../../services/HttpClient' +import { httpClient } from '../../services/HttpClient' import { commonSelectors } from '../selectors' import { commonActions } from '../actions' import _ from 'lodash' import messaging from '@react-native-firebase/messaging' -import { closeOutTTs } from '../../../services/textToSpeech' +import { closeOutTTs } from '../../services/textToSpeech' function* onRehydrate(action: RehydrateAction) { const locale = yield select(commonSelectors.currentLocaleSelector) diff --git a/packages/components/src/redux/common/sagas/index.ts b/packages/components/src/redux/sagas/index.ts similarity index 100% rename from packages/components/src/redux/common/sagas/index.ts rename to packages/components/src/redux/sagas/index.ts diff --git a/packages/components/src/redux/common/sagas/smartPredictionSaga.ts b/packages/components/src/redux/sagas/smartPredictionSaga.ts similarity index 93% rename from packages/components/src/redux/common/sagas/smartPredictionSaga.ts rename to packages/components/src/redux/sagas/smartPredictionSaga.ts index f62ae1621..2f65aa6b1 100644 --- a/packages/components/src/redux/common/sagas/smartPredictionSaga.ts +++ b/packages/components/src/redux/sagas/smartPredictionSaga.ts @@ -1,11 +1,11 @@ import { all, put, takeLatest } from 'redux-saga/effects' import { ExtractActionFromActionType } from '../types' -import { httpClient } from '../../../services/HttpClient' +import { httpClient } from '../../services/HttpClient' import { commonActions } from '../actions' import _ from 'lodash' -import { PredictionState } from '../../../prediction' +import { PredictionState } from '../../prediction' function* onFetchUpdatedPredictedCycles( action: ExtractActionFromActionType<'SMART_PREDICTION_REQUEST'>, diff --git a/packages/components/src/redux/common/selectors/analyticsSelectors.ts b/packages/components/src/redux/selectors/analyticsSelectors.ts similarity index 100% rename from packages/components/src/redux/common/selectors/analyticsSelectors.ts rename to packages/components/src/redux/selectors/analyticsSelectors.ts diff --git a/packages/components/src/redux/common/selectors/answerSelectors.ts b/packages/components/src/redux/selectors/answerSelectors.ts similarity index 98% rename from packages/components/src/redux/common/selectors/answerSelectors.ts rename to packages/components/src/redux/selectors/answerSelectors.ts index f5988383c..7506a93aa 100644 --- a/packages/components/src/redux/common/selectors/answerSelectors.ts +++ b/packages/components/src/redux/selectors/answerSelectors.ts @@ -1,7 +1,7 @@ import { allQuizzesSelectors } from './contentSelectors' import { Moment } from 'moment' -import { toShortISO } from '../../../services/dateUtils' +import { toShortISO } from '../../services/dateUtils' import _ from 'lodash' import { CommonReduxState } from '../reducers' diff --git a/packages/components/src/redux/common/selectors/appSelectors.ts b/packages/components/src/redux/selectors/appSelectors.ts similarity index 100% rename from packages/components/src/redux/common/selectors/appSelectors.ts rename to packages/components/src/redux/selectors/appSelectors.ts diff --git a/packages/components/src/redux/common/selectors/authSelectors.ts b/packages/components/src/redux/selectors/authSelectors.ts similarity index 100% rename from packages/components/src/redux/common/selectors/authSelectors.ts rename to packages/components/src/redux/selectors/authSelectors.ts diff --git a/packages/components/src/redux/common/selectors/contentSelectors.ts b/packages/components/src/redux/selectors/contentSelectors.ts similarity index 100% rename from packages/components/src/redux/common/selectors/contentSelectors.ts rename to packages/components/src/redux/selectors/contentSelectors.ts diff --git a/packages/components/src/redux/common/selectors/index.ts b/packages/components/src/redux/selectors/index.ts similarity index 100% rename from packages/components/src/redux/common/selectors/index.ts rename to packages/components/src/redux/selectors/index.ts diff --git a/packages/components/src/redux/common/sync/index.ts b/packages/components/src/redux/sync/index.ts similarity index 100% rename from packages/components/src/redux/common/sync/index.ts rename to packages/components/src/redux/sync/index.ts diff --git a/packages/components/src/redux/common/types/index.ts b/packages/components/src/redux/types/index.ts similarity index 83% rename from packages/components/src/redux/common/types/index.ts rename to packages/components/src/redux/types/index.ts index 8b3dd1e6d..13d607dbf 100644 --- a/packages/components/src/redux/common/types/index.ts +++ b/packages/components/src/redux/types/index.ts @@ -1,5 +1,5 @@ import { commonActions } from '../actions' -import { ActionsUnion, ActionsOfType } from '../../types' +import { ActionsUnion, ActionsOfType } from './types' export type CommonActions = ActionsUnion diff --git a/packages/components/src/redux/types.ts b/packages/components/src/redux/types/types.ts similarity index 100% rename from packages/components/src/redux/types.ts rename to packages/components/src/redux/types/types.ts diff --git a/packages/components/src/redux/common/useCommonSelector.ts b/packages/components/src/redux/useCommonSelector.ts similarity index 100% rename from packages/components/src/redux/common/useCommonSelector.ts rename to packages/components/src/redux/useCommonSelector.ts diff --git a/packages/components/src/screens/ArticlesScreen.tsx b/packages/components/src/screens/ArticlesScreen.tsx index 7f83d8e16..3cc8b8ef8 100644 --- a/packages/components/src/screens/ArticlesScreen.tsx +++ b/packages/components/src/screens/ArticlesScreen.tsx @@ -3,8 +3,8 @@ import styled from 'styled-components/native' import { FlatList } from 'react-native' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' -import { useCommonSelector } from '../redux/common/useCommonSelector' -import { commonSelectors } from '../redux/common/selectors' +import { useCommonSelector } from '../redux/useCommonSelector' +import { commonSelectors } from '../redux/selectors' import { Header } from '../components/common/Header' import { TextWithoutTranslation } from '../components/common/Text' import { useTextToSpeechHook } from '../hooks/useTextToSpeechHook' diff --git a/packages/components/src/screens/AvatarAndThemeScreen.tsx b/packages/components/src/screens/AvatarAndThemeScreen.tsx index 313cc6f6a..20e3c61b2 100644 --- a/packages/components/src/screens/AvatarAndThemeScreen.tsx +++ b/packages/components/src/screens/AvatarAndThemeScreen.tsx @@ -6,12 +6,12 @@ import { ThemeSelect } from './avatarAndTheme/ThemeSelect' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { PrimaryButton } from '../components/common/buttons/PrimaryButton' -import { commonActions } from '../redux/common/actions/index' +import { commonActions } from '../redux/actions/index' import { Header } from '../components/common/Header' import { useTheme } from '../components/context/ThemeContext' import { BackOneScreen, navigate } from '../services/navigationService' -import { useCommonSelector } from '../redux/common/useCommonSelector' -import { commonSelectors } from '../redux/common/selectors' +import { useCommonSelector } from '../redux/useCommonSelector' +import { commonSelectors } from '../redux/selectors' import styled from 'styled-components/native' import { Text } from '../components/common/Text' import { ScrollView } from 'react-native-gesture-handler' diff --git a/packages/components/src/screens/ContactUsScreen.tsx b/packages/components/src/screens/ContactUsScreen.tsx index 5f844d296..51bc95db7 100644 --- a/packages/components/src/screens/ContactUsScreen.tsx +++ b/packages/components/src/screens/ContactUsScreen.tsx @@ -9,8 +9,8 @@ import { PageContainer } from '../components/layout/PageContainer' import { TextInput } from '../components/common/TextInput' import { KeyboardAwareAvoidance } from '../components/common/KeyboardAwareAvoidance' import { httpClient } from '../services/HttpClient' -import { useCommonSelector } from '../redux/common/useCommonSelector' -import { commonSelectors } from '../redux/common/selectors' +import { useCommonSelector } from '../redux/useCommonSelector' +import { commonSelectors } from '../redux/selectors' import moment from 'moment' import { Text } from '../components/common/Text' import { ThemedModal } from '../components/common/ThemedModal' diff --git a/packages/components/src/screens/DayScreen.tsx b/packages/components/src/screens/DayScreen.tsx index b1603e6bd..be766980f 100644 --- a/packages/components/src/screens/DayScreen.tsx +++ b/packages/components/src/screens/DayScreen.tsx @@ -12,8 +12,8 @@ import { assets } from '../assets' import { usePredictDay } from '../components/context/PredictionProvider' import { ThemedModal } from '../components/common/ThemedModal' import { ColourButtons } from './mainScreen/ColourButtons' -import { commonSelectors } from '../redux/common/selectors' -import { useCommonSelector } from '../redux/common/useCommonSelector' +import { commonSelectors } from '../redux/selectors' +import { useCommonSelector } from '../redux/useCommonSelector' import moment from 'moment' export function DayScreen({ navigation }) { diff --git a/packages/components/src/screens/EditProfileScreen.tsx b/packages/components/src/screens/EditProfileScreen.tsx index 84bae3538..74fb66f4c 100644 --- a/packages/components/src/screens/EditProfileScreen.tsx +++ b/packages/components/src/screens/EditProfileScreen.tsx @@ -8,9 +8,9 @@ import { Icon } from '../components/common/Icon' import { SelectBox } from '../components/common/SelectBox' import { DateOfBirthInput } from '../components/common/DateOfBirthInput' import { assets } from '../assets/index' -import { useCommonSelector } from '../redux/common/useCommonSelector' -import { commonSelectors } from '../redux/common/selectors' -import { commonActions } from '../redux/common/actions' +import { useCommonSelector } from '../redux/useCommonSelector' +import { commonSelectors } from '../redux/selectors' +import { commonActions } from '../redux/actions' import { useDispatch } from 'react-redux' import { BackOneScreen } from '../services/navigationService' import { httpClient } from '../services/HttpClient' diff --git a/packages/components/src/screens/EncyclopediaScreen.tsx b/packages/components/src/screens/EncyclopediaScreen.tsx index de445547c..fa3559693 100644 --- a/packages/components/src/screens/EncyclopediaScreen.tsx +++ b/packages/components/src/screens/EncyclopediaScreen.tsx @@ -3,8 +3,8 @@ import styled from 'styled-components/native' import { ScrollView, Animated } from 'react-native' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' -import { useCommonSelector } from '../redux/common/useCommonSelector' -import { commonSelectors } from '../redux/common/selectors' +import { useCommonSelector } from '../redux/useCommonSelector' +import { commonSelectors } from '../redux/selectors' import { Category } from './encyclopediaScreen/Category' import { SubCategoryCard, VideoSubCategoryCard } from './encyclopediaScreen/SubCategoryCard' import Accordion from 'react-native-collapsible/Accordion' diff --git a/packages/components/src/screens/FindHelpScreen.tsx b/packages/components/src/screens/FindHelpScreen.tsx index 1878d1791..491a4589d 100644 --- a/packages/components/src/screens/FindHelpScreen.tsx +++ b/packages/components/src/screens/FindHelpScreen.tsx @@ -7,8 +7,8 @@ import { Avatar } from '../components/common/Avatar/Avatar' import { TextWithoutTranslation, Text } from '../components/common/Text' import { SwiperContainer } from '../components/common/SwiperContainer' import { PageContainer } from '../components/layout/PageContainer' -import { useCommonSelector } from '../redux/common/useCommonSelector' -import { commonSelectors } from '../redux/common/selectors' +import { useCommonSelector } from '../redux/useCommonSelector' +import { commonSelectors } from '../redux/selectors' import { useTextToSpeechHook } from '../hooks/useTextToSpeechHook' import analytics from '@react-native-firebase/analytics' import { fetchNetworkConnectionStatus } from '../services/network' diff --git a/packages/components/src/screens/MainScreen.tsx b/packages/components/src/screens/MainScreen.tsx index 696bb42e1..200ed64f9 100644 --- a/packages/components/src/screens/MainScreen.tsx +++ b/packages/components/src/screens/MainScreen.tsx @@ -20,10 +20,10 @@ import { import { useRandomText } from '../hooks/useRandomText' import { InformationButton } from '../components/common/InformationButton' import { assets } from '../assets' -import { commonActions } from '../redux/common/actions' +import { commonActions } from '../redux/actions' import { useDispatch } from 'react-redux' -import { useCommonSelector } from '../redux/common/useCommonSelector' -import { commonSelectors } from '../redux/common/selectors' +import { useCommonSelector } from '../redux/useCommonSelector' +import { commonSelectors } from '../redux/selectors' import moment from 'moment' import { FlowerButton, FlowerModal } from '../optional/Flower' diff --git a/packages/components/src/screens/OnboardingScreen.tsx b/packages/components/src/screens/OnboardingScreen.tsx index 84c7a0866..3f848e961 100644 --- a/packages/components/src/screens/OnboardingScreen.tsx +++ b/packages/components/src/screens/OnboardingScreen.tsx @@ -7,7 +7,7 @@ import { OnboardingCard } from './onboardingScreen/OnboardingCard' import { assets } from '../assets/index' import { PrimaryButton } from '../components/common/buttons/PrimaryButton' import { navigateAndReset } from '../services/navigationService' -import { commonActions } from '../redux/common/actions' +import { commonActions } from '../redux/actions' import { useDispatch } from 'react-redux' import { Animated } from 'react-native' import { Text } from '../components/common/Text' diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index 21ebaa2e5..f0f43704f 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -1,14 +1,14 @@ import React from 'react' import styled from 'styled-components/native' import { useDispatch } from 'react-redux' -import { commonActions } from '../redux/common/actions' +import { commonActions } from '../redux/actions' import { Text } from '../components/common/Text' import { TextInput } from '../components/common/TextInput' -import { commonSelectors } from '../redux/common/selectors' +import { commonSelectors } from '../redux/selectors' import { navigateAndReset } from '../services/navigationService' import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { PageContainer } from '../components/layout/PageContainer' -import { useCommonSelector } from '../redux/common/useCommonSelector' +import { useCommonSelector } from '../redux/useCommonSelector' import { KeyboardAwareAvoidance } from '../components/common/KeyboardAwareAvoidance' import { SpinLoader } from '../components/common/SpinLoader' import _ from 'lodash' diff --git a/packages/components/src/screens/ProfileScreen.tsx b/packages/components/src/screens/ProfileScreen.tsx index df9d87b4f..aa143b004 100644 --- a/packages/components/src/screens/ProfileScreen.tsx +++ b/packages/components/src/screens/ProfileScreen.tsx @@ -16,9 +16,9 @@ import { PrimaryButton } from '../components/common/buttons/PrimaryButton' import { AvatarOption } from './avatarAndTheme/avatarSelect/AvatarOption' import { ThemeSelectItem } from './avatarAndTheme/ThemeSelectItem' import { useHistoryPrediction, useTodayPrediction } from '../components/context/PredictionProvider' -import { useCommonSelector } from '../redux/common/useCommonSelector' -import { commonActions } from '../redux/common/actions' -import { commonSelectors } from '../redux/common/selectors' +import { useCommonSelector } from '../redux/useCommonSelector' +import { commonActions } from '../redux/actions' +import { commonSelectors } from '../redux/selectors' import { translate } from '../i18n/index' import { IconButton } from '../components/common/buttons/IconButton' import Modal from 'react-native-modal' diff --git a/packages/components/src/screens/SettingsScreen.tsx b/packages/components/src/screens/SettingsScreen.tsx index d0f969b0a..99419146f 100644 --- a/packages/components/src/screens/SettingsScreen.tsx +++ b/packages/components/src/screens/SettingsScreen.tsx @@ -8,10 +8,10 @@ import { PrimaryButton } from '../components/common/buttons/PrimaryButton' import { Switcher } from './settings/Switcher' import { navigate } from '../services/navigationService' import { useDispatch } from 'react-redux' -import { commonActions } from '../redux/common/actions' +import { commonActions } from '../redux/actions' import { ConfirmAlert } from '../components/common/ConfirmAlert' -import { useCommonSelector } from '../redux/common/useCommonSelector' -import { commonSelectors } from '../redux/common/selectors' +import { useCommonSelector } from '../redux/useCommonSelector' +import { commonSelectors } from '../redux/selectors' import { translate } from '../i18n/index' import { SpinLoader } from '../components/common/SpinLoader' import { settingsScreenText } from '../config' diff --git a/packages/components/src/screens/SplashScreen.tsx b/packages/components/src/screens/SplashScreen.tsx index 61d94aaa3..73fa17eee 100644 --- a/packages/components/src/screens/SplashScreen.tsx +++ b/packages/components/src/screens/SplashScreen.tsx @@ -4,8 +4,8 @@ import { PageContainer } from '../components/layout/PageContainer' import { assets } from '../assets/index' import styled from 'styled-components/native' import { useDispatch } from 'react-redux' -import { commonSelectors } from '../redux/common/selectors' -import { commonActions } from '../redux/common/actions' +import { commonSelectors } from '../redux/selectors' +import { commonActions } from '../redux/actions' import { navigateAndReset } from '../services/navigationService' import { Animated, Easing } from 'react-native' import { createNotificationChannel, requestUserPermission } from '../services/notifications' @@ -15,7 +15,7 @@ import { useAlert } from '../components/context/AlertContext' import { httpClient } from '../services/HttpClient' import { fetchNetworkConnectionStatus } from '../services/network' import messaging from '@react-native-firebase/messaging' -import { useCommonSelector } from '../redux/common/useCommonSelector' +import { useCommonSelector } from '../redux/useCommonSelector' export function SplashScreen() { const dispatch = useDispatch() diff --git a/packages/components/src/screens/TutorialFirstScreen.tsx b/packages/components/src/screens/TutorialFirstScreen.tsx index 354a95138..f41fe337a 100644 --- a/packages/components/src/screens/TutorialFirstScreen.tsx +++ b/packages/components/src/screens/TutorialFirstScreen.tsx @@ -11,15 +11,15 @@ import { useInfiniteScroll } from './mainScreen/wheelCarousel/useInfiniteScroll' import { navigateAndReset } from '../services/navigationService' import { Animated, Dimensions, Platform } from 'react-native' import { useDispatch } from 'react-redux' -import { commonActions } from '../redux/common/actions' +import { commonActions } from '../redux/actions' import { Text } from '../components/common/Text' import { Icon } from '../components/common/Icon' import { assets } from '../assets' import { ColourButtonsDemo } from './tutorial/ColourButtonsDemo' import { SpinLoader } from '../components/common/SpinLoader' import DeviceInfo from 'react-native-device-info' -import { useCommonSelector } from '../redux/common/useCommonSelector' -import { commonSelectors } from '../redux/common/selectors' +import { useCommonSelector } from '../redux/useCommonSelector' +import { commonSelectors } from '../redux/selectors' import moment from 'moment' import Tts from 'react-native-tts' import { translate } from '../i18n' diff --git a/packages/components/src/screens/TutorialSecondScreen.tsx b/packages/components/src/screens/TutorialSecondScreen.tsx index fc1e9fb58..0ea5fbb2b 100644 --- a/packages/components/src/screens/TutorialSecondScreen.tsx +++ b/packages/components/src/screens/TutorialSecondScreen.tsx @@ -11,7 +11,7 @@ import { useInfiniteScroll } from './mainScreen/wheelCarousel/useInfiniteScroll' import { navigateAndReset } from '../services/navigationService' import { Animated, Dimensions, Image, Platform, View } from 'react-native' import { useDispatch } from 'react-redux' -import { commonActions } from '../redux/common/actions' +import { commonActions } from '../redux/actions' import { Text } from '../components/common/Text' import { Icon } from '../components/common/Icon' import { assets } from '../assets' @@ -19,8 +19,8 @@ import { DayAssetDemo } from './tutorial/DayAssetDemo' import { CalendarAssetDemo } from './tutorial/CalendarAssetDemo' import { SpinLoader } from '../components/common/SpinLoader' import { NoteAssetDemo } from './tutorial/NoteAssetDemo' -import { useCommonSelector } from '../redux/common/useCommonSelector' -import { commonSelectors } from '../redux/common/selectors' +import { useCommonSelector } from '../redux/useCommonSelector' +import { commonSelectors } from '../redux/selectors' import moment from 'moment' import Tts from 'react-native-tts' import { translate } from '../i18n' diff --git a/packages/components/src/screens/VideosScreen.tsx b/packages/components/src/screens/VideosScreen.tsx index 02da60ee9..f28ca2032 100644 --- a/packages/components/src/screens/VideosScreen.tsx +++ b/packages/components/src/screens/VideosScreen.tsx @@ -3,8 +3,8 @@ import styled from 'styled-components/native' import { FlatList } from 'react-native' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' -import { useCommonSelector } from '../redux/common/useCommonSelector' -import { commonSelectors } from '../redux/common/selectors' +import { useCommonSelector } from '../redux/useCommonSelector' +import { commonSelectors } from '../redux/selectors' import { Header } from '../components/common/Header' import { TextWithoutTranslation } from '../components/common/Text' import { VideoData } from '../types' diff --git a/packages/components/src/screens/authScreen/Login.tsx b/packages/components/src/screens/authScreen/Login.tsx index 95112776f..c362c1578 100644 --- a/packages/components/src/screens/authScreen/Login.tsx +++ b/packages/components/src/screens/authScreen/Login.tsx @@ -3,8 +3,8 @@ import styled from 'styled-components/native' import { Text } from '../../components/common/Text' import { TextInput } from '../../components/common/TextInput' import { useDispatch } from 'react-redux' -import { commonActions } from '../../redux/common/actions' -import { useCommonSelector } from '../../redux/common/useCommonSelector' +import { commonActions } from '../../redux/actions' +import { useCommonSelector } from '../../redux/useCommonSelector' import { SpinLoader } from '../../components/common/SpinLoader' import _ from 'lodash' diff --git a/packages/components/src/screens/authScreen/SignUp.tsx b/packages/components/src/screens/authScreen/SignUp.tsx index 3ab1b7311..e3281d967 100644 --- a/packages/components/src/screens/authScreen/SignUp.tsx +++ b/packages/components/src/screens/authScreen/SignUp.tsx @@ -8,7 +8,7 @@ import { AskAge } from './signUp/AskAge' import { AskLocation } from './signUp/AskLocation' import { AskUserConfirmation } from './signUp/AskUserConfirmation' import { navigate } from '../../services/navigationService' -import { commonActions } from '../../redux/common/actions' +import { commonActions } from '../../redux/actions' import _ from 'lodash' import { FAST_SIGN_UP } from '../../config' diff --git a/packages/components/src/screens/authScreen/signUp/AskLocation.tsx b/packages/components/src/screens/authScreen/signUp/AskLocation.tsx index 33b6fa54c..b579b6972 100644 --- a/packages/components/src/screens/authScreen/signUp/AskLocation.tsx +++ b/packages/components/src/screens/authScreen/signUp/AskLocation.tsx @@ -7,8 +7,8 @@ import { Text } from '../../../components/common/Text' import { GenderSelectItem } from '../../../components/common/GenderSelectItem' import { formHeights } from './FormHeights' import { ModalSearchBox } from '../../../components/common/ModalSearchBox' -import { useCommonSelector } from '../../../redux/common/useCommonSelector' -import { commonSelectors } from '../../../redux/common/selectors' +import { useCommonSelector } from '../../../redux/useCommonSelector' +import { commonSelectors } from '../../../redux/selectors' import { translate } from '../../../i18n' import { FAST_SIGN_UP } from '../../../config' diff --git a/packages/components/src/screens/dayScreen/DayCarousel.tsx b/packages/components/src/screens/dayScreen/DayCarousel.tsx index 6ec5fca0d..623271409 100644 --- a/packages/components/src/screens/dayScreen/DayCarousel.tsx +++ b/packages/components/src/screens/dayScreen/DayCarousel.tsx @@ -3,13 +3,13 @@ import _ from 'lodash' import { FlatList, Dimensions, KeyboardAvoidingView } from 'react-native' import { DayCarouselItem } from './DayCarouselItem' import { useDispatch } from 'react-redux' -import { commonActions } from '../../redux/common/actions' +import { commonActions } from '../../redux/actions' import { NoteCard } from './NoteCard' import { QuizCard } from './QuizCard' import { DidYouKnowCard } from './DidYouKnowCard' import { SurveyCard } from './SurveyCard' -import { useCommonSelector } from '../../redux/common/useCommonSelector' -import { commonSelectors } from '../../redux/common/selectors' +import { useCommonSelector } from '../../redux/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { translate } from '../../i18n' import { ThemedModal } from '../../components/common/ThemedModal' diff --git a/packages/components/src/screens/dayScreen/DayCarouselItem.tsx b/packages/components/src/screens/dayScreen/DayCarouselItem.tsx index eaa8201fd..c809554f3 100644 --- a/packages/components/src/screens/dayScreen/DayCarouselItem.tsx +++ b/packages/components/src/screens/dayScreen/DayCarouselItem.tsx @@ -6,8 +6,8 @@ import { assets } from '../../assets/index' import { Text } from '../../components/common/Text' import { EmojiSelector } from '../../components/common/EmojiSelector' import { Icon } from '../../components/common/Icon' -import { commonSelectors } from '../../redux/common/selectors' -import { useCommonSelector } from '../../redux/common/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' +import { useCommonSelector } from '../../redux/useCommonSelector' import { useColor } from '../../hooks/useColor' import { translate } from '../../i18n' diff --git a/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx b/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx index 1e5d72957..7c3ca0fe0 100644 --- a/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx +++ b/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx @@ -2,9 +2,9 @@ import React from 'react' import { Dimensions } from 'react-native' import styled from 'styled-components/native' import { TextWithoutTranslation, Text } from '../../components/common/Text' -import { useCommonSelector } from '../../redux/common/useCommonSelector' +import { useCommonSelector } from '../../redux/useCommonSelector' import _ from 'lodash' -import { commonSelectors } from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/selectors' import { TitleText } from '../../components/common/TitleText' const deviceWidth = Dimensions.get('window').width diff --git a/packages/components/src/screens/dayScreen/NoteCard.tsx b/packages/components/src/screens/dayScreen/NoteCard.tsx index 247c48d60..af07d96b7 100644 --- a/packages/components/src/screens/dayScreen/NoteCard.tsx +++ b/packages/components/src/screens/dayScreen/NoteCard.tsx @@ -1,9 +1,9 @@ import React from 'react' import { Dimensions } from 'react-native' import styled from 'styled-components/native' -import { commonSelectors } from '../../redux/common/selectors' -import { commonActions } from '../../redux/common/actions' -import { useCommonSelector } from '../../redux/common/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' +import { commonActions } from '../../redux/actions' +import { useCommonSelector } from '../../redux/useCommonSelector' import { TextInput } from '../../components/common/TextInput' import { useDispatch } from 'react-redux' import { BackOneScreen } from '../../services/navigationService' diff --git a/packages/components/src/screens/dayScreen/QuizCard.tsx b/packages/components/src/screens/dayScreen/QuizCard.tsx index a222b3ac1..6c95165c7 100644 --- a/packages/components/src/screens/dayScreen/QuizCard.tsx +++ b/packages/components/src/screens/dayScreen/QuizCard.tsx @@ -4,10 +4,10 @@ import { Dimensions } from 'react-native' import { TextWithoutTranslation, Text } from '../../components/common/Text' import { EmojiSelector } from '../../components/common/EmojiSelector' import { TitleText } from '../../components/common/TitleText' -import { useCommonSelector } from '../../redux/common/useCommonSelector' +import { useCommonSelector } from '../../redux/useCommonSelector' import _ from 'lodash' -import { commonSelectors } from '../../redux/common/selectors' -import { commonActions } from '../../redux/common/actions' +import { commonSelectors } from '../../redux/selectors' +import { commonActions } from '../../redux/actions' import { useDispatch } from 'react-redux' const deviceWidth = Dimensions.get('window').width diff --git a/packages/components/src/screens/dayScreen/SurveyCard.tsx b/packages/components/src/screens/dayScreen/SurveyCard.tsx index 9f6b6595f..83af1ab12 100644 --- a/packages/components/src/screens/dayScreen/SurveyCard.tsx +++ b/packages/components/src/screens/dayScreen/SurveyCard.tsx @@ -4,10 +4,10 @@ import { Dimensions } from 'react-native' import { TextWithoutTranslation, Text } from '../../components/common/Text' import { EmojiSelector } from '../../components/common/EmojiSelector' import { TitleText } from '../../components/common/TitleText' -import { useCommonSelector } from '../../redux/common/useCommonSelector' +import { useCommonSelector } from '../../redux/useCommonSelector' import _ from 'lodash' -import { commonSelectors } from '../../redux/common/selectors' -import { commonActions } from '../../redux/common/actions' +import { commonSelectors } from '../../redux/selectors' +import { commonActions } from '../../redux/actions' import { useDispatch } from 'react-redux' import { TextInput } from '../../components/common/TextInput' import { SurveyInformationButton } from '../../components/common/SurveyInformationButton' diff --git a/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx b/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx index 59962a7df..e0f71519a 100644 --- a/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx +++ b/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx @@ -6,8 +6,8 @@ import { IconButton } from '../../components/common/buttons/IconButton' import styled from 'styled-components/native' import { handleCategoriesFilter, handleSearchResult } from './searchFunctions' import { EmojiSelector } from '../../components/common/EmojiSelector' -import { useCommonSelector } from '../../redux/common/useCommonSelector' -import { commonSelectors } from '../../redux/common/selectors' +import { useCommonSelector } from '../../redux/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' import { translate } from '../../i18n' export const SearchBar = ({ diff --git a/packages/components/src/screens/journeyScreen/FinalJourneyCard.tsx b/packages/components/src/screens/journeyScreen/FinalJourneyCard.tsx index c7b9eb885..f5539aa76 100644 --- a/packages/components/src/screens/journeyScreen/FinalJourneyCard.tsx +++ b/packages/components/src/screens/journeyScreen/FinalJourneyCard.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components/native' import { Text, TextWithoutTranslation } from '../../components/common/Text' import { assets } from '../../assets/index' import { translate } from '../../i18n/index' -import { commonActions } from '../../redux/common/actions/index' +import { commonActions } from '../../redux/actions/index' import { useDispatch } from 'react-redux' import moment from 'moment' import { SpinLoader } from '../../components/common/SpinLoader' diff --git a/packages/components/src/screens/journeyScreen/JourneyCard.tsx b/packages/components/src/screens/journeyScreen/JourneyCard.tsx index 90b1661b9..df3390d2e 100644 --- a/packages/components/src/screens/journeyScreen/JourneyCard.tsx +++ b/packages/components/src/screens/journeyScreen/JourneyCard.tsx @@ -5,8 +5,8 @@ import { CalendarCardContent } from './CalendarCardContent' import { WheelPickerContent } from '../../components/WheelPickerContent' import { Avatar } from '../../components/common/Avatar/Avatar' import { assets } from '../../assets' -import { useCommonSelector } from '../../redux/common/useCommonSelector' -import { commonSelectors } from '../../redux/common/selectors' +import { useCommonSelector } from '../../redux/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' export function JourneyCard({ question, diff --git a/packages/components/src/screens/mainScreen/Calendar.tsx b/packages/components/src/screens/mainScreen/Calendar.tsx index c0a3333d7..e3a6cffa9 100644 --- a/packages/components/src/screens/mainScreen/Calendar.tsx +++ b/packages/components/src/screens/mainScreen/Calendar.tsx @@ -21,8 +21,8 @@ import { assets } from '../../assets' import { translate } from '../../i18n' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { calendarScreenSpeech } from '../../config' -import { commonSelectors } from '../../redux/common/selectors' -import { useCommonSelector } from '../../redux/common/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' +import { useCommonSelector } from '../../redux/useCommonSelector' const width = Dimensions.get('window').width const height = Dimensions.get('window').height diff --git a/packages/components/src/screens/mainScreen/ColourButtons.tsx b/packages/components/src/screens/mainScreen/ColourButtons.tsx index 4fcc5b9d4..7a99fbd53 100644 --- a/packages/components/src/screens/mainScreen/ColourButtons.tsx +++ b/packages/components/src/screens/mainScreen/ColourButtons.tsx @@ -18,13 +18,13 @@ import { InformationButton } from '../../components/common/InformationButton' import { decisionProcessNonPeriod, decisionProcessPeriod } from './predictionLogic/predictionLogic' import { translate } from '../../i18n' import { useDispatch } from 'react-redux' -import { commonActions } from '../../redux/common/actions/index' -import { commonSelectors } from '../../redux/common/selectors' +import { commonActions } from '../../redux/actions/index' +import { commonSelectors } from '../../redux/selectors' import analytics from '@react-native-firebase/analytics' import moment from 'moment' import { fetchNetworkConnectionStatus } from '../../services/network' -import { useCommonSelector } from '../../redux/common/useCommonSelector' +import { useCommonSelector } from '../../redux/useCommonSelector' import { incrementFlowerProgress, useFlowerStateSelector, FlowerModal } from '../../optional/Flower' const minBufferBetweenCycles = 2 diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx index 5b66a850e..851ee3e70 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx @@ -7,8 +7,8 @@ import { TouchableOpacity } from 'react-native-gesture-handler' import { navigate, navigateAndReset } from '../../../services/navigationService' import moment from 'moment' import { useDisplayText } from '../../../components/context/DisplayTextContext' -import { useCommonSelector } from '../../../redux/common/useCommonSelector' -import { commonSelectors } from '../../../redux/common/selectors' +import { useCommonSelector } from '../../../redux/useCommonSelector' +import { commonSelectors } from '../../../redux/selectors' import { SpinLoader } from '../../../components/common/SpinLoader' const screenWidth = Dimensions.get('window').width diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx index 2acdd1725..8d032a4dd 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx @@ -6,9 +6,9 @@ import { DayBadge } from '../../../components/common/DayBadge' import { DateBadge } from '../../../components/common/DateBadge' import { assets } from '../../../assets/index' import { EmojiSelector } from '../../../components/common/EmojiSelector' -import { useCommonSelector } from '../../../redux/common/useCommonSelector' +import { useCommonSelector } from '../../../redux/useCommonSelector' import { emojis } from '../../../config' -import { commonSelectors } from '../../../redux/common/selectors' +import { commonSelectors } from '../../../redux/selectors' import { useColor } from '../../../hooks/useColor' import styled from 'styled-components/native' import moment from 'moment' diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx index 593995520..a04b40afb 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx @@ -6,14 +6,14 @@ import { useTheme } from '../../../components/context/ThemeContext' import { TouchableOpacity } from 'react-native-gesture-handler' import { assets } from '../../../assets/index' import { translate } from '../../../i18n' -import { commonSelectors } from '../../../redux/common/selectors' +import { commonSelectors } from '../../../redux/selectors' import moment from 'moment' import { useTodayPrediction, useActualCurrentStartDateSelector, } from '../../../components/context/PredictionProvider' import _ from 'lodash' -import { useCommonSelector } from '../../../redux/common/useCommonSelector' +import { useCommonSelector } from '../../../redux/useCommonSelector' const { Value, diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx index 54f17d24e..0ac840fe6 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx @@ -6,14 +6,14 @@ import { CircularElement } from './CircularElement' import { PanGesture } from './PanGesture' import { TouchableOpacity } from 'react-native-gesture-handler' import { ColourButtons } from '../ColourButtons' -import { useCommonSelector } from '../../../redux/common/useCommonSelector' -import { commonSelectors } from '../../../redux/common/selectors' +import { useCommonSelector } from '../../../redux/useCommonSelector' +import { commonSelectors } from '../../../redux/selectors' import { navigateAndReset } from '../../../services/navigationService' import { useCheckDayWarning } from '../../../hooks/usePredictionWarnings' import { ThemedModal } from '../../../components/common/ThemedModal' import { SpinLoader } from '../../../components/common/SpinLoader' import moment from 'moment' -import { CommonReduxState } from '../../../redux/common/reducers' +import { CommonReduxState } from '../../../redux/reducers' const reduxState = (state: CommonReduxState) => state diff --git a/packages/components/src/screens/profileScreen/CycleCard.tsx b/packages/components/src/screens/profileScreen/CycleCard.tsx index b6d48aa39..231d258d0 100644 --- a/packages/components/src/screens/profileScreen/CycleCard.tsx +++ b/packages/components/src/screens/profileScreen/CycleCard.tsx @@ -3,9 +3,9 @@ import styled from 'styled-components/native' import { Text } from '../../components/common/Text' import { Icon } from '../../components/common/Icon' import { assets } from '../../assets/index' -import { commonSelectors } from '../../redux/common/selectors' +import { commonSelectors } from '../../redux/selectors' import { EmojiSelector } from '../../components/common/EmojiSelector' -import { useCommonSelector } from '../../redux/common/useCommonSelector' +import { useCommonSelector } from '../../redux/useCommonSelector' import { emojis } from '../../config' import { translate } from '../../i18n' diff --git a/packages/components/src/screens/settings/AboutScreen.tsx b/packages/components/src/screens/settings/AboutScreen.tsx index 6b18bb624..4038da4ab 100644 --- a/packages/components/src/screens/settings/AboutScreen.tsx +++ b/packages/components/src/screens/settings/AboutScreen.tsx @@ -4,8 +4,8 @@ import { BackgroundTheme } from '../../components/layout/BackgroundTheme' import { TextWithoutTranslation } from '../../components/common/Text' import { Header } from '../../components/common/Header' import { Icon } from '../../components/common/Icon' -import { useCommonSelector } from '../../redux/common/useCommonSelector' -import { commonSelectors } from '../../redux/common/selectors' +import { useCommonSelector } from '../../redux/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { aboutScreenText } from '../../config' import { Dimensions } from 'react-native' diff --git a/packages/components/src/screens/settings/AccessScreen.tsx b/packages/components/src/screens/settings/AccessScreen.tsx index 3f8a468e4..184d955de 100644 --- a/packages/components/src/screens/settings/AccessScreen.tsx +++ b/packages/components/src/screens/settings/AccessScreen.tsx @@ -4,9 +4,9 @@ import { BackgroundTheme } from '../../components/layout/BackgroundTheme' import styled from 'styled-components/native' import { ListItem } from './accessScreen/ListItem' import { Header } from '../../components/common/Header' -import { useCommonSelector } from '../../redux/common/useCommonSelector' -import { commonSelectors } from '../../redux/common/selectors' -import { commonActions } from '../../redux/common/actions/index' +import { useCommonSelector } from '../../redux/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' +import { commonActions } from '../../redux/actions/index' import { useDispatch } from 'react-redux' import { navigateAndReset } from '../../services/navigationService' import { TouchableOpacity } from 'react-native' diff --git a/packages/components/src/screens/settings/PrivacyScreen.tsx b/packages/components/src/screens/settings/PrivacyScreen.tsx index 1ce4e8a32..0976bfef0 100644 --- a/packages/components/src/screens/settings/PrivacyScreen.tsx +++ b/packages/components/src/screens/settings/PrivacyScreen.tsx @@ -4,8 +4,8 @@ import { BackgroundTheme } from '../../components/layout/BackgroundTheme' import { Header } from '../../components/common/Header' import { TextWithoutTranslation } from '../../components/common/Text' import { ScrollView, Dimensions } from 'react-native' -import { useCommonSelector } from '../../redux/common/useCommonSelector' -import { commonSelectors } from '../../redux/common/selectors' +import { useCommonSelector } from '../../redux/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' import { chunk } from 'lodash' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' diff --git a/packages/components/src/screens/settings/TermsScreen.tsx b/packages/components/src/screens/settings/TermsScreen.tsx index f5f66238d..595058b34 100644 --- a/packages/components/src/screens/settings/TermsScreen.tsx +++ b/packages/components/src/screens/settings/TermsScreen.tsx @@ -4,8 +4,8 @@ import { BackgroundTheme } from '../../components/layout/BackgroundTheme' import { Header } from '../../components/common/Header' import { TextWithoutTranslation } from '../../components/common/Text' import { ScrollView, Dimensions, Platform } from 'react-native' -import { useCommonSelector } from '../../redux/common/useCommonSelector' -import { commonSelectors } from '../../redux/common/selectors' +import { useCommonSelector } from '../../redux/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' import { chunk } from 'lodash' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' diff --git a/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx b/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx index 4f17e1d60..f76cecb66 100644 --- a/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx +++ b/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx @@ -2,8 +2,8 @@ import React from 'react' import { Dimensions } from 'react-native' import styled from 'styled-components/native' import { assets } from '../../assets/index' -import { useCommonSelector } from '../../redux/common/useCommonSelector' -import { commonSelectors } from '../../redux/common/selectors' +import { useCommonSelector } from '../../redux/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' const deviceWidth = Dimensions.get('window').width const deviceHeight = Dimensions.get('screen').height diff --git a/packages/components/src/screens/tutorial/DayAssetDemo.tsx b/packages/components/src/screens/tutorial/DayAssetDemo.tsx index 6b0ad820f..fe3980da5 100644 --- a/packages/components/src/screens/tutorial/DayAssetDemo.tsx +++ b/packages/components/src/screens/tutorial/DayAssetDemo.tsx @@ -8,8 +8,8 @@ import { Icon } from '../../components/common/Icon' import { EmojiSelector } from '../../components/common/EmojiSelector' import { translate } from '../../i18n' import Tts from 'react-native-tts' -import { useCommonSelector } from '../../redux/common/useCommonSelector' -import { commonSelectors } from '../../redux/common/selectors' +import { useCommonSelector } from '../../redux/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' const deviceWidth = Dimensions.get('window').width const deviceHeight = Dimensions.get('window').height diff --git a/packages/components/src/screens/tutorial/NoteAssetDemo.tsx b/packages/components/src/screens/tutorial/NoteAssetDemo.tsx index 8f79b8174..bafe7eb6e 100644 --- a/packages/components/src/screens/tutorial/NoteAssetDemo.tsx +++ b/packages/components/src/screens/tutorial/NoteAssetDemo.tsx @@ -7,8 +7,8 @@ import { TextInput } from '../../components/common/TextInput' import { Text } from '../../components/common/Text' import { translate } from '../../i18n' import Tts from 'react-native-tts' -import { useCommonSelector } from '../../redux/common/useCommonSelector' -import { commonSelectors } from '../../redux/common/selectors' +import { useCommonSelector } from '../../redux/useCommonSelector' +import { commonSelectors } from '../../redux/selectors' const deviceWidth = Dimensions.get('window').width const deviceHeight = Dimensions.get('screen').height From d12d813e9a64a492f8e854c99eecb7bd99abfee5 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Tue, 2 Jan 2024 20:19:22 +0800 Subject: [PATCH 26/83] Revert 'Common' naming --- .../redux/actions/appActions.test.ts | 6 +- .../redux/reducers/authReducer.test.ts | 10 +-- .../src/components/common/Avatar/Avatar.tsx | 10 +-- .../src/components/common/CalendarList.tsx | 6 +- .../src/components/common/DateBadge.tsx | 6 +- .../src/components/common/DayBadge.tsx | 6 +- .../src/components/common/LanguageSelect.tsx | 10 +-- .../components/context/DisplayTextContext.tsx | 8 +- .../src/components/context/LocaleContext.tsx | 4 +- .../components/context/PredictionProvider.tsx | 12 +-- .../src/components/context/ThemeContext.tsx | 8 +- .../src/hooks/useTextToSpeechHook.ts | 6 +- .../components/src/redux/StoreCoordinator.tsx | 33 ++++---- .../components/src/redux/actions/index.ts | 21 ++--- .../src/redux/reducers/accessReducer.ts | 4 +- .../src/redux/reducers/analyticsReducer.ts | 4 +- .../src/redux/reducers/answerReducer.ts | 19 ++--- .../src/redux/reducers/appReducer.ts | 4 +- .../src/redux/reducers/authReducer.ts | 7 +- .../src/redux/reducers/contentReducer.ts | 4 +- .../components/src/redux/reducers/index.ts | 6 +- .../src/redux/reducers/keysReducer.ts | 5 +- .../src/redux/reducers/predictionReducer.ts | 4 +- .../src/redux/sagas/analyticsSaga.ts | 18 ++--- .../components/src/redux/sagas/appSaga.ts | 14 ++-- .../components/src/redux/sagas/authSaga.ts | 78 +++++++++---------- .../components/src/redux/sagas/contentSaga.ts | 38 ++++----- packages/components/src/redux/sagas/index.ts | 2 +- .../src/redux/sagas/smartPredictionSaga.ts | 11 +-- .../src/redux/selectors/analyticsSelectors.ts | 6 +- .../src/redux/selectors/answerSelectors.ts | 30 ++++--- .../src/redux/selectors/appSelectors.ts | 37 +++++---- .../src/redux/selectors/authSelectors.ts | 10 +-- .../src/redux/selectors/contentSelectors.ts | 48 ++++++------ .../components/src/redux/selectors/index.ts | 18 ++--- packages/components/src/redux/types/index.ts | 8 +- .../components/src/redux/useCommonSelector.ts | 4 - packages/components/src/redux/useSelector.ts | 4 + .../components/src/screens/ArticlesScreen.tsx | 14 ++-- .../components/src/screens/AuthScreen.tsx | 2 +- .../src/screens/AvatarAndThemeScreen.tsx | 12 +-- .../src/screens/ContactUsScreen.tsx | 8 +- packages/components/src/screens/DayScreen.tsx | 8 +- .../src/screens/EditProfileScreen.tsx | 18 ++--- .../src/screens/EncyclopediaScreen.tsx | 14 ++-- .../components/src/screens/FindHelpScreen.tsx | 6 +- .../components/src/screens/MainScreen.tsx | 18 ++--- .../src/screens/OnboardingScreen.tsx | 10 +-- .../src/screens/PasswordRequestScreen.tsx | 14 ++-- .../components/src/screens/ProfileScreen.tsx | 16 ++-- .../components/src/screens/SettingsScreen.tsx | 20 ++--- .../components/src/screens/SplashScreen.tsx | 24 +++--- .../src/screens/TutorialFirstScreen.tsx | 16 ++-- .../src/screens/TutorialSecondScreen.tsx | 16 ++-- .../components/src/screens/VideosScreen.tsx | 12 +-- .../src/screens/authScreen/Login.tsx | 8 +- .../src/screens/authScreen/SignUp.tsx | 4 +- .../screens/authScreen/signUp/AskLocation.tsx | 6 +- .../src/screens/dayScreen/DayCarousel.tsx | 26 +++---- .../src/screens/dayScreen/DayCarouselItem.tsx | 8 +- .../src/screens/dayScreen/DidYouKnowCard.tsx | 6 +- .../src/screens/dayScreen/NoteCard.tsx | 16 ++-- .../src/screens/dayScreen/QuizCard.tsx | 18 ++--- .../src/screens/dayScreen/SurveyCard.tsx | 20 ++--- .../screens/encyclopediaScreen/SearchBar.tsx | 8 +- .../journeyScreen/FinalJourneyCard.tsx | 4 +- .../src/screens/journeyScreen/JourneyCard.tsx | 6 +- .../src/screens/mainScreen/Calendar.tsx | 10 +-- .../src/screens/mainScreen/ColourButtons.tsx | 32 ++++---- .../mainScreen/wheelCarousel/Carousel.tsx | 6 +- .../wheelCarousel/CarouselElement.tsx | 12 +-- .../wheelCarousel/CircularElement.tsx | 6 +- .../wheelCarousel/CircularSelection.tsx | 17 ++-- .../src/screens/profileScreen/CycleCard.tsx | 8 +- .../src/screens/settings/AboutScreen.tsx | 10 +-- .../src/screens/settings/AccessScreen.tsx | 14 ++-- .../src/screens/settings/PrivacyScreen.tsx | 6 +- .../src/screens/settings/TermsScreen.tsx | 6 +- .../screens/tutorial/CalendarAssetDemo.tsx | 6 +- .../src/screens/tutorial/DayAssetDemo.tsx | 6 +- .../src/screens/tutorial/NoteAssetDemo.tsx | 6 +- 81 files changed, 490 insertions(+), 542 deletions(-) delete mode 100644 packages/components/src/redux/useCommonSelector.ts create mode 100644 packages/components/src/redux/useSelector.ts diff --git a/packages/components/__tests__/redux/actions/appActions.test.ts b/packages/components/__tests__/redux/actions/appActions.test.ts index c21231924..9adac412c 100644 --- a/packages/components/__tests__/redux/actions/appActions.test.ts +++ b/packages/components/__tests__/redux/actions/appActions.test.ts @@ -1,7 +1,7 @@ import configureStore from 'redux-mock-store' import _ from 'lodash' -import { commonActions } from '../../../src/redux/actions' +import * as actions from '../../../src/redux/actions' const middleWares = [] const mockStore = configureStore(middleWares) @@ -11,7 +11,7 @@ describe('appActions', () => { const store = mockStore(initialState) it('Set Avatar action', () => { - const action = commonActions.setAvatar('nur') + const action = actions.setAvatar('nur') // Dispatch the action store.dispatch(action) // Test if your store dispatched the expected actions @@ -21,7 +21,7 @@ describe('appActions', () => { }) it('Set Theme action', () => { - const action = commonActions.setTheme('hills') + const action = actions.setTheme('hills') // Dispatch the action store.dispatch(action) // Test if your store dispatched the expected actions diff --git a/packages/components/__tests__/redux/reducers/authReducer.test.ts b/packages/components/__tests__/redux/reducers/authReducer.test.ts index a3303a0c5..9f4b0ec7f 100644 --- a/packages/components/__tests__/redux/reducers/authReducer.test.ts +++ b/packages/components/__tests__/redux/reducers/authReducer.test.ts @@ -3,7 +3,7 @@ import moment from 'moment' import configureStore from 'redux-mock-store' import _ from 'lodash' -import { commonActions } from '../../../src/redux/actions' +import * as actions from '../../../src/redux/actions' import { authReducer } from '../../../src/redux/reducers/authReducer' const middleWares = [] @@ -32,7 +32,7 @@ describe('authReducer', () => { }) it('Create account request actions', () => { - const action = commonActions.createAccountRequest({ ...mockPayload, id: undefined }) + const action = actions.createAccountRequest({ ...mockPayload, id: undefined }) // Dispatch the action store.dispatch(action) // Test if your store dispatched the expected actions @@ -41,7 +41,7 @@ describe('authReducer', () => { expect(scopedActions[0].type).toEqual(expectedType) }) it('Login As guest account', () => { - const action = commonActions.loginSuccessAsGuestAccount(mockPayload) + const action = actions.loginSuccessAsGuestAccount(mockPayload) const newStore = authReducer(undefined, action) // Dispatch the action @@ -49,14 +49,14 @@ describe('authReducer', () => { expect(newStore?.user?.isGuest).toEqual(true) }) it('Login Out guest account', () => { - const action = commonActions.logout() + const action = actions.logout() const newStore = authReducer(undefined, action) // Dispatch the action expect(newStore.user).toEqual(null) }) it('Create account faiure', () => { - const action = commonActions.createAccountFailure() + const action = actions.createAccountFailure() const newStore = authReducer(undefined, action) // Dispatch the action diff --git a/packages/components/src/components/common/Avatar/Avatar.tsx b/packages/components/src/components/common/Avatar/Avatar.tsx index 9d47b2bcf..992e124eb 100644 --- a/packages/components/src/components/common/Avatar/Avatar.tsx +++ b/packages/components/src/components/common/Avatar/Avatar.tsx @@ -8,8 +8,8 @@ import { FloatingQuestion } from './FloatingQuestion' import styled from 'styled-components/native' import { Icon } from '../Icon' import { HeartAnimation } from './HeartAnimation' -import { useCommonSelector } from '../../../redux/useCommonSelector' -import { commonSelectors } from '../../../redux/selectors/index' +import { useSelector } from '../../../redux/useSelector' +import * as selectors from '../../../redux/selectors/index' import moment from 'moment' import { useDisplayText } from '../../context/DisplayTextContext' import { useTodayPrediction } from '../../context/PredictionProvider' @@ -37,13 +37,11 @@ export function Avatar({ const isJumpingToggled = React.useRef(false) const isDancingToggled = React.useRef(false) const randomDance = React.useRef(1) - const selectedAvatar = useCommonSelector(commonSelectors.currentAvatarSelector) + const selectedAvatar = useSelector(selectors.currentAvatarSelector) const [animatedProgress] = React.useState(new Animated.Value(0)) const { onPeriod } = useTodayPrediction() - const cardAnswersToday = useCommonSelector((state) => - commonSelectors.cardAnswerSelector(state, moment.utc()), - ) + const cardAnswersToday = useSelector((state) => selectors.cardAnswerSelector(state, moment.utc())) React.useEffect(() => { const intervalId = setTimeout(hideDisplayText, 3000) return () => { diff --git a/packages/components/src/components/common/CalendarList.tsx b/packages/components/src/components/common/CalendarList.tsx index 553086f01..8be91b14f 100644 --- a/packages/components/src/components/common/CalendarList.tsx +++ b/packages/components/src/components/common/CalendarList.tsx @@ -3,8 +3,8 @@ import { Image } from 'react-native' import { CalendarList as DefaultCalendarList, LocaleConfig } from 'react-native-calendars' import momentTimezone from 'moment-timezone' import { assets } from '../../assets/index' -import { useCommonSelector } from '../../redux/useCommonSelector' -import { commonSelectors } from '../../redux/selectors' +import { useSelector } from '../../redux/useSelector' +import * as selectors from '../../redux/selectors' import { calendarTranslations } from '@oky/core' LocaleConfig.locales = { @@ -19,7 +19,7 @@ export function CalendarList({ setInputDay, width = null, }: any) { - const locale = useCommonSelector(commonSelectors.currentLocaleSelector) + const locale = useSelector(selectors.currentLocaleSelector) LocaleConfig.defaultLocale = locale const [markedDates, setMarkedDates] = React.useState({}) const calendarRef = React.useRef() diff --git a/packages/components/src/components/common/DateBadge.tsx b/packages/components/src/components/common/DateBadge.tsx index a618ac4c8..09873c716 100644 --- a/packages/components/src/components/common/DateBadge.tsx +++ b/packages/components/src/components/common/DateBadge.tsx @@ -6,12 +6,12 @@ import { translate } from '../../i18n' import { TouchableOpacity } from 'react-native' import _ from 'lodash' import moment from 'moment' -import { commonSelectors } from '../../redux/selectors' +import * as selectors from '../../redux/selectors' import { useTodayPrediction, useActualCurrentStartDateSelector, } from '../../components/context/PredictionProvider' -import { useCommonSelector } from '../../redux/useCommonSelector' +import { useSelector } from '../../redux/useSelector' function checkForVerifiedDay(cardValues) { if (_.has(cardValues, 'periodDay')) { @@ -69,7 +69,7 @@ export function DateBadge({ dataEntry, style, textStyle = null, showModal, cardV const { id: themeName } = useTheme() const currentCycleInfo = useTodayPrediction() const actualCurrentStartDate = useActualCurrentStartDateSelector() - const hasFuturePredictionActive = useCommonSelector(commonSelectors.isFuturePredictionSelector) + const hasFuturePredictionActive = useSelector(selectors.isFuturePredictionSelector) const source = useStatusForSource( dataEntry, diff --git a/packages/components/src/components/common/DayBadge.tsx b/packages/components/src/components/common/DayBadge.tsx index 5d0e0a1ea..c42faf690 100644 --- a/packages/components/src/components/common/DayBadge.tsx +++ b/packages/components/src/components/common/DayBadge.tsx @@ -4,12 +4,12 @@ import styled from 'styled-components/native' import { TextWithoutTranslation, Text } from './Text' import _ from 'lodash' import moment from 'moment' -import { commonSelectors } from '../../redux/selectors' +import * as selectors from '../../redux/selectors' import { useTodayPrediction, useActualCurrentStartDateSelector, } from '../../components/context/PredictionProvider' -import { useCommonSelector } from '../../redux/useCommonSelector' +import { useSelector } from '../../redux/useSelector' function checkForVerifiedDay(cardValues) { if (_.has(cardValues, 'periodDay')) { @@ -51,7 +51,7 @@ function useStatusForSource( export const DayBadge = ({ dataEntry, style, fontSizes, cardValues }) => { const currentCycleInfo = useTodayPrediction() - const hasFuturePredictionActive = useCommonSelector(commonSelectors.isFuturePredictionSelector) + const hasFuturePredictionActive = useSelector(selectors.isFuturePredictionSelector) const actualCurrentStartDate = useActualCurrentStartDateSelector() diff --git a/packages/components/src/components/common/LanguageSelect.tsx b/packages/components/src/components/common/LanguageSelect.tsx index e5de49835..9bcde351d 100644 --- a/packages/components/src/components/common/LanguageSelect.tsx +++ b/packages/components/src/components/common/LanguageSelect.tsx @@ -1,8 +1,8 @@ import React from 'react' import styled from 'styled-components/native' -import { useCommonSelector } from '../../redux/useCommonSelector' -import { commonSelectors } from '../../redux/selectors' -import { commonActions } from '../../redux/actions/index' +import { useSelector } from '../../redux/useSelector' +import * as selectors from '../../redux/selectors' +import * as actions from '../../redux/actions/index' import { useDispatch } from 'react-redux' import { PrimaryButton } from './buttons/PrimaryButton' @@ -13,7 +13,7 @@ import { availableAppLocales } from '@oky/core' export const LanguageSelect = ({ style = null, textStyle = null, onPress = null }) => { const [modalVisible, setModalVisible] = React.useState(false) const [lang, setLang] = React.useState('') - const locale = useCommonSelector(commonSelectors.currentLocaleSelector) + const locale = useSelector(selectors.currentLocaleSelector) const dispatch = useDispatch() return ( <> @@ -27,7 +27,7 @@ export const LanguageSelect = ({ style = null, textStyle = null, onPress = null }} onModalHide={() => { if (lang === '') return - dispatch(commonActions.setLocale(lang)) + dispatch(actions.setLocale(lang)) }} setIsVisible={setModalVisible} isVisible={modalVisible} diff --git a/packages/components/src/components/context/DisplayTextContext.tsx b/packages/components/src/components/context/DisplayTextContext.tsx index 81cda0323..fbed8cb6d 100644 --- a/packages/components/src/components/context/DisplayTextContext.tsx +++ b/packages/components/src/components/context/DisplayTextContext.tsx @@ -2,8 +2,8 @@ import React from 'react' import Tts from 'react-native-tts' import _ from 'lodash' import { translate } from '../../i18n' -import { useCommonSelector } from '../../redux/useCommonSelector' -import { commonSelectors } from '../../redux/selectors' +import { useSelector } from '../../redux/useSelector' +import * as selectors from '../../redux/selectors' interface Props { text: null | string @@ -15,10 +15,10 @@ interface Props { const DisplayTextContext = React.createContext(undefined) export function DisplayTextProvider({ children }) { - const availableText = useCommonSelector(commonSelectors.allAvatarText) + const availableText = useSelector(selectors.allAvatarText) const [text, setText] = React.useState(null) - const hasTtsActive = useCommonSelector(commonSelectors.isTtsActiveSelector) + const hasTtsActive = useSelector(selectors.isTtsActiveSelector) const setDisplayTextRandom = () => { setText(_.sample(availableText).content) diff --git a/packages/components/src/components/context/LocaleContext.tsx b/packages/components/src/components/context/LocaleContext.tsx index ef34e5ed8..e10bbdb2d 100644 --- a/packages/components/src/components/context/LocaleContext.tsx +++ b/packages/components/src/components/context/LocaleContext.tsx @@ -1,9 +1,9 @@ import React from 'react' -import { useCommonSelector } from '../../redux/useCommonSelector' +import { useSelector } from '../../redux/useSelector' import { currentLocale, configureI18n } from '../../i18n' export function LocaleProvider({ children }) { - const locale = useCommonSelector((state) => state.app.locale) + const locale = useSelector((state) => state.app.locale) const [syncLocale, setSyncLocale] = React.useState(currentLocale()) React.useLayoutEffect(() => { diff --git a/packages/components/src/components/context/PredictionProvider.tsx b/packages/components/src/components/context/PredictionProvider.tsx index d84b57a18..0de2de66e 100644 --- a/packages/components/src/components/context/PredictionProvider.tsx +++ b/packages/components/src/components/context/PredictionProvider.tsx @@ -3,9 +3,9 @@ import moment, { Moment } from 'moment' import _ from 'lodash' import { PredictionState, PredictionEngine } from '../../prediction' -import { useCommonSelector } from '../../redux/useCommonSelector' +import { useSelector } from '../../redux/useSelector' import { useDispatch } from 'react-redux' -import { commonActions } from '../../redux/actions' +import * as actions from '../../redux/actions' type PredictionDispatch = typeof PredictionEngine.prototype.userInputDispatch @@ -23,7 +23,7 @@ const defaultState = PredictionState.fromData({ export function PredictionProvider({ children }) { const reduxDispatch = useDispatch() - const predictionState = useCommonSelector((state) => state.prediction) + const predictionState = useSelector((state) => state.prediction) const [predictionSnapshots, setPredictionSnapshots] = React.useState([]) const predictionEngine = React.useMemo(() => { @@ -38,21 +38,21 @@ export function PredictionProvider({ children }) { (action) => { setPredictionSnapshots((snapshots) => snapshots.concat(predictionState)) predictionEngine.userInputDispatch(action) - reduxDispatch(commonActions.adjustPrediction(action)) + reduxDispatch(actions.adjustPrediction(action)) }, [predictionState, reduxDispatch, predictionEngine], ) React.useEffect(() => { return predictionEngine.subscribe((nextPredictionState) => { - reduxDispatch(commonActions.setPredictionEngineState(nextPredictionState)) + reduxDispatch(actions.setPredictionEngineState(nextPredictionState)) }) }, [reduxDispatch, predictionEngine]) const undo = React.useCallback(() => { if (predictionSnapshots.length > 0) { const lastSnapshot = _.last(predictionSnapshots) - reduxDispatch(commonActions.setPredictionEngineState(PredictionState.fromJSON(lastSnapshot))) + reduxDispatch(actions.setPredictionEngineState(PredictionState.fromJSON(lastSnapshot))) setPredictionSnapshots((snapshots) => snapshots.slice(0, -1)) } }, [predictionSnapshots]) diff --git a/packages/components/src/components/context/ThemeContext.tsx b/packages/components/src/components/context/ThemeContext.tsx index dd4696a45..bc2fa06f7 100644 --- a/packages/components/src/components/context/ThemeContext.tsx +++ b/packages/components/src/components/context/ThemeContext.tsx @@ -1,12 +1,12 @@ import React from 'react' import { ThemeContext, ThemeProvider as StyledThemeProvider } from 'styled-components' -import { useCommonSelector } from '../../redux/useCommonSelector' -import { commonSelectors } from '../../redux/selectors' +import { useSelector } from '../../redux/useSelector' +import * as selectors from '../../redux/selectors' import { themes } from '@oky/core' export function ThemeProvider({ children }) { - const themeName = useCommonSelector((state) => state.app.theme) - const locale = useCommonSelector(commonSelectors.currentLocaleSelector) + const themeName = useSelector((state) => state.app.theme) + const locale = useSelector(selectors.currentLocaleSelector) return ( { diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index 1e412af1a..511d44c24 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -3,10 +3,9 @@ import { Provider as ReduxProvider } from 'react-redux' import { PersistGate } from 'redux-persist/integration/react' import { configureStore } from './store' import { config } from './config' -import { commonRootReducer } from './reducers' -import { commonRootSaga } from './sagas' +import { rootReducer } from './reducers' +import { rootSaga } from './sagas' import { REHYDRATE } from 'redux-persist' -import { commonActions } from './actions' interface Keys { key: string @@ -15,35 +14,35 @@ interface Keys { interface Context { switchStore: (keys: Keys) => void - switchToCommonStore: () => void + switchToPrimaryStore: () => void } const StoreCoordinatorContext = React.createContext({ switchStore: () => { // }, - switchToCommonStore: () => { + switchToPrimaryStore: () => { // }, }) -const commonStore = configureStore({ +const primaryStore = configureStore({ key: 'primary', secretKey: config.REDUX_ENCRYPT_KEY, - rootReducer: commonRootReducer, - rootSaga: commonRootSaga, + rootReducer, + rootSaga, }) export function StoreCoordinator({ children }) { - const [{ persistor, store }, setStore] = React.useState(commonStore) + const [{ persistor, store }, setStore] = React.useState(primaryStore) const [state, setState] = React.useState(undefined) const [keys, setKeys] = React.useState(undefined) const [shouldSwitch, setShouldSwitch] = React.useState(false) const [shouldMigrate, setShouldMigrate] = React.useState(false) - const switchToCommonStore = () => { - setStore(commonStore) + const switchToPrimaryStore = () => { + setStore(primaryStore) } const switchStore = ({ key, secretKey }: Keys) => { @@ -54,8 +53,8 @@ export function StoreCoordinator({ children }) { configureStore({ key, secretKey, - rootReducer: commonRootReducer, - rootSaga: commonRootSaga, + rootReducer, + rootSaga, }), ) } @@ -63,14 +62,14 @@ export function StoreCoordinator({ children }) { // ===== Step 1: Detect key change ===== // React.useEffect(() => { const unsubscribe = store.subscribe(() => { - const commonState = store.getState() + const primaryState = store.getState() // TODO: // @ts-ignore - const currentKeys = commonState?.keys.keys + const currentKeys = primaryState?.keys.keys if (currentKeys && currentKeys !== keys) { setKeys(currentKeys) - setState(commonState) + setState(primaryState) setShouldSwitch(true) } }) @@ -105,7 +104,7 @@ export function StoreCoordinator({ children }) { diff --git a/packages/components/src/redux/actions/index.ts b/packages/components/src/redux/actions/index.ts index bef24dbf6..df4826cce 100644 --- a/packages/components/src/redux/actions/index.ts +++ b/packages/components/src/redux/actions/index.ts @@ -1,15 +1,6 @@ -import * as analyticsActions from './analyticsActions' -import * as answerActions from './answerActions' -import * as appActions from './appActions' -import * as authActions from './authActions' -import * as contentActions from './contentActions' -import * as predictionActions from './predictionActions' - -export const commonActions = { - ...analyticsActions, - ...answerActions, - ...appActions, - ...authActions, - ...contentActions, - ...predictionActions, -} +export * from './analyticsActions' +export * from './answerActions' +export * from './appActions' +export * from './authActions' +export * from './contentActions' +export * from './predictionActions' diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index cb10679ac..a3059ccdb 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -1,5 +1,5 @@ import { hash } from '../../services/hash' -import { CommonActions } from '../types' +import { Actions } from '../types' import { v4 as uuidv4 } from 'uuid' import _ from 'lodash' @@ -17,7 +17,7 @@ const initialState: AccessState = { lastLoggedInUsername: undefined, } -export function accessReducer(state = initialState, action: CommonActions): AccessState { +export function accessReducer(state = initialState, action: Actions): AccessState { switch (action.type) { case 'CREATE_ACCOUNT_SUCCESS': { const usernameHash = hash(action.payload.user.name) diff --git a/packages/components/src/redux/reducers/analyticsReducer.ts b/packages/components/src/redux/reducers/analyticsReducer.ts index 2172160a8..9d1636359 100644 --- a/packages/components/src/redux/reducers/analyticsReducer.ts +++ b/packages/components/src/redux/reducers/analyticsReducer.ts @@ -1,4 +1,4 @@ -import { CommonActions } from '../types' +import { Actions } from '../types' export type AnalyticsState = Array<{ id: string @@ -9,7 +9,7 @@ export type AnalyticsState = Array<{ const initialState: AnalyticsState = [] -export function analyticsReducer(state = initialState, action: CommonActions): AnalyticsState { +export function analyticsReducer(state = initialState, action: Actions): AnalyticsState { switch (action.type) { case 'QUEUE_EVENT': return state.concat({ diff --git a/packages/components/src/redux/reducers/answerReducer.ts b/packages/components/src/redux/reducers/answerReducer.ts index cd5291b31..30edb9dd4 100644 --- a/packages/components/src/redux/reducers/answerReducer.ts +++ b/packages/components/src/redux/reducers/answerReducer.ts @@ -1,4 +1,4 @@ -import { CommonActions } from '../types' +import { Actions } from '../types' import { combineReducers } from 'redux' import { toShortISO } from '../../services/dateUtils' import { DailyCard } from '../../types' @@ -47,7 +47,7 @@ export interface AnswerState { [userId: string]: AnswerForUserState } -function surveysReducer(state = {}, action: CommonActions): AnswerForUserState['surveys'] { +function surveysReducer(state = {}, action: Actions): AnswerForUserState['surveys'] { if (action.type === 'ANSWER_SURVEY') { return { ...state, @@ -65,7 +65,7 @@ function surveysReducer(state = {}, action: CommonActions): AnswerForUserState[' return state } -function quizzesReducer(state = {}, action: CommonActions): AnswerForUserState['quizzes'] { +function quizzesReducer(state = {}, action: Actions): AnswerForUserState['quizzes'] { if (action.type === 'ANSWER_QUIZ') { return { ...state, @@ -85,7 +85,7 @@ function quizzesReducer(state = {}, action: CommonActions): AnswerForUserState[' return state } -function cardsReducer(state = {}, action: CommonActions): AnswerForUserState['cards'] { +function cardsReducer(state = {}, action: Actions): AnswerForUserState['cards'] { if (action.type === 'ANSWER_DAILY_CARD') { const keyCard = toShortISO(action.payload.utcDateTime) let answersToInsert = [] @@ -125,10 +125,7 @@ function cardsReducer(state = {}, action: CommonActions): AnswerForUserState['ca } return state } -function periodVerifyReducer( - state = {}, - action: CommonActions, -): AnswerForUserState['verifiedDates'] { +function periodVerifyReducer(state = {}, action: Actions): AnswerForUserState['verifiedDates'] { if (action.type === 'ANSWER_VERIFY_DATES') { const keyCard = toShortISO(action.payload.utcDateTime) const answersToInsert = [] @@ -144,7 +141,7 @@ function periodVerifyReducer( return state } -function notesReducer(state = {}, action: CommonActions): AnswerForUserState['notes'] { +function notesReducer(state = {}, action: Actions): AnswerForUserState['notes'] { if (action.type === 'ANSWER_NOTES_CARD') { const keyCard = toShortISO(action.payload.utcDateTime) return { @@ -159,7 +156,7 @@ function notesReducer(state = {}, action: CommonActions): AnswerForUserState['no return state } -const answerForUserReducer = combineReducers({ +const answerForUserReducer = combineReducers({ surveys: surveysReducer, quizzes: quizzesReducer, cards: cardsReducer, @@ -167,7 +164,7 @@ const answerForUserReducer = combineReducers( verifiedDates: periodVerifyReducer, }) -export function answerReducer(state: AnswerState = {}, action: CommonActions): AnswerState { +export function answerReducer(state: AnswerState = {}, action: Actions): AnswerState { // TODO_ALEX: survey if (action.type === 'ANSWER_SURVEY') { return { diff --git a/packages/components/src/redux/reducers/appReducer.ts b/packages/components/src/redux/reducers/appReducer.ts index b03f816f0..27e48e41c 100644 --- a/packages/components/src/redux/reducers/appReducer.ts +++ b/packages/components/src/redux/reducers/appReducer.ts @@ -1,5 +1,5 @@ import _ from 'lodash' -import { CommonActions } from '../types' +import { Actions } from '../types' import { currentLocale } from '../../i18n' import DeviceInfo from 'react-native-device-info' import { AvatarName, ThemeName, defaultAvatar, defaultTheme } from '@oky/core' @@ -44,7 +44,7 @@ const initialState: AppState = { predicted_periods: [], } -export function appReducer(state = initialState, action: CommonActions): AppState { +export function appReducer(state = initialState, action: Actions): AppState { switch (action.type) { case 'SET_THEME': return { diff --git a/packages/components/src/redux/reducers/authReducer.ts b/packages/components/src/redux/reducers/authReducer.ts index df4a61c7f..07710bba1 100644 --- a/packages/components/src/redux/reducers/authReducer.ts +++ b/packages/components/src/redux/reducers/authReducer.ts @@ -1,6 +1,6 @@ import { REHYDRATE, RehydrateAction } from 'redux-persist' import _ from 'lodash' -import { CommonActions } from '../types/index' +import { Actions } from '../types/index' export interface User { id: string @@ -36,10 +36,7 @@ const initialState: AuthState = { user: null, } -export function authReducer( - state = initialState, - action: CommonActions | RehydrateAction, -): AuthState { +export function authReducer(state = initialState, action: Actions | RehydrateAction): AuthState { switch (action.type) { case REHYDRATE: return { diff --git a/packages/components/src/redux/reducers/contentReducer.ts b/packages/components/src/redux/reducers/contentReducer.ts index e26bfb614..a468ca9e2 100644 --- a/packages/components/src/redux/reducers/contentReducer.ts +++ b/packages/components/src/redux/reducers/contentReducer.ts @@ -15,7 +15,7 @@ import { CompletedSurveys, Videos, } from '../../types' -import { CommonActions } from '../types/index' +import { Actions } from '../types/index' export interface ContentState { timeFetched?: number @@ -89,7 +89,7 @@ const initialState: ContentState = { }, } -export function contentReducer(state = initialState, action: CommonActions): ContentState { +export function contentReducer(state = initialState, action: Actions): ContentState { switch (action.type) { case 'INIT_STALE_CONTENT': return { diff --git a/packages/components/src/redux/reducers/index.ts b/packages/components/src/redux/reducers/index.ts index 61bd2eac2..ad1177388 100644 --- a/packages/components/src/redux/reducers/index.ts +++ b/packages/components/src/redux/reducers/index.ts @@ -1,7 +1,7 @@ import _ from 'lodash' import { combineReducers } from 'redux' import { syncReducers } from '../sync' -import { CommonActions } from '../types' +import { Actions } from '../types' import { analyticsReducer } from './analyticsReducer' import { answerReducer } from './answerReducer' @@ -31,7 +31,7 @@ const reducer = combineReducers( ), ) -export function commonRootReducer(state, action: CommonActions) { +export function rootReducer(state, action: Actions) { switch (action.type) { case 'LOGOUT': // @ts-ignore @@ -42,4 +42,4 @@ export function commonRootReducer(state, action: CommonActions) { } } -export type CommonReduxState = ReturnType +export type ReduxState = ReturnType diff --git a/packages/components/src/redux/reducers/keysReducer.ts b/packages/components/src/redux/reducers/keysReducer.ts index 204074386..2e840b704 100644 --- a/packages/components/src/redux/reducers/keysReducer.ts +++ b/packages/components/src/redux/reducers/keysReducer.ts @@ -1,4 +1,5 @@ -import { CommonActions } from '../types' +import { hash } from '../../services/hash' +import { Actions } from '../types' import _ from 'lodash' export interface KeysState { @@ -14,7 +15,7 @@ const initialState: KeysState = { keys: undefined, } -export function keysReducer(state = initialState, action: CommonActions): KeysState { +export function keysReducer(state = initialState, action: Actions): KeysState { switch (action.type) { case 'SET_STORE_KEYS': return { diff --git a/packages/components/src/redux/reducers/predictionReducer.ts b/packages/components/src/redux/reducers/predictionReducer.ts index 228526366..720fbd36d 100644 --- a/packages/components/src/redux/reducers/predictionReducer.ts +++ b/packages/components/src/redux/reducers/predictionReducer.ts @@ -1,13 +1,13 @@ import _ from 'lodash' import { PredictionSerializableState } from '../../prediction' -import { CommonActions } from '../types/index' +import { Actions } from '../types/index' export type PredictionState = PredictionSerializableState | null const initialState: PredictionState = null -export function predictionReducer(state = initialState, action: CommonActions): PredictionState { +export function predictionReducer(state = initialState, action: Actions): PredictionState { switch (action.type) { case 'SET_PREDICTION_ENGINE_STATE': return action.payload.predictionState.toJSON() diff --git a/packages/components/src/redux/sagas/analyticsSaga.ts b/packages/components/src/redux/sagas/analyticsSaga.ts index 992cf92f2..5016b5b97 100644 --- a/packages/components/src/redux/sagas/analyticsSaga.ts +++ b/packages/components/src/redux/sagas/analyticsSaga.ts @@ -3,11 +3,11 @@ import { v4 as uuidv4 } from 'uuid' import moment from 'moment' import { fetchNetworkConnectionStatus } from '../../services/network' import { httpClient } from '../../services/HttpClient' -import { commonActions } from '../actions' -import { commonSelectors } from '../selectors' -import { CommonActionTypes } from '../types' +import * as actions from '../actions' +import * as selectors from '../selectors' +import { ActionTypes } from '../types' -const ACTIONS_TO_TRACK: CommonActionTypes[] = [ +const ACTIONS_TO_TRACK: ActionTypes[] = [ // app 'SET_THEME', 'SET_LOCALE', @@ -21,9 +21,9 @@ const ACTIONS_TO_TRACK: CommonActionTypes[] = [ ] function* onTrackAction(action) { - const currentUser = yield select(commonSelectors.currentUserSelector) + const currentUser = yield select(selectors.currentUserSelector) yield put( - commonActions.queueEvent({ + actions.queueEvent({ id: uuidv4(), type: action.type, payload: action.payload || {}, @@ -40,8 +40,8 @@ function* processEventQueue() { // process queue every minute yield delay(60 * 1000) - const appToken = yield select(commonSelectors.appTokenSelector) - const events = yield select(commonSelectors.allAnalyticsEventsSelector) + const appToken = yield select(selectors.appTokenSelector) + const events = yield select(selectors.allAnalyticsEventsSelector) const isQueueEmpty = events.length === 0 if (isQueueEmpty) { @@ -56,7 +56,7 @@ function* processEventQueue() { try { yield httpClient.appendEvents({ events, appToken }) - yield put(commonActions.resetQueue()) + yield put(actions.resetQueue()) } catch (err) { // ignore error, we'll try later } diff --git a/packages/components/src/redux/sagas/appSaga.ts b/packages/components/src/redux/sagas/appSaga.ts index e2605e2d9..b8d362543 100644 --- a/packages/components/src/redux/sagas/appSaga.ts +++ b/packages/components/src/redux/sagas/appSaga.ts @@ -3,10 +3,10 @@ import { all, delay, fork, put, select, takeLatest } from 'redux-saga/effects' import { httpClient } from '../../services/HttpClient' import { fetchNetworkConnectionStatus } from '../../services/network' import { extractReducerState } from '../sync' -import { CommonReduxState, exportReducerNames } from '../reducers' +import { ReduxState, exportReducerNames } from '../reducers' import { version as storeVersion } from '../store' -import { commonActions } from '../actions' -import { commonSelectors } from '../selectors' +import * as actions from '../actions' +import * as selectors from '../selectors' import messaging from '@react-native-firebase/messaging' function* syncAppState() { @@ -16,13 +16,13 @@ function* syncAppState() { // process queue every minute yield delay(60 * 1000) - const appToken = yield select(commonSelectors.appTokenSelector) + const appToken = yield select(selectors.appTokenSelector) if (!appToken) { // not logged continue } - const state: CommonReduxState = yield select() + const state: ReduxState = yield select() const appState = extractReducerState(state, exportReducerNames) if (_.isEqual(appState, lastAppState)) { @@ -42,7 +42,7 @@ function* syncAppState() { appToken, }) - const temp = yield put(commonActions.syncStore()) + const temp = yield put(actions.syncStore()) lastAppState = appState } catch (err) { @@ -55,7 +55,7 @@ function* onRequestStoreFirebaseKey() { if (yield fetchNetworkConnectionStatus()) { // no internet connection const firebaseToken = yield messaging().getToken() - yield put(commonActions.storeFirebaseKey(firebaseToken)) + yield put(actions.storeFirebaseKey(firebaseToken)) } } diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index b7f2fe4f2..33b200999 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -4,9 +4,9 @@ import { Alert } from 'react-native' import { v4 as uuidv4 } from 'uuid' import { ExtractActionFromActionType } from '../types' import { httpClient } from '../../services/HttpClient' -import { CommonReduxState, exportReducerNames } from '../reducers' -import { commonActions } from '../actions' -import { commonSelectors } from '../selectors' +import { ReduxState, exportReducerNames } from '../reducers' +import * as actions from '../actions' +import * as selectors from '../selectors' import { navigateAndReset } from '../../services/navigationService' import { PredictionState } from '../../prediction' import moment from 'moment' @@ -18,14 +18,14 @@ import { hash } from '../../services/hash' type Await = T extends Promise ? U : T function* onRehydrate() { - const state: CommonReduxState = yield select() + const state: ReduxState = yield select() - const appToken = commonSelectors.appTokenSelector(state) - const user = commonSelectors.currentUserSelector(state) + const appToken = selectors.appTokenSelector(state) + const user = selectors.currentUserSelector(state) // convert guest account if (!appToken && user && user.isGuest) { - yield put(commonActions.convertGuestAccount(user)) + yield put(actions.convertGuestAccount(user)) } } @@ -44,7 +44,7 @@ function* onConvertGuestAccount(action: ExtractActionFromActionType<'CONVERT_GUE } = action.payload yield put( - commonActions.createAccountRequest({ + actions.createAccountRequest({ id, name, dateOfBirth, @@ -61,9 +61,9 @@ function* onConvertGuestAccount(action: ExtractActionFromActionType<'CONVERT_GUE function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { const { name, password } = action.payload - const stateRedux: CommonReduxState = yield select() - const localeapp = commonSelectors.currentLocaleSelector(stateRedux) - yield commonActions.setLocale(localeapp) + const stateRedux: ReduxState = yield select() + const localeapp = selectors.currentLocaleSelector(stateRedux) + yield actions.setLocale(localeapp) try { const { @@ -76,7 +76,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { }) yield put( - commonActions.loginSuccess({ + actions.loginSuccess({ appToken, user: { id: user.id, @@ -107,7 +107,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { }, {}) // @TODO: execute migration based on storeVersion - yield put(commonActions.refreshStore(newAppState)) + yield put(actions.refreshStore(newAppState)) } yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones @@ -134,7 +134,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { // Change boolean to trigger switch in coordinator ? // Somehow need to pass the keys to the coordinator though // yield put( - // commonActions.loginOfflineSuccess({ + // actions.loginOfflineSuccess({ // appToken: null, // user: { // id: user.id, @@ -154,7 +154,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { } yield put( - commonActions.loginFailure({ + actions.loginFailure({ error: errorMessage, }), ) @@ -176,7 +176,7 @@ function* onLoginSuccess(action: ExtractActionFromActionType<'LOGIN_SUCCESS'>) { secretKey: hash(action.payload.user.password + salt), } - yield put(commonActions.setStoreKeys(keys)) + yield put(actions.setStoreKeys(keys)) } function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACCOUNT_REQUEST'>) { @@ -212,7 +212,7 @@ function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACC } yield put( - commonActions.createAccountSuccess({ + actions.createAccountSuccess({ appToken, user: { id: user.id, @@ -231,8 +231,8 @@ function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACC } catch (error) { const errorStatusCode = error && error.response && error.response.status ? error.response.status : null // to check various error codes and respond accordingly - yield put(commonActions.setAuthError({ error: errorStatusCode })) - yield put(commonActions.createAccountFailure()) + yield put(actions.setAuthError({ error: errorStatusCode })) + yield put(actions.createAccountFailure()) // Check username is not already taken const usernameHash = hash(name) @@ -246,7 +246,7 @@ function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACC } yield put( - commonActions.createGuestAccountSuccess({ + actions.createGuestAccountSuccess({ id: id || uuidv4(), name, dateOfBirth, @@ -265,7 +265,7 @@ function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACC function* onCreateAccountSuccess(action: ExtractActionFromActionType<'CREATE_ACCOUNT_SUCCESS'>) { const { appToken, user } = action.payload yield put( - commonActions.loginSuccess({ + actions.loginSuccess({ appToken, user: { id: user.id, @@ -285,8 +285,8 @@ function* onCreateAccountSuccess(action: ExtractActionFromActionType<'CREATE_ACC function* onDeleteAccountRequest(action: ExtractActionFromActionType<'DELETE_ACCOUNT_REQUEST'>) { const { setLoading } = action.payload - const state: CommonReduxState = yield select() - const user = commonSelectors.currentUserSelector(state) + const state: ReduxState = yield select() + const user = selectors.currentUserSelector(state) setLoading(true) try { const { name, password } = action.payload @@ -294,17 +294,17 @@ function* onDeleteAccountRequest(action: ExtractActionFromActionType<'DELETE_ACC name, password, }) - yield put(commonActions.updateAllSurveyContent([])) // TODO_ALEX - yield put(commonActions.updateCompletedSurveys([])) // TODO_ALEX + yield put(actions.updateAllSurveyContent([])) // TODO_ALEX + yield put(actions.updateCompletedSurveys([])) // TODO_ALEX yield put( - commonActions.fetchSurveyContentSuccess({ + actions.fetchSurveyContentSuccess({ surveys: null, }), // TODO_ALEX ) yield call(navigateAndReset, 'LoginStack', null) if (user) { - yield put(commonActions.logout()) + yield put(actions.logout()) } } catch (err) { setLoading(false) @@ -313,27 +313,27 @@ function* onDeleteAccountRequest(action: ExtractActionFromActionType<'DELETE_ACC } function* onLogoutRequest() { - const isTtsActive = yield select(commonSelectors.isTtsActiveSelector) + const isTtsActive = yield select(selectors.isTtsActiveSelector) if (isTtsActive) { yield call(closeOutTTs) - yield put(commonActions.setTtsActive(false)) - yield put(commonActions.verifyPeriodDayByUser([])) // TODO_ALEX: survey + yield put(actions.setTtsActive(false)) + yield put(actions.verifyPeriodDayByUser([])) // TODO_ALEX: survey } - yield put(commonActions.updateAllSurveyContent([])) // TODO_ALEX: survey + yield put(actions.updateAllSurveyContent([])) // TODO_ALEX: survey yield put( - commonActions.fetchSurveyContentSuccess({ + actions.fetchSurveyContentSuccess({ surveys: null, }), ) - yield put(commonActions.updateCompletedSurveys([])) // TODO_ALEX: survey + yield put(actions.updateCompletedSurveys([])) // TODO_ALEX: survey yield call(navigateAndReset, 'LoginStack', null) - yield put(commonActions.logout()) + yield put(actions.logout()) } function* onJourneyCompletion(action: ExtractActionFromActionType<'JOURNEY_COMPLETION'>) { const { data } = action.payload - const currentUser = yield select(commonSelectors.currentUserSelector) + const currentUser = yield select(selectors.currentUserSelector) let periodResult = null if (yield fetchNetworkConnectionStatus()) { try { @@ -358,10 +358,10 @@ function* onJourneyCompletion(action: ExtractActionFromActionType<'JOURNEY_COMPL history: [], }) - yield put(commonActions.setPredictionEngineState(stateToSet)) - yield put(commonActions.updateFuturePrediction(true, null)) - yield put(commonActions.setTutorialOneActive(true)) - yield put(commonActions.setTutorialTwoActive(true)) + yield put(actions.setPredictionEngineState(stateToSet)) + yield put(actions.updateFuturePrediction(true, null)) + yield put(actions.setTutorialOneActive(true)) + yield put(actions.setTutorialTwoActive(true)) yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones yield call(navigateAndReset, 'MainStack', null) } diff --git a/packages/components/src/redux/sagas/contentSaga.ts b/packages/components/src/redux/sagas/contentSaga.ts index 00f786a83..08b21197b 100644 --- a/packages/components/src/redux/sagas/contentSaga.ts +++ b/packages/components/src/redux/sagas/contentSaga.ts @@ -11,19 +11,19 @@ import { liveContent as staleContent, } from '@oky/core' import { httpClient } from '../../services/HttpClient' -import { commonSelectors } from '../selectors' -import { commonActions } from '../actions' +import * as selectors from '../selectors' +import * as actions from '../actions' import _ from 'lodash' import messaging from '@react-native-firebase/messaging' import { closeOutTTs } from '../../services/textToSpeech' function* onRehydrate(action: RehydrateAction) { - const locale = yield select(commonSelectors.currentLocaleSelector) + const locale = yield select(selectors.currentLocaleSelector) const hasPreviousContentFromStorage = action.payload && action.payload.content if (!hasPreviousContentFromStorage) { - yield put(commonActions.initStaleContent(staleContent[locale])) + yield put(actions.initStaleContent(staleContent[locale])) } const now = new Date().getTime() @@ -33,7 +33,7 @@ function* onRehydrate(action: RehydrateAction) { const shouldFetch = !timeFetched || timeFetched + fetchInterval < now if (shouldFetch) { - yield put(commonActions.fetchContentRequest(locale)) + yield put(actions.fetchContentRequest(locale)) } } @@ -41,15 +41,15 @@ function* onRehydrate(action: RehydrateAction) { function* onFetchSurveyContent( action: ExtractActionFromActionType<'FETCH_SURVEY_CONTENT_REQUEST'>, ) { - const locale = yield select(commonSelectors.currentLocaleSelector) - const userID = yield select(commonSelectors.currentUserSelector) + const locale = yield select(selectors.currentLocaleSelector) + const userID = yield select(selectors.currentUserSelector) try { const surveys = yield httpClient.fetchSurveys({ locale, userID, }) - const previousSurveys = yield select(commonSelectors.allSurveys) - const completedSurveys = yield select(commonSelectors.completedSurveys) + const previousSurveys = yield select(selectors.allSurveys) + const completedSurveys = yield select(selectors.completedSurveys) const newSurveyArr = previousSurveys?.length ? previousSurveys : [] surveys.forEach((item) => { const itemExits = _.find(previousSurveys, { id: item.id }) @@ -65,7 +65,7 @@ function* onFetchSurveyContent( } }) - yield put(commonActions.updateAllSurveyContent(finalArr)) + yield put(actions.updateAllSurveyContent(finalArr)) } catch (error) { // } @@ -150,7 +150,7 @@ function* onFetchContentRequest(action: ExtractActionFromActionType<'FETCH_CONTE const aboutBannerData = yield fetchAboutBannerConditional() yield put( - commonActions.fetchContentSuccess({ + actions.fetchContentSuccess({ timeFetched: new Date().getTime(), articles: _.isEmpty(articles.allIds) ? staleContent[locale].articles : articles, videos: _.isEmpty(videos.allIds) ? staleContent[locale].videos : videos, @@ -176,22 +176,22 @@ function* onFetchContentRequest(action: ExtractActionFromActionType<'FETCH_CONTE }), ) } catch (error) { - yield put(commonActions.fetchContentFailure()) - const aboutContent = yield select(commonSelectors.aboutContent) + yield put(actions.fetchContentFailure()) + const aboutContent = yield select(selectors.aboutContent) if (!aboutContent) { - const localeInit = yield select(commonSelectors.currentLocaleSelector) - yield put(commonActions.initStaleContent(staleContent[localeInit])) + const localeInit = yield select(selectors.currentLocaleSelector) + yield put(actions.initStaleContent(staleContent[localeInit])) } } } function* onSetLocale(action: ExtractActionFromActionType<'SET_LOCALE'>) { const { locale } = action.payload - const isTtsActive = yield select(commonSelectors.isTtsActiveSelector) + const isTtsActive = yield select(selectors.isTtsActiveSelector) if (isTtsActive) { // TODO_ALEX why? yield call(closeOutTTs) - yield put(commonActions.setTtsActive(false)) + yield put(actions.setTtsActive(false)) } // unsubscribe from topic // TODO_ALEX: use locales from submodule @@ -199,9 +199,9 @@ function* onSetLocale(action: ExtractActionFromActionType<'SET_LOCALE'>) { messaging().unsubscribeFromTopic('oky_id_notifications') messaging().unsubscribeFromTopic('oky_mn_notifications') messaging().subscribeToTopic(`oky_${locale}_notifications`) - yield put(commonActions.initStaleContent(staleContent[locale])) + yield put(actions.initStaleContent(staleContent[locale])) - yield put(commonActions.fetchContentRequest(locale)) + yield put(actions.fetchContentRequest(locale)) } export function* contentSaga() { diff --git a/packages/components/src/redux/sagas/index.ts b/packages/components/src/redux/sagas/index.ts index 43d5b5a85..9617fcd84 100644 --- a/packages/components/src/redux/sagas/index.ts +++ b/packages/components/src/redux/sagas/index.ts @@ -6,7 +6,7 @@ import { authSaga } from './authSaga' import { contentSaga } from './contentSaga' import { smartPredictionbSaga } from './smartPredictionSaga' -export function* commonRootSaga() { +export function* rootSaga() { yield all([ fork(analyticsSaga), fork(appSaga), diff --git a/packages/components/src/redux/sagas/smartPredictionSaga.ts b/packages/components/src/redux/sagas/smartPredictionSaga.ts index 2f65aa6b1..e8964e8a0 100644 --- a/packages/components/src/redux/sagas/smartPredictionSaga.ts +++ b/packages/components/src/redux/sagas/smartPredictionSaga.ts @@ -3,7 +3,7 @@ import { ExtractActionFromActionType } from '../types' import { httpClient } from '../../services/HttpClient' -import { commonActions } from '../actions' +import * as actions from '../actions' import _ from 'lodash' import { PredictionState } from '../../prediction' @@ -34,15 +34,12 @@ function* onFetchUpdatedPredictedCycles( history: predictionFullState.history, actualCurrentStartDate: predictionFullState.currentCycle, }) - yield put(commonActions.setPredictionEngineState(stateToSet)) + yield put(actions.setPredictionEngineState(stateToSet)) yield put( - commonActions.updateFuturePrediction( - futurePredictionStatus, - predictionFullState.currentCycle, - ), + actions.updateFuturePrediction(futurePredictionStatus, predictionFullState.currentCycle), ) } catch (error) { - yield put(commonActions.setSmartPredictionFailure(error)) + yield put(actions.setSmartPredictionFailure(error)) } } diff --git a/packages/components/src/redux/selectors/analyticsSelectors.ts b/packages/components/src/redux/selectors/analyticsSelectors.ts index 10fc2dedc..9780d5425 100644 --- a/packages/components/src/redux/selectors/analyticsSelectors.ts +++ b/packages/components/src/redux/selectors/analyticsSelectors.ts @@ -1,5 +1,5 @@ -import { CommonReduxState } from '../reducers' +import { ReduxState } from '../reducers' -const s = (state: CommonReduxState) => state.analytics +const s = (state: ReduxState) => state.analytics -export const allAnalyticsEventsSelector = (state: CommonReduxState) => s(state) +export const allAnalyticsEventsSelector = (state: ReduxState) => s(state) diff --git a/packages/components/src/redux/selectors/answerSelectors.ts b/packages/components/src/redux/selectors/answerSelectors.ts index 7506a93aa..1aeada0a5 100644 --- a/packages/components/src/redux/selectors/answerSelectors.ts +++ b/packages/components/src/redux/selectors/answerSelectors.ts @@ -3,26 +3,26 @@ import { allQuizzesSelectors } from './contentSelectors' import { Moment } from 'moment' import { toShortISO } from '../../services/dateUtils' import _ from 'lodash' -import { CommonReduxState } from '../reducers' +import { ReduxState } from '../reducers' -const s = (state: CommonReduxState) => state.answer +const s = (state: ReduxState) => state.answer -export const surveyHasAnswerSelector = (state: CommonReduxState, id: string) => { +export const surveyHasAnswerSelector = (state: ReduxState, id: string) => { if (!s(state)[state.auth.user.id]) return false return id in s(state)[state.auth.user.id].surveys } -// export const surveysWithoutAnswersSelector = (state: CommonReduxState) => { +// export const surveysWithoutAnswersSelector = (state: ReduxState) => { // return allSurveysSelectors(state).filter(({ id }) => !surveyHasAnswerSelector(state, id)) // } -export const quizHasAnswerSelector = (state: CommonReduxState, id: string) => { +export const quizHasAnswerSelector = (state: ReduxState, id: string) => { if (!s(state)[state.auth.user.id]) return false return id in s(state)[state.auth.user.id].quizzes } // Had a type error here had to add any to avoid -export const quizAnswerByDate: any = (state: CommonReduxState, date: Moment) => { +export const quizAnswerByDate: any = (state: ReduxState, date: Moment) => { if (!s(state)[state.auth.user.id]) return null return Object.values(s(state)[state.auth.user.id].quizzes).filter( ({ utcDateTime }) => utcDateTime === date.toISOString(), @@ -30,23 +30,23 @@ export const quizAnswerByDate: any = (state: CommonReduxState, date: Moment) => } // Had a type error here had to add any to avoid -export const surveyAnswerByDate: any = (state: CommonReduxState, date: Moment) => { +export const surveyAnswerByDate: any = (state: ReduxState, date: Moment) => { if (!s(state)[state.auth.user.id]) return null return Object.values(s(state)[state.auth.user.id].surveys).filter( ({ utcDateTime }) => utcDateTime === date.toISOString(), )[0] } -export const quizzesWithoutAnswersSelector = (state: CommonReduxState) => { +export const quizzesWithoutAnswersSelector = (state: ReduxState) => { return allQuizzesSelectors(state).filter(({ id }) => !quizHasAnswerSelector(state, id)) } -export const cardAnswerSelector = (state: CommonReduxState, date: Moment) => { +export const cardAnswerSelector = (state: ReduxState, date: Moment) => { if (!state.auth.user) return {} // for the use case on info screen where there is no authed user if (!s(state)[state.auth.user.id]) return {} return s(state)[state.auth.user.id]?.cards[toShortISO(date)] || {} } -export const verifyPeriodDaySelectorWithDate = (state: CommonReduxState, date: Moment) => { +export const verifyPeriodDaySelectorWithDate = (state: ReduxState, date: Moment) => { if (!state.auth.user) return {} // for the use case on info screen where there is no authed user if (!s(state)[state.auth.user.id]) return {} if (s(state)[state.auth.user.id]?.verifiedDates) { @@ -55,22 +55,18 @@ export const verifyPeriodDaySelectorWithDate = (state: CommonReduxState, date: M return {} // return s(state)[state.auth.user.id]?.verifiedDates[toShortISO(date)] || {} } -export const allCardAnswersSelector = (state: CommonReduxState) => { +export const allCardAnswersSelector = (state: ReduxState) => { if (!state.auth.user) return {} // for the use case on info screen where there is no authed user if (!s(state)[state.auth.user.id]) return {} return s(state)[state.auth.user.id]?.verifiedDates || {} } -export const notesAnswerSelector = (state: CommonReduxState, date: Moment) => { +export const notesAnswerSelector = (state: ReduxState, date: Moment) => { if (!s(state)[state.auth.user.id]) return {} return s(state)[state.auth.user.id].notes[toShortISO(date)] || {} } -export const mostAnsweredSelector = ( - state: CommonReduxState, - startDate: Moment, - endDate: Moment, -) => { +export const mostAnsweredSelector = (state: ReduxState, startDate: Moment, endDate: Moment) => { if (!s(state)[state.auth.user.id]) return {} const dates = Object.keys(s(state)[state.auth.user.id].cards) const filteredDates = dates.filter((item) => { diff --git a/packages/components/src/redux/selectors/appSelectors.ts b/packages/components/src/redux/selectors/appSelectors.ts index 3d58ecbb9..e0ec924bb 100644 --- a/packages/components/src/redux/selectors/appSelectors.ts +++ b/packages/components/src/redux/selectors/appSelectors.ts @@ -1,35 +1,34 @@ -import { CommonReduxState } from '../reducers' +import { ReduxState } from '../reducers' -const s = (state: CommonReduxState) => state.app -const predictionS = (state: CommonReduxState) => state.prediction -export const currentLocaleSelector = (state: CommonReduxState) => s(state).locale +const s = (state: ReduxState) => state.app +const predictionS = (state: ReduxState) => state.prediction +export const currentLocaleSelector = (state: ReduxState) => s(state).locale -export const currentChosenRegionSelector = (state: CommonReduxState) => s(state).chosenRegion +export const currentChosenRegionSelector = (state: ReduxState) => s(state).chosenRegion -export const currentThemeSelector = (state: CommonReduxState) => s(state).theme +export const currentThemeSelector = (state: ReduxState) => s(state).theme -export const currentAvatarSelector = (state: CommonReduxState) => s(state).avatar +export const currentAvatarSelector = (state: ReduxState) => s(state).avatar -export const hasOpenedSelector = (state: CommonReduxState) => s(state).hasOpened +export const hasOpenedSelector = (state: ReduxState) => s(state).hasOpened -export const isTutorialOneActiveSelector = (state: CommonReduxState) => s(state).isTutorialOneActive +export const isTutorialOneActiveSelector = (state: ReduxState) => s(state).isTutorialOneActive -export const isTutorialTwoActiveSelector = (state: CommonReduxState) => s(state).isTutorialTwoActive +export const isTutorialTwoActiveSelector = (state: ReduxState) => s(state).isTutorialTwoActive -export const isTtsActiveSelector = (state: CommonReduxState) => s(state).isTtsActive +export const isTtsActiveSelector = (state: ReduxState) => s(state).isTtsActive -export const isLoginPasswordActiveSelector = (state: CommonReduxState) => - s(state).isLoginPasswordActive +export const isLoginPasswordActiveSelector = (state: ReduxState) => s(state).isLoginPasswordActive -export const currentAppVersion = (state: CommonReduxState) => s(state).appVersionName +export const currentAppVersion = (state: ReduxState) => s(state).appVersionName -export const currentFirebaseToken = (state: CommonReduxState) => s(state).firebaseToken +export const currentFirebaseToken = (state: ReduxState) => s(state).firebaseToken -export const userVerifiedDates = (state: CommonReduxState) => s(state).verifiedDates +export const userVerifiedDates = (state: ReduxState) => s(state).verifiedDates // Smart precition selectors -export const isFuturePredictionSelector = (state: CommonReduxState) => predictionS(state) -export const isFuturePredictionActiveSelector = (state: CommonReduxState) => +export const isFuturePredictionSelector = (state: ReduxState) => predictionS(state) +export const isFuturePredictionActiveSelector = (state: ReduxState) => predictionS(state)?.futurePredictionStatus -// export const smartPredictedPeriods = (state: CommonReduxState) => s(state).predicted_periods +// export const smartPredictedPeriods = (state: ReduxState) => s(state).predicted_periods diff --git a/packages/components/src/redux/selectors/authSelectors.ts b/packages/components/src/redux/selectors/authSelectors.ts index 3f288f37e..baae80918 100644 --- a/packages/components/src/redux/selectors/authSelectors.ts +++ b/packages/components/src/redux/selectors/authSelectors.ts @@ -1,9 +1,9 @@ -import { CommonReduxState } from '../reducers' +import { ReduxState } from '../reducers' -const s = (state: CommonReduxState) => state.auth +const s = (state: ReduxState) => state.auth -export const appTokenSelector = (state: CommonReduxState) => s(state).appToken +export const appTokenSelector = (state: ReduxState) => s(state).appToken -export const authError = (state: CommonReduxState) => s(state).error +export const authError = (state: ReduxState) => s(state).error -export const currentUserSelector = (state: CommonReduxState) => s(state).user +export const currentUserSelector = (state: ReduxState) => s(state).user diff --git a/packages/components/src/redux/selectors/contentSelectors.ts b/packages/components/src/redux/selectors/contentSelectors.ts index 613902b40..034038718 100644 --- a/packages/components/src/redux/selectors/contentSelectors.ts +++ b/packages/components/src/redux/selectors/contentSelectors.ts @@ -1,29 +1,29 @@ import _ from 'lodash' -import { CommonReduxState } from '../reducers' +import { ReduxState } from '../reducers' -const s = (state: CommonReduxState) => state.content +const s = (state: ReduxState) => state.content -export const allArticlesSelector = (state: CommonReduxState) => +export const allArticlesSelector = (state: ReduxState) => s(state).articles.allIds.map((id) => s(state).articles.byId[id]) -export const allVideosSelector = (state: CommonReduxState) => { +export const allVideosSelector = (state: ReduxState) => { if (!s(state)?.videos?.allIds || !s(state)?.videos?.byId) return [] return s(state).videos.allIds.map((id) => s(state).videos.byId[id]) } -export const articleByIDSelector = (state: CommonReduxState, id) => s(state).articles.byId[id] -export const videoByIDSelector = (state: CommonReduxState, id) => s(state)?.videos?.byId[id] +export const articleByIDSelector = (state: ReduxState, id) => s(state).articles.byId[id] +export const videoByIDSelector = (state: ReduxState, id) => s(state)?.videos?.byId[id] -export const articlesObjectByIDSelector = (state: CommonReduxState) => s(state).articles.byId +export const articlesObjectByIDSelector = (state: ReduxState) => s(state).articles.byId // @ts-ignore -export const allHelpCentersForCurrentLocale: any = (state: CommonReduxState) => +export const allHelpCentersForCurrentLocale: any = (state: ReduxState) => s(state).helpCenters.filter((item) => item.lang === state.app.locale) -export const allCategoriesSelector = (state: CommonReduxState) => +export const allCategoriesSelector = (state: ReduxState) => s(state).categories.allIds.map((id) => s(state).categories.byId[id]) -export const allCategoryEmojis = (state: CommonReduxState) => { +export const allCategoryEmojis = (state: ReduxState) => { const categories = allCategoriesSelector(state) return categories.map((item) => { @@ -31,32 +31,30 @@ export const allCategoryEmojis = (state: CommonReduxState) => { }) } -export const allSubCategoriesSelector = (state: CommonReduxState) => +export const allSubCategoriesSelector = (state: ReduxState) => s(state).subCategories.allIds.map((id) => s(state).subCategories.byId[id]) -export const allSubCategoriesObjectSelector = (state: CommonReduxState) => - s(state).subCategories.byId +export const allSubCategoriesObjectSelector = (state: ReduxState) => s(state).subCategories.byId -export const categoryByIDSelector = (state: CommonReduxState, id) => s(state).categories.byId[id] +export const categoryByIDSelector = (state: ReduxState, id) => s(state).categories.byId[id] -export const subCategoryByIDSelector = (state: CommonReduxState, id) => - s(state).subCategories.byId[id] +export const subCategoryByIDSelector = (state: ReduxState, id) => s(state).subCategories.byId[id] -export const allAvatarText = (state: CommonReduxState) => s(state).avatarMessages +export const allAvatarText = (state: ReduxState) => s(state).avatarMessages -export const privacyContent = (state: CommonReduxState) => s(state).privacyPolicy +export const privacyContent = (state: ReduxState) => s(state).privacyPolicy -export const termsAndConditionsContent = (state: CommonReduxState) => s(state).termsAndConditions +export const termsAndConditionsContent = (state: ReduxState) => s(state).termsAndConditions -export const aboutContent = (state: CommonReduxState) => s(state).about +export const aboutContent = (state: ReduxState) => s(state).about -export const allSurveys = (state: CommonReduxState) => s(state).allSurveys +export const allSurveys = (state: ReduxState) => s(state).allSurveys -export const completedSurveys = (state: CommonReduxState) => s(state).completedSurveys +export const completedSurveys = (state: ReduxState) => s(state).completedSurveys -export const aboutBanner = (state: CommonReduxState) => s(state).aboutBanner +export const aboutBanner = (state: ReduxState) => s(state).aboutBanner -export const allQuizzesSelectors = (state: CommonReduxState) => { +export const allQuizzesSelectors = (state: ReduxState) => { // TODO: FIXME const isUserYoungerThan15 = true // moment() @@ -88,7 +86,7 @@ export const allQuizzesSelectors = (state: CommonReduxState) => { return filteredArray } -export const allDidYouKnowsSelectors = (state: CommonReduxState) => { +export const allDidYouKnowsSelectors = (state: ReduxState) => { // TODO_ALEX: FIXME const isUserYoungerThan15 = true // moment() diff --git a/packages/components/src/redux/selectors/index.ts b/packages/components/src/redux/selectors/index.ts index 54f46f1b2..6d37f9edb 100644 --- a/packages/components/src/redux/selectors/index.ts +++ b/packages/components/src/redux/selectors/index.ts @@ -1,13 +1,5 @@ -import * as analyticsSelectors from './analyticsSelectors' -import * as answerSelectors from './answerSelectors' -import * as appSelectors from './appSelectors' -import * as authSelectors from './authSelectors' -import * as contentSelectors from './contentSelectors' - -export const commonSelectors = { - ...analyticsSelectors, - ...answerSelectors, - ...appSelectors, - ...authSelectors, - ...contentSelectors, -} +export * from './analyticsSelectors' +export * from './answerSelectors' +export * from './appSelectors' +export * from './authSelectors' +export * from './contentSelectors' diff --git a/packages/components/src/redux/types/index.ts b/packages/components/src/redux/types/index.ts index 13d607dbf..a71885637 100644 --- a/packages/components/src/redux/types/index.ts +++ b/packages/components/src/redux/types/index.ts @@ -1,11 +1,11 @@ -import { commonActions } from '../actions' +import * as actions from '../actions' import { ActionsUnion, ActionsOfType } from './types' -export type CommonActions = ActionsUnion +export type Actions = ActionsUnion -export type CommonActionTypes = CommonActions[keyof CommonActions] +export type ActionTypes = Actions[keyof Actions] export type ExtractActionFromActionType = ActionsOfType< - CommonActions, + Actions, ActionType > diff --git a/packages/components/src/redux/useCommonSelector.ts b/packages/components/src/redux/useCommonSelector.ts deleted file mode 100644 index 2ac7a2cfa..000000000 --- a/packages/components/src/redux/useCommonSelector.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { useSelector as useReduxSelector, TypedUseSelectorHook } from 'react-redux' -import { CommonReduxState } from './reducers' - -export const useCommonSelector: TypedUseSelectorHook = useReduxSelector diff --git a/packages/components/src/redux/useSelector.ts b/packages/components/src/redux/useSelector.ts new file mode 100644 index 000000000..83cc8960d --- /dev/null +++ b/packages/components/src/redux/useSelector.ts @@ -0,0 +1,4 @@ +import { useSelector as useReduxSelector, TypedUseSelectorHook } from 'react-redux' +import { ReduxState } from './reducers' + +export const useSelector: TypedUseSelectorHook = useReduxSelector diff --git a/packages/components/src/screens/ArticlesScreen.tsx b/packages/components/src/screens/ArticlesScreen.tsx index 3cc8b8ef8..fff8b23eb 100644 --- a/packages/components/src/screens/ArticlesScreen.tsx +++ b/packages/components/src/screens/ArticlesScreen.tsx @@ -3,16 +3,14 @@ import styled from 'styled-components/native' import { FlatList } from 'react-native' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' -import { useCommonSelector } from '../redux/useCommonSelector' -import { commonSelectors } from '../redux/selectors' +import { useSelector } from '../redux/useSelector' +import * as selectors from '../redux/selectors' import { Header } from '../components/common/Header' import { TextWithoutTranslation } from '../components/common/Text' import { useTextToSpeechHook } from '../hooks/useTextToSpeechHook' const ArticleItem = ({ article, index, articles }) => { - const articleObject = useCommonSelector((state) => - commonSelectors.articleByIDSelector(state, article), - ) + const articleObject = useSelector((state) => selectors.articleByIDSelector(state, article)) if (!articleObject) { return null @@ -39,10 +37,10 @@ const ArticleItem = ({ article, index, articles }) => { export function ArticlesScreen({ navigation }) { const subCategory = navigation.getParam('subCategory') - const subCategoryObject = useCommonSelector((state) => - commonSelectors.subCategoryByIDSelector(state, subCategory), + const subCategoryObject = useSelector((state) => + selectors.subCategoryByIDSelector(state, subCategory), ) - const allArticlesByIDObject = useCommonSelector(commonSelectors.articlesObjectByIDSelector) + const allArticlesByIDObject = useSelector(selectors.articlesObjectByIDSelector) const articles = subCategoryObject.articles const articlesTextArray = articles.reduce((acc, item) => { const selectedArticle = allArticlesByIDObject[item] diff --git a/packages/components/src/screens/AuthScreen.tsx b/packages/components/src/screens/AuthScreen.tsx index fc8e221c0..c3b35e5bc 100644 --- a/packages/components/src/screens/AuthScreen.tsx +++ b/packages/components/src/screens/AuthScreen.tsx @@ -13,7 +13,7 @@ import { navigate } from '../services/navigationService' export function AuthScreen() { const [toggled, setToggled] = React.useState(true) // @TODO: LANGUAGES This is commented in case the client wants multiple languages - // const locale = useCommonSelector(commonSelectors.currentLocaleSelector) + // const locale = useSelector(selectors.currentLocaleSelector) return ( diff --git a/packages/components/src/screens/AvatarAndThemeScreen.tsx b/packages/components/src/screens/AvatarAndThemeScreen.tsx index 20e3c61b2..a4c8c5f64 100644 --- a/packages/components/src/screens/AvatarAndThemeScreen.tsx +++ b/packages/components/src/screens/AvatarAndThemeScreen.tsx @@ -6,12 +6,12 @@ import { ThemeSelect } from './avatarAndTheme/ThemeSelect' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { PrimaryButton } from '../components/common/buttons/PrimaryButton' -import { commonActions } from '../redux/actions/index' +import * as actions from '../redux/actions/index' import { Header } from '../components/common/Header' import { useTheme } from '../components/context/ThemeContext' import { BackOneScreen, navigate } from '../services/navigationService' -import { useCommonSelector } from '../redux/useCommonSelector' -import { commonSelectors } from '../redux/selectors' +import { useSelector } from '../redux/useSelector' +import * as selectors from '../redux/selectors' import styled from 'styled-components/native' import { Text } from '../components/common/Text' import { ScrollView } from 'react-native-gesture-handler' @@ -21,7 +21,7 @@ export function AvatarAndThemeScreen({ navigation }) { const signingUp = navigation.getParam('signingUp') const newUser = navigation.getParam('newUser') const [loading, setLoading] = React.useState(false) - const selectedAvatar = useCommonSelector(commonSelectors.currentAvatarSelector) + const selectedAvatar = useSelector(selectors.currentAvatarSelector) const dispatch = useDispatch() const { id } = useTheme() @@ -57,7 +57,7 @@ export function AvatarAndThemeScreen({ navigation }) { dispatch(commonActions.setAvatar(avatar))} + onSelect={(avatar) => dispatch(actions.setAvatar(avatar))} /> { - dispatch(commonActions.setTheme(theme)) + dispatch(actions.setTheme(theme)) }) } }} diff --git a/packages/components/src/screens/ContactUsScreen.tsx b/packages/components/src/screens/ContactUsScreen.tsx index 51bc95db7..1b661a53b 100644 --- a/packages/components/src/screens/ContactUsScreen.tsx +++ b/packages/components/src/screens/ContactUsScreen.tsx @@ -9,8 +9,8 @@ import { PageContainer } from '../components/layout/PageContainer' import { TextInput } from '../components/common/TextInput' import { KeyboardAwareAvoidance } from '../components/common/KeyboardAwareAvoidance' import { httpClient } from '../services/HttpClient' -import { useCommonSelector } from '../redux/useCommonSelector' -import { commonSelectors } from '../redux/selectors' +import { useSelector } from '../redux/useSelector' +import * as selectors from '../redux/selectors' import moment from 'moment' import { Text } from '../components/common/Text' import { ThemedModal } from '../components/common/ThemedModal' @@ -23,8 +23,8 @@ const Reasons = ['reason', 'report_bug', 'request_topic', 'Other', 'problem_app' export function ContactUsScreen({ navigation }) { const [email, setEmail] = React.useState('') - const user = useCommonSelector(commonSelectors.currentUserSelector) - const locale = useCommonSelector(commonSelectors.currentLocaleSelector) + const user = useSelector(selectors.currentUserSelector) + const locale = useSelector(selectors.currentLocaleSelector) const [reason, setReason] = React.useState('') const [message, setMessage] = React.useState('') const [notValid, setNotValid] = React.useState(false) diff --git a/packages/components/src/screens/DayScreen.tsx b/packages/components/src/screens/DayScreen.tsx index be766980f..fdaf4e09f 100644 --- a/packages/components/src/screens/DayScreen.tsx +++ b/packages/components/src/screens/DayScreen.tsx @@ -12,8 +12,8 @@ import { assets } from '../assets' import { usePredictDay } from '../components/context/PredictionProvider' import { ThemedModal } from '../components/common/ThemedModal' import { ColourButtons } from './mainScreen/ColourButtons' -import { commonSelectors } from '../redux/selectors' -import { useCommonSelector } from '../redux/useCommonSelector' +import * as selectors from '../redux/selectors' +import { useSelector } from '../redux/useSelector' import moment from 'moment' export function DayScreen({ navigation }) { @@ -21,8 +21,8 @@ export function DayScreen({ navigation }) { const dataEntry = usePredictDay(temp.date) const [isVisible, setIsVisible] = React.useState(false) const { keyboardIsOpen, dismiss } = useKeyboardController() - const cardAnswersToday = useCommonSelector((state) => - commonSelectors.verifyPeriodDaySelectorWithDate(state, moment(dataEntry.date)), + const cardAnswersToday = useSelector((state) => + selectors.verifyPeriodDaySelectorWithDate(state, moment(dataEntry.date)), ) const goBack = () => { diff --git a/packages/components/src/screens/EditProfileScreen.tsx b/packages/components/src/screens/EditProfileScreen.tsx index 74fb66f4c..695fcb647 100644 --- a/packages/components/src/screens/EditProfileScreen.tsx +++ b/packages/components/src/screens/EditProfileScreen.tsx @@ -8,9 +8,9 @@ import { Icon } from '../components/common/Icon' import { SelectBox } from '../components/common/SelectBox' import { DateOfBirthInput } from '../components/common/DateOfBirthInput' import { assets } from '../assets/index' -import { useCommonSelector } from '../redux/useCommonSelector' -import { commonSelectors } from '../redux/selectors' -import { commonActions } from '../redux/actions' +import { useSelector } from '../redux/useSelector' +import * as selectors from '../redux/selectors' +import * as actions from '../redux/actions' import { useDispatch } from 'react-redux' import { BackOneScreen } from '../services/navigationService' import { httpClient } from '../services/HttpClient' @@ -71,8 +71,8 @@ async function runInSequence(functions) { export function EditProfileScreen() { const dispatch = useDispatch() - const currentUser = useCommonSelector(commonSelectors.currentUserSelector) - const appToken = useCommonSelector(commonSelectors.appTokenSelector) + const currentUser = useSelector(selectors.currentUserSelector) + const appToken = useSelector(selectors.appTokenSelector) const [name, setName] = React.useState(currentUser.name) const [notValid, setNotValid] = React.useState(false) @@ -117,7 +117,7 @@ export function EditProfileScreen() { }) dispatch( - commonActions.editUser({ + actions.editUser({ name, dateOfBirth, gender, @@ -144,7 +144,7 @@ export function EditProfileScreen() { }) dispatch( - commonActions.editUser({ + actions.editUser({ secretAnswer: _.toLower(secretAnswer).trim(), }), ) @@ -182,7 +182,7 @@ export function EditProfileScreen() { }) dispatch( - commonActions.editUser({ + actions.editUser({ password: _.toLower(password).trim(), }), ) @@ -205,7 +205,7 @@ export function EditProfileScreen() { } dispatch( - commonActions.editUser({ + actions.editUser({ name, dateOfBirth, gender, diff --git a/packages/components/src/screens/EncyclopediaScreen.tsx b/packages/components/src/screens/EncyclopediaScreen.tsx index fa3559693..72a4f755b 100644 --- a/packages/components/src/screens/EncyclopediaScreen.tsx +++ b/packages/components/src/screens/EncyclopediaScreen.tsx @@ -3,8 +3,8 @@ import styled from 'styled-components/native' import { ScrollView, Animated } from 'react-native' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' -import { useCommonSelector } from '../redux/useCommonSelector' -import { commonSelectors } from '../redux/selectors' +import { useSelector } from '../redux/useSelector' +import * as selectors from '../redux/selectors' import { Category } from './encyclopediaScreen/Category' import { SubCategoryCard, VideoSubCategoryCard } from './encyclopediaScreen/SubCategoryCard' import Accordion from 'react-native-collapsible/Accordion' @@ -18,16 +18,16 @@ import analytics from '@react-native-firebase/analytics' import { fetchNetworkConnectionStatus } from '../services/network' export function EncyclopediaScreen({ navigation }) { - const categories = useCommonSelector(commonSelectors.allCategoriesSelector) - const articles = useCommonSelector(commonSelectors.allArticlesSelector) - const subCategories = useCommonSelector(commonSelectors.allSubCategoriesSelector) - const subCategoriesObject = useCommonSelector(commonSelectors.allSubCategoriesObjectSelector) + const categories = useSelector(selectors.allCategoriesSelector) + const articles = useSelector(selectors.allArticlesSelector) + const subCategories = useSelector(selectors.allSubCategoriesSelector) + const subCategoriesObject = useSelector(selectors.allSubCategoriesObjectSelector) const [activeCategories, setActiveCategory] = React.useState([]) const [filteredCategories, setFilteredCategories] = React.useState(categories) const [shownCategories, setShownCategories] = React.useState(categories) const [searching, setSearching] = React.useState(false) const [position] = React.useState(new Animated.Value(0)) - const currentUser = useCommonSelector(commonSelectors.currentUserSelector) + const currentUser = useSelector(selectors.currentUserSelector) const categoryNames = categories.map((item) => item?.name) const [textArray, setTextArray] = React.useState(categoryNames) diff --git a/packages/components/src/screens/FindHelpScreen.tsx b/packages/components/src/screens/FindHelpScreen.tsx index 491a4589d..c4d1603c8 100644 --- a/packages/components/src/screens/FindHelpScreen.tsx +++ b/packages/components/src/screens/FindHelpScreen.tsx @@ -7,8 +7,8 @@ import { Avatar } from '../components/common/Avatar/Avatar' import { TextWithoutTranslation, Text } from '../components/common/Text' import { SwiperContainer } from '../components/common/SwiperContainer' import { PageContainer } from '../components/layout/PageContainer' -import { useCommonSelector } from '../redux/useCommonSelector' -import { commonSelectors } from '../redux/selectors' +import { useSelector } from '../redux/useSelector' +import * as selectors from '../redux/selectors' import { useTextToSpeechHook } from '../hooks/useTextToSpeechHook' import analytics from '@react-native-firebase/analytics' import { fetchNetworkConnectionStatus } from '../services/network' @@ -18,7 +18,7 @@ const screenHeight = Dimensions.get('screen').height const heightOfCarousel = screenHeight * 0.5 export function FindHelpScreen({ navigation }) { - const helpCenters: any = useCommonSelector(commonSelectors.allHelpCentersForCurrentLocale) + const helpCenters: any = useSelector(selectors.allHelpCentersForCurrentLocale) const [textToSpeak, setTextToSpeak] = React.useState([]) React.useEffect(() => { diff --git a/packages/components/src/screens/MainScreen.tsx b/packages/components/src/screens/MainScreen.tsx index 200ed64f9..6ecb366d1 100644 --- a/packages/components/src/screens/MainScreen.tsx +++ b/packages/components/src/screens/MainScreen.tsx @@ -20,10 +20,10 @@ import { import { useRandomText } from '../hooks/useRandomText' import { InformationButton } from '../components/common/InformationButton' import { assets } from '../assets' -import { commonActions } from '../redux/actions' +import * as actions from '../redux/actions' import { useDispatch } from 'react-redux' -import { useCommonSelector } from '../redux/useCommonSelector' -import { commonSelectors } from '../redux/selectors' +import { useSelector } from '../redux/useSelector' +import * as selectors from '../redux/selectors' import moment from 'moment' import { FlowerButton, FlowerModal } from '../optional/Flower' @@ -44,14 +44,14 @@ const MainScreenContainer = ({ navigation }) => { const theme = useTheme() const todayInfo = useTodayPrediction() const dispatch = useDispatch() - const userID = useCommonSelector(commonSelectors.currentUserSelector)?.id + const userID = useSelector(selectors.currentUserSelector)?.id const fullState = useFullState() const history = useHistoryPrediction() - const currentUser = useCommonSelector(commonSelectors.currentUserSelector) + const currentUser = useSelector(selectors.currentUserSelector) // @TODO: careful note here, may be worth the performance increase though May not work with Memo now React.useEffect(() => { - dispatch(commonActions.fetchSurveyContentRequest(userID)) + dispatch(actions.fetchSurveyContentRequest(userID)) }, []) // TODO: Cant use hook like this? @@ -62,11 +62,11 @@ const MainScreenContainer = ({ navigation }) => { const MainScreenActual = React.memo(() => { const { data, index, isActive, currentIndex, absoluteIndex } = useInfiniteScroll() // TODO_ALEX: DO NOT USE HOOKS LIKE THIS - const renamedUseSelector = useCommonSelector - const allCardsData = renamedUseSelector((state) => commonSelectors.allCardAnswersSelector(state)) + const renamedUseSelector = useSelector + const allCardsData = renamedUseSelector((state) => selectors.allCardAnswersSelector(state)) const getCardAnswersValues = (inputDay: any) => { const verifiedPeriodDaysData = renamedUseSelector((state) => - commonSelectors.verifyPeriodDaySelectorWithDate(state, moment(inputDay.date)), + selectors.verifyPeriodDaySelectorWithDate(state, moment(inputDay.date)), ) return verifiedPeriodDaysData } diff --git a/packages/components/src/screens/OnboardingScreen.tsx b/packages/components/src/screens/OnboardingScreen.tsx index 3f848e961..f1cb5da20 100644 --- a/packages/components/src/screens/OnboardingScreen.tsx +++ b/packages/components/src/screens/OnboardingScreen.tsx @@ -7,7 +7,7 @@ import { OnboardingCard } from './onboardingScreen/OnboardingCard' import { assets } from '../assets/index' import { PrimaryButton } from '../components/common/buttons/PrimaryButton' import { navigateAndReset } from '../services/navigationService' -import { commonActions } from '../redux/actions' +import * as actions from '../redux/actions' import { useDispatch } from 'react-redux' import { Animated } from 'react-native' import { Text } from '../components/common/Text' @@ -18,7 +18,7 @@ export function OnboardingScreen() { const [index, setIndex] = React.useState(0) const [isButtonVisible, setIsButtonVisible] = React.useState(false) // @TODO: LANGUAGES This is commented in case the client wants multiple languages - // const region = useCommonSelector(commonSelectors.currentChosenRegionSelector) + // const region = useSelector(selectors.currentChosenRegionSelector) React.useEffect(() => { if (index === 2) { @@ -28,8 +28,8 @@ export function OnboardingScreen() { // @TODO: LANGUAGES This is commented in case the client wants multiple languages // const onPenalCodeComplete = lang => { - // dispatch(commonActions.setChosenRegion(lang)) - // dispatch(commonActions.setLocale(lang)) + // dispatch(actions.setChosenRegion(lang)) + // dispatch(actions.setLocale(lang)) // } return ( @@ -70,7 +70,7 @@ export function OnboardingScreen() { }} textStyle={{ color: 'white' }} onPress={() => { - dispatch(commonActions.setHasOpened(true)) + dispatch(actions.setHasOpened(true)) navigateAndReset('LoginStack', null) }} > diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index f0f43704f..f31d3afef 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -1,14 +1,14 @@ import React from 'react' import styled from 'styled-components/native' import { useDispatch } from 'react-redux' -import { commonActions } from '../redux/actions' +import * as actions from '../redux/actions' import { Text } from '../components/common/Text' import { TextInput } from '../components/common/TextInput' -import { commonSelectors } from '../redux/selectors' +import * as selectors from '../redux/selectors' import { navigateAndReset } from '../services/navigationService' import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { PageContainer } from '../components/layout/PageContainer' -import { useCommonSelector } from '../redux/useCommonSelector' +import { useSelector } from '../redux/useSelector' import { KeyboardAwareAvoidance } from '../components/common/KeyboardAwareAvoidance' import { SpinLoader } from '../components/common/SpinLoader' import _ from 'lodash' @@ -16,8 +16,8 @@ import { hash } from '../services/hash' export function PasswordRequestScreen() { const dispatch = useDispatch() - const user = useCommonSelector(commonSelectors.currentUserSelector) - const credentials = useCommonSelector((s) => s.access.credentials) + const user = useSelector(selectors.currentUserSelector) + const credentials = useSelector((s) => s.access.credentials) const [loading, setLoading] = React.useState(false) const [valid, setValid] = React.useState(false) const [passwordError, setPasswordError] = React.useState(false) @@ -85,7 +85,7 @@ export function PasswordRequestScreen() { // TODO_ALEX Consider moving to MainScreen for safer transition setTimeout(() => { - dispatch(commonActions.setStoreKeys(keys)) + dispatch(actions.setStoreKeys(keys)) }, 1000) } else if (trimmedPassword === user.password && name !== user.name) { setLoading(false) @@ -110,7 +110,7 @@ export function PasswordRequestScreen() { { - dispatch(commonActions.logoutRequest()) + dispatch(actions.logoutRequest()) }} > state.auth.connectAccountAttempts) + const connectAccountCount = useSelector((state) => state.auth.connectAccountAttempts) const dateOfBirth = moment(currentUser?.dateOfBirth) useTextToSpeechHook({ @@ -148,7 +148,7 @@ export function ProfileScreen({ navigation }) { onPress={() => { setError(false) dispatch( - commonActions.convertGuestAccount({ + actions.convertGuestAccount({ id: currentUser.id, name: currentUser.name, dateOfBirth: currentUser.dateOfBirth, diff --git a/packages/components/src/screens/SettingsScreen.tsx b/packages/components/src/screens/SettingsScreen.tsx index 99419146f..a4965cc38 100644 --- a/packages/components/src/screens/SettingsScreen.tsx +++ b/packages/components/src/screens/SettingsScreen.tsx @@ -8,10 +8,10 @@ import { PrimaryButton } from '../components/common/buttons/PrimaryButton' import { Switcher } from './settings/Switcher' import { navigate } from '../services/navigationService' import { useDispatch } from 'react-redux' -import { commonActions } from '../redux/actions' +import * as actions from '../redux/actions' import { ConfirmAlert } from '../components/common/ConfirmAlert' -import { useCommonSelector } from '../redux/useCommonSelector' -import { commonSelectors } from '../redux/selectors' +import { useSelector } from '../redux/useSelector' +import * as selectors from '../redux/selectors' import { translate } from '../i18n/index' import { SpinLoader } from '../components/common/SpinLoader' import { settingsScreenText } from '../config' @@ -25,9 +25,9 @@ export function SettingsScreen({ navigation }) { const dispatch = useDispatch() const [loading, setLoading] = React.useState(false) const currentCycleInfo = useTodayPrediction() - const currentUser = useCommonSelector(commonSelectors.currentUserSelector) - const hasTtsActive = useCommonSelector(commonSelectors.isTtsActiveSelector) - const hasFuturePredictionActive = useCommonSelector(commonSelectors.isFuturePredictionSelector) + const currentUser = useSelector(selectors.currentUserSelector) + const hasTtsActive = useSelector(selectors.isTtsActiveSelector) + const hasFuturePredictionActive = useSelector(selectors.isFuturePredictionSelector) useTextToSpeechHook({ navigation, @@ -64,7 +64,7 @@ export function SettingsScreen({ navigation }) { } } closeOutTTs() - dispatch(commonActions.setTtsActive(val)) + dispatch(actions.setTtsActive(val)) }} /> )} @@ -77,7 +77,7 @@ export function SettingsScreen({ navigation }) { value={hasFuturePredictionActive?.futurePredictionStatus} onSwitch={(val) => { const currentStartDate = currentCycleInfo - dispatch(commonActions.updateFuturePrediction(val, currentStartDate)) + dispatch(actions.updateFuturePrediction(val, currentStartDate)) }} /> )} @@ -94,7 +94,7 @@ export function SettingsScreen({ navigation }) { () => { setLoading(true) setTimeout(() => { - dispatch(commonActions.logoutRequest()) + dispatch(actions.logoutRequest()) }, 100) }, ) @@ -114,7 +114,7 @@ export function SettingsScreen({ navigation }) { analytics().logEvent('delete_account', { user: currentUser }) } dispatch( - commonActions.deleteAccountRequest({ + actions.deleteAccountRequest({ name: currentUser.name, password: currentUser.password, setLoading, diff --git a/packages/components/src/screens/SplashScreen.tsx b/packages/components/src/screens/SplashScreen.tsx index 73fa17eee..11a1e739c 100644 --- a/packages/components/src/screens/SplashScreen.tsx +++ b/packages/components/src/screens/SplashScreen.tsx @@ -4,8 +4,8 @@ import { PageContainer } from '../components/layout/PageContainer' import { assets } from '../assets/index' import styled from 'styled-components/native' import { useDispatch } from 'react-redux' -import { commonSelectors } from '../redux/selectors' -import { commonActions } from '../redux/actions' +import * as selectors from '../redux/selectors' +import * as actions from '../redux/actions' import { navigateAndReset } from '../services/navigationService' import { Animated, Easing } from 'react-native' import { createNotificationChannel, requestUserPermission } from '../services/notifications' @@ -15,18 +15,18 @@ import { useAlert } from '../components/context/AlertContext' import { httpClient } from '../services/HttpClient' import { fetchNetworkConnectionStatus } from '../services/network' import messaging from '@react-native-firebase/messaging' -import { useCommonSelector } from '../redux/useCommonSelector' +import { useSelector } from '../redux/useSelector' export function SplashScreen() { const dispatch = useDispatch() - const user: any = useCommonSelector(commonSelectors.currentUserSelector) + const user: any = useSelector(selectors.currentUserSelector) const Alert = useAlert() - const locale = useCommonSelector(commonSelectors.currentLocaleSelector) - const hasOpened = useCommonSelector(commonSelectors.hasOpenedSelector) - const currentAppVersion = useCommonSelector(commonSelectors.currentAppVersion) - const currentFirebaseToken = useCommonSelector(commonSelectors.currentFirebaseToken) - const hasPasswordRequestOn = useCommonSelector(commonSelectors.isLoginPasswordActiveSelector) + const locale = useSelector(selectors.currentLocaleSelector) + const hasOpened = useSelector(selectors.hasOpenedSelector) + const currentAppVersion = useSelector(selectors.currentAppVersion) + const currentFirebaseToken = useSelector(selectors.currentFirebaseToken) + const hasPasswordRequestOn = useSelector(selectors.isLoginPasswordActiveSelector) const [animatedValue] = React.useState(new Animated.Value(0)) async function checkForPermanentAlerts() { @@ -58,12 +58,12 @@ export function SplashScreen() { messaging().unsubscribeFromTopic('oky_mn_notifications') messaging().subscribeToTopic(`oky_${locale}_notifications`) if (currentAppVersion !== DeviceInfo.getVersion()) { - dispatch(commonActions.setUpdatedVersion()) - dispatch(commonActions.updateFuturePrediction(true, null)) + dispatch(actions.setUpdatedVersion()) + dispatch(actions.updateFuturePrediction(true, null)) } if (fetchNetworkConnectionStatus()) { if (currentFirebaseToken === null) { - dispatch(commonActions.requestStoreFirebaseKey()) + dispatch(actions.requestStoreFirebaseKey()) } } diff --git a/packages/components/src/screens/TutorialFirstScreen.tsx b/packages/components/src/screens/TutorialFirstScreen.tsx index f41fe337a..105ac9b18 100644 --- a/packages/components/src/screens/TutorialFirstScreen.tsx +++ b/packages/components/src/screens/TutorialFirstScreen.tsx @@ -11,15 +11,15 @@ import { useInfiniteScroll } from './mainScreen/wheelCarousel/useInfiniteScroll' import { navigateAndReset } from '../services/navigationService' import { Animated, Dimensions, Platform } from 'react-native' import { useDispatch } from 'react-redux' -import { commonActions } from '../redux/actions' +import * as actions from '../redux/actions' import { Text } from '../components/common/Text' import { Icon } from '../components/common/Icon' import { assets } from '../assets' import { ColourButtonsDemo } from './tutorial/ColourButtonsDemo' import { SpinLoader } from '../components/common/SpinLoader' import DeviceInfo from 'react-native-device-info' -import { useCommonSelector } from '../redux/useCommonSelector' -import { commonSelectors } from '../redux/selectors' +import { useSelector } from '../redux/useSelector' +import * as selectors from '../redux/selectors' import moment from 'moment' import Tts from 'react-native-tts' import { translate } from '../i18n' @@ -43,17 +43,17 @@ export function TutorialFirstScreen() { const dispatch = useDispatch() const [completedStep, setCompletedStep] = React.useState(0) - const hasTtsActive = useCommonSelector(commonSelectors.isTtsActiveSelector) + const hasTtsActive = useSelector(selectors.isTtsActiveSelector) const normalizePosition = (percentage, dimension) => { return percentage * dimension - arrowSize / 2 } // TODO_ALEX: DO NOT USE HOOKS LIKE THIS - const renamedUseSelector = useCommonSelector + const renamedUseSelector = useSelector const getCardAnswersValues = (inputDay) => { const cardData = renamedUseSelector((state) => - commonSelectors.verifyPeriodDaySelectorWithDate(state, moment(inputDay.date)), + selectors.verifyPeriodDaySelectorWithDate(state, moment(inputDay.date)), ) return cardData } @@ -204,7 +204,7 @@ export function TutorialFirstScreen() { flag.current = true } if (flag.current) { - dispatch(commonActions.setTutorialOneActive(false)) + dispatch(actions.setTutorialOneActive(false)) setLoading(true) requestAnimationFrame(() => { setTimeout(() => { @@ -260,7 +260,7 @@ export function TutorialFirstScreen() { const skip = () => { flag.current = true - dispatch(commonActions.setTutorialOneActive(false)) + dispatch(actions.setTutorialOneActive(false)) setLoading(true) requestAnimationFrame(() => { setTimeout(() => { diff --git a/packages/components/src/screens/TutorialSecondScreen.tsx b/packages/components/src/screens/TutorialSecondScreen.tsx index 0ea5fbb2b..f5b477933 100644 --- a/packages/components/src/screens/TutorialSecondScreen.tsx +++ b/packages/components/src/screens/TutorialSecondScreen.tsx @@ -11,7 +11,7 @@ import { useInfiniteScroll } from './mainScreen/wheelCarousel/useInfiniteScroll' import { navigateAndReset } from '../services/navigationService' import { Animated, Dimensions, Image, Platform, View } from 'react-native' import { useDispatch } from 'react-redux' -import { commonActions } from '../redux/actions' +import * as actions from '../redux/actions' import { Text } from '../components/common/Text' import { Icon } from '../components/common/Icon' import { assets } from '../assets' @@ -19,8 +19,8 @@ import { DayAssetDemo } from './tutorial/DayAssetDemo' import { CalendarAssetDemo } from './tutorial/CalendarAssetDemo' import { SpinLoader } from '../components/common/SpinLoader' import { NoteAssetDemo } from './tutorial/NoteAssetDemo' -import { useCommonSelector } from '../redux/useCommonSelector' -import { commonSelectors } from '../redux/selectors' +import { useSelector } from '../redux/useSelector' +import * as selectors from '../redux/selectors' import moment from 'moment' import Tts from 'react-native-tts' import { translate } from '../i18n' @@ -50,8 +50,8 @@ export function TutorialSecondScreen({ navigation }) { const flag = React.useRef(false) const dispatch = useDispatch() // TODO_ALEX: DO NOT USE HOOKS LIKE THIS - const renamedUseSelector = useCommonSelector - const hasTtsActive = useCommonSelector(commonSelectors.isTtsActiveSelector) + const renamedUseSelector = useSelector + const hasTtsActive = useSelector(selectors.isTtsActiveSelector) const normalizePosition = (percentage, dimension) => { return percentage * dimension - arrowSize / 2 @@ -209,7 +209,7 @@ export function TutorialSecondScreen({ navigation }) { flag.current = true } if (flag.current) { - dispatch(commonActions.setTutorialTwoActive(false)) + dispatch(actions.setTutorialTwoActive(false)) setLoading(true) requestAnimationFrame(() => { setTimeout(() => { @@ -375,13 +375,13 @@ export function TutorialSecondScreen({ navigation }) { const getCardAnswersValues = (inputDay) => { const cardData = renamedUseSelector((state) => - commonSelectors.verifyPeriodDaySelectorWithDate(state, moment(inputDay.date)), + selectors.verifyPeriodDaySelectorWithDate(state, moment(inputDay.date)), ) return cardData } const skip = () => { - dispatch(commonActions.setTutorialTwoActive(false)) + dispatch(actions.setTutorialTwoActive(false)) setLoading(true) requestAnimationFrame(() => { setTimeout(() => { diff --git a/packages/components/src/screens/VideosScreen.tsx b/packages/components/src/screens/VideosScreen.tsx index f28ca2032..049fc6601 100644 --- a/packages/components/src/screens/VideosScreen.tsx +++ b/packages/components/src/screens/VideosScreen.tsx @@ -3,8 +3,8 @@ import styled from 'styled-components/native' import { FlatList } from 'react-native' import { PageContainer } from '../components/layout/PageContainer' import { BackgroundTheme } from '../components/layout/BackgroundTheme' -import { useCommonSelector } from '../redux/useCommonSelector' -import { commonSelectors } from '../redux/selectors' +import { useSelector } from '../redux/useSelector' +import * as selectors from '../redux/selectors' import { Header } from '../components/common/Header' import { TextWithoutTranslation } from '../components/common/Text' import { VideoData } from '../types' @@ -17,9 +17,7 @@ export const VideoItem = ({ videoId: string onSelect: React.Dispatch> }) => { - const videoObject = useCommonSelector((state) => - commonSelectors.videoByIDSelector(state, videoId), - ) + const videoObject = useSelector((state) => selectors.videoByIDSelector(state, videoId)) if (!videoObject) { return null @@ -42,9 +40,7 @@ export const VideoItem = ({ export function VideosScreen({ navigation }) { const categoryId = navigation.getParam('categoryId') - const category = useCommonSelector((state) => - commonSelectors.categoryByIDSelector(state, categoryId), - ) + const category = useSelector((state) => selectors.categoryByIDSelector(state, categoryId)) const videos = category?.videos || [] return ( diff --git a/packages/components/src/screens/authScreen/Login.tsx b/packages/components/src/screens/authScreen/Login.tsx index c362c1578..e3e16e43d 100644 --- a/packages/components/src/screens/authScreen/Login.tsx +++ b/packages/components/src/screens/authScreen/Login.tsx @@ -3,14 +3,14 @@ import styled from 'styled-components/native' import { Text } from '../../components/common/Text' import { TextInput } from '../../components/common/TextInput' import { useDispatch } from 'react-redux' -import { commonActions } from '../../redux/actions' -import { useCommonSelector } from '../../redux/useCommonSelector' +import * as actions from '../../redux/actions' +import { useSelector } from '../../redux/useSelector' import { SpinLoader } from '../../components/common/SpinLoader' import _ from 'lodash' export function Login() { const dispatch = useDispatch() - const { error: loginError, isLoggingIn } = useCommonSelector((state) => state.auth) + const { error: loginError, isLoggingIn } = useSelector((state) => state.auth) const [loading, setLoading] = React.useState(false) const [name, setName] = React.useState('') @@ -46,7 +46,7 @@ export function Login() { onPress={() => { setLoading(true) requestAnimationFrame(() => { - dispatch(commonActions.loginRequest({ name, password: _.toLower(password).trim() })) + dispatch(actions.loginRequest({ name, password: _.toLower(password).trim() })) }) }} > diff --git a/packages/components/src/screens/authScreen/SignUp.tsx b/packages/components/src/screens/authScreen/SignUp.tsx index e3281d967..c4cbbfe87 100644 --- a/packages/components/src/screens/authScreen/SignUp.tsx +++ b/packages/components/src/screens/authScreen/SignUp.tsx @@ -8,7 +8,7 @@ import { AskAge } from './signUp/AskAge' import { AskLocation } from './signUp/AskLocation' import { AskUserConfirmation } from './signUp/AskUserConfirmation' import { navigate } from '../../services/navigationService' -import { commonActions } from '../../redux/actions' +import * as actions from '../../redux/actions' import _ from 'lodash' import { FAST_SIGN_UP } from '../../config' @@ -64,7 +64,7 @@ export function SignUp({ heightInner }) { answer, }) => { dispatch( - commonActions.createAccountRequest({ + actions.createAccountRequest({ id: uuidv4(), name, dateOfBirth, diff --git a/packages/components/src/screens/authScreen/signUp/AskLocation.tsx b/packages/components/src/screens/authScreen/signUp/AskLocation.tsx index b579b6972..dd1a62c07 100644 --- a/packages/components/src/screens/authScreen/signUp/AskLocation.tsx +++ b/packages/components/src/screens/authScreen/signUp/AskLocation.tsx @@ -7,14 +7,14 @@ import { Text } from '../../../components/common/Text' import { GenderSelectItem } from '../../../components/common/GenderSelectItem' import { formHeights } from './FormHeights' import { ModalSearchBox } from '../../../components/common/ModalSearchBox' -import { useCommonSelector } from '../../../redux/useCommonSelector' -import { commonSelectors } from '../../../redux/selectors' +import { useSelector } from '../../../redux/useSelector' +import * as selectors from '../../../redux/selectors' import { translate } from '../../../i18n' import { FAST_SIGN_UP } from '../../../config' export function AskLocation({ step, createAccount }) { const [{ app: state }, dispatch] = useMultiStepForm() - const lang = useCommonSelector(commonSelectors.currentLocaleSelector) + const lang = useSelector(selectors.currentLocaleSelector) const { country, province, location } = state const [derivedCountry, setDerivedCountry] = React.useState( FAST_SIGN_UP ? { code: 'AF', item: 'Afghanistan' } : null, diff --git a/packages/components/src/screens/dayScreen/DayCarousel.tsx b/packages/components/src/screens/dayScreen/DayCarousel.tsx index 623271409..fc2d24b6b 100644 --- a/packages/components/src/screens/dayScreen/DayCarousel.tsx +++ b/packages/components/src/screens/dayScreen/DayCarousel.tsx @@ -3,13 +3,13 @@ import _ from 'lodash' import { FlatList, Dimensions, KeyboardAvoidingView } from 'react-native' import { DayCarouselItem } from './DayCarouselItem' import { useDispatch } from 'react-redux' -import { commonActions } from '../../redux/actions' +import * as actions from '../../redux/actions' import { NoteCard } from './NoteCard' import { QuizCard } from './QuizCard' import { DidYouKnowCard } from './DidYouKnowCard' import { SurveyCard } from './SurveyCard' -import { useCommonSelector } from '../../redux/useCommonSelector' -import { commonSelectors } from '../../redux/selectors' +import { useSelector } from '../../redux/useSelector' +import * as selectors from '../../redux/selectors' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { translate } from '../../i18n' import { ThemedModal } from '../../components/common/ThemedModal' @@ -27,9 +27,9 @@ export function DayCarousel({ navigation, dataEntry }) { const [tempCardName, setTempCardName] = React.useState(null) const [tempCardAnswer, setTempCardAnswer] = React.useState(null) const viewConfigRef = React.useRef({ viewAreaCoveragePercentThreshold: 50 }) - const userID = useCommonSelector(commonSelectors.currentUserSelector).id - const allSurveys = useCommonSelector(commonSelectors.allSurveys) - const completedSurveys = useCommonSelector(commonSelectors.completedSurveys) + const userID = useSelector(selectors.currentUserSelector).id + const allSurveys = useSelector(selectors.allSurveys) + const completedSurveys = useSelector(selectors.completedSurveys) const newSurveys = allSurveys?.length ? allSurveys[0] : null const cards = { @@ -225,7 +225,7 @@ export function DayCarousel({ navigation, dataEntry }) { setEndSurvey(true) }, 5000) dispatch( - commonActions.answerSurvey({ + actions.answerSurvey({ id: newSurveys?.id, isCompleted: true, isSurveyAnswered: true, @@ -238,10 +238,10 @@ export function DayCarousel({ navigation, dataEntry }) { if (allSurveys?.length) { const tempData = allSurveys const tempCompletedSurveys = completedSurveys ? completedSurveys : [] - dispatch(commonActions.updateCompletedSurveys([tempData[0], ...tempCompletedSurveys])) + dispatch(actions.updateCompletedSurveys([tempData[0], ...tempCompletedSurveys])) tempData.shift() - dispatch(commonActions.updateAllSurveyContent(tempData)) + dispatch(actions.updateAllSurveyContent(tempData)) } }, 2000) } else { @@ -261,7 +261,7 @@ export function DayCarousel({ navigation, dataEntry }) { currentSurvey.answeredQuestion = currQuestion?.is_multiple ? tempAnswer : answersArray const tempData = [...allSurveys] tempData[0] = currentSurvey - dispatch(commonActions.updateAllSurveyContent(tempData)) + dispatch(actions.updateAllSurveyContent(tempData)) } } } @@ -332,7 +332,7 @@ export function DayCarousel({ navigation, dataEntry }) { setTempCardAnswer(answer) setIsVisible(true) dispatch( - commonActions.answerDailyCard({ + actions.answerDailyCard({ cardName: tempCardName, answer: tempCardAnswer, userID, @@ -344,7 +344,7 @@ export function DayCarousel({ navigation, dataEntry }) { return } dispatch( - commonActions.answerDailyCard({ + actions.answerDailyCard({ cardName, answer, userID, @@ -370,7 +370,7 @@ export function DayCarousel({ navigation, dataEntry }) { hide={() => { setIsVisible(false) dispatch( - commonActions.answerDailyCard({ + actions.answerDailyCard({ cardName: tempCardName, answer: tempCardAnswer, userID, diff --git a/packages/components/src/screens/dayScreen/DayCarouselItem.tsx b/packages/components/src/screens/dayScreen/DayCarouselItem.tsx index c809554f3..5aec8c9ee 100644 --- a/packages/components/src/screens/dayScreen/DayCarouselItem.tsx +++ b/packages/components/src/screens/dayScreen/DayCarouselItem.tsx @@ -6,17 +6,15 @@ import { assets } from '../../assets/index' import { Text } from '../../components/common/Text' import { EmojiSelector } from '../../components/common/EmojiSelector' import { Icon } from '../../components/common/Icon' -import { commonSelectors } from '../../redux/selectors' -import { useCommonSelector } from '../../redux/useCommonSelector' +import * as selectors from '../../redux/selectors' +import { useSelector } from '../../redux/useSelector' import { useColor } from '../../hooks/useColor' import { translate } from '../../i18n' const deviceWidth = Dimensions.get('window').width export function DayCarouselItem({ content, cardName, dataEntry, onPress, index }) { - const selectedEmojis = useCommonSelector((state) => - commonSelectors.cardAnswerSelector(state, dataEntry.date), - ) + const selectedEmojis = useSelector((state) => selectors.cardAnswerSelector(state, dataEntry.date)) const color = useColor(dataEntry.onPeriod, dataEntry.onFertile) const source = selectedEmojis[cardName] diff --git a/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx b/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx index 7c3ca0fe0..eea117056 100644 --- a/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx +++ b/packages/components/src/screens/dayScreen/DidYouKnowCard.tsx @@ -2,15 +2,15 @@ import React from 'react' import { Dimensions } from 'react-native' import styled from 'styled-components/native' import { TextWithoutTranslation, Text } from '../../components/common/Text' -import { useCommonSelector } from '../../redux/useCommonSelector' +import { useSelector } from '../../redux/useSelector' import _ from 'lodash' -import { commonSelectors } from '../../redux/selectors' +import * as selectors from '../../redux/selectors' import { TitleText } from '../../components/common/TitleText' const deviceWidth = Dimensions.get('window').width function useDidYouKnow() { - const allDidYouKnows = useCommonSelector(commonSelectors.allDidYouKnowsSelectors) + const allDidYouKnows = useSelector(selectors.allDidYouKnowsSelectors) const randomDidYouKnow = React.useMemo(() => { return _.sample(allDidYouKnows) }, []) diff --git a/packages/components/src/screens/dayScreen/NoteCard.tsx b/packages/components/src/screens/dayScreen/NoteCard.tsx index af07d96b7..1cfe6b2f8 100644 --- a/packages/components/src/screens/dayScreen/NoteCard.tsx +++ b/packages/components/src/screens/dayScreen/NoteCard.tsx @@ -1,9 +1,9 @@ import React from 'react' import { Dimensions } from 'react-native' import styled from 'styled-components/native' -import { commonSelectors } from '../../redux/selectors' -import { commonActions } from '../../redux/actions' -import { useCommonSelector } from '../../redux/useCommonSelector' +import * as selectors from '../../redux/selectors' +import * as actions from '../../redux/actions' +import { useSelector } from '../../redux/useSelector' import { TextInput } from '../../components/common/TextInput' import { useDispatch } from 'react-redux' import { BackOneScreen } from '../../services/navigationService' @@ -16,10 +16,10 @@ import { translate } from '../../i18n' const deviceWidth = Dimensions.get('window').width export function NoteCard({ dataEntry }) { - const noteObject: any = useCommonSelector((state) => - commonSelectors.notesAnswerSelector(state, dataEntry.date), + const noteObject: any = useSelector((state) => + selectors.notesAnswerSelector(state, dataEntry.date), ) - const userID = useCommonSelector(commonSelectors.currentUserSelector).id + const userID = useSelector(selectors.currentUserSelector).id const [title, setTitle] = React.useState(noteObject.title || '') const [titlePlaceholder, setTitlePlaceholder] = React.useState('title') const [notesPlaceholder, setNotesPlaceholder] = React.useState('daily_note_description') @@ -49,7 +49,7 @@ export function NoteCard({ dataEntry }) { onChange={(text) => setTitle(text)} onEndEditing={() => dispatch( - commonActions.answerNotesCard({ + actions.answerNotesCard({ title, notes, userID, @@ -74,7 +74,7 @@ export function NoteCard({ dataEntry }) { onBlur={() => setNotesPlaceholder('daily_note_description')} onEndEditing={() => dispatch( - commonActions.answerNotesCard({ + actions.answerNotesCard({ title, notes, userID, diff --git a/packages/components/src/screens/dayScreen/QuizCard.tsx b/packages/components/src/screens/dayScreen/QuizCard.tsx index 6c95165c7..59656e101 100644 --- a/packages/components/src/screens/dayScreen/QuizCard.tsx +++ b/packages/components/src/screens/dayScreen/QuizCard.tsx @@ -4,18 +4,18 @@ import { Dimensions } from 'react-native' import { TextWithoutTranslation, Text } from '../../components/common/Text' import { EmojiSelector } from '../../components/common/EmojiSelector' import { TitleText } from '../../components/common/TitleText' -import { useCommonSelector } from '../../redux/useCommonSelector' +import { useSelector } from '../../redux/useSelector' import _ from 'lodash' -import { commonSelectors } from '../../redux/selectors' -import { commonActions } from '../../redux/actions' +import * as selectors from '../../redux/selectors' +import * as actions from '../../redux/actions' import { useDispatch } from 'react-redux' const deviceWidth = Dimensions.get('window').width function useQuiz() { - const unansweredQuizzes = useCommonSelector(commonSelectors.quizzesWithoutAnswersSelector) + const unansweredQuizzes = useSelector(selectors.quizzesWithoutAnswersSelector) - const allQuizzes = useCommonSelector(commonSelectors.allQuizzesSelectors) + const allQuizzes = useSelector(selectors.allQuizzesSelectors) const randomQuiz = React.useMemo(() => { if (_.isEmpty(unansweredQuizzes)) { return _.sample(allQuizzes) @@ -27,11 +27,9 @@ function useQuiz() { export const QuizCard = React.memo<{ dataEntry: any; index: number }>(({ dataEntry, index }) => { const dispatch = useDispatch() - const userID = useCommonSelector(commonSelectors.currentUserSelector).id + const userID = useSelector(selectors.currentUserSelector).id const selectedQuestion = useQuiz() - const answeredQuestion = useCommonSelector((state) => - commonSelectors.quizAnswerByDate(state, dataEntry.date), - ) + const answeredQuestion = useSelector((state) => selectors.quizAnswerByDate(state, dataEntry.date)) const QuizContent = () => { return selectedQuestion.answers.map((item, ind) => { @@ -42,7 +40,7 @@ export const QuizCard = React.memo<{ dataEntry: any; index: number }>(({ dataEnt color={'pink'} onPress={() => dispatch( - commonActions.answerQuiz({ + actions.answerQuiz({ id: selectedQuestion.id, answerID: ind + 1, question: selectedQuestion.question, diff --git a/packages/components/src/screens/dayScreen/SurveyCard.tsx b/packages/components/src/screens/dayScreen/SurveyCard.tsx index 83af1ab12..56adabe82 100644 --- a/packages/components/src/screens/dayScreen/SurveyCard.tsx +++ b/packages/components/src/screens/dayScreen/SurveyCard.tsx @@ -4,10 +4,10 @@ import { Dimensions } from 'react-native' import { TextWithoutTranslation, Text } from '../../components/common/Text' import { EmojiSelector } from '../../components/common/EmojiSelector' import { TitleText } from '../../components/common/TitleText' -import { useCommonSelector } from '../../redux/useCommonSelector' +import { useSelector } from '../../redux/useSelector' import _ from 'lodash' -import { commonSelectors } from '../../redux/selectors' -import { commonActions } from '../../redux/actions' +import * as selectors from '../../redux/selectors' +import * as actions from '../../redux/actions' import { useDispatch } from 'react-redux' import { TextInput } from '../../components/common/TextInput' import { SurveyInformationButton } from '../../components/common/SurveyInformationButton' @@ -34,12 +34,12 @@ export const SurveyCard = React.memo<{ const [title, setTitle] = React.useState('') const [titlePlaceholder, setTitlePlaceholder] = React.useState('type_answer_placeholder') const [isSkip, setSkip] = React.useState(null) - const userID = useCommonSelector(commonSelectors.currentUserSelector).id + const userID = useSelector(selectors.currentUserSelector).id const dispatch = useDispatch() const [showThankYouMsg, setThankYouMsg] = React.useState(null) const [selectedIndex, setSelectedIndex] = React.useState(null) - const completedSurveys = useCommonSelector(commonSelectors.completedSurveys) - const allSurveys = useCommonSelector(commonSelectors.allSurveys) + const completedSurveys = useSelector(selectors.completedSurveys) + const allSurveys = useSelector(selectors.allSurveys) const checkUserPermission = (option, optionIndex) => { setSelectedIndex(optionIndex) @@ -57,7 +57,7 @@ export const SurveyCard = React.memo<{ // TODO_ALEX: Does this do anything? dispatch( - commonActions.answerSurvey({ + actions.answerSurvey({ id: dataEntry.surveyId, isCompleted: true, isSurveyAnswered: false, @@ -68,10 +68,10 @@ export const SurveyCard = React.memo<{ ) const tempData = allSurveys const tempCompletedSurveys = completedSurveys ? completedSurveys : [] - dispatch(commonActions.updateCompletedSurveys([tempData[0], ...tempCompletedSurveys])) + dispatch(actions.updateCompletedSurveys([tempData[0], ...tempCompletedSurveys])) tempData.shift() - dispatch(commonActions.updateAllSurveyContent(tempData)) - dispatch(commonActions.fetchSurveyContentRequest(userID)) + dispatch(actions.updateAllSurveyContent(tempData)) + dispatch(actions.fetchSurveyContentRequest(userID)) } } else { setTimeout(() => { diff --git a/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx b/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx index e0f71519a..f8d8673f7 100644 --- a/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx +++ b/packages/components/src/screens/encyclopediaScreen/SearchBar.tsx @@ -6,8 +6,8 @@ import { IconButton } from '../../components/common/buttons/IconButton' import styled from 'styled-components/native' import { handleCategoriesFilter, handleSearchResult } from './searchFunctions' import { EmojiSelector } from '../../components/common/EmojiSelector' -import { useCommonSelector } from '../../redux/useCommonSelector' -import { commonSelectors } from '../../redux/selectors' +import { useSelector } from '../../redux/useSelector' +import * as selectors from '../../redux/selectors' import { translate } from '../../i18n' export const SearchBar = ({ @@ -22,8 +22,8 @@ export const SearchBar = ({ }) => { const [searchStr, setSearchStr] = React.useState('') const [emojiFilter, updateEmojiFilter] = React.useState([]) - const locale = useCommonSelector(commonSelectors.currentLocaleSelector) - const emojiList = useCommonSelector(commonSelectors.allCategoryEmojis) + const locale = useSelector(selectors.currentLocaleSelector) + const emojiList = useSelector(selectors.allCategoryEmojis) return ( <> diff --git a/packages/components/src/screens/journeyScreen/FinalJourneyCard.tsx b/packages/components/src/screens/journeyScreen/FinalJourneyCard.tsx index f5539aa76..1d22f3525 100644 --- a/packages/components/src/screens/journeyScreen/FinalJourneyCard.tsx +++ b/packages/components/src/screens/journeyScreen/FinalJourneyCard.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components/native' import { Text, TextWithoutTranslation } from '../../components/common/Text' import { assets } from '../../assets/index' import { translate } from '../../i18n/index' -import { commonActions } from '../../redux/actions/index' +import * as actions from '../../redux/actions/index' import { useDispatch } from 'react-redux' import moment from 'moment' import { SpinLoader } from '../../components/common/SpinLoader' @@ -74,7 +74,7 @@ export function FinalJourneyCard({ cards, questionAnswers, goToQuestion }) { onPress={() => { setLoading(true) requestAnimationFrame(() => { - dispatch(commonActions.journeyCompletion(questionAnswers)) + dispatch(actions.journeyCompletion(questionAnswers)) }) }} > diff --git a/packages/components/src/screens/journeyScreen/JourneyCard.tsx b/packages/components/src/screens/journeyScreen/JourneyCard.tsx index df3390d2e..d99cac066 100644 --- a/packages/components/src/screens/journeyScreen/JourneyCard.tsx +++ b/packages/components/src/screens/journeyScreen/JourneyCard.tsx @@ -5,8 +5,8 @@ import { CalendarCardContent } from './CalendarCardContent' import { WheelPickerContent } from '../../components/WheelPickerContent' import { Avatar } from '../../components/common/Avatar/Avatar' import { assets } from '../../assets' -import { useCommonSelector } from '../../redux/useCommonSelector' -import { commonSelectors } from '../../redux/selectors' +import { useSelector } from '../../redux/useSelector' +import * as selectors from '../../redux/selectors' export function JourneyCard({ question, @@ -29,7 +29,7 @@ export function JourneyCard({ leftButtonTitle = 'i_dont_remember', rightButtonTitle = 'i_remember', }) { - const selectedAvatar = useCommonSelector(commonSelectors.currentAvatarSelector) + const selectedAvatar = useSelector(selectors.currentAvatarSelector) return ( <> {status === 'initial' && ( diff --git a/packages/components/src/screens/mainScreen/Calendar.tsx b/packages/components/src/screens/mainScreen/Calendar.tsx index e3a6cffa9..b3bf05c77 100644 --- a/packages/components/src/screens/mainScreen/Calendar.tsx +++ b/packages/components/src/screens/mainScreen/Calendar.tsx @@ -21,8 +21,8 @@ import { assets } from '../../assets' import { translate } from '../../i18n' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { calendarScreenSpeech } from '../../config' -import { commonSelectors } from '../../redux/selectors' -import { useCommonSelector } from '../../redux/useCommonSelector' +import * as selectors from '../../redux/selectors' +import { useSelector } from '../../redux/useSelector' const width = Dimensions.get('window').width const height = Dimensions.get('window').height @@ -34,10 +34,8 @@ const startDate = moment().startOf('day').subtract(24, 'months') const endDate = moment().startOf('day').add(12, 'months') export const Calendar = ({ navigation }) => { - const hasFuturePredictionActive = useCommonSelector(commonSelectors.isFuturePredictionSelector) - const verifiedPeriodsData = useCommonSelector((state: any) => - commonSelectors.allCardAnswersSelector(state), - ) + const hasFuturePredictionActive = useSelector(selectors.isFuturePredictionSelector) + const verifiedPeriodsData = useSelector((state: any) => selectors.allCardAnswersSelector(state)) const highlightedDates = useCalculateStatusForDateRange( startDate, endDate, diff --git a/packages/components/src/screens/mainScreen/ColourButtons.tsx b/packages/components/src/screens/mainScreen/ColourButtons.tsx index 7a99fbd53..00e678a2e 100644 --- a/packages/components/src/screens/mainScreen/ColourButtons.tsx +++ b/packages/components/src/screens/mainScreen/ColourButtons.tsx @@ -18,13 +18,13 @@ import { InformationButton } from '../../components/common/InformationButton' import { decisionProcessNonPeriod, decisionProcessPeriod } from './predictionLogic/predictionLogic' import { translate } from '../../i18n' import { useDispatch } from 'react-redux' -import { commonActions } from '../../redux/actions/index' -import { commonSelectors } from '../../redux/selectors' +import * as actions from '../../redux/actions/index' +import * as selectors from '../../redux/selectors' import analytics from '@react-native-firebase/analytics' import moment from 'moment' import { fetchNetworkConnectionStatus } from '../../services/network' -import { useCommonSelector } from '../../redux/useCommonSelector' +import { useSelector } from '../../redux/useSelector' import { incrementFlowerProgress, useFlowerStateSelector, FlowerModal } from '../../optional/Flower' const minBufferBetweenCycles = 2 @@ -59,8 +59,8 @@ export function ColourButtons({ const selectedDayInfoEngine = usePredictDay(inputDay) const isActive = useIsActiveSelector() const appDispatch = useDispatch() - const userID = useCommonSelector(commonSelectors.currentUserSelector).id - const currentUser = useCommonSelector(commonSelectors.currentUserSelector) + const userID = useSelector(selectors.currentUserSelector).id + const currentUser = useSelector(selectors.currentUserSelector) const currentCycleInfo = useTodayPrediction() const inputDayStr = moment(inputDay).format('YYYY-MM-DD') const todayStr = moment().format('YYYY-MM-DD') @@ -68,13 +68,13 @@ export function ColourButtons({ const [isFlowerVisible, setFlowerVisible] = React.useState(false) const flowerState = useFlowerStateSelector() - const cardAnswersToday = useCommonSelector((state) => - commonSelectors.verifyPeriodDaySelectorWithDate(state, moment(inputDayStr)), + const cardAnswersToday = useSelector((state) => + selectors.verifyPeriodDaySelectorWithDate(state, moment(inputDayStr)), ) as any const fullState = useFullState() const [addNewCycleHistory, setNewCycleHistory] = React.useState(false) - const hasFuturePredictionActive = useCommonSelector(commonSelectors.isFuturePredictionSelector) + const hasFuturePredictionActive = useSelector(selectors.isFuturePredictionSelector) // TODO_ALEX this useState is redundant const [futurePredictionStatus, setFuturePredictionStatus] = useState(false) @@ -128,7 +128,7 @@ export function ColourButtons({ } } appDispatch( - commonActions.smartPredictionRequest({ + actions.smartPredictionRequest({ cycle_lengths: tempPeriodsCycles, period_lengths: tempPeriodsLength, age: moment().diff(moment(currentUser.dateOfBirth), 'years'), @@ -138,9 +138,7 @@ export function ColourButtons({ ) } } - appDispatch( - commonActions.updateFuturePrediction(futurePredictionStatus, fullState.currentCycle), - ) + appDispatch(actions.updateFuturePrediction(futurePredictionStatus, fullState.currentCycle)) } const actionPink = decisionProcessPeriod({ @@ -217,7 +215,7 @@ export function ColourButtons({ }) } appDispatch( - commonActions.answerVerifyDates({ + actions.answerVerifyDates({ userID, utcDateTime: inputDay, periodDay: true, @@ -237,7 +235,7 @@ export function ColourButtons({ if (addNewCycleHistory) { if (selectedDayInfo.onPeriod) { appDispatch( - commonActions.answerVerifyDates({ + actions.answerVerifyDates({ userID, utcDateTime: inputDay, periodDay: true, @@ -252,7 +250,7 @@ export function ColourButtons({ getPredictedCycles, }) appDispatch( - commonActions.answerVerifyDates({ + actions.answerVerifyDates({ userID, utcDateTime: inputDay, periodDay: true, @@ -263,7 +261,7 @@ export function ColourButtons({ } else { if (selectedDayInfo.onPeriod) { appDispatch( - commonActions.answerVerifyDates({ + actions.answerVerifyDates({ userID, utcDateTime: inputDay, periodDay: true, @@ -296,7 +294,7 @@ export function ColourButtons({ getPredictedCycles, }) appDispatch( - commonActions.answerVerifyDates({ + actions.answerVerifyDates({ userID, utcDateTime: inputDay, periodDay: false, diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx index 851ee3e70..517942341 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/Carousel.tsx @@ -7,8 +7,8 @@ import { TouchableOpacity } from 'react-native-gesture-handler' import { navigate, navigateAndReset } from '../../../services/navigationService' import moment from 'moment' import { useDisplayText } from '../../../components/context/DisplayTextContext' -import { useCommonSelector } from '../../../redux/useCommonSelector' -import { commonSelectors } from '../../../redux/selectors' +import { useSelector } from '../../../redux/useSelector' +import * as selectors from '../../../redux/selectors' import { SpinLoader } from '../../../components/common/SpinLoader' const screenWidth = Dimensions.get('window').width @@ -28,7 +28,7 @@ export function Carousel({ absoluteIndex, disableInteraction = false, }) { - const isTutorialTwoOn = useCommonSelector(commonSelectors.isTutorialTwoActiveSelector) + const isTutorialTwoOn = useSelector(selectors.isTutorialTwoActiveSelector) const [isVisible, setIsVisible] = React.useState(false) const { setDisplayTextStatic } = useDisplayText() diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx index 8d032a4dd..e05922409 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CarouselElement.tsx @@ -6,9 +6,9 @@ import { DayBadge } from '../../../components/common/DayBadge' import { DateBadge } from '../../../components/common/DateBadge' import { assets } from '../../../assets/index' import { EmojiSelector } from '../../../components/common/EmojiSelector' -import { useCommonSelector } from '../../../redux/useCommonSelector' +import { useSelector } from '../../../redux/useSelector' import { emojis } from '../../../config' -import { commonSelectors } from '../../../redux/selectors' +import * as selectors from '../../../redux/selectors' import { useColor } from '../../../hooks/useColor' import styled from 'styled-components/native' import moment from 'moment' @@ -46,8 +46,8 @@ export function CarouselElement({ dataEntry, index, isActive, currentIndex }) { const clock = new Clock() const value = new Value(0) const color = useColor(dataEntry.onPeriod, dataEntry.onFertile) - const cardAnswersValues = useCommonSelector((state) => - commonSelectors.cardAnswerSelector(state, moment(dataEntry.date)), + const cardAnswersValues = useSelector((state) => + selectors.cardAnswerSelector(state, moment(dataEntry.date)), ) const [isVisible, setIsVisible] = React.useState(false) @@ -72,8 +72,8 @@ export function CarouselElement({ dataEntry, index, isActive, currentIndex }) { navigateAndReset('TutorialFirstStack', null) }) } - const verifiedPeriodDaysData = useCommonSelector((state) => - commonSelectors.verifyPeriodDaySelectorWithDate(state, moment(dataEntry.date)), + const verifiedPeriodDaysData = useSelector((state) => + selectors.verifyPeriodDaySelectorWithDate(state, moment(dataEntry.date)), ) return ( diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx index a04b40afb..b67d1a96e 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CircularElement.tsx @@ -6,14 +6,14 @@ import { useTheme } from '../../../components/context/ThemeContext' import { TouchableOpacity } from 'react-native-gesture-handler' import { assets } from '../../../assets/index' import { translate } from '../../../i18n' -import { commonSelectors } from '../../../redux/selectors' +import * as selectors from '../../../redux/selectors' import moment from 'moment' import { useTodayPrediction, useActualCurrentStartDateSelector, } from '../../../components/context/PredictionProvider' import _ from 'lodash' -import { useCommonSelector } from '../../../redux/useCommonSelector' +import { useSelector } from '../../../redux/useSelector' const { Value, @@ -95,7 +95,7 @@ export function CircularElement({ state, }) { const currentCycleInfo = useTodayPrediction() - const hasFuturePredictionActive = useCommonSelector(commonSelectors.isFuturePredictionSelector) + const hasFuturePredictionActive = useSelector(selectors.isFuturePredictionSelector) const actualCurrentStartDate = useActualCurrentStartDateSelector() const { id: themeName } = useTheme() const clock = new Clock() diff --git a/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx b/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx index 0ac840fe6..93ca0f1c3 100644 --- a/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx +++ b/packages/components/src/screens/mainScreen/wheelCarousel/CircularSelection.tsx @@ -6,16 +6,16 @@ import { CircularElement } from './CircularElement' import { PanGesture } from './PanGesture' import { TouchableOpacity } from 'react-native-gesture-handler' import { ColourButtons } from '../ColourButtons' -import { useCommonSelector } from '../../../redux/useCommonSelector' -import { commonSelectors } from '../../../redux/selectors' +import { useSelector } from '../../../redux/useSelector' +import * as selectors from '../../../redux/selectors' import { navigateAndReset } from '../../../services/navigationService' import { useCheckDayWarning } from '../../../hooks/usePredictionWarnings' import { ThemedModal } from '../../../components/common/ThemedModal' import { SpinLoader } from '../../../components/common/SpinLoader' import moment from 'moment' -import { CommonReduxState } from '../../../redux/reducers' +import { ReduxState } from '../../../redux/reducers' -const reduxState = (state: CommonReduxState) => state +const reduxState = (state: ReduxState) => state const { interpolate } = Animated const height = 0.55 * Dimensions.get('window').height @@ -44,7 +44,7 @@ export function CircularSelection({ inputRange: [0, data.length], outputRange: [0, -2 * Math.PI], }) - const isTutorialOneOn = useCommonSelector(commonSelectors.isTutorialOneActiveSelector) + const isTutorialOneOn = useSelector(selectors.isTutorialOneActiveSelector) const checkIfWarning = useCheckDayWarning() // automatically close the modal if the wheel start scrolling React.useEffect(() => { @@ -138,11 +138,8 @@ export function CircularSelection({ isCalendar={false} onPress={() => setIsVisible(false)} selectedDayInfo={data[currentIndex]} - cardValues={useCommonSelector((state) => - commonSelectors.verifyPeriodDaySelectorWithDate( - state, - moment(data[currentIndex].date), - ), + cardValues={useSelector((state) => + selectors.verifyPeriodDaySelectorWithDate(state, moment(data[currentIndex].date)), )} /> diff --git a/packages/components/src/screens/profileScreen/CycleCard.tsx b/packages/components/src/screens/profileScreen/CycleCard.tsx index 231d258d0..0a9eea178 100644 --- a/packages/components/src/screens/profileScreen/CycleCard.tsx +++ b/packages/components/src/screens/profileScreen/CycleCard.tsx @@ -3,17 +3,17 @@ import styled from 'styled-components/native' import { Text } from '../../components/common/Text' import { Icon } from '../../components/common/Icon' import { assets } from '../../assets/index' -import { commonSelectors } from '../../redux/selectors' +import * as selectors from '../../redux/selectors' import { EmojiSelector } from '../../components/common/EmojiSelector' -import { useCommonSelector } from '../../redux/useCommonSelector' +import { useSelector } from '../../redux/useSelector' import { emojis } from '../../config' import { translate } from '../../i18n' const cardNames = ['mood', 'body', 'activity', 'flow'] export const CycleCard = ({ item, cycleNumber }) => { - const cardAnswersValues = useCommonSelector((state) => - commonSelectors.mostAnsweredSelector(state, item.cycleStartDate, item.cycleEndDate), + const cardAnswersValues = useSelector((state) => + selectors.mostAnsweredSelector(state, item.cycleStartDate, item.cycleEndDate), ) return ( diff --git a/packages/components/src/screens/settings/AboutScreen.tsx b/packages/components/src/screens/settings/AboutScreen.tsx index 4038da4ab..5ac43b587 100644 --- a/packages/components/src/screens/settings/AboutScreen.tsx +++ b/packages/components/src/screens/settings/AboutScreen.tsx @@ -4,8 +4,8 @@ import { BackgroundTheme } from '../../components/layout/BackgroundTheme' import { TextWithoutTranslation } from '../../components/common/Text' import { Header } from '../../components/common/Header' import { Icon } from '../../components/common/Icon' -import { useCommonSelector } from '../../redux/useCommonSelector' -import { commonSelectors } from '../../redux/selectors' +import { useSelector } from '../../redux/useSelector' +import * as selectors from '../../redux/selectors' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { aboutScreenText } from '../../config' import { Dimensions } from 'react-native' @@ -13,9 +13,9 @@ import { assets } from '../../assets' const width = Dimensions.get('window').width const imageWidth = width - 30 export const AboutScreen = ({ navigation }) => { - const aboutContent = useCommonSelector(commonSelectors.aboutContent) - const aboutBanner = useCommonSelector(commonSelectors.aboutBanner) - const locale = useCommonSelector(commonSelectors.currentLocaleSelector) + const aboutContent = useSelector(selectors.aboutContent) + const aboutBanner = useSelector(selectors.aboutBanner) + const locale = useSelector(selectors.currentLocaleSelector) const iconSource = aboutBanner ? { uri: aboutBanner } : assets.general.aboutBanner[locale] diff --git a/packages/components/src/screens/settings/AccessScreen.tsx b/packages/components/src/screens/settings/AccessScreen.tsx index 184d955de..fb501c89b 100644 --- a/packages/components/src/screens/settings/AccessScreen.tsx +++ b/packages/components/src/screens/settings/AccessScreen.tsx @@ -4,9 +4,9 @@ import { BackgroundTheme } from '../../components/layout/BackgroundTheme' import styled from 'styled-components/native' import { ListItem } from './accessScreen/ListItem' import { Header } from '../../components/common/Header' -import { useCommonSelector } from '../../redux/useCommonSelector' -import { commonSelectors } from '../../redux/selectors' -import { commonActions } from '../../redux/actions/index' +import { useSelector } from '../../redux/useSelector' +import * as selectors from '../../redux/selectors' +import * as actions from '../../redux/actions/index' import { useDispatch } from 'react-redux' import { navigateAndReset } from '../../services/navigationService' import { TouchableOpacity } from 'react-native' @@ -19,15 +19,15 @@ import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' import { acessSettingsScreenText, WEBSITE_URL } from '../../config' export function AccessScreen({ navigation }) { - const locale = useCommonSelector(commonSelectors.currentLocaleSelector) + const locale = useSelector(selectors.currentLocaleSelector) const dispatch = useDispatch() const [loading, setLoading] = React.useState(false) - const privacyContent = useCommonSelector(commonSelectors.privacyContent) + const privacyContent = useSelector(selectors.privacyContent) const speechText = privacyContent.map((item) => item.content) const shareLink = () => { // @TODO: app event - dispatch(commonActions.shareApp()) + dispatch(actions.shareApp()) const options = { url: WEBSITE_URL, message: translate('join_oky_message'), @@ -57,7 +57,7 @@ export function AccessScreen({ navigation }) { if (lang !== locale) { setLoading(true) requestAnimationFrame(() => { - dispatch(commonActions.setLocale(lang)) + dispatch(actions.setLocale(lang)) }) } }} diff --git a/packages/components/src/screens/settings/PrivacyScreen.tsx b/packages/components/src/screens/settings/PrivacyScreen.tsx index 0976bfef0..c8d01fc9c 100644 --- a/packages/components/src/screens/settings/PrivacyScreen.tsx +++ b/packages/components/src/screens/settings/PrivacyScreen.tsx @@ -4,15 +4,15 @@ import { BackgroundTheme } from '../../components/layout/BackgroundTheme' import { Header } from '../../components/common/Header' import { TextWithoutTranslation } from '../../components/common/Text' import { ScrollView, Dimensions } from 'react-native' -import { useCommonSelector } from '../../redux/useCommonSelector' -import { commonSelectors } from '../../redux/selectors' +import { useSelector } from '../../redux/useSelector' +import * as selectors from '../../redux/selectors' import { chunk } from 'lodash' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' const width = Dimensions.get('window').width export function PrivacyScreen({ navigation }) { const [page, setPage] = React.useState(0) - const privacyContent = useCommonSelector(commonSelectors.privacyContent) + const privacyContent = useSelector(selectors.privacyContent) const speechText = privacyContent.map((item) => item.content) const content = privacyContent.map((item, ind) => { if (item.type === 'HEADING') { diff --git a/packages/components/src/screens/settings/TermsScreen.tsx b/packages/components/src/screens/settings/TermsScreen.tsx index 595058b34..a8d228b5b 100644 --- a/packages/components/src/screens/settings/TermsScreen.tsx +++ b/packages/components/src/screens/settings/TermsScreen.tsx @@ -4,15 +4,15 @@ import { BackgroundTheme } from '../../components/layout/BackgroundTheme' import { Header } from '../../components/common/Header' import { TextWithoutTranslation } from '../../components/common/Text' import { ScrollView, Dimensions, Platform } from 'react-native' -import { useCommonSelector } from '../../redux/useCommonSelector' -import { commonSelectors } from '../../redux/selectors' +import { useSelector } from '../../redux/useSelector' +import * as selectors from '../../redux/selectors' import { chunk } from 'lodash' import { useTextToSpeechHook } from '../../hooks/useTextToSpeechHook' const width = Dimensions.get('window').width export function TermsScreen({ navigation }) { const [page, setPage] = React.useState(0) - const termsAndConditions = useCommonSelector(commonSelectors.termsAndConditionsContent) + const termsAndConditions = useSelector(selectors.termsAndConditionsContent) const speechText = termsAndConditions.map((item) => item.content) const content = termsAndConditions.map((item, ind) => { if (item.type === 'HEADING') { diff --git a/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx b/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx index f76cecb66..2c06e2c6b 100644 --- a/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx +++ b/packages/components/src/screens/tutorial/CalendarAssetDemo.tsx @@ -2,13 +2,13 @@ import React from 'react' import { Dimensions } from 'react-native' import styled from 'styled-components/native' import { assets } from '../../assets/index' -import { useCommonSelector } from '../../redux/useCommonSelector' -import { commonSelectors } from '../../redux/selectors' +import { useSelector } from '../../redux/useSelector' +import * as selectors from '../../redux/selectors' const deviceWidth = Dimensions.get('window').width const deviceHeight = Dimensions.get('screen').height export function CalendarAssetDemo() { - const locale = useCommonSelector(commonSelectors.currentLocaleSelector) + const locale = useSelector(selectors.currentLocaleSelector) const source = assets.general.calendarStatic[locale] return ( diff --git a/packages/components/src/screens/tutorial/DayAssetDemo.tsx b/packages/components/src/screens/tutorial/DayAssetDemo.tsx index fe3980da5..3570e562f 100644 --- a/packages/components/src/screens/tutorial/DayAssetDemo.tsx +++ b/packages/components/src/screens/tutorial/DayAssetDemo.tsx @@ -8,13 +8,13 @@ import { Icon } from '../../components/common/Icon' import { EmojiSelector } from '../../components/common/EmojiSelector' import { translate } from '../../i18n' import Tts from 'react-native-tts' -import { useCommonSelector } from '../../redux/useCommonSelector' -import { commonSelectors } from '../../redux/selectors' +import { useSelector } from '../../redux/useSelector' +import * as selectors from '../../redux/selectors' const deviceWidth = Dimensions.get('window').width const deviceHeight = Dimensions.get('window').height export function DayAssetDemo({ step }) { - const hasTtsActive = useCommonSelector(commonSelectors.isTtsActiveSelector) + const hasTtsActive = useSelector(selectors.isTtsActiveSelector) React.useEffect(() => { if (hasTtsActive) { diff --git a/packages/components/src/screens/tutorial/NoteAssetDemo.tsx b/packages/components/src/screens/tutorial/NoteAssetDemo.tsx index bafe7eb6e..db99a37c8 100644 --- a/packages/components/src/screens/tutorial/NoteAssetDemo.tsx +++ b/packages/components/src/screens/tutorial/NoteAssetDemo.tsx @@ -7,14 +7,14 @@ import { TextInput } from '../../components/common/TextInput' import { Text } from '../../components/common/Text' import { translate } from '../../i18n' import Tts from 'react-native-tts' -import { useCommonSelector } from '../../redux/useCommonSelector' -import { commonSelectors } from '../../redux/selectors' +import { useSelector } from '../../redux/useSelector' +import * as selectors from '../../redux/selectors' const deviceWidth = Dimensions.get('window').width const deviceHeight = Dimensions.get('screen').height export function NoteAssetDemo({ step }) { - const hasTtsActive = useCommonSelector(commonSelectors.isTtsActiveSelector) + const hasTtsActive = useSelector(selectors.isTtsActiveSelector) React.useEffect(() => { if (hasTtsActive) { From bb58050b989f8e6f564ce45919bbfb718a4dfc3d Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Tue, 2 Jan 2024 20:28:45 +0800 Subject: [PATCH 27/83] Remove unused store context --- .../components/src/redux/StoreCoordinator.tsx | 44 +++---------------- 1 file changed, 5 insertions(+), 39 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index 511d44c24..5e1663463 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -12,20 +12,6 @@ interface Keys { secretKey: string } -interface Context { - switchStore: (keys: Keys) => void - switchToPrimaryStore: () => void -} - -const StoreCoordinatorContext = React.createContext({ - switchStore: () => { - // - }, - switchToPrimaryStore: () => { - // - }, -}) - const primaryStore = configureStore({ key: 'primary', secretKey: config.REDUX_ENCRYPT_KEY, @@ -41,10 +27,6 @@ export function StoreCoordinator({ children }) { const [shouldSwitch, setShouldSwitch] = React.useState(false) const [shouldMigrate, setShouldMigrate] = React.useState(false) - const switchToPrimaryStore = () => { - setStore(primaryStore) - } - const switchStore = ({ key, secretKey }: Keys) => { if (!key || !secretKey) { return @@ -101,26 +83,10 @@ export function StoreCoordinator({ children }) { }, [shouldMigrate]) return ( - - - - {children} - - - + + + {children} + + ) } - -export function useStoreCoordinator() { - const storeCoordinatorContext = React.useContext(StoreCoordinatorContext) - if (storeCoordinatorContext === undefined) { - throw new Error(`useStoreCoordinator must be used within a StoreCoordinator`) - } - - return storeCoordinatorContext -} From 3c3cb3e1fa9e1c9c409e5ffc5da01b98f6c0d346 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Tue, 2 Jan 2024 20:51:23 +0800 Subject: [PATCH 28/83] Rename storeCredentials --- .../components/src/redux/reducers/accessReducer.ts | 12 ++++++------ packages/components/src/redux/sagas/authSaga.ts | 12 ++++++------ .../components/src/screens/PasswordRequestScreen.tsx | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index a3059ccdb..b8c66b737 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -4,7 +4,7 @@ import { v4 as uuidv4 } from 'uuid' import _ from 'lodash' export interface AccessState { - credentials: { + storeCredentials: { [usernameHash: string]: { passwordSalt: string } @@ -13,7 +13,7 @@ export interface AccessState { } const initialState: AccessState = { - credentials: {}, + storeCredentials: {}, lastLoggedInUsername: undefined, } @@ -26,8 +26,8 @@ export function accessReducer(state = initialState, action: Actions): AccessStat return { ...state, lastLoggedInUsername: action.payload.user.name, - credentials: { - ...state.credentials, + storeCredentials: { + ...state.storeCredentials, [usernameHash]: { passwordSalt, }, @@ -42,8 +42,8 @@ export function accessReducer(state = initialState, action: Actions): AccessStat return { ...state, lastLoggedInUsername: action.payload.name, - credentials: { - ...state.credentials, + storeCredentials: { + ...state.storeCredentials, [usernameHash]: { passwordSalt, }, diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index 33b200999..be50afe12 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -125,8 +125,8 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { // Attempt offline login const usernameHash = hash(name) - const credentials = yield select((s) => s.access.credentials) - const credential = credentials[usernameHash] + const storeCredentials = yield select((s) => s.access.storeCredentials) + const credential = storeCredentials[usernameHash] if (credential) { // ??????????????? @@ -162,9 +162,9 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { } function* onLoginSuccess(action: ExtractActionFromActionType<'LOGIN_SUCCESS'>) { - const credentials = yield select((s) => s.access.credentials) + const storeCredentials = yield select((s) => s.access.storeCredentials) const usernameHash = hash(action.payload.user.name) - const salt = credentials[usernameHash]?.passwordSalt + const salt = storeCredentials[usernameHash]?.passwordSalt if (!salt) { // TODO_ALEX ??? @@ -236,8 +236,8 @@ function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACC // Check username is not already taken const usernameHash = hash(name) - const credentials = yield select((s) => s.access.credentials) - const credential = credentials[usernameHash] + const storeCredentials = yield select((s) => s.access.storeCredentials) + const credential = storeCredentials[usernameHash] if (credential) { // username already taken diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index f31d3afef..ccdda7b18 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -17,7 +17,7 @@ import { hash } from '../services/hash' export function PasswordRequestScreen() { const dispatch = useDispatch() const user = useSelector(selectors.currentUserSelector) - const credentials = useSelector((s) => s.access.credentials) + const storeCredentials = useSelector((s) => s.access.storeCredentials) const [loading, setLoading] = React.useState(false) const [valid, setValid] = React.useState(false) const [passwordError, setPasswordError] = React.useState(false) @@ -26,7 +26,7 @@ export function PasswordRequestScreen() { const [password, setPassword] = React.useState('') const usernameHash = hash(user.name) - const salt = credentials[usernameHash]?.passwordSalt + const salt = storeCredentials[usernameHash]?.passwordSalt if (!salt) { // TODO_ALEX: handle this case, navigate away? From 8cb95b6250dd512a56115e1969ed248195e58ab2 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Tue, 2 Jan 2024 20:53:20 +0800 Subject: [PATCH 29/83] Check store username unique during signup --- .../authScreen/signUp/AskUserInformation.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/components/src/screens/authScreen/signUp/AskUserInformation.tsx b/packages/components/src/screens/authScreen/signUp/AskUserInformation.tsx index 4830ec81e..c5ab22b4d 100644 --- a/packages/components/src/screens/authScreen/signUp/AskUserInformation.tsx +++ b/packages/components/src/screens/authScreen/signUp/AskUserInformation.tsx @@ -10,6 +10,8 @@ import { httpClient } from '../../../services/HttpClient' import { useDebounce } from '../../../hooks/useDebounce' import { formHeights } from './FormHeights' import { translate } from '../../../i18n' +import { useSelector } from '../../../redux/useSelector' +import { hash } from '../../../services/hash' export function AskUserInformation({ step, heightInner }) { const [{ app: state }, dispatch] = useMultiStepForm() @@ -23,8 +25,19 @@ export function AskUserInformation({ step, heightInner }) { const { name, password, passwordConfirm, gender } = state const [debouncedName] = useDebounce(name, 500) // to stop fast typing calls + const storeCredentials = useSelector((s) => s.access.storeCredentials) + React.useEffect(() => { let ignore = false + + // Check local store credentials + const credentials = storeCredentials[hash(name)] + if (credentials) { + setNameNotAvailable(true) + return + } + + // Check online async function checkUserNameAvailability() { try { const response = await httpClient.getUserInfo(name) @@ -52,7 +65,8 @@ export function AskUserInformation({ step, heightInner }) { return ( password.length >= minPasswordLength && passwordConfirm === password && - name.length >= minNameLength + name.length >= minNameLength && + !nameNotAvailable ) } From 97bace6d5502aacf84fcde6b87558a903f2af27c Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Tue, 2 Jan 2024 20:56:32 +0800 Subject: [PATCH 30/83] Refactor accessSelectors --- packages/components/src/redux/sagas/authSaga.ts | 6 +++--- packages/components/src/redux/selectors/accessSelectors.ts | 5 +++++ packages/components/src/redux/selectors/index.ts | 1 + packages/components/src/screens/PasswordRequestScreen.tsx | 2 +- .../src/screens/authScreen/signUp/AskUserInformation.tsx | 3 ++- 5 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 packages/components/src/redux/selectors/accessSelectors.ts diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index be50afe12..1a68bd118 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -125,7 +125,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { // Attempt offline login const usernameHash = hash(name) - const storeCredentials = yield select((s) => s.access.storeCredentials) + const storeCredentials = yield select(selectors.storeCredentialsSelector) const credential = storeCredentials[usernameHash] if (credential) { @@ -162,7 +162,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { } function* onLoginSuccess(action: ExtractActionFromActionType<'LOGIN_SUCCESS'>) { - const storeCredentials = yield select((s) => s.access.storeCredentials) + const storeCredentials = yield select(selectors.storeCredentialsSelector) const usernameHash = hash(action.payload.user.name) const salt = storeCredentials[usernameHash]?.passwordSalt @@ -236,7 +236,7 @@ function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACC // Check username is not already taken const usernameHash = hash(name) - const storeCredentials = yield select((s) => s.access.storeCredentials) + const storeCredentials = yield select(selectors.storeCredentialsSelector) const credential = storeCredentials[usernameHash] if (credential) { diff --git a/packages/components/src/redux/selectors/accessSelectors.ts b/packages/components/src/redux/selectors/accessSelectors.ts new file mode 100644 index 000000000..e3f4bf647 --- /dev/null +++ b/packages/components/src/redux/selectors/accessSelectors.ts @@ -0,0 +1,5 @@ +import { ReduxState } from '../reducers' + +const s = (state: ReduxState) => state.access + +export const storeCredentialsSelector = (state: ReduxState) => s(state).storeCredentials diff --git a/packages/components/src/redux/selectors/index.ts b/packages/components/src/redux/selectors/index.ts index 6d37f9edb..c9e1f63e4 100644 --- a/packages/components/src/redux/selectors/index.ts +++ b/packages/components/src/redux/selectors/index.ts @@ -1,3 +1,4 @@ +export * from './accessSelectors' export * from './analyticsSelectors' export * from './answerSelectors' export * from './appSelectors' diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index ccdda7b18..5f71831bc 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -17,7 +17,7 @@ import { hash } from '../services/hash' export function PasswordRequestScreen() { const dispatch = useDispatch() const user = useSelector(selectors.currentUserSelector) - const storeCredentials = useSelector((s) => s.access.storeCredentials) + const storeCredentials = useSelector(selectors.storeCredentialsSelector) const [loading, setLoading] = React.useState(false) const [valid, setValid] = React.useState(false) const [passwordError, setPasswordError] = React.useState(false) diff --git a/packages/components/src/screens/authScreen/signUp/AskUserInformation.tsx b/packages/components/src/screens/authScreen/signUp/AskUserInformation.tsx index c5ab22b4d..cd88277bc 100644 --- a/packages/components/src/screens/authScreen/signUp/AskUserInformation.tsx +++ b/packages/components/src/screens/authScreen/signUp/AskUserInformation.tsx @@ -12,6 +12,7 @@ import { formHeights } from './FormHeights' import { translate } from '../../../i18n' import { useSelector } from '../../../redux/useSelector' import { hash } from '../../../services/hash' +import * as selectors from '../../../redux/selectors' export function AskUserInformation({ step, heightInner }) { const [{ app: state }, dispatch] = useMultiStepForm() @@ -25,7 +26,7 @@ export function AskUserInformation({ step, heightInner }) { const { name, password, passwordConfirm, gender } = state const [debouncedName] = useDebounce(name, 500) // to stop fast typing calls - const storeCredentials = useSelector((s) => s.access.storeCredentials) + const storeCredentials = useSelector(selectors.storeCredentialsSelector) React.useEffect(() => { let ignore = false From 7dc44c5d16ccc636ebea7391522fe83edacca5a8 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Tue, 2 Jan 2024 21:34:23 +0800 Subject: [PATCH 31/83] Add StoreSwitch WIP --- .../src/components/layout/BackgroundTheme.tsx | 8 +++- .../src/navigators/AppNavigator.tsx | 7 +++ packages/components/src/redux/StoreSwitch.tsx | 6 +++ .../components/src/screens/SplashScreen.tsx | 43 ++++++++++++++++++- 4 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 packages/components/src/redux/StoreSwitch.tsx diff --git a/packages/components/src/components/layout/BackgroundTheme.tsx b/packages/components/src/components/layout/BackgroundTheme.tsx index 06b3f85e1..106a1c3df 100644 --- a/packages/components/src/components/layout/BackgroundTheme.tsx +++ b/packages/components/src/components/layout/BackgroundTheme.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components/native' import { useTheme } from '../context/ThemeContext' import { useTodayPrediction } from '../context/PredictionProvider' import { assets } from '../../assets' -import { ThemeName } from '@oky/core' +import { ThemeName, defaultTheme } from '@oky/core' function getBackgroundImage(theme: ThemeName, onPeriod: boolean) { const background = assets.backgrounds[theme] @@ -25,6 +25,12 @@ export function BackgroundTheme({ theme = null, ...props }) { return } +export function DefaultBackgroundTheme({ ...props }) { + const backgroundImage = assets.backgrounds[defaultTheme] + + return +} + const Background = styled.ImageBackground` width: 100%; height: 100%; diff --git a/packages/components/src/navigators/AppNavigator.tsx b/packages/components/src/navigators/AppNavigator.tsx index c6c9fdaee..1392f27d1 100644 --- a/packages/components/src/navigators/AppNavigator.tsx +++ b/packages/components/src/navigators/AppNavigator.tsx @@ -28,6 +28,12 @@ import { FindHelpScreen } from '../screens/FindHelpScreen' import { PasswordRequestScreen } from '../screens/PasswordRequestScreen' import { VideoScreen } from '../screens/VideoScreen' import { VideosScreen } from '../screens/VideosScreen' +import { StoreSwitch } from '../redux/StoreSwitch' + +const StoreSwitchStack = createStackNavigator( + { StoreSwitch }, + { headerMode: 'none', initialRouteName: 'StoreSwitch' }, +) const TutorialFirstStack = createStackNavigator( { TutorialFirstScreen }, @@ -131,6 +137,7 @@ const MainStack = createBottomTabNavigator( const AppNavigator = createStackNavigator( { + StoreSwitchStack, SplashScreen, OnboardingScreen, PasswordRequestScreen, diff --git a/packages/components/src/redux/StoreSwitch.tsx b/packages/components/src/redux/StoreSwitch.tsx new file mode 100644 index 000000000..5ead6e288 --- /dev/null +++ b/packages/components/src/redux/StoreSwitch.tsx @@ -0,0 +1,6 @@ +import React from 'react' +import { SimpleSplashScreen } from '../screens/SplashScreen' + +export function StoreSwitch() { + return +} diff --git a/packages/components/src/screens/SplashScreen.tsx b/packages/components/src/screens/SplashScreen.tsx index 11a1e739c..c395018a9 100644 --- a/packages/components/src/screens/SplashScreen.tsx +++ b/packages/components/src/screens/SplashScreen.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { BackgroundTheme } from '../components/layout/BackgroundTheme' +import { BackgroundTheme, DefaultBackgroundTheme } from '../components/layout/BackgroundTheme' import { PageContainer } from '../components/layout/PageContainer' import { assets } from '../assets/index' import styled from 'styled-components/native' @@ -78,7 +78,7 @@ export function SplashScreen() { navigateAndReset('PasswordRequestScreen', null) return } - navigateAndReset('MainStack', null) + navigateAndReset('StoreSwitchStack', null) return } navigateAndReset('LoginStack', null) @@ -121,6 +121,45 @@ export function SplashScreen() { ) } +export function SimpleSplashScreen() { + const [animatedValue] = React.useState(new Animated.Value(0)) + + React.useEffect(() => { + Spin() + }) + + const Spin = () => { + Animated.timing(animatedValue, { + duration: 50000, + toValue: 36000, + easing: Easing.linear, + useNativeDriver: true, + }).start() + } + + const rotation = animatedValue.interpolate({ + inputRange: [0, 36000], + outputRange: ['0deg', '36000deg'], + }) + + return ( + + + + + + + + + + + ) +} + const Container = styled.View` flex: 1; align-items: center; From 9f43ff29ba89826d1a860898f2d76ef44a3eb0f0 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Tue, 2 Jan 2024 21:36:37 +0800 Subject: [PATCH 32/83] Fix background --- packages/components/src/components/layout/BackgroundTheme.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/components/src/components/layout/BackgroundTheme.tsx b/packages/components/src/components/layout/BackgroundTheme.tsx index 106a1c3df..e0da94bb4 100644 --- a/packages/components/src/components/layout/BackgroundTheme.tsx +++ b/packages/components/src/components/layout/BackgroundTheme.tsx @@ -26,7 +26,8 @@ export function BackgroundTheme({ theme = null, ...props }) { } export function DefaultBackgroundTheme({ ...props }) { - const backgroundImage = assets.backgrounds[defaultTheme] + // TODO_ALEX getAsset() safely + const backgroundImage = assets.backgrounds[defaultTheme].default return } From 16508d4a370e8a40a85b8b4c8aac526a298b4f7e Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Tue, 2 Jan 2024 22:55:57 +0800 Subject: [PATCH 33/83] Trigger store switch from splash screen --- .../components/src/redux/StoreCoordinator.tsx | 87 +++++++++++++------ packages/components/src/redux/StoreSwitch.tsx | 14 +++ .../src/redux/reducers/accessReducer.ts | 8 +- .../components/src/redux/sagas/authSaga.ts | 4 +- .../src/screens/PasswordRequestScreen.tsx | 11 +-- 5 files changed, 84 insertions(+), 40 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index 5e1663463..c49a50155 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -12,6 +12,18 @@ interface Keys { secretKey: string } +interface Context { + triggerStoreSwitch: () => void + switchComplete: boolean +} + +const StoreCoordinatorContext = React.createContext({ + triggerStoreSwitch: () => { + // + }, + switchComplete: false, +}) + const primaryStore = configureStore({ key: 'primary', secretKey: config.REDUX_ENCRYPT_KEY, @@ -23,13 +35,13 @@ export function StoreCoordinator({ children }) { const [{ persistor, store }, setStore] = React.useState(primaryStore) const [state, setState] = React.useState(undefined) - const [keys, setKeys] = React.useState(undefined) + const [switchComplete, setSwitchComplete] = React.useState(false) const [shouldSwitch, setShouldSwitch] = React.useState(false) const [shouldMigrate, setShouldMigrate] = React.useState(false) const switchStore = ({ key, secretKey }: Keys) => { if (!key || !secretKey) { - return + return // ERROR } setStore( configureStore({ @@ -41,35 +53,38 @@ export function StoreCoordinator({ children }) { ) } - // ===== Step 1: Detect key change ===== // - React.useEffect(() => { - const unsubscribe = store.subscribe(() => { - const primaryState = store.getState() - // TODO: - // @ts-ignore - const currentKeys = primaryState?.keys.keys - - if (currentKeys && currentKeys !== keys) { - setKeys(currentKeys) - setState(primaryState) - setShouldSwitch(true) - } - }) - - return () => unsubscribe() - }, [store]) - - // ===== Step 2: Switch stores ===== // + const triggerStoreSwitch = () => { + if (shouldSwitch) { + return + } + setShouldSwitch(true) + setShouldMigrate(false) + setSwitchComplete(false) + } + + // ===== Switch stores ===== // React.useEffect(() => { if (!shouldSwitch) { return } + const primaryState = store.getState() + // TODO: + // @ts-ignore + const keys = primaryState?.keys.keys + + if (!keys) { + return // ERROR + } + + setState(primaryState) + switchStore(keys) setShouldMigrate(true) + setShouldSwitch(false) }, [shouldSwitch]) - // ===== Step 3: Migrate state ===== // + // ===== Migrate state ===== // React.useEffect(() => { if (!shouldMigrate) { return @@ -77,16 +92,32 @@ export function StoreCoordinator({ children }) { store.dispatch({ type: REHYDRATE, payload: state }) - setShouldSwitch(false) setShouldMigrate(false) setState(undefined) + setSwitchComplete(true) }, [shouldMigrate]) return ( - - - {children} - - + + + + {children} + + + ) } + +export function useStoreCoordinator() { + const storeCoordinatorContext = React.useContext(StoreCoordinatorContext) + if (storeCoordinatorContext === undefined) { + throw new Error(`useStoreCoordinator must be used within a StoreCoordinator`) + } + + return storeCoordinatorContext +} diff --git a/packages/components/src/redux/StoreSwitch.tsx b/packages/components/src/redux/StoreSwitch.tsx index 5ead6e288..8bb0296c6 100644 --- a/packages/components/src/redux/StoreSwitch.tsx +++ b/packages/components/src/redux/StoreSwitch.tsx @@ -1,6 +1,20 @@ import React from 'react' import { SimpleSplashScreen } from '../screens/SplashScreen' +import { navigateAndReset } from '../services/navigationService' +import { useStoreCoordinator } from './StoreCoordinator' export function StoreSwitch() { + const { triggerStoreSwitch, switchComplete } = useStoreCoordinator() + + React.useEffect(() => { + triggerStoreSwitch() + }, []) + + React.useEffect(() => { + if (switchComplete) { + navigateAndReset('MainStack', null) + } + }, [switchComplete]) + return } diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index b8c66b737..8013db358 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -10,14 +10,16 @@ export interface AccessState { } } lastLoggedInUsername?: string + storeId: string } -const initialState: AccessState = { +const initialState = (): AccessState => ({ storeCredentials: {}, lastLoggedInUsername: undefined, -} + storeId: uuidv4(), +}) -export function accessReducer(state = initialState, action: Actions): AccessState { +export function accessReducer(state = initialState(), action: Actions): AccessState { switch (action.type) { case 'CREATE_ACCOUNT_SUCCESS': { const usernameHash = hash(action.payload.user.name) diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index 1a68bd118..813c665cf 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -111,7 +111,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { } yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones - yield call(navigateAndReset, 'MainStack', null) + yield call(navigateAndReset, 'StoreSwitchStack', null) } catch (error) { let errorMessage = 'request_fail' if (error && error.response && error.response.data) { @@ -363,7 +363,7 @@ function* onJourneyCompletion(action: ExtractActionFromActionType<'JOURNEY_COMPL yield put(actions.setTutorialOneActive(true)) yield put(actions.setTutorialTwoActive(true)) yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones - yield call(navigateAndReset, 'MainStack', null) + yield call(navigateAndReset, 'StoreSwitchStack', null) } export function* authSaga() { diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index 5f71831bc..d79e11efd 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -74,19 +74,16 @@ export function PasswordRequestScreen() { setNameError(false) setPasswordError(false) setValid(true) - requestAnimationFrame(() => { - navigateAndReset('MainStack', null) - }) const keys = { key: usernameHash, secretKey: hash(trimmedPassword + salt), } + dispatch(actions.setStoreKeys(keys)) - // TODO_ALEX Consider moving to MainScreen for safer transition - setTimeout(() => { - dispatch(actions.setStoreKeys(keys)) - }, 1000) + requestAnimationFrame(() => { + navigateAndReset('StoreSwitchStack', null) + }) } else if (trimmedPassword === user.password && name !== user.name) { setLoading(false) setPasswordError(false) From aaeb84f79a8763df12833f569cea9ebf77fac7e0 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Wed, 3 Jan 2024 18:32:56 +0800 Subject: [PATCH 34/83] Refactor to reduce number of actions needed --- .../src/redux/actions/authActions.ts | 30 +++---------------- .../src/redux/reducers/accessReducer.ts | 16 ---------- .../src/redux/reducers/authReducer.ts | 23 +------------- .../components/src/redux/sagas/authSaga.ts | 30 ++++++++++++------- 4 files changed, 24 insertions(+), 75 deletions(-) diff --git a/packages/components/src/redux/actions/authActions.ts b/packages/components/src/redux/actions/authActions.ts index f8d395d2c..752a546a3 100644 --- a/packages/components/src/redux/actions/authActions.ts +++ b/packages/components/src/redux/actions/authActions.ts @@ -17,6 +17,7 @@ export function loginSuccess({ password, secretQuestion, secretAnswer, + isGuest, }, }) { return createAction('LOGIN_SUCCESS', { @@ -32,6 +33,7 @@ export function loginSuccess({ password, secretQuestion, secretAnswer, + isGuest, }, }) } @@ -40,32 +42,6 @@ export function setStoreKeys(payload: { key: string; secretKey: string }) { return createAction('SET_STORE_KEYS', payload) } -export function createGuestAccountSuccess({ - id, - name, - dateOfBirth, - gender, - location, - country, - province, - password, - secretQuestion, - secretAnswer, -}) { - return createAction('CREATE_GUEST_ACCOUNT_SUCCESS', { - id, - name, - dateOfBirth, - gender, - location, - country, - province, - password, - secretQuestion, - secretAnswer, - }) -} - export function loginFailure({ error }) { return createAction('LOGIN_FAILURE', { error }) } @@ -124,6 +100,7 @@ export function createAccountSuccess({ province, secretQuestion, secretAnswer, + isGuest, }, }) { return createAction('CREATE_ACCOUNT_SUCCESS', { @@ -139,6 +116,7 @@ export function createAccountSuccess({ password, secretQuestion, secretAnswer, + isGuest, }, }) } diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index 8013db358..82afc55f1 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -37,22 +37,6 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt } } - case 'CREATE_GUEST_ACCOUNT_SUCCESS': { - const usernameHash = hash(action.payload.name) - const passwordSalt = uuidv4() - - return { - ...state, - lastLoggedInUsername: action.payload.name, - storeCredentials: { - ...state.storeCredentials, - [usernameHash]: { - passwordSalt, - }, - }, - } - } - case 'LOGIN_SUCCESS': { // TODO_ALEX Can just update keys here instead of via saga ? return state diff --git a/packages/components/src/redux/reducers/authReducer.ts b/packages/components/src/redux/reducers/authReducer.ts index 07710bba1..1b2ccc1ad 100644 --- a/packages/components/src/redux/reducers/authReducer.ts +++ b/packages/components/src/redux/reducers/authReducer.ts @@ -71,28 +71,7 @@ export function authReducer(state = initialState, action: Actions | RehydrateAct password: action.payload.user.password, secretQuestion: action.payload.user.secretQuestion, secretAnswer: action.payload.user.secretAnswer, - isGuest: false, - }, - } - - case 'CREATE_GUEST_ACCOUNT_SUCCESS': - return { - ...state, - appToken: null, - isLoggingIn: false, - loginFailedCount: 0, - user: { - id: action.payload.id, - name: action.payload.name, - dateOfBirth: action.payload.dateOfBirth, - gender: action.payload.gender, - location: action.payload.location, - country: action.payload.country, - province: action.payload.province, - password: action.payload.password, - secretQuestion: action.payload.secretQuestion, - secretAnswer: action.payload.secretAnswer, - isGuest: true, + isGuest: action.payload.user.isGuest, }, } diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index 813c665cf..39d71aecf 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -89,6 +89,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { secretQuestion: user.secretQuestion, secretAnswer: user.secretAnswer, password, + isGuest: false, }, }), ) @@ -147,6 +148,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { // secretQuestion: user.secretQuestion, // secretAnswer: user.secretAnswer, // password, + // isGuest: false, // }, // }), // ) @@ -225,6 +227,7 @@ function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACC secretQuestion: user.secretQuestion, secretAnswer: user.secretAnswer, password, + isGuest: false, }, }), ) @@ -246,17 +249,21 @@ function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACC } yield put( - actions.createGuestAccountSuccess({ - id: id || uuidv4(), - name, - dateOfBirth, - gender, - location, - country, - province, - password, - secretAnswer, - secretQuestion, + actions.createAccountSuccess({ + appToken: null, + user: { + id: id || uuidv4(), + name, + dateOfBirth, + gender, + location, + country, + province, + password, + secretAnswer, + secretQuestion, + isGuest: true, + }, }), ) } @@ -278,6 +285,7 @@ function* onCreateAccountSuccess(action: ExtractActionFromActionType<'CREATE_ACC password: user.password, secretQuestion: user.secretQuestion, secretAnswer: user.secretAnswer, + isGuest: user.isGuest, }, }), ) From 93fd7d7c91e1e05a6f6d65eeb3b604fa53eea52e Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Wed, 3 Jan 2024 19:40:52 +0800 Subject: [PATCH 35/83] Login offline --- .../components/src/redux/StoreCoordinator.tsx | 6 ++- .../src/redux/actions/authActions.ts | 7 +++ .../src/redux/reducers/keysReducer.ts | 10 ++++ .../components/src/redux/sagas/authSaga.ts | 47 +++++++------------ 4 files changed, 39 insertions(+), 31 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index c49a50155..ea91a9e3d 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -71,7 +71,9 @@ export function StoreCoordinator({ children }) { const primaryState = store.getState() // TODO: // @ts-ignore - const keys = primaryState?.keys.keys + const keys = primaryState?.keys?.keys + // @ts-ignore + const shouldMigrateData = primaryState?.keys?.shouldMigrateData if (!keys) { return // ERROR @@ -80,7 +82,7 @@ export function StoreCoordinator({ children }) { setState(primaryState) switchStore(keys) - setShouldMigrate(true) + setShouldMigrate(shouldMigrateData) setShouldSwitch(false) }, [shouldSwitch]) diff --git a/packages/components/src/redux/actions/authActions.ts b/packages/components/src/redux/actions/authActions.ts index 752a546a3..53b0cd067 100644 --- a/packages/components/src/redux/actions/authActions.ts +++ b/packages/components/src/redux/actions/authActions.ts @@ -38,6 +38,13 @@ export function loginSuccess({ }) } +export function loginOfflineSuccess(payload: { + keys: { key: string; secretKey: string } + shouldMigrateData: boolean +}) { + return createAction('LOGIN_OFFLINE_SUCCESS', payload) +} + export function setStoreKeys(payload: { key: string; secretKey: string }) { return createAction('SET_STORE_KEYS', payload) } diff --git a/packages/components/src/redux/reducers/keysReducer.ts b/packages/components/src/redux/reducers/keysReducer.ts index 2e840b704..63fdab51c 100644 --- a/packages/components/src/redux/reducers/keysReducer.ts +++ b/packages/components/src/redux/reducers/keysReducer.ts @@ -9,10 +9,13 @@ export interface KeysState { secretKey: string } | undefined + + shouldMigrateData: boolean } const initialState: KeysState = { keys: undefined, + shouldMigrateData: false, } export function keysReducer(state = initialState, action: Actions): KeysState { @@ -23,6 +26,13 @@ export function keysReducer(state = initialState, action: Actions): KeysState { keys: action.payload, } + case 'LOGIN_OFFLINE_SUCCESS': + return { + ...state, + keys: action.payload.keys, + shouldMigrateData: action.payload.shouldMigrateData, + } + default: return state } diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index 39d71aecf..a016af92e 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -130,36 +130,22 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { const credential = storeCredentials[usernameHash] if (credential) { - // ??????????????? - // Switch stores here ? - // Change boolean to trigger switch in coordinator ? - // Somehow need to pass the keys to the coordinator though - // yield put( - // actions.loginOfflineSuccess({ - // appToken: null, - // user: { - // id: user.id, - // name, - // dateOfBirth: user.dateOfBirth, - // gender: user.gender, - // location: user.location, - // country: user.country, - // province: user.province, - // secretQuestion: user.secretQuestion, - // secretAnswer: user.secretAnswer, - // password, - // isGuest: false, - // }, - // }), - // ) - // return + yield put( + actions.loginOfflineSuccess({ + keys: { + key: usernameHash, + secretKey: hash(password + credential.passwordSalt), + }, + shouldMigrateData: false, + }), + ) + } else { + yield put( + actions.loginFailure({ + error: errorMessage, + }), + ) } - - yield put( - actions.loginFailure({ - error: errorMessage, - }), - ) } } @@ -248,6 +234,7 @@ function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACC return } + // CREATE OFFLINE GUEST ACCOUNT yield put( actions.createAccountSuccess({ appToken: null, @@ -271,6 +258,8 @@ function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACC function* onCreateAccountSuccess(action: ExtractActionFromActionType<'CREATE_ACCOUNT_SUCCESS'>) { const { appToken, user } = action.payload + // Is this even necessary ???? + // Because we already update the redux state when account is created, so why the extra log in step? yield put( actions.loginSuccess({ appToken, From eee28225aebb0080f29ee60fa51a0ff41239c7aa Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Wed, 3 Jan 2024 21:11:45 +0800 Subject: [PATCH 36/83] Migrate store with interval completion check --- .../components/src/redux/StoreCoordinator.tsx | 59 +++++++++++++++++-- .../components/src/redux/actions/index.ts | 7 +++ .../src/redux/reducers/authReducer.ts | 6 ++ .../src/redux/reducers/keysReducer.ts | 11 +++- 4 files changed, 76 insertions(+), 7 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index ea91a9e3d..e913689dc 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -5,7 +5,7 @@ import { configureStore } from './store' import { config } from './config' import { rootReducer } from './reducers' import { rootSaga } from './sagas' -import { REHYDRATE } from 'redux-persist' +import * as actions from './actions' interface Keys { key: string @@ -84,19 +84,68 @@ export function StoreCoordinator({ children }) { switchStore(keys) setShouldMigrate(shouldMigrateData) setShouldSwitch(false) + + if (!shouldMigrateData) { + setSwitchComplete(true) + } }, [shouldSwitch]) // ===== Migrate state ===== // + const interval = 500 + const maxAttempts = 10 + let attempts = 0 + React.useEffect(() => { if (!shouldMigrate) { return } - store.dispatch({ type: REHYDRATE, payload: state }) + let cleanup = false - setShouldMigrate(false) - setState(undefined) - setSwitchComplete(true) + const attemptMigration = () => { + store.dispatch( + actions.migrateStore({ + auth: state.auth, + }), + ) + + setTimeout(onTimeout, interval) + attempts++ + } + + const onTimeout = () => { + if (cleanup) { + return + } + + if (attempts > maxAttempts) { + return // ERROR + } + + if (attempts === 0) { + attemptMigration() + return + } + + const currentState = store.getState() + // @ts-ignore + const migrationComplete = currentState?.keys.migrationComplete + + if (migrationComplete) { + setShouldMigrate(false) + setState(undefined) + setSwitchComplete(true) + return + } + + attemptMigration() + } + + setTimeout(onTimeout, interval) + + return () => { + cleanup = true + } }, [shouldMigrate]) return ( diff --git a/packages/components/src/redux/actions/index.ts b/packages/components/src/redux/actions/index.ts index df4826cce..82e9e23f2 100644 --- a/packages/components/src/redux/actions/index.ts +++ b/packages/components/src/redux/actions/index.ts @@ -1,6 +1,13 @@ +import { createAction } from '../helpers' +import { ReduxState } from '../reducers' + export * from './analyticsActions' export * from './answerActions' export * from './appActions' export * from './authActions' export * from './contentActions' export * from './predictionActions' + +export function migrateStore(payload: { auth: ReduxState['auth'] }) { + return createAction('MIGRATE_STORE', payload) +} diff --git a/packages/components/src/redux/reducers/authReducer.ts b/packages/components/src/redux/reducers/authReducer.ts index 1b2ccc1ad..d3bf6df38 100644 --- a/packages/components/src/redux/reducers/authReducer.ts +++ b/packages/components/src/redux/reducers/authReducer.ts @@ -45,6 +45,12 @@ export function authReducer(state = initialState, action: Actions | RehydrateAct ..._.pick(initialState, ['error', 'isLoggingIn', 'loginFailedCount', 'isCreatingAccount']), } + case 'MIGRATE_STORE': + return { + ...state, + ...action.payload.auth, + } + case 'LOGIN_REQUEST': return { ...state, diff --git a/packages/components/src/redux/reducers/keysReducer.ts b/packages/components/src/redux/reducers/keysReducer.ts index 63fdab51c..bd097b069 100644 --- a/packages/components/src/redux/reducers/keysReducer.ts +++ b/packages/components/src/redux/reducers/keysReducer.ts @@ -1,4 +1,3 @@ -import { hash } from '../../services/hash' import { Actions } from '../types' import _ from 'lodash' @@ -11,15 +10,23 @@ export interface KeysState { | undefined shouldMigrateData: boolean + migrationComplete: boolean } const initialState: KeysState = { keys: undefined, - shouldMigrateData: false, + shouldMigrateData: true, + migrationComplete: false, } export function keysReducer(state = initialState, action: Actions): KeysState { switch (action.type) { + case 'MIGRATE_STORE': + return { + ...state, + migrationComplete: true, + } + case 'SET_STORE_KEYS': return { ...state, From b5d8abbb93b8e3da1f0dbeec89fc6bf5910fe980 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Thu, 4 Jan 2024 19:17:00 +0800 Subject: [PATCH 37/83] Refactor StoreCoordinator with useReducer --- .../components/src/redux/StoreCoordinator.tsx | 161 +++++++++++------- packages/components/src/redux/StoreSwitch.tsx | 4 +- 2 files changed, 105 insertions(+), 60 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index e913689dc..85a056ab1 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -6,19 +6,15 @@ import { config } from './config' import { rootReducer } from './reducers' import { rootSaga } from './sagas' import * as actions from './actions' - -interface Keys { - key: string - secretKey: string -} +import { PersistPartial } from 'redux-persist' interface Context { - triggerStoreSwitch: () => void + switchStore: () => void switchComplete: boolean } const StoreCoordinatorContext = React.createContext({ - triggerStoreSwitch: () => { + switchStore: () => { // }, switchComplete: false, @@ -31,43 +27,87 @@ const primaryStore = configureStore({ rootSaga, }) -export function StoreCoordinator({ children }) { - const [{ persistor, store }, setStore] = React.useState(primaryStore) - - const [state, setState] = React.useState(undefined) - const [switchComplete, setSwitchComplete] = React.useState(false) - const [shouldSwitch, setShouldSwitch] = React.useState(false) - const [shouldMigrate, setShouldMigrate] = React.useState(false) +interface State { + redux: ReturnType + storeStateSnapshot: PersistPartial | undefined + shouldMigrate: boolean + switchComplete: boolean +} - const switchStore = ({ key, secretKey }: Keys) => { - if (!key || !secretKey) { - return // ERROR +type Action = + | { + type: 'set_redux' + payload: ReturnType } - setStore( - configureStore({ - key, - secretKey, - rootReducer, - rootSaga, - }), - ) - } - - const triggerStoreSwitch = () => { - if (shouldSwitch) { - return + | { + type: 'set_snapshot' + payload: PersistPartial } - setShouldSwitch(true) - setShouldMigrate(false) - setSwitchComplete(false) - } - - // ===== Switch stores ===== // - React.useEffect(() => { - if (!shouldSwitch) { - return + | { + type: 'switch_store' + payload: { + redux: ReturnType + storeStateSnapshot: PersistPartial + shouldMigrate: boolean + switchComplete: boolean + } + } + | { + type: 'complete_migration' } +const initialState: State = { + redux: primaryStore, + storeStateSnapshot: undefined, + shouldMigrate: false, + switchComplete: false, +} + +function reducer(state: State, action: Action): State { + switch (action.type) { + case 'set_redux': + return { + ...state, + redux: action.payload, + } + + case 'set_snapshot': + return { + ...state, + storeStateSnapshot: action.payload, + } + + case 'switch_store': + return { + ...state, + redux: action.payload.redux, + storeStateSnapshot: action.payload.storeStateSnapshot, + shouldMigrate: action.payload.shouldMigrate, + switchComplete: action.payload.switchComplete, + } + + case 'complete_migration': + return { + ...state, + storeStateSnapshot: undefined, + shouldMigrate: false, + switchComplete: true, + } + } +} + +export function StoreCoordinator({ children }) { + const [ + { + redux: { store, persistor }, + storeStateSnapshot, + shouldMigrate, + switchComplete, + }, + dispatch, + ] = React.useReducer(reducer, initialState) + + const switchStore = () => { const primaryState = store.getState() // TODO: // @ts-ignore @@ -79,16 +119,23 @@ export function StoreCoordinator({ children }) { return // ERROR } - setState(primaryState) - - switchStore(keys) - setShouldMigrate(shouldMigrateData) - setShouldSwitch(false) - - if (!shouldMigrateData) { - setSwitchComplete(true) - } - }, [shouldSwitch]) + const userStore = configureStore({ + key: keys.key, + secretKey: keys.secretKey, + rootReducer, + rootSaga, + }) + + dispatch({ + type: 'switch_store', + payload: { + redux: userStore, + storeStateSnapshot: primaryState, + shouldMigrate: shouldMigrateData, + switchComplete: !shouldMigrateData, + }, + }) + } // ===== Migrate state ===== // const interval = 500 @@ -96,16 +143,16 @@ export function StoreCoordinator({ children }) { let attempts = 0 React.useEffect(() => { + let ignore = false if (!shouldMigrate) { return } - let cleanup = false - const attemptMigration = () => { store.dispatch( actions.migrateStore({ - auth: state.auth, + // @ts-ignore + auth: storeStateSnapshot?.auth, }), ) @@ -114,7 +161,7 @@ export function StoreCoordinator({ children }) { } const onTimeout = () => { - if (cleanup) { + if (ignore) { return } @@ -132,9 +179,7 @@ export function StoreCoordinator({ children }) { const migrationComplete = currentState?.keys.migrationComplete if (migrationComplete) { - setShouldMigrate(false) - setState(undefined) - setSwitchComplete(true) + dispatch({ type: 'complete_migration' }) return } @@ -144,14 +189,14 @@ export function StoreCoordinator({ children }) { setTimeout(onTimeout, interval) return () => { - cleanup = true + ignore = true } }, [shouldMigrate]) return ( diff --git a/packages/components/src/redux/StoreSwitch.tsx b/packages/components/src/redux/StoreSwitch.tsx index 8bb0296c6..ab119fa0a 100644 --- a/packages/components/src/redux/StoreSwitch.tsx +++ b/packages/components/src/redux/StoreSwitch.tsx @@ -4,10 +4,10 @@ import { navigateAndReset } from '../services/navigationService' import { useStoreCoordinator } from './StoreCoordinator' export function StoreSwitch() { - const { triggerStoreSwitch, switchComplete } = useStoreCoordinator() + const { switchStore, switchComplete } = useStoreCoordinator() React.useEffect(() => { - triggerStoreSwitch() + switchStore() }, []) React.useEffect(() => { From 9f53992a9a31d11395a55745df6281f71ff2a117 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Thu, 4 Jan 2024 19:29:13 +0800 Subject: [PATCH 38/83] Type assert to remove @ts-ignore comments --- .../components/src/redux/StoreCoordinator.tsx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index 85a056ab1..c44fc24f8 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -3,11 +3,13 @@ import { Provider as ReduxProvider } from 'react-redux' import { PersistGate } from 'redux-persist/integration/react' import { configureStore } from './store' import { config } from './config' -import { rootReducer } from './reducers' +import { ReduxState, rootReducer } from './reducers' import { rootSaga } from './sagas' import * as actions from './actions' import { PersistPartial } from 'redux-persist' +type ReduxPersistState = ReduxState & PersistPartial + interface Context { switchStore: () => void switchComplete: boolean @@ -29,7 +31,7 @@ const primaryStore = configureStore({ interface State { redux: ReturnType - storeStateSnapshot: PersistPartial | undefined + storeStateSnapshot: ReduxPersistState | undefined shouldMigrate: boolean switchComplete: boolean } @@ -41,13 +43,13 @@ type Action = } | { type: 'set_snapshot' - payload: PersistPartial + payload: ReduxPersistState } | { type: 'switch_store' payload: { redux: ReturnType - storeStateSnapshot: PersistPartial + storeStateSnapshot: ReduxPersistState shouldMigrate: boolean switchComplete: boolean } @@ -108,11 +110,8 @@ export function StoreCoordinator({ children }) { ] = React.useReducer(reducer, initialState) const switchStore = () => { - const primaryState = store.getState() - // TODO: - // @ts-ignore + const primaryState = store.getState() as ReduxPersistState const keys = primaryState?.keys?.keys - // @ts-ignore const shouldMigrateData = primaryState?.keys?.shouldMigrateData if (!keys) { @@ -151,7 +150,6 @@ export function StoreCoordinator({ children }) { const attemptMigration = () => { store.dispatch( actions.migrateStore({ - // @ts-ignore auth: storeStateSnapshot?.auth, }), ) @@ -174,8 +172,7 @@ export function StoreCoordinator({ children }) { return } - const currentState = store.getState() - // @ts-ignore + const currentState = store.getState() as ReduxPersistState const migrationComplete = currentState?.keys.migrationComplete if (migrationComplete) { From 4a2a341151d0d2f24d2f4ca2ed309794b2de0af1 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Thu, 4 Jan 2024 19:43:38 +0800 Subject: [PATCH 39/83] Remove unused StoreCoordinator actions --- .../components/src/redux/StoreCoordinator.tsx | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index c44fc24f8..1aff52343 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -37,14 +37,6 @@ interface State { } type Action = - | { - type: 'set_redux' - payload: ReturnType - } - | { - type: 'set_snapshot' - payload: ReduxPersistState - } | { type: 'switch_store' payload: { @@ -67,18 +59,6 @@ const initialState: State = { function reducer(state: State, action: Action): State { switch (action.type) { - case 'set_redux': - return { - ...state, - redux: action.payload, - } - - case 'set_snapshot': - return { - ...state, - storeStateSnapshot: action.payload, - } - case 'switch_store': return { ...state, From 5e2c904717445ec5012ec01fd769ea0fb62d3d12 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 5 Jan 2024 21:15:34 +0800 Subject: [PATCH 40/83] Add redux to workspace --- periodtracker.code-workspace | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/periodtracker.code-workspace b/periodtracker.code-workspace index 38f3ef4c4..ea7670a6a 100644 --- a/periodtracker.code-workspace +++ b/periodtracker.code-workspace @@ -6,7 +6,9 @@ { "path": "packages/cms" }, - + { + "path": "packages/components/src/redux" + }, { "path": "packages/components" }, From 7bdc1a0b7b7fb620f775de6be1336368b0dce100 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 8 Jan 2024 16:40:59 +0800 Subject: [PATCH 41/83] Verify password w/ hash & add to redux blacklists --- .../components/src/redux/StoreCoordinator.tsx | 14 +++++++++++ .../src/redux/reducers/accessReducer.ts | 13 +++++++--- .../components/src/redux/sagas/authSaga.ts | 9 ++++--- .../src/redux/selectors/accessSelectors.ts | 2 ++ packages/components/src/redux/store.ts | 4 ++-- .../src/screens/PasswordRequestScreen.tsx | 24 +++++++++++-------- .../components/src/screens/SplashScreen.tsx | 3 ++- 7 files changed, 50 insertions(+), 19 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index 1aff52343..e46b373e0 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -22,9 +22,22 @@ const StoreCoordinatorContext = React.createContext({ switchComplete: false, }) +const primaryStoreBlacklist = [ + 'keys', // Not persisted for security + 'content', // Moved to async storage + 'auth', // Persisted in secure userStore + 'prediction', // Persisted in secure userStore +] +const userStoreBlacklist = [ + 'keys', // Not persisted for security + 'content', // Moved to async storage + 'access', // Not required after store switch +] + const primaryStore = configureStore({ key: 'primary', secretKey: config.REDUX_ENCRYPT_KEY, + blacklist: primaryStoreBlacklist, rootReducer, rootSaga, }) @@ -101,6 +114,7 @@ export function StoreCoordinator({ children }) { const userStore = configureStore({ key: keys.key, secretKey: keys.secretKey, + blacklist: userStoreBlacklist, rootReducer, rootSaga, }) diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index 82afc55f1..b4c50b2e0 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -6,7 +6,9 @@ import _ from 'lodash' export interface AccessState { storeCredentials: { [usernameHash: string]: { - passwordSalt: string + storeSalt: string + verificationSalt: string + passwordHash: string } } lastLoggedInUsername?: string @@ -22,8 +24,11 @@ const initialState = (): AccessState => ({ export function accessReducer(state = initialState(), action: Actions): AccessState { switch (action.type) { case 'CREATE_ACCOUNT_SUCCESS': { + const storeSalt = uuidv4() + const verificationSalt = uuidv4() const usernameHash = hash(action.payload.user.name) - const passwordSalt = uuidv4() + const password = _.toLower(action.payload.user.password).trim() + const passwordHash = hash(password + verificationSalt) return { ...state, @@ -31,7 +36,9 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt storeCredentials: { ...state.storeCredentials, [usernameHash]: { - passwordSalt, + storeSalt, + verificationSalt, + passwordHash, }, }, } diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index a016af92e..8dcfb7986 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -13,6 +13,7 @@ import moment from 'moment' import { closeOutTTs } from '../../services/textToSpeech' import { fetchNetworkConnectionStatus } from '../../services/network' import { hash } from '../../services/hash' +import _ from 'lodash' // unwrap promise type Await = T extends Promise ? U : T @@ -134,7 +135,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { actions.loginOfflineSuccess({ keys: { key: usernameHash, - secretKey: hash(password + credential.passwordSalt), + secretKey: hash(password + credential.storeSalt), }, shouldMigrateData: false, }), @@ -152,16 +153,18 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { function* onLoginSuccess(action: ExtractActionFromActionType<'LOGIN_SUCCESS'>) { const storeCredentials = yield select(selectors.storeCredentialsSelector) const usernameHash = hash(action.payload.user.name) - const salt = storeCredentials[usernameHash]?.passwordSalt + const salt = storeCredentials[usernameHash]?.storeSalt if (!salt) { // TODO_ALEX ??? // Cant just return because they could have online account from another device that they're logging in to } + const password = _.toLower(action.payload.user.password) + const keys = { key: usernameHash, - secretKey: hash(action.payload.user.password + salt), + secretKey: hash(password + salt), } yield put(actions.setStoreKeys(keys)) diff --git a/packages/components/src/redux/selectors/accessSelectors.ts b/packages/components/src/redux/selectors/accessSelectors.ts index e3f4bf647..d7f4b1665 100644 --- a/packages/components/src/redux/selectors/accessSelectors.ts +++ b/packages/components/src/redux/selectors/accessSelectors.ts @@ -3,3 +3,5 @@ import { ReduxState } from '../reducers' const s = (state: ReduxState) => state.access export const storeCredentialsSelector = (state: ReduxState) => s(state).storeCredentials + +export const lastLoggedInUsernameSelector = (state: ReduxState) => s(state).lastLoggedInUsername diff --git a/packages/components/src/redux/store.ts b/packages/components/src/redux/store.ts index a46967008..578134f78 100644 --- a/packages/components/src/redux/store.ts +++ b/packages/components/src/redux/store.ts @@ -16,7 +16,7 @@ const createEncryptor = (secretKey) => export const version = -1 -export function configureStore({ key, secretKey, rootReducer, rootSaga }) { +export function configureStore({ key, secretKey, rootReducer, rootSaga, blacklist }) { const encryptor = createEncryptor(secretKey) const persistConfig = { @@ -25,7 +25,7 @@ export function configureStore({ key, secretKey, rootReducer, rootSaga }) { storage, timeout: 10000, throttle: 500, - blacklist: ['keys'], + blacklist, transforms: [encryptor], } diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index d79e11efd..fdb36f160 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -16,22 +16,27 @@ import { hash } from '../services/hash' export function PasswordRequestScreen() { const dispatch = useDispatch() - const user = useSelector(selectors.currentUserSelector) + const username = useSelector(selectors.lastLoggedInUsernameSelector) const storeCredentials = useSelector(selectors.storeCredentialsSelector) const [loading, setLoading] = React.useState(false) const [valid, setValid] = React.useState(false) const [passwordError, setPasswordError] = React.useState(false) const [nameError, setNameError] = React.useState(false) - const [name, setName] = React.useState(user.name) + const [name, setName] = React.useState(username) const [password, setPassword] = React.useState('') - const usernameHash = hash(user.name) - const salt = storeCredentials[usernameHash]?.passwordSalt + const usernameHash = hash(username) + const userCredentials = storeCredentials[usernameHash] - if (!salt) { + if (!userCredentials) { // TODO_ALEX: handle this case, navigate away? } + const { storeSalt, verificationSalt, passwordHash } = userCredentials + + const enteredPassword = _.toLower(password).trim() + const enteredPasswordHash = hash(enteredPassword + verificationSalt) + return ( @@ -68,27 +73,26 @@ export function PasswordRequestScreen() { { - const trimmedPassword = _.toLower(password).trim() setLoading(true) - if (trimmedPassword === user.password && name === user.name) { + if (enteredPasswordHash === passwordHash && name === username) { setNameError(false) setPasswordError(false) setValid(true) const keys = { key: usernameHash, - secretKey: hash(trimmedPassword + salt), + secretKey: hash(enteredPassword + storeSalt), } dispatch(actions.setStoreKeys(keys)) requestAnimationFrame(() => { navigateAndReset('StoreSwitchStack', null) }) - } else if (trimmedPassword === user.password && name !== user.name) { + } else if (enteredPasswordHash === passwordHash && name !== username) { setLoading(false) setPasswordError(false) setNameError(true) - } else if (trimmedPassword !== user.password && name === user.name) { + } else if (enteredPasswordHash !== passwordHash && name === username) { setLoading(false) setNameError(false) setPasswordError(true) diff --git a/packages/components/src/screens/SplashScreen.tsx b/packages/components/src/screens/SplashScreen.tsx index c395018a9..fd6c6e702 100644 --- a/packages/components/src/screens/SplashScreen.tsx +++ b/packages/components/src/screens/SplashScreen.tsx @@ -20,6 +20,7 @@ import { useSelector } from '../redux/useSelector' export function SplashScreen() { const dispatch = useDispatch() const user: any = useSelector(selectors.currentUserSelector) + const lastLoggedInUsername: any = useSelector(selectors.lastLoggedInUsernameSelector) const Alert = useAlert() const locale = useSelector(selectors.currentLocaleSelector) @@ -73,7 +74,7 @@ export function SplashScreen() { navigateAndReset('OnboardingScreen', null) return } - if (user) { + if (lastLoggedInUsername) { if (hasPasswordRequestOn) { navigateAndReset('PasswordRequestScreen', null) return From 2ed61e15176d63c6fb89e64505ced3c8877f4aa0 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 8 Jan 2024 15:12:53 +0800 Subject: [PATCH 42/83] Rename splash component --- packages/components/src/navigators/AppNavigator.tsx | 6 +++--- .../src/redux/{StoreSwitch.tsx => StoreSwitchSplash.tsx} | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename packages/components/src/redux/{StoreSwitch.tsx => StoreSwitchSplash.tsx} (92%) diff --git a/packages/components/src/navigators/AppNavigator.tsx b/packages/components/src/navigators/AppNavigator.tsx index 1392f27d1..4b70d48d3 100644 --- a/packages/components/src/navigators/AppNavigator.tsx +++ b/packages/components/src/navigators/AppNavigator.tsx @@ -28,11 +28,11 @@ import { FindHelpScreen } from '../screens/FindHelpScreen' import { PasswordRequestScreen } from '../screens/PasswordRequestScreen' import { VideoScreen } from '../screens/VideoScreen' import { VideosScreen } from '../screens/VideosScreen' -import { StoreSwitch } from '../redux/StoreSwitch' +import { StoreSwitchSplash } from '../redux/StoreSwitchSplash' const StoreSwitchStack = createStackNavigator( - { StoreSwitch }, - { headerMode: 'none', initialRouteName: 'StoreSwitch' }, + { StoreSwitchSplash }, + { headerMode: 'none', initialRouteName: 'StoreSwitchSplash' }, ) const TutorialFirstStack = createStackNavigator( diff --git a/packages/components/src/redux/StoreSwitch.tsx b/packages/components/src/redux/StoreSwitchSplash.tsx similarity index 92% rename from packages/components/src/redux/StoreSwitch.tsx rename to packages/components/src/redux/StoreSwitchSplash.tsx index ab119fa0a..77f039607 100644 --- a/packages/components/src/redux/StoreSwitch.tsx +++ b/packages/components/src/redux/StoreSwitchSplash.tsx @@ -3,7 +3,7 @@ import { SimpleSplashScreen } from '../screens/SplashScreen' import { navigateAndReset } from '../services/navigationService' import { useStoreCoordinator } from './StoreCoordinator' -export function StoreSwitch() { +export function StoreSwitchSplash() { const { switchStore, switchComplete } = useStoreCoordinator() React.useEffect(() => { From a88b91168498373421c3e012d74bf6946af5f89c Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 8 Jan 2024 16:47:31 +0800 Subject: [PATCH 43/83] Refactor password formatting --- .../redux/reducers/authReducer.test.ts | 19 +++++++++-------- .../src/redux/reducers/accessReducer.ts | 4 ++-- .../components/src/redux/sagas/authSaga.ts | 4 ++-- .../src/screens/EditProfileScreen.tsx | 21 ++++++++++--------- .../src/screens/PasswordRequestScreen.tsx | 4 ++-- .../src/screens/authScreen/Login.tsx | 3 ++- .../src/screens/authScreen/SignUp.tsx | 5 +++-- .../authScreen/deleteAccount/AskPassword.tsx | 3 ++- .../forgetPassword/AskNewPassword.tsx | 7 ++++--- .../authScreen/signUp/AskUserInformation.tsx | 2 +- packages/components/src/services/auth.ts | 11 ++++++++++ packages/components/src/services/hash.ts | 5 ----- 12 files changed, 50 insertions(+), 38 deletions(-) create mode 100644 packages/components/src/services/auth.ts delete mode 100644 packages/components/src/services/hash.ts diff --git a/packages/components/__tests__/redux/reducers/authReducer.test.ts b/packages/components/__tests__/redux/reducers/authReducer.test.ts index 9f4b0ec7f..83a3174ee 100644 --- a/packages/components/__tests__/redux/reducers/authReducer.test.ts +++ b/packages/components/__tests__/redux/reducers/authReducer.test.ts @@ -5,6 +5,7 @@ import configureStore from 'redux-mock-store' import _ from 'lodash' import * as actions from '../../../src/redux/actions' import { authReducer } from '../../../src/redux/reducers/authReducer' +import { formatPassword } from '../../../src/services/auth' const middleWares = [] const mockStore = configureStore(middleWares) @@ -20,9 +21,9 @@ describe('authReducer', () => { location: 'Urban', country: 'ZA', province: '', - password: _.toLower('00AAaa').trim(), + password: formatPassword('00AAaa'), secretQuestion: 'favourite_teacher', - secretAnswer: _.toLower('secret_answer').trim(), + secretAnswer: formatPassword('secret_answer'), } it('returns the initial state', () => { @@ -40,14 +41,14 @@ describe('authReducer', () => { const expectedType = `CREATE_ACCOUNT_REQUEST` expect(scopedActions[0].type).toEqual(expectedType) }) - it('Login As guest account', () => { - const action = actions.loginSuccessAsGuestAccount(mockPayload) - const newStore = authReducer(undefined, action) - // Dispatch the action + // it('Login As guest account', () => { + // const action = actions.loginSuccessAsGuestAccount(mockPayload) + // const newStore = authReducer(undefined, action) + // // Dispatch the action - expect(newStore?.user?.name).toEqual(mockPayload.name) - expect(newStore?.user?.isGuest).toEqual(true) - }) + // expect(newStore?.user?.name).toEqual(mockPayload.name) + // expect(newStore?.user?.isGuest).toEqual(true) + // }) it('Login Out guest account', () => { const action = actions.logout() const newStore = authReducer(undefined, action) diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index b4c50b2e0..d370dcc22 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -1,4 +1,4 @@ -import { hash } from '../../services/hash' +import { formatPassword, hash } from '../../services/auth' import { Actions } from '../types' import { v4 as uuidv4 } from 'uuid' import _ from 'lodash' @@ -27,7 +27,7 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt const storeSalt = uuidv4() const verificationSalt = uuidv4() const usernameHash = hash(action.payload.user.name) - const password = _.toLower(action.payload.user.password).trim() + const password = formatPassword(action.payload.user.password) const passwordHash = hash(password + verificationSalt) return { diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index 8dcfb7986..ee41022d3 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -12,8 +12,8 @@ import { PredictionState } from '../../prediction' import moment from 'moment' import { closeOutTTs } from '../../services/textToSpeech' import { fetchNetworkConnectionStatus } from '../../services/network' -import { hash } from '../../services/hash' import _ from 'lodash' +import { formatPassword, hash } from '../../services/auth' // unwrap promise type Await = T extends Promise ? U : T @@ -160,7 +160,7 @@ function* onLoginSuccess(action: ExtractActionFromActionType<'LOGIN_SUCCESS'>) { // Cant just return because they could have online account from another device that they're logging in to } - const password = _.toLower(action.payload.user.password) + const password = formatPassword(action.payload.user.password) const keys = { key: usernameHash, diff --git a/packages/components/src/screens/EditProfileScreen.tsx b/packages/components/src/screens/EditProfileScreen.tsx index 695fcb647..9816a7a68 100644 --- a/packages/components/src/screens/EditProfileScreen.tsx +++ b/packages/components/src/screens/EditProfileScreen.tsx @@ -22,6 +22,7 @@ import { TextInput } from '../components/common/TextInput' import { VerticalSelectBox } from '../components/common/VerticalSelectBox' import { translate } from '../i18n' import _ from 'lodash' +import { formatPassword } from '../services/auth' const deviceWidth = Dimensions.get('window').width const minPasswordLength = 1 @@ -131,7 +132,7 @@ export function EditProfileScreen() { } const tryToChangeSecretAnswer = async () => { - const hasSecretAnswerChanged = secretAnswer !== '' && _.toLower(oldSecretAnswer).trim() !== '' + const hasSecretAnswerChanged = secretAnswer !== '' && formatPassword(oldSecretAnswer) !== '' if (!hasSecretAnswerChanged) { return null } @@ -139,13 +140,13 @@ export function EditProfileScreen() { try { await httpClient.editUserSecretAnswer({ appToken, - previousSecretAnswer: _.toLower(oldSecretAnswer).trim(), - nextSecretAnswer: _.toLower(secretAnswer).trim(), + previousSecretAnswer: formatPassword(oldSecretAnswer), + nextSecretAnswer: formatPassword(secretAnswer), }) dispatch( actions.editUser({ - secretAnswer: _.toLower(secretAnswer).trim(), + secretAnswer: formatPassword(secretAnswer), }), ) setSecretAnswer('') @@ -177,13 +178,13 @@ export function EditProfileScreen() { try { await httpClient.resetPassword({ name, - secretAnswer: _.toLower(secretAnswer).trim(), - password: _.toLower(password).trim(), + secretAnswer: formatPassword(secretAnswer), + password: formatPassword(password), }) dispatch( actions.editUser({ - password: _.toLower(password).trim(), + password: formatPassword(password), }), ) } catch (err) { @@ -196,9 +197,9 @@ export function EditProfileScreen() { setIsVisible(false) // for non-logged user, save the changes locally immediately if (!appToken) { - const hasSecretAnswerChanged = secretAnswer !== '' && _.toLower(oldSecretAnswer).trim() !== '' + const hasSecretAnswerChanged = secretAnswer !== '' && formatPassword(oldSecretAnswer) !== '' if (hasSecretAnswerChanged) { - if (_.toLower(oldSecretAnswer).trim() !== _.toLower(currentUser.secretAnswer).trim()) { + if (formatPassword(oldSecretAnswer) !== formatPassword(currentUser.secretAnswer)) { showAcceptAlert(translate('wrong_old_secret_answer')) return } @@ -213,7 +214,7 @@ export function EditProfileScreen() { location, secretQuestion, secretAnswer: - secretAnswer === '' ? currentUser.secretAnswer : _.toLower(secretAnswer).trim(), + secretAnswer === '' ? currentUser.secretAnswer : formatPassword(secretAnswer), }), ) BackOneScreen() diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index fdb36f160..8537eeef3 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -12,7 +12,7 @@ import { useSelector } from '../redux/useSelector' import { KeyboardAwareAvoidance } from '../components/common/KeyboardAwareAvoidance' import { SpinLoader } from '../components/common/SpinLoader' import _ from 'lodash' -import { hash } from '../services/hash' +import { formatPassword, hash } from '../services/auth' export function PasswordRequestScreen() { const dispatch = useDispatch() @@ -34,7 +34,7 @@ export function PasswordRequestScreen() { const { storeSalt, verificationSalt, passwordHash } = userCredentials - const enteredPassword = _.toLower(password).trim() + const enteredPassword = formatPassword(password) const enteredPasswordHash = hash(enteredPassword + verificationSalt) return ( diff --git a/packages/components/src/screens/authScreen/Login.tsx b/packages/components/src/screens/authScreen/Login.tsx index e3e16e43d..d913412af 100644 --- a/packages/components/src/screens/authScreen/Login.tsx +++ b/packages/components/src/screens/authScreen/Login.tsx @@ -7,6 +7,7 @@ import * as actions from '../../redux/actions' import { useSelector } from '../../redux/useSelector' import { SpinLoader } from '../../components/common/SpinLoader' import _ from 'lodash' +import { formatPassword } from '../../services/auth' export function Login() { const dispatch = useDispatch() @@ -46,7 +47,7 @@ export function Login() { onPress={() => { setLoading(true) requestAnimationFrame(() => { - dispatch(actions.loginRequest({ name, password: _.toLower(password).trim() })) + dispatch(actions.loginRequest({ name, password: formatPassword(password) })) }) }} > diff --git a/packages/components/src/screens/authScreen/SignUp.tsx b/packages/components/src/screens/authScreen/SignUp.tsx index c4cbbfe87..414b37ff7 100644 --- a/packages/components/src/screens/authScreen/SignUp.tsx +++ b/packages/components/src/screens/authScreen/SignUp.tsx @@ -11,6 +11,7 @@ import { navigate } from '../../services/navigationService' import * as actions from '../../redux/actions' import _ from 'lodash' import { FAST_SIGN_UP } from '../../config' +import { formatPassword } from '../../services/auth' const randomLetters = () => { const letters = 'abcdefghijklmnopqrstuvwxyz' @@ -72,9 +73,9 @@ export function SignUp({ heightInner }) { location, country, province, - password: _.toLower(password).trim(), + password: formatPassword(password), secretQuestion: selectedQuestion, - secretAnswer: _.toLower(answer).trim(), + secretAnswer: formatPassword(answer), }), ) navigate('AvatarAndThemeScreen', { signingUp: true, newUser: { gender } }) // @TODO: wait on isCreatingAccount diff --git a/packages/components/src/screens/authScreen/deleteAccount/AskPassword.tsx b/packages/components/src/screens/authScreen/deleteAccount/AskPassword.tsx index e94432b6f..fc69342b8 100644 --- a/packages/components/src/screens/authScreen/deleteAccount/AskPassword.tsx +++ b/packages/components/src/screens/authScreen/deleteAccount/AskPassword.tsx @@ -6,6 +6,7 @@ import { useMultiStepForm, formActions } from '../../../components/common/MultiS import { DeleteFormLayout } from './DeleteFormLayout' import { httpClient } from '../../../services/HttpClient' import _ from 'lodash' +import { formatPassword } from '../../../services/auth' export function AskPassword({ step }) { const [{ app: state }, dispatch] = useMultiStepForm() @@ -13,7 +14,7 @@ export function AskPassword({ step }) { try { await httpClient.deleteUserFromPassword({ name: state.name, - password: _.toLower(state.password).trim(), + password: formatPassword(state.password), }) dispatch({ formAction: formActions.goToStep('completed') }) } catch (error) { diff --git a/packages/components/src/screens/authScreen/forgetPassword/AskNewPassword.tsx b/packages/components/src/screens/authScreen/forgetPassword/AskNewPassword.tsx index 85773d4df..27dcc3277 100644 --- a/packages/components/src/screens/authScreen/forgetPassword/AskNewPassword.tsx +++ b/packages/components/src/screens/authScreen/forgetPassword/AskNewPassword.tsx @@ -4,6 +4,7 @@ import { useMultiStepForm, formActions } from '../../../components/common/MultiS import { httpClient } from '../../../services/HttpClient' import { ForgotPasswordFormLayout } from './ForgotPasswordFormLayout' import _ from 'lodash' +import { formatPassword } from '../../../services/auth' export function AskNewPassword({ step }) { const [{ app: state }, dispatch] = useMultiStepForm() @@ -22,8 +23,8 @@ export function AskNewPassword({ step }) { try { await httpClient.resetPassword({ name: state.name, - secretAnswer: _.toLower(state.secretAnswer).trim(), - password: _.toLower(state.password).trim(), + secretAnswer: formatPassword(state.secretAnswer), + password: formatPassword(state.password), }) dispatch({ formAction: formActions.goToStep('completed') }) @@ -40,7 +41,7 @@ export function AskNewPassword({ step }) { dispatch({ type: 'change-password', password })} + onChange={(password) => dispatch({ type: 'change-password', password })} label="password" secureTextEntry={true} isValid={passwordIsValid} diff --git a/packages/components/src/screens/authScreen/signUp/AskUserInformation.tsx b/packages/components/src/screens/authScreen/signUp/AskUserInformation.tsx index cd88277bc..f19255e09 100644 --- a/packages/components/src/screens/authScreen/signUp/AskUserInformation.tsx +++ b/packages/components/src/screens/authScreen/signUp/AskUserInformation.tsx @@ -11,7 +11,7 @@ import { useDebounce } from '../../../hooks/useDebounce' import { formHeights } from './FormHeights' import { translate } from '../../../i18n' import { useSelector } from '../../../redux/useSelector' -import { hash } from '../../../services/hash' +import { hash } from '../../../services/auth' import * as selectors from '../../../redux/selectors' export function AskUserInformation({ step, heightInner }) { diff --git a/packages/components/src/services/auth.ts b/packages/components/src/services/auth.ts new file mode 100644 index 000000000..436691480 --- /dev/null +++ b/packages/components/src/services/auth.ts @@ -0,0 +1,11 @@ +import { sha256 } from 'js-sha256' +import _ from 'lodash' + +export const hash = (str: string) => { + return sha256(str) +} + +export const formatPassword = (password: string) => { + // I don't agree with setting the password to lowercase but it has been like this for a long time, changing it now could lock out users + return _.toLower(password).trim() +} diff --git a/packages/components/src/services/hash.ts b/packages/components/src/services/hash.ts deleted file mode 100644 index 407086058..000000000 --- a/packages/components/src/services/hash.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { sha256 } from 'js-sha256' - -export const hash = (str: string) => { - return sha256(str) -} From d819087021c385c6be0ec3ebf0f620ae9bedfe73 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 8 Jan 2024 17:14:17 +0800 Subject: [PATCH 44/83] Rename redux keys state & refactor --- .../components/src/redux/StoreCoordinator.tsx | 10 +++---- .../src/redux/actions/authActions.ts | 12 +++------ .../components/src/redux/reducers/index.ts | 4 +-- .../{keysReducer.ts => storeSwitchReducer.ts} | 26 +++++++------------ .../components/src/redux/sagas/authSaga.ts | 4 +-- .../src/screens/PasswordRequestScreen.tsx | 2 +- 6 files changed, 22 insertions(+), 36 deletions(-) rename packages/components/src/redux/reducers/{keysReducer.ts => storeSwitchReducer.ts} (55%) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index e46b373e0..87f13036b 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -23,13 +23,13 @@ const StoreCoordinatorContext = React.createContext({ }) const primaryStoreBlacklist = [ - 'keys', // Not persisted for security + 'storeSwitch', // Not persisted for security 'content', // Moved to async storage 'auth', // Persisted in secure userStore 'prediction', // Persisted in secure userStore ] const userStoreBlacklist = [ - 'keys', // Not persisted for security + 'storeSwitch', // Not persisted for security 'content', // Moved to async storage 'access', // Not required after store switch ] @@ -104,8 +104,8 @@ export function StoreCoordinator({ children }) { const switchStore = () => { const primaryState = store.getState() as ReduxPersistState - const keys = primaryState?.keys?.keys - const shouldMigrateData = primaryState?.keys?.shouldMigrateData + const keys = primaryState?.storeSwitch?.keys + const shouldMigrateData = primaryState?.storeSwitch?.shouldMigrateData if (!keys) { return // ERROR @@ -167,7 +167,7 @@ export function StoreCoordinator({ children }) { } const currentState = store.getState() as ReduxPersistState - const migrationComplete = currentState?.keys.migrationComplete + const migrationComplete = currentState?.storeSwitch.migrationComplete if (migrationComplete) { dispatch({ type: 'complete_migration' }) diff --git a/packages/components/src/redux/actions/authActions.ts b/packages/components/src/redux/actions/authActions.ts index 53b0cd067..da138deb0 100644 --- a/packages/components/src/redux/actions/authActions.ts +++ b/packages/components/src/redux/actions/authActions.ts @@ -1,4 +1,5 @@ import { createAction } from '../helpers' +import { StoreKeys } from '../reducers/storeSwitchReducer' export function loginRequest({ name, password }) { return createAction('LOGIN_REQUEST', { name, password }) @@ -38,15 +39,8 @@ export function loginSuccess({ }) } -export function loginOfflineSuccess(payload: { - keys: { key: string; secretKey: string } - shouldMigrateData: boolean -}) { - return createAction('LOGIN_OFFLINE_SUCCESS', payload) -} - -export function setStoreKeys(payload: { key: string; secretKey: string }) { - return createAction('SET_STORE_KEYS', payload) +export function initiateStoreSwitch(payload: { keys: StoreKeys; shouldMigrateData: boolean }) { + return createAction('INITIATE_STORE_SWITCH', payload) } export function loginFailure({ error }) { diff --git a/packages/components/src/redux/reducers/index.ts b/packages/components/src/redux/reducers/index.ts index ad1177388..3a4ef5f43 100644 --- a/packages/components/src/redux/reducers/index.ts +++ b/packages/components/src/redux/reducers/index.ts @@ -10,15 +10,15 @@ import { authReducer } from './authReducer' import { contentReducer } from './contentReducer' import { predictionReducer } from './predictionReducer' import { accessReducer } from './accessReducer' -import { keysReducer } from './keysReducer' +import { storeSwitchReducer } from './storeSwitchReducer' export const exportReducerNames = ['app', 'prediction'] const reducer = combineReducers( syncReducers( { - keys: keysReducer, access: accessReducer, + storeSwitch: storeSwitchReducer, analytics: analyticsReducer, answer: answerReducer, app: appReducer, diff --git a/packages/components/src/redux/reducers/keysReducer.ts b/packages/components/src/redux/reducers/storeSwitchReducer.ts similarity index 55% rename from packages/components/src/redux/reducers/keysReducer.ts rename to packages/components/src/redux/reducers/storeSwitchReducer.ts index bd097b069..1f52e6edb 100644 --- a/packages/components/src/redux/reducers/keysReducer.ts +++ b/packages/components/src/redux/reducers/storeSwitchReducer.ts @@ -1,25 +1,23 @@ import { Actions } from '../types' -import _ from 'lodash' -export interface KeysState { - keys: - | { - key: string - secretKey: string - } - | undefined +export interface StoreKeys { + key: string + secretKey: string +} +export interface StoreSwitchState { + keys: StoreKeys | undefined shouldMigrateData: boolean migrationComplete: boolean } -const initialState: KeysState = { +const initialState: StoreSwitchState = { keys: undefined, shouldMigrateData: true, migrationComplete: false, } -export function keysReducer(state = initialState, action: Actions): KeysState { +export function storeSwitchReducer(state = initialState, action: Actions): StoreSwitchState { switch (action.type) { case 'MIGRATE_STORE': return { @@ -27,13 +25,7 @@ export function keysReducer(state = initialState, action: Actions): KeysState { migrationComplete: true, } - case 'SET_STORE_KEYS': - return { - ...state, - keys: action.payload, - } - - case 'LOGIN_OFFLINE_SUCCESS': + case 'INITIATE_STORE_SWITCH': return { ...state, keys: action.payload.keys, diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index ee41022d3..f4ac4576a 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -132,7 +132,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { if (credential) { yield put( - actions.loginOfflineSuccess({ + actions.initiateStoreSwitch({ keys: { key: usernameHash, secretKey: hash(password + credential.storeSalt), @@ -167,7 +167,7 @@ function* onLoginSuccess(action: ExtractActionFromActionType<'LOGIN_SUCCESS'>) { secretKey: hash(password + salt), } - yield put(actions.setStoreKeys(keys)) + yield put(actions.initiateStoreSwitch({ keys, shouldMigrateData: false })) } function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACCOUNT_REQUEST'>) { diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index 8537eeef3..17917a758 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -83,7 +83,7 @@ export function PasswordRequestScreen() { key: usernameHash, secretKey: hash(enteredPassword + storeSalt), } - dispatch(actions.setStoreKeys(keys)) + dispatch(actions.initiateStoreSwitch({ keys, shouldMigrateData: false })) requestAnimationFrame(() => { navigateAndReset('StoreSwitchStack', null) From 5dfbf1384c9d9c6601ccba4adec136805540d849 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 8 Jan 2024 17:40:59 +0800 Subject: [PATCH 45/83] Migrate data depending on whether store exists --- packages/components/src/redux/StoreCoordinator.tsx | 13 ++++++++++--- .../components/src/redux/actions/accessActions.ts | 5 +++++ .../components/src/redux/actions/authActions.ts | 2 +- packages/components/src/redux/actions/index.ts | 1 + .../components/src/redux/reducers/accessReducer.ts | 14 ++++++++++++++ .../src/redux/reducers/storeSwitchReducer.ts | 3 --- packages/components/src/redux/sagas/authSaga.ts | 3 +-- .../src/screens/PasswordRequestScreen.tsx | 2 +- 8 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 packages/components/src/redux/actions/accessActions.ts diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index 87f13036b..5df97138b 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -105,12 +105,19 @@ export function StoreCoordinator({ children }) { const switchStore = () => { const primaryState = store.getState() as ReduxPersistState const keys = primaryState?.storeSwitch?.keys - const shouldMigrateData = primaryState?.storeSwitch?.shouldMigrateData + const accessState = primaryState?.access if (!keys) { return // ERROR } + const usernameHash = keys.key + const storeExists = accessState.storeCredentials?.[usernameHash]?.storeExists + + if (!storeExists) { + store.dispatch(actions.setStoreExists({ usernameHash })) + } + const userStore = configureStore({ key: keys.key, secretKey: keys.secretKey, @@ -124,8 +131,8 @@ export function StoreCoordinator({ children }) { payload: { redux: userStore, storeStateSnapshot: primaryState, - shouldMigrate: shouldMigrateData, - switchComplete: !shouldMigrateData, + shouldMigrate: !storeExists, + switchComplete: storeExists, }, }) } diff --git a/packages/components/src/redux/actions/accessActions.ts b/packages/components/src/redux/actions/accessActions.ts new file mode 100644 index 000000000..aca2846d3 --- /dev/null +++ b/packages/components/src/redux/actions/accessActions.ts @@ -0,0 +1,5 @@ +import { createAction } from '../helpers' + +export function setStoreExists(payload: { usernameHash: string }) { + return createAction('SET_STORE_EXISTS', payload) +} diff --git a/packages/components/src/redux/actions/authActions.ts b/packages/components/src/redux/actions/authActions.ts index da138deb0..c709559c9 100644 --- a/packages/components/src/redux/actions/authActions.ts +++ b/packages/components/src/redux/actions/authActions.ts @@ -39,7 +39,7 @@ export function loginSuccess({ }) } -export function initiateStoreSwitch(payload: { keys: StoreKeys; shouldMigrateData: boolean }) { +export function initiateStoreSwitch(payload: { keys: StoreKeys }) { return createAction('INITIATE_STORE_SWITCH', payload) } diff --git a/packages/components/src/redux/actions/index.ts b/packages/components/src/redux/actions/index.ts index 82e9e23f2..d19d0aee9 100644 --- a/packages/components/src/redux/actions/index.ts +++ b/packages/components/src/redux/actions/index.ts @@ -1,6 +1,7 @@ import { createAction } from '../helpers' import { ReduxState } from '../reducers' +export * from './accessActions' export * from './analyticsActions' export * from './answerActions' export * from './appActions' diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index d370dcc22..b870861c1 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -6,6 +6,7 @@ import _ from 'lodash' export interface AccessState { storeCredentials: { [usernameHash: string]: { + storeExists: boolean storeSalt: string verificationSalt: string passwordHash: string @@ -36,6 +37,7 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt storeCredentials: { ...state.storeCredentials, [usernameHash]: { + storeExists: false, storeSalt, verificationSalt, passwordHash, @@ -44,6 +46,18 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt } } + case 'SET_STORE_EXISTS': + return { + ...state, + storeCredentials: { + ...state.storeCredentials, + [action.payload.usernameHash]: { + ...state.storeCredentials[action.payload.usernameHash], + storeExists: true, + }, + }, + } + case 'LOGIN_SUCCESS': { // TODO_ALEX Can just update keys here instead of via saga ? return state diff --git a/packages/components/src/redux/reducers/storeSwitchReducer.ts b/packages/components/src/redux/reducers/storeSwitchReducer.ts index 1f52e6edb..9f6174fb6 100644 --- a/packages/components/src/redux/reducers/storeSwitchReducer.ts +++ b/packages/components/src/redux/reducers/storeSwitchReducer.ts @@ -7,13 +7,11 @@ export interface StoreKeys { export interface StoreSwitchState { keys: StoreKeys | undefined - shouldMigrateData: boolean migrationComplete: boolean } const initialState: StoreSwitchState = { keys: undefined, - shouldMigrateData: true, migrationComplete: false, } @@ -29,7 +27,6 @@ export function storeSwitchReducer(state = initialState, action: Actions): Store return { ...state, keys: action.payload.keys, - shouldMigrateData: action.payload.shouldMigrateData, } default: diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index f4ac4576a..2d4d8f7de 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -137,7 +137,6 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { key: usernameHash, secretKey: hash(password + credential.storeSalt), }, - shouldMigrateData: false, }), ) } else { @@ -167,7 +166,7 @@ function* onLoginSuccess(action: ExtractActionFromActionType<'LOGIN_SUCCESS'>) { secretKey: hash(password + salt), } - yield put(actions.initiateStoreSwitch({ keys, shouldMigrateData: false })) + yield put(actions.initiateStoreSwitch({ keys })) } function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACCOUNT_REQUEST'>) { diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index 17917a758..00dbd4b45 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -83,7 +83,7 @@ export function PasswordRequestScreen() { key: usernameHash, secretKey: hash(enteredPassword + storeSalt), } - dispatch(actions.initiateStoreSwitch({ keys, shouldMigrateData: false })) + dispatch(actions.initiateStoreSwitch({ keys })) requestAnimationFrame(() => { navigateAndReset('StoreSwitchStack', null) From 82215b9b45400e19d3fa1c396a2b5840570016a0 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 8 Jan 2024 18:05:02 +0800 Subject: [PATCH 46/83] Save creds for existing user on new device --- .../src/redux/actions/accessActions.ts | 34 +++++++++++++++++++ .../src/redux/reducers/accessReducer.ts | 1 + .../components/src/redux/sagas/authSaga.ts | 12 ++++--- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/packages/components/src/redux/actions/accessActions.ts b/packages/components/src/redux/actions/accessActions.ts index aca2846d3..595c901d2 100644 --- a/packages/components/src/redux/actions/accessActions.ts +++ b/packages/components/src/redux/actions/accessActions.ts @@ -1,5 +1,39 @@ import { createAction } from '../helpers' +export function saveLocalCredentials({ + appToken, + user: { + id, + name, + password, + dateOfBirth, + gender, + location, + country, + province, + secretQuestion, + secretAnswer, + isGuest, + }, +}) { + return createAction('SAVE_LOCAL_CREDENTIALS', { + appToken, + user: { + id, + name, + dateOfBirth, + gender, + location, + country, + province, + password, + secretQuestion, + secretAnswer, + isGuest, + }, + }) +} + export function setStoreExists(payload: { usernameHash: string }) { return createAction('SET_STORE_EXISTS', payload) } diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index b870861c1..558313001 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -24,6 +24,7 @@ const initialState = (): AccessState => ({ export function accessReducer(state = initialState(), action: Actions): AccessState { switch (action.type) { + case 'SAVE_LOCAL_CREDENTIALS': case 'CREATE_ACCOUNT_SUCCESS': { const storeSalt = uuidv4() const verificationSalt = uuidv4() diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index 2d4d8f7de..aad1449a2 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -150,15 +150,17 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { } function* onLoginSuccess(action: ExtractActionFromActionType<'LOGIN_SUCCESS'>) { - const storeCredentials = yield select(selectors.storeCredentialsSelector) + let storeCredentials = yield select(selectors.storeCredentialsSelector) const usernameHash = hash(action.payload.user.name) - const salt = storeCredentials[usernameHash]?.storeSalt - if (!salt) { - // TODO_ALEX ??? - // Cant just return because they could have online account from another device that they're logging in to + if (!storeCredentials[usernameHash]) { + // Can occur when the account was created online via a different device + yield put(actions.saveLocalCredentials(action.payload)) + storeCredentials = yield select(selectors.storeCredentialsSelector) + return } + const salt = storeCredentials[usernameHash].storeSalt const password = formatPassword(action.payload.user.password) const keys = { From f8124c33904cb34e3bc1e5ce2e9d76b62e378b7b Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 8 Jan 2024 19:43:03 +0800 Subject: [PATCH 47/83] Clear last login from PasswordRequestScreen --- .../components/src/redux/actions/accessActions.ts | 4 ++++ .../src/redux/reducers/accessReducer.ts | 15 +++++++++++---- packages/components/src/redux/sagas/authSaga.ts | 5 +++++ .../src/screens/PasswordRequestScreen.tsx | 2 +- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/packages/components/src/redux/actions/accessActions.ts b/packages/components/src/redux/actions/accessActions.ts index 595c901d2..88a23ce3a 100644 --- a/packages/components/src/redux/actions/accessActions.ts +++ b/packages/components/src/redux/actions/accessActions.ts @@ -37,3 +37,7 @@ export function saveLocalCredentials({ export function setStoreExists(payload: { usernameHash: string }) { return createAction('SET_STORE_EXISTS', payload) } + +export function clearLastLogin() { + return createAction('CLEAR_LAST_LOGIN') +} diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index 558313001..71e48894c 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -59,10 +59,17 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt }, } - case 'LOGIN_SUCCESS': { - // TODO_ALEX Can just update keys here instead of via saga ? - return state - } + case 'LOGIN_SUCCESS': + return { + ...state, + lastLoggedInUsername: action.payload.user.name, + } + + case 'CLEAR_LAST_LOGIN': + return { + ...state, + lastLoggedInUsername: undefined, + } default: return state diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index aad1449a2..c5bc661a8 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -332,6 +332,10 @@ function* onLogoutRequest() { yield put(actions.logout()) } +function* onClearLastLogin() { + yield call(navigateAndReset, 'LoginStack', null) +} + function* onJourneyCompletion(action: ExtractActionFromActionType<'JOURNEY_COMPLETION'>) { const { data } = action.payload const currentUser = yield select(selectors.currentUserSelector) @@ -371,6 +375,7 @@ export function* authSaga() { yield all([ takeLatest(REHYDRATE, onRehydrate), takeLatest('LOGOUT_REQUEST', onLogoutRequest), + takeLatest('CLEAR_LAST_LOGIN', onClearLastLogin), takeLatest('LOGIN_REQUEST', onLoginRequest), takeLatest('LOGIN_SUCCESS', onLoginSuccess), takeLatest('DELETE_ACCOUNT_REQUEST', onDeleteAccountRequest), diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index 00dbd4b45..3a368361f 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -111,7 +111,7 @@ export function PasswordRequestScreen() { { - dispatch(actions.logoutRequest()) + dispatch(actions.clearLastLogin()) }} > Date: Mon, 8 Jan 2024 21:11:25 +0800 Subject: [PATCH 48/83] Switch stores on logout --- .../src/navigators/AppNavigator.tsx | 8 ++- .../components/src/redux/StoreCoordinator.tsx | 63 ++++++++++++++++--- .../src/redux/StoreSwitchSplash.tsx | 17 +++++ .../src/redux/actions/authActions.ts | 4 -- .../src/redux/reducers/authReducer.ts | 8 --- .../components/src/redux/reducers/index.ts | 13 +--- .../components/src/redux/sagas/authSaga.ts | 7 ++- 7 files changed, 84 insertions(+), 36 deletions(-) diff --git a/packages/components/src/navigators/AppNavigator.tsx b/packages/components/src/navigators/AppNavigator.tsx index 4b70d48d3..348db6086 100644 --- a/packages/components/src/navigators/AppNavigator.tsx +++ b/packages/components/src/navigators/AppNavigator.tsx @@ -28,13 +28,18 @@ import { FindHelpScreen } from '../screens/FindHelpScreen' import { PasswordRequestScreen } from '../screens/PasswordRequestScreen' import { VideoScreen } from '../screens/VideoScreen' import { VideosScreen } from '../screens/VideosScreen' -import { StoreSwitchSplash } from '../redux/StoreSwitchSplash' +import { StoreSwitchSplash, LogoutSplash } from '../redux/StoreSwitchSplash' const StoreSwitchStack = createStackNavigator( { StoreSwitchSplash }, { headerMode: 'none', initialRouteName: 'StoreSwitchSplash' }, ) +const LogOutStack = createStackNavigator( + { LogoutSplash }, + { headerMode: 'none', initialRouteName: 'LogoutSplash' }, +) + const TutorialFirstStack = createStackNavigator( { TutorialFirstScreen }, { headerMode: 'none', initialRouteName: 'TutorialFirstScreen' }, @@ -138,6 +143,7 @@ const MainStack = createBottomTabNavigator( const AppNavigator = createStackNavigator( { StoreSwitchStack, + LogOutStack, SplashScreen, OnboardingScreen, PasswordRequestScreen, diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index 5df97138b..e4898d58e 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -13,6 +13,8 @@ type ReduxPersistState = ReduxState & PersistPartial interface Context { switchStore: () => void switchComplete: boolean + logout: () => void + loggedOut: boolean } const StoreCoordinatorContext = React.createContext({ @@ -20,6 +22,10 @@ const StoreCoordinatorContext = React.createContext({ // }, switchComplete: false, + logout: () => { + // + }, + loggedOut: true, }) const primaryStoreBlacklist = [ @@ -34,19 +40,21 @@ const userStoreBlacklist = [ 'access', // Not required after store switch ] -const primaryStore = configureStore({ - key: 'primary', - secretKey: config.REDUX_ENCRYPT_KEY, - blacklist: primaryStoreBlacklist, - rootReducer, - rootSaga, -}) +const primaryStore = () => + configureStore({ + key: 'primary', + secretKey: config.REDUX_ENCRYPT_KEY, + blacklist: primaryStoreBlacklist, + rootReducer, + rootSaga, + }) interface State { redux: ReturnType storeStateSnapshot: ReduxPersistState | undefined shouldMigrate: boolean switchComplete: boolean + loggedOut: boolean } type Action = @@ -62,12 +70,19 @@ type Action = | { type: 'complete_migration' } + | { + type: 'logout_request' + } + | { + type: 'complete_logout' + } const initialState: State = { - redux: primaryStore, + redux: primaryStore(), storeStateSnapshot: undefined, shouldMigrate: false, switchComplete: false, + loggedOut: true, } function reducer(state: State, action: Action): State { @@ -88,6 +103,19 @@ function reducer(state: State, action: Action): State { shouldMigrate: false, switchComplete: true, } + + case 'logout_request': + return { + ...initialState, + redux: primaryStore(), + loggedOut: false, + } + + case 'complete_logout': + return { + ...state, + loggedOut: true, + } } } @@ -98,10 +126,12 @@ export function StoreCoordinator({ children }) { storeStateSnapshot, shouldMigrate, switchComplete, + loggedOut, }, dispatch, ] = React.useReducer(reducer, initialState) + // ===== Switch store ===== // const switchStore = () => { const primaryState = store.getState() as ReduxPersistState const keys = primaryState?.storeSwitch?.keys @@ -191,11 +221,28 @@ export function StoreCoordinator({ children }) { } }, [shouldMigrate]) + // ===== Log out ===== // + const logout = () => dispatch({ type: 'logout_request' }) + + React.useEffect(() => { + if (loggedOut) { + return + } + + setTimeout(() => { + // TODO: confirm store is ready + store.dispatch(actions.clearLastLogin()) + dispatch({ type: 'complete_logout' }) + }, 2000) + }, [loggedOut]) + return ( diff --git a/packages/components/src/redux/StoreSwitchSplash.tsx b/packages/components/src/redux/StoreSwitchSplash.tsx index 77f039607..eed8ec9ec 100644 --- a/packages/components/src/redux/StoreSwitchSplash.tsx +++ b/packages/components/src/redux/StoreSwitchSplash.tsx @@ -18,3 +18,20 @@ export function StoreSwitchSplash() { return } + +export function LogoutSplash() { + const { switchComplete, logout, loggedOut } = useStoreCoordinator() + + React.useEffect(() => { + logout() + }, []) + + React.useEffect(() => { + if (!loggedOut || switchComplete) { + return + } + navigateAndReset('LoginStack', null) + }, [loggedOut]) + + return +} diff --git a/packages/components/src/redux/actions/authActions.ts b/packages/components/src/redux/actions/authActions.ts index c709559c9..432accb0a 100644 --- a/packages/components/src/redux/actions/authActions.ts +++ b/packages/components/src/redux/actions/authActions.ts @@ -51,10 +51,6 @@ export function logoutRequest() { return createAction('LOGOUT_REQUEST') } -export function logout() { - return createAction('LOGOUT') -} - export function createAccountRequest({ id = null, name, diff --git a/packages/components/src/redux/reducers/authReducer.ts b/packages/components/src/redux/reducers/authReducer.ts index d3bf6df38..c7e000207 100644 --- a/packages/components/src/redux/reducers/authReducer.ts +++ b/packages/components/src/redux/reducers/authReducer.ts @@ -91,14 +91,6 @@ export function authReducer(state = initialState, action: Actions | RehydrateAct user: null, } - case 'LOGOUT': - return { - ...state, - appToken: null, - isLoggingIn: false, - user: null, - } - case 'SET_AUTH_ERROR': return { ...state, diff --git a/packages/components/src/redux/reducers/index.ts b/packages/components/src/redux/reducers/index.ts index 3a4ef5f43..fce639257 100644 --- a/packages/components/src/redux/reducers/index.ts +++ b/packages/components/src/redux/reducers/index.ts @@ -14,7 +14,7 @@ import { storeSwitchReducer } from './storeSwitchReducer' export const exportReducerNames = ['app', 'prediction'] -const reducer = combineReducers( +export const rootReducer = combineReducers( syncReducers( { access: accessReducer, @@ -31,15 +31,4 @@ const reducer = combineReducers( ), ) -export function rootReducer(state, action: Actions) { - switch (action.type) { - case 'LOGOUT': - // @ts-ignore - return reducer(_.pick(state, 'app', 'content', 'answer', 'access'), action) - - default: - return reducer(state, action) - } -} - export type ReduxState = ReturnType diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index c5bc661a8..50c577afa 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -305,7 +305,8 @@ function* onDeleteAccountRequest(action: ExtractActionFromActionType<'DELETE_ACC yield call(navigateAndReset, 'LoginStack', null) if (user) { - yield put(actions.logout()) + // TODO: TODO_ALEX + // yield put(actions.logout()) } } catch (err) { setLoading(false) @@ -328,8 +329,8 @@ function* onLogoutRequest() { }), ) yield put(actions.updateCompletedSurveys([])) // TODO_ALEX: survey - yield call(navigateAndReset, 'LoginStack', null) - yield put(actions.logout()) + + yield call(navigateAndReset, 'LogOutStack', null) } function* onClearLastLogin() { From 15f6fe0669722990642c1ac35bd88143c34ed6b1 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Tue, 9 Jan 2024 12:34:46 +0800 Subject: [PATCH 49/83] Delete store --- .../src/navigators/AppNavigator.tsx | 8 ++++- .../components/src/redux/StoreCoordinator.tsx | 30 ++++++++++++++++++- .../src/redux/StoreSwitchSplash.tsx | 17 +++++++++++ .../src/redux/actions/accessActions.ts | 4 +++ .../src/redux/reducers/accessReducer.ts | 6 ++++ .../components/src/redux/sagas/authSaga.ts | 16 ++++++---- 6 files changed, 74 insertions(+), 7 deletions(-) diff --git a/packages/components/src/navigators/AppNavigator.tsx b/packages/components/src/navigators/AppNavigator.tsx index 348db6086..fb9d86c4a 100644 --- a/packages/components/src/navigators/AppNavigator.tsx +++ b/packages/components/src/navigators/AppNavigator.tsx @@ -28,7 +28,7 @@ import { FindHelpScreen } from '../screens/FindHelpScreen' import { PasswordRequestScreen } from '../screens/PasswordRequestScreen' import { VideoScreen } from '../screens/VideoScreen' import { VideosScreen } from '../screens/VideosScreen' -import { StoreSwitchSplash, LogoutSplash } from '../redux/StoreSwitchSplash' +import { StoreSwitchSplash, LogoutSplash, DeleteAccountSplash } from '../redux/StoreSwitchSplash' const StoreSwitchStack = createStackNavigator( { StoreSwitchSplash }, @@ -40,6 +40,11 @@ const LogOutStack = createStackNavigator( { headerMode: 'none', initialRouteName: 'LogoutSplash' }, ) +const DeleteAccountStack = createStackNavigator( + { DeleteAccountSplash }, + { headerMode: 'none', initialRouteName: 'DeleteAccountSplash' }, +) + const TutorialFirstStack = createStackNavigator( { TutorialFirstScreen }, { headerMode: 'none', initialRouteName: 'TutorialFirstScreen' }, @@ -144,6 +149,7 @@ const AppNavigator = createStackNavigator( { StoreSwitchStack, LogOutStack, + DeleteAccountStack, SplashScreen, OnboardingScreen, PasswordRequestScreen, diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index e4898d58e..e0b95cec6 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -7,6 +7,7 @@ import { ReduxState, rootReducer } from './reducers' import { rootSaga } from './sagas' import * as actions from './actions' import { PersistPartial } from 'redux-persist' +import { hash } from '../services/auth' type ReduxPersistState = ReduxState & PersistPartial @@ -15,6 +16,7 @@ interface Context { switchComplete: boolean logout: () => void loggedOut: boolean + deleteStore: () => void } const StoreCoordinatorContext = React.createContext({ @@ -26,6 +28,9 @@ const StoreCoordinatorContext = React.createContext({ // }, loggedOut: true, + deleteStore: () => { + // + }, }) const primaryStoreBlacklist = [ @@ -55,6 +60,7 @@ interface State { shouldMigrate: boolean switchComplete: boolean loggedOut: boolean + userToDelete?: string // nameHash } type Action = @@ -72,6 +78,9 @@ type Action = } | { type: 'logout_request' + payload?: { + userToDelete: string + } } | { type: 'complete_logout' @@ -83,6 +92,7 @@ const initialState: State = { shouldMigrate: false, switchComplete: false, loggedOut: true, + userToDelete: undefined, } function reducer(state: State, action: Action): State { @@ -108,6 +118,7 @@ function reducer(state: State, action: Action): State { return { ...initialState, redux: primaryStore(), + userToDelete: action.payload?.userToDelete, loggedOut: false, } @@ -127,6 +138,7 @@ export function StoreCoordinator({ children }) { shouldMigrate, switchComplete, loggedOut, + userToDelete, }, dispatch, ] = React.useReducer(reducer, initialState) @@ -222,7 +234,8 @@ export function StoreCoordinator({ children }) { }, [shouldMigrate]) // ===== Log out ===== // - const logout = () => dispatch({ type: 'logout_request' }) + const logout = (usernameHash?: string) => + dispatch({ type: 'logout_request', payload: { userToDelete: usernameHash } }) React.useEffect(() => { if (loggedOut) { @@ -232,10 +245,24 @@ export function StoreCoordinator({ children }) { setTimeout(() => { // TODO: confirm store is ready store.dispatch(actions.clearLastLogin()) + if (userToDelete) { + store.dispatch(actions.deleteUserAccess({ usernameHash: userToDelete })) + } dispatch({ type: 'complete_logout' }) }, 2000) }, [loggedOut]) + // ===== Delete store ===== // + const deleteStore = () => { + const currentState = store.getState() as ReduxPersistState + const username = currentState?.auth?.user.name + const usernameHash = hash(username) + + persistor.purge().then(() => { + logout(usernameHash) + }) + } + return ( diff --git a/packages/components/src/redux/StoreSwitchSplash.tsx b/packages/components/src/redux/StoreSwitchSplash.tsx index eed8ec9ec..3c05daa81 100644 --- a/packages/components/src/redux/StoreSwitchSplash.tsx +++ b/packages/components/src/redux/StoreSwitchSplash.tsx @@ -35,3 +35,20 @@ export function LogoutSplash() { return } + +export function DeleteAccountSplash() { + const { switchComplete, deleteStore, loggedOut } = useStoreCoordinator() + + React.useEffect(() => { + deleteStore() + }, []) + + React.useEffect(() => { + if (!loggedOut || switchComplete) { + return + } + navigateAndReset('LoginStack', null) + }, [loggedOut]) + + return +} diff --git a/packages/components/src/redux/actions/accessActions.ts b/packages/components/src/redux/actions/accessActions.ts index 88a23ce3a..baf4113ce 100644 --- a/packages/components/src/redux/actions/accessActions.ts +++ b/packages/components/src/redux/actions/accessActions.ts @@ -41,3 +41,7 @@ export function setStoreExists(payload: { usernameHash: string }) { export function clearLastLogin() { return createAction('CLEAR_LAST_LOGIN') } + +export function deleteUserAccess(payload: { usernameHash: string }) { + return createAction('DELETE_USER_ACCESS', payload) +} diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index 71e48894c..8f0854213 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -71,6 +71,12 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt lastLoggedInUsername: undefined, } + case 'DELETE_USER_ACCESS': + return { + ...state, + storeCredentials: _.omit(state.storeCredentials, action.payload.usernameHash), + } + default: return state } diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index 50c577afa..4f90be21c 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -289,6 +289,14 @@ function* onDeleteAccountRequest(action: ExtractActionFromActionType<'DELETE_ACC const state: ReduxState = yield select() const user = selectors.currentUserSelector(state) setLoading(true) + + if (user.isGuest) { + // No online account to delete + // Delete local store + yield call(navigateAndReset, 'DeleteAccountStack', null) + return + } + try { const { name, password } = action.payload yield httpClient.deleteUserFromPassword({ @@ -302,12 +310,10 @@ function* onDeleteAccountRequest(action: ExtractActionFromActionType<'DELETE_ACC surveys: null, }), // TODO_ALEX ) - yield call(navigateAndReset, 'LoginStack', null) - if (user) { - // TODO: TODO_ALEX - // yield put(actions.logout()) - } + // Online account successfully deleted + // Delete local store + yield call(navigateAndReset, 'DeleteAccountStack', null) } catch (err) { setLoading(false) Alert.alert('Error', 'Unable to delete the account') From 3a3b6d7b41ebe9287a1d06f5b690f1a93cf73c85 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Tue, 9 Jan 2024 13:56:45 +0800 Subject: [PATCH 50/83] Refactor StoreSwitchSplash --- .../src/navigators/AppNavigator.tsx | 14 +---- .../src/redux/StoreSwitchSplash.tsx | 63 +++++++++---------- .../components/src/redux/sagas/authSaga.ts | 11 ++-- .../src/screens/PasswordRequestScreen.tsx | 3 +- .../components/src/screens/SplashScreen.tsx | 3 +- 5 files changed, 42 insertions(+), 52 deletions(-) diff --git a/packages/components/src/navigators/AppNavigator.tsx b/packages/components/src/navigators/AppNavigator.tsx index fb9d86c4a..4b70d48d3 100644 --- a/packages/components/src/navigators/AppNavigator.tsx +++ b/packages/components/src/navigators/AppNavigator.tsx @@ -28,23 +28,13 @@ import { FindHelpScreen } from '../screens/FindHelpScreen' import { PasswordRequestScreen } from '../screens/PasswordRequestScreen' import { VideoScreen } from '../screens/VideoScreen' import { VideosScreen } from '../screens/VideosScreen' -import { StoreSwitchSplash, LogoutSplash, DeleteAccountSplash } from '../redux/StoreSwitchSplash' +import { StoreSwitchSplash } from '../redux/StoreSwitchSplash' const StoreSwitchStack = createStackNavigator( { StoreSwitchSplash }, { headerMode: 'none', initialRouteName: 'StoreSwitchSplash' }, ) -const LogOutStack = createStackNavigator( - { LogoutSplash }, - { headerMode: 'none', initialRouteName: 'LogoutSplash' }, -) - -const DeleteAccountStack = createStackNavigator( - { DeleteAccountSplash }, - { headerMode: 'none', initialRouteName: 'DeleteAccountSplash' }, -) - const TutorialFirstStack = createStackNavigator( { TutorialFirstScreen }, { headerMode: 'none', initialRouteName: 'TutorialFirstScreen' }, @@ -148,8 +138,6 @@ const MainStack = createBottomTabNavigator( const AppNavigator = createStackNavigator( { StoreSwitchStack, - LogOutStack, - DeleteAccountStack, SplashScreen, OnboardingScreen, PasswordRequestScreen, diff --git a/packages/components/src/redux/StoreSwitchSplash.tsx b/packages/components/src/redux/StoreSwitchSplash.tsx index 3c05daa81..ee2553899 100644 --- a/packages/components/src/redux/StoreSwitchSplash.tsx +++ b/packages/components/src/redux/StoreSwitchSplash.tsx @@ -1,54 +1,53 @@ import React from 'react' +import { useStoreCoordinator } from './StoreCoordinator' import { SimpleSplashScreen } from '../screens/SplashScreen' import { navigateAndReset } from '../services/navigationService' -import { useStoreCoordinator } from './StoreCoordinator' -export function StoreSwitchSplash() { - const { switchStore, switchComplete } = useStoreCoordinator() +type StoreSwitchAction = 'login' | 'logout' | 'delete' - React.useEffect(() => { - switchStore() - }, []) +type ActionMap = Record void> - React.useEffect(() => { - if (switchComplete) { - navigateAndReset('MainStack', null) - } - }, [switchComplete]) +type DestinationMap = Record - return +const destinations: DestinationMap = { + login: 'MainStack', + logout: 'LoginStack', + delete: 'LoginStack', } -export function LogoutSplash() { - const { switchComplete, logout, loggedOut } = useStoreCoordinator() +export const StoreSwitchSplash = ({ navigation }) => { + const action = navigation.getParam('action') as StoreSwitchAction + const { switchStore, switchComplete, logout, loggedOut, deleteStore } = useStoreCoordinator() + + const actions: ActionMap = { + login: switchStore, + logout, + delete: deleteStore, + } React.useEffect(() => { - logout() - }, []) + actions[action]() + }, [action]) React.useEffect(() => { - if (!loggedOut || switchComplete) { + if (action === 'login' && !switchComplete) { return } - navigateAndReset('LoginStack', null) - }, [loggedOut]) - - return -} - -export function DeleteAccountSplash() { - const { switchComplete, deleteStore, loggedOut } = useStoreCoordinator() - React.useEffect(() => { - deleteStore() - }, []) + if (action === 'logout' && (!loggedOut || switchComplete)) { + return + } - React.useEffect(() => { - if (!loggedOut || switchComplete) { + if (action === 'delete' && (!loggedOut || switchComplete)) { return } - navigateAndReset('LoginStack', null) - }, [loggedOut]) + + navigateAndReset(destinations[action], null) + }, [switchComplete, loggedOut]) return } + +export const navigateToStoreSwitch = (action: StoreSwitchAction) => { + return navigateAndReset('StoreSwitchStack', { action }) +} diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index 4f90be21c..f34533349 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -14,6 +14,7 @@ import { closeOutTTs } from '../../services/textToSpeech' import { fetchNetworkConnectionStatus } from '../../services/network' import _ from 'lodash' import { formatPassword, hash } from '../../services/auth' +import { navigateToStoreSwitch } from '../StoreSwitchSplash' // unwrap promise type Await = T extends Promise ? U : T @@ -113,7 +114,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { } yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones - yield call(navigateAndReset, 'StoreSwitchStack', null) + yield call(navigateToStoreSwitch, 'login') } catch (error) { let errorMessage = 'request_fail' if (error && error.response && error.response.data) { @@ -293,7 +294,7 @@ function* onDeleteAccountRequest(action: ExtractActionFromActionType<'DELETE_ACC if (user.isGuest) { // No online account to delete // Delete local store - yield call(navigateAndReset, 'DeleteAccountStack', null) + yield call(navigateToStoreSwitch, 'delete') return } @@ -313,7 +314,7 @@ function* onDeleteAccountRequest(action: ExtractActionFromActionType<'DELETE_ACC // Online account successfully deleted // Delete local store - yield call(navigateAndReset, 'DeleteAccountStack', null) + yield call(navigateToStoreSwitch, 'delete') } catch (err) { setLoading(false) Alert.alert('Error', 'Unable to delete the account') @@ -336,7 +337,7 @@ function* onLogoutRequest() { ) yield put(actions.updateCompletedSurveys([])) // TODO_ALEX: survey - yield call(navigateAndReset, 'LogOutStack', null) + yield call(navigateToStoreSwitch, 'logout') } function* onClearLastLogin() { @@ -375,7 +376,7 @@ function* onJourneyCompletion(action: ExtractActionFromActionType<'JOURNEY_COMPL yield put(actions.setTutorialOneActive(true)) yield put(actions.setTutorialTwoActive(true)) yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones - yield call(navigateAndReset, 'StoreSwitchStack', null) + yield call(navigateToStoreSwitch, 'login') } export function* authSaga() { diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index 3a368361f..5f0da9634 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -13,6 +13,7 @@ import { KeyboardAwareAvoidance } from '../components/common/KeyboardAwareAvoida import { SpinLoader } from '../components/common/SpinLoader' import _ from 'lodash' import { formatPassword, hash } from '../services/auth' +import { navigateToStoreSwitch } from '../redux/StoreSwitchSplash' export function PasswordRequestScreen() { const dispatch = useDispatch() @@ -86,7 +87,7 @@ export function PasswordRequestScreen() { dispatch(actions.initiateStoreSwitch({ keys })) requestAnimationFrame(() => { - navigateAndReset('StoreSwitchStack', null) + navigateToStoreSwitch('login') }) } else if (enteredPasswordHash === passwordHash && name !== username) { setLoading(false) diff --git a/packages/components/src/screens/SplashScreen.tsx b/packages/components/src/screens/SplashScreen.tsx index fd6c6e702..c5e3b6fd3 100644 --- a/packages/components/src/screens/SplashScreen.tsx +++ b/packages/components/src/screens/SplashScreen.tsx @@ -16,6 +16,7 @@ import { httpClient } from '../services/HttpClient' import { fetchNetworkConnectionStatus } from '../services/network' import messaging from '@react-native-firebase/messaging' import { useSelector } from '../redux/useSelector' +import { navigateToStoreSwitch } from '../redux/StoreSwitchSplash' export function SplashScreen() { const dispatch = useDispatch() @@ -79,7 +80,7 @@ export function SplashScreen() { navigateAndReset('PasswordRequestScreen', null) return } - navigateAndReset('StoreSwitchStack', null) + navigateToStoreSwitch('login') return } navigateAndReset('LoginStack', null) From 6b9a8d3e5d3bad82af70643a71c2a7d68455de5e Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Tue, 9 Jan 2024 14:05:56 +0800 Subject: [PATCH 51/83] Redux store blacklists typesafety --- .../components/src/redux/StoreCoordinator.tsx | 30 +++++++++-------- .../components/src/redux/reducers/index.ts | 33 +++++++++---------- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index e0b95cec6..0737a198f 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -3,7 +3,7 @@ import { Provider as ReduxProvider } from 'react-redux' import { PersistGate } from 'redux-persist/integration/react' import { configureStore } from './store' import { config } from './config' -import { ReduxState, rootReducer } from './reducers' +import { ReduxState, ReduxStateProperties, rootReducer } from './reducers' import { rootSaga } from './sagas' import * as actions from './actions' import { PersistPartial } from 'redux-persist' @@ -33,23 +33,25 @@ const StoreCoordinatorContext = React.createContext({ }, }) -const primaryStoreBlacklist = [ - 'storeSwitch', // Not persisted for security - 'content', // Moved to async storage - 'auth', // Persisted in secure userStore - 'prediction', // Persisted in secure userStore -] -const userStoreBlacklist = [ - 'storeSwitch', // Not persisted for security - 'content', // Moved to async storage - 'access', // Not required after store switch -] +const blacklists: Record = { + primary: [ + 'storeSwitch', // Not persisted for security + 'content', // Moved to async storage + 'auth', // Persisted in secure userStore + 'prediction', // Persisted in secure userStore + ], + secure: [ + 'storeSwitch', // Not persisted for security + 'content', // Moved to async storage + 'access', // Not required after store switch + ], +} const primaryStore = () => configureStore({ key: 'primary', secretKey: config.REDUX_ENCRYPT_KEY, - blacklist: primaryStoreBlacklist, + blacklist: blacklists.primary, rootReducer, rootSaga, }) @@ -163,7 +165,7 @@ export function StoreCoordinator({ children }) { const userStore = configureStore({ key: keys.key, secretKey: keys.secretKey, - blacklist: userStoreBlacklist, + blacklist: blacklists.secure, rootReducer, rootSaga, }) diff --git a/packages/components/src/redux/reducers/index.ts b/packages/components/src/redux/reducers/index.ts index fce639257..8b4e1f654 100644 --- a/packages/components/src/redux/reducers/index.ts +++ b/packages/components/src/redux/reducers/index.ts @@ -1,8 +1,6 @@ import _ from 'lodash' import { combineReducers } from 'redux' import { syncReducers } from '../sync' -import { Actions } from '../types' - import { analyticsReducer } from './analyticsReducer' import { answerReducer } from './answerReducer' import { appReducer } from './appReducer' @@ -14,21 +12,20 @@ import { storeSwitchReducer } from './storeSwitchReducer' export const exportReducerNames = ['app', 'prediction'] -export const rootReducer = combineReducers( - syncReducers( - { - access: accessReducer, - storeSwitch: storeSwitchReducer, - analytics: analyticsReducer, - answer: answerReducer, - app: appReducer, - auth: authReducer, - content: contentReducer, - prediction: predictionReducer, - // flower: flowerReducer, TODO: Flower state should be saved per user - }, - exportReducerNames, - ), -) +export const allReducers = { + access: accessReducer, + storeSwitch: storeSwitchReducer, + analytics: analyticsReducer, + answer: answerReducer, + app: appReducer, + auth: authReducer, + content: contentReducer, + prediction: predictionReducer, + // flower: flowerReducer, TODO: Flower state should be saved per user +} + +export const rootReducer = combineReducers(syncReducers(allReducers, exportReducerNames)) export type ReduxState = ReturnType + +export type ReduxStateProperties = keyof ReduxState From 5026e1a687a1bad46c651124442a6cdf15afbebb Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Tue, 9 Jan 2024 14:08:52 +0800 Subject: [PATCH 52/83] Rename loggedOut context --- .../components/src/redux/StoreCoordinator.tsx | 20 +++++++++---------- .../src/redux/StoreSwitchSplash.tsx | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index 0737a198f..6176aee4a 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -15,7 +15,7 @@ interface Context { switchStore: () => void switchComplete: boolean logout: () => void - loggedOut: boolean + logoutComplete: boolean deleteStore: () => void } @@ -27,7 +27,7 @@ const StoreCoordinatorContext = React.createContext({ logout: () => { // }, - loggedOut: true, + logoutComplete: true, deleteStore: () => { // }, @@ -61,7 +61,7 @@ interface State { storeStateSnapshot: ReduxPersistState | undefined shouldMigrate: boolean switchComplete: boolean - loggedOut: boolean + logoutComplete: boolean userToDelete?: string // nameHash } @@ -93,7 +93,7 @@ const initialState: State = { storeStateSnapshot: undefined, shouldMigrate: false, switchComplete: false, - loggedOut: true, + logoutComplete: true, userToDelete: undefined, } @@ -121,13 +121,13 @@ function reducer(state: State, action: Action): State { ...initialState, redux: primaryStore(), userToDelete: action.payload?.userToDelete, - loggedOut: false, + logoutComplete: false, } case 'complete_logout': return { ...state, - loggedOut: true, + logoutComplete: true, } } } @@ -139,7 +139,7 @@ export function StoreCoordinator({ children }) { storeStateSnapshot, shouldMigrate, switchComplete, - loggedOut, + logoutComplete, userToDelete, }, dispatch, @@ -240,7 +240,7 @@ export function StoreCoordinator({ children }) { dispatch({ type: 'logout_request', payload: { userToDelete: usernameHash } }) React.useEffect(() => { - if (loggedOut) { + if (logoutComplete) { return } @@ -252,7 +252,7 @@ export function StoreCoordinator({ children }) { } dispatch({ type: 'complete_logout' }) }, 2000) - }, [loggedOut]) + }, [logoutComplete]) // ===== Delete store ===== // const deleteStore = () => { @@ -271,7 +271,7 @@ export function StoreCoordinator({ children }) { switchStore, switchComplete, logout, - loggedOut, + logoutComplete, deleteStore, }} > diff --git a/packages/components/src/redux/StoreSwitchSplash.tsx b/packages/components/src/redux/StoreSwitchSplash.tsx index ee2553899..b26dfd937 100644 --- a/packages/components/src/redux/StoreSwitchSplash.tsx +++ b/packages/components/src/redux/StoreSwitchSplash.tsx @@ -17,7 +17,7 @@ const destinations: DestinationMap = { export const StoreSwitchSplash = ({ navigation }) => { const action = navigation.getParam('action') as StoreSwitchAction - const { switchStore, switchComplete, logout, loggedOut, deleteStore } = useStoreCoordinator() + const { switchStore, switchComplete, logout, logoutComplete, deleteStore } = useStoreCoordinator() const actions: ActionMap = { login: switchStore, @@ -34,16 +34,16 @@ export const StoreSwitchSplash = ({ navigation }) => { return } - if (action === 'logout' && (!loggedOut || switchComplete)) { + if (action === 'logout' && (!logoutComplete || switchComplete)) { return } - if (action === 'delete' && (!loggedOut || switchComplete)) { + if (action === 'delete' && (!logoutComplete || switchComplete)) { return } navigateAndReset(destinations[action], null) - }, [switchComplete, loggedOut]) + }, [switchComplete, logoutComplete]) return } From 85154eb48725c044929dafb976cbb28478f1310e Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Tue, 9 Jan 2024 14:37:11 +0800 Subject: [PATCH 53/83] Add guard statements to StoreSwitchSplash --- packages/components/src/redux/StoreSwitchSplash.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/components/src/redux/StoreSwitchSplash.tsx b/packages/components/src/redux/StoreSwitchSplash.tsx index b26dfd937..b5fa38592 100644 --- a/packages/components/src/redux/StoreSwitchSplash.tsx +++ b/packages/components/src/redux/StoreSwitchSplash.tsx @@ -26,10 +26,18 @@ export const StoreSwitchSplash = ({ navigation }) => { } React.useEffect(() => { + if (!action) { + return + } + actions[action]() }, [action]) React.useEffect(() => { + if (!action) { + return + } + if (action === 'login' && !switchComplete) { return } From 09f75f292a4b2542004265c2923a4ded72b42e87 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Thu, 11 Jan 2024 17:40:02 +0800 Subject: [PATCH 54/83] Centralise store switch saga logic --- .../src/redux/actions/accessActions.ts | 43 +++------- .../src/redux/actions/authActions.ts | 2 +- .../src/redux/reducers/accessReducer.ts | 26 ++---- .../src/redux/reducers/storeSwitchReducer.ts | 2 +- .../components/src/redux/sagas/authSaga.ts | 86 ++++++++++++------- .../src/screens/PasswordRequestScreen.tsx | 16 ++-- 6 files changed, 85 insertions(+), 90 deletions(-) diff --git a/packages/components/src/redux/actions/accessActions.ts b/packages/components/src/redux/actions/accessActions.ts index baf4113ce..5c8d7b6e4 100644 --- a/packages/components/src/redux/actions/accessActions.ts +++ b/packages/components/src/redux/actions/accessActions.ts @@ -1,37 +1,18 @@ import { createAction } from '../helpers' +import { StoreKeys } from '../reducers/storeSwitchReducer' -export function saveLocalCredentials({ - appToken, - user: { - id, - name, - password, - dateOfBirth, - gender, - location, - country, - province, - secretQuestion, - secretAnswer, - isGuest, - }, +export function saveStoreCredentials(payload: { + usernameHash: string + storeExists: boolean + storeSalt: string + verificationSalt: string + passwordHash: string }) { - return createAction('SAVE_LOCAL_CREDENTIALS', { - appToken, - user: { - id, - name, - dateOfBirth, - gender, - location, - country, - province, - password, - secretQuestion, - secretAnswer, - isGuest, - }, - }) + return createAction('SAVE_STORE_CREDENTIALS', payload) +} + +export function setStoreKeys(payload: { keys: StoreKeys }) { + return createAction('SET_STORE_KEYS', payload) } export function setStoreExists(payload: { usernameHash: string }) { diff --git a/packages/components/src/redux/actions/authActions.ts b/packages/components/src/redux/actions/authActions.ts index 432accb0a..05ae21f77 100644 --- a/packages/components/src/redux/actions/authActions.ts +++ b/packages/components/src/redux/actions/authActions.ts @@ -39,7 +39,7 @@ export function loginSuccess({ }) } -export function initiateStoreSwitch(payload: { keys: StoreKeys }) { +export function initiateStoreSwitch(payload: { username: string; password: string }) { return createAction('INITIATE_STORE_SWITCH', payload) } diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index 8f0854213..9f6e24609 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -1,4 +1,3 @@ -import { formatPassword, hash } from '../../services/auth' import { Actions } from '../types' import { v4 as uuidv4 } from 'uuid' import _ from 'lodash' @@ -24,24 +23,17 @@ const initialState = (): AccessState => ({ export function accessReducer(state = initialState(), action: Actions): AccessState { switch (action.type) { - case 'SAVE_LOCAL_CREDENTIALS': - case 'CREATE_ACCOUNT_SUCCESS': { - const storeSalt = uuidv4() - const verificationSalt = uuidv4() - const usernameHash = hash(action.payload.user.name) - const password = formatPassword(action.payload.user.password) - const passwordHash = hash(password + verificationSalt) - + case 'SAVE_STORE_CREDENTIALS': { return { ...state, - lastLoggedInUsername: action.payload.user.name, storeCredentials: { ...state.storeCredentials, - [usernameHash]: { - storeExists: false, - storeSalt, - verificationSalt, - passwordHash, + [action.payload.usernameHash]: { + ...state.storeCredentials[action.payload.usernameHash], + storeExists: action.payload.storeExists, + storeSalt: action.payload.storeSalt, + verificationSalt: action.payload.verificationSalt, + passwordHash: action.payload.passwordHash, }, }, } @@ -59,10 +51,10 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt }, } - case 'LOGIN_SUCCESS': + case 'INITIATE_STORE_SWITCH': return { ...state, - lastLoggedInUsername: action.payload.user.name, + lastLoggedInUsername: action.payload.username, } case 'CLEAR_LAST_LOGIN': diff --git a/packages/components/src/redux/reducers/storeSwitchReducer.ts b/packages/components/src/redux/reducers/storeSwitchReducer.ts index 9f6174fb6..e9f50c391 100644 --- a/packages/components/src/redux/reducers/storeSwitchReducer.ts +++ b/packages/components/src/redux/reducers/storeSwitchReducer.ts @@ -23,7 +23,7 @@ export function storeSwitchReducer(state = initialState, action: Actions): Store migrationComplete: true, } - case 'INITIATE_STORE_SWITCH': + case 'SET_STORE_KEYS': return { ...state, keys: action.payload.keys, diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index f34533349..1cbcf345c 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -114,7 +114,8 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { } yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones - yield call(navigateToStoreSwitch, 'login') + + yield put(actions.initiateStoreSwitch({ username: name, password })) } catch (error) { let errorMessage = 'request_fail' if (error && error.response && error.response.data) { @@ -131,45 +132,28 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { const storeCredentials = yield select(selectors.storeCredentialsSelector) const credential = storeCredentials[usernameHash] - if (credential) { + if (!credential) { yield put( - actions.initiateStoreSwitch({ - keys: { - key: usernameHash, - secretKey: hash(password + credential.storeSalt), - }, + actions.loginFailure({ + error: errorMessage, }), ) - } else { + } + + const enteredPassword = formatPassword(password) + const enteredPasswordHash = hash(enteredPassword + credential.verificationSalt) + const passwordCorrect = enteredPasswordHash === credential.passwordHash + + if (!passwordCorrect) { yield put( actions.loginFailure({ error: errorMessage, }), ) } - } -} - -function* onLoginSuccess(action: ExtractActionFromActionType<'LOGIN_SUCCESS'>) { - let storeCredentials = yield select(selectors.storeCredentialsSelector) - const usernameHash = hash(action.payload.user.name) - if (!storeCredentials[usernameHash]) { - // Can occur when the account was created online via a different device - yield put(actions.saveLocalCredentials(action.payload)) - storeCredentials = yield select(selectors.storeCredentialsSelector) - return + yield put(actions.initiateStoreSwitch({ username: name, password })) } - - const salt = storeCredentials[usernameHash].storeSalt - const password = formatPassword(action.payload.user.password) - - const keys = { - key: usernameHash, - secretKey: hash(password + salt), - } - - yield put(actions.initiateStoreSwitch({ keys })) } function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACCOUNT_REQUEST'>) { @@ -234,6 +218,7 @@ function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACC const credential = storeCredentials[usernameHash] if (credential) { + // TODO: TODO_ALEX // username already taken // yield put(secureActions.setAuthError({ error: errorStatusCode })) return @@ -376,6 +361,47 @@ function* onJourneyCompletion(action: ExtractActionFromActionType<'JOURNEY_COMPL yield put(actions.setTutorialOneActive(true)) yield put(actions.setTutorialTwoActive(true)) yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones + + yield put( + actions.initiateStoreSwitch({ username: currentUser.name, password: currentUser.password }), + ) +} + +function* onInitiateStoreSwitch(action: ExtractActionFromActionType<'INITIATE_STORE_SWITCH'>) { + // Please note: Assumes password has already been verified + + let storeCredentials = yield select(selectors.storeCredentialsSelector) + const usernameHash = hash(action.payload.username) + const password = formatPassword(action.payload.password) + + if (!storeCredentials[usernameHash]) { + // New account / Online account on new device + const storeSalt = uuidv4() + const verificationSalt = uuidv4() + const passwordHash = hash(password + verificationSalt) + + yield put( + actions.saveStoreCredentials({ + usernameHash, + storeExists: false, + storeSalt, + verificationSalt, + passwordHash, + }), + ) + + storeCredentials = yield select(selectors.storeCredentialsSelector) + } + + const credentials = storeCredentials[usernameHash] + + const keys = { + key: usernameHash, + secretKey: hash(password + credentials.storeSalt), + } + + yield put(actions.setStoreKeys({ keys })) + yield call(navigateToStoreSwitch, 'login') } @@ -385,11 +411,11 @@ export function* authSaga() { takeLatest('LOGOUT_REQUEST', onLogoutRequest), takeLatest('CLEAR_LAST_LOGIN', onClearLastLogin), takeLatest('LOGIN_REQUEST', onLoginRequest), - takeLatest('LOGIN_SUCCESS', onLoginSuccess), takeLatest('DELETE_ACCOUNT_REQUEST', onDeleteAccountRequest), takeLatest('CREATE_ACCOUNT_REQUEST', onCreateAccountRequest), takeLatest('CREATE_ACCOUNT_SUCCESS', onCreateAccountSuccess), takeLatest('CONVERT_GUEST_ACCOUNT', onConvertGuestAccount), takeLatest('JOURNEY_COMPLETION', onJourneyCompletion), + takeLatest('INITIATE_STORE_SWITCH', onInitiateStoreSwitch), ]) } diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index 5f0da9634..745f7ab53 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -13,7 +13,6 @@ import { KeyboardAwareAvoidance } from '../components/common/KeyboardAwareAvoida import { SpinLoader } from '../components/common/SpinLoader' import _ from 'lodash' import { formatPassword, hash } from '../services/auth' -import { navigateToStoreSwitch } from '../redux/StoreSwitchSplash' export function PasswordRequestScreen() { const dispatch = useDispatch() @@ -80,15 +79,12 @@ export function PasswordRequestScreen() { setPasswordError(false) setValid(true) - const keys = { - key: usernameHash, - secretKey: hash(enteredPassword + storeSalt), - } - dispatch(actions.initiateStoreSwitch({ keys })) - - requestAnimationFrame(() => { - navigateToStoreSwitch('login') - }) + dispatch( + actions.initiateStoreSwitch({ + username, + password: enteredPassword, + }), + ) } else if (enteredPasswordHash === passwordHash && name !== username) { setLoading(false) setPasswordError(false) From a0ae429aa25dbfef9274424524516d2b84d4ebf5 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 12 Jan 2024 09:43:04 +0800 Subject: [PATCH 55/83] Comment --- packages/components/src/redux/sagas/authSaga.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index 1cbcf345c..b735cab47 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -221,6 +221,7 @@ function* onCreateAccountRequest(action: ExtractActionFromActionType<'CREATE_ACC // TODO: TODO_ALEX // username already taken // yield put(secureActions.setAuthError({ error: errorStatusCode })) + // Its not just on error, this will be hit when attempting convert guest account offline return } From 8948d7718668df754182d7f8164c25a2e1b8ea14 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 12 Jan 2024 10:34:56 +0800 Subject: [PATCH 56/83] Refactor password check & request screen --- .../src/components/common/TextInput.tsx | 2 + .../src/redux/reducers/accessReducer.ts | 18 +++-- .../components/src/redux/sagas/authSaga.ts | 22 ++---- .../src/screens/PasswordRequestScreen.tsx | 79 +++++++------------ packages/components/src/services/auth.ts | 28 +++++++ 5 files changed, 76 insertions(+), 73 deletions(-) diff --git a/packages/components/src/components/common/TextInput.tsx b/packages/components/src/components/common/TextInput.tsx index 86e914458..4d1faa57e 100644 --- a/packages/components/src/components/common/TextInput.tsx +++ b/packages/components/src/components/common/TextInput.tsx @@ -27,6 +27,7 @@ export const TextInput = ({ errorContent = 'No message', placeholderColor = '#28b9cb', infoAccessibilityLabel = '', + editable = true, }) => { const [isVisible, setIsVisible] = React.useState(false) return ( @@ -47,6 +48,7 @@ export const TextInput = ({ style={{ color: '#555', ...inputStyle }} secureTextEntry={secureTextEntry} value={value} + editable={editable} /> {isValid && !hasError && ( ) { } } - // Attempt offline login - const usernameHash = hash(name) const storeCredentials = yield select(selectors.storeCredentialsSelector) - const credential = storeCredentials[usernameHash] - - if (!credential) { - yield put( - actions.loginFailure({ - error: errorMessage, - }), - ) - } - - const enteredPassword = formatPassword(password) - const enteredPasswordHash = hash(enteredPassword + credential.verificationSalt) - const passwordCorrect = enteredPasswordHash === credential.passwordHash + const passwordCorrect = verifyStoreCredentials({ + username: name, + password, + storeCredentials, + }) if (!passwordCorrect) { yield put( diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index 745f7ab53..b8c8d83c1 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -5,37 +5,52 @@ import * as actions from '../redux/actions' import { Text } from '../components/common/Text' import { TextInput } from '../components/common/TextInput' import * as selectors from '../redux/selectors' -import { navigateAndReset } from '../services/navigationService' import { BackgroundTheme } from '../components/layout/BackgroundTheme' import { PageContainer } from '../components/layout/PageContainer' import { useSelector } from '../redux/useSelector' import { KeyboardAwareAvoidance } from '../components/common/KeyboardAwareAvoidance' import { SpinLoader } from '../components/common/SpinLoader' import _ from 'lodash' -import { formatPassword, hash } from '../services/auth' +import { verifyStoreCredentials } from '../services/auth' export function PasswordRequestScreen() { const dispatch = useDispatch() const username = useSelector(selectors.lastLoggedInUsernameSelector) const storeCredentials = useSelector(selectors.storeCredentialsSelector) + const [loading, setLoading] = React.useState(false) - const [valid, setValid] = React.useState(false) const [passwordError, setPasswordError] = React.useState(false) - const [nameError, setNameError] = React.useState(false) const [name, setName] = React.useState(username) const [password, setPassword] = React.useState('') - const usernameHash = hash(username) - const userCredentials = storeCredentials[usernameHash] + const onConfirm = () => { + setLoading(true) - if (!userCredentials) { - // TODO_ALEX: handle this case, navigate away? - } + const passwordCorrect = verifyStoreCredentials({ + username: name, + password, + storeCredentials, + }) + + if (!passwordCorrect) { + setLoading(false) + setPasswordError(true) + return + } + + setPasswordError(false) - const { storeSalt, verificationSalt, passwordHash } = userCredentials + dispatch( + actions.initiateStoreSwitch({ + username, + password, + }), + ) + } - const enteredPassword = formatPassword(password) - const enteredPasswordHash = hash(enteredPassword + verificationSalt) + const onBack = () => { + dispatch(actions.clearLastLogin()) + } return ( @@ -58,59 +73,25 @@ export function PasswordRequestScreen() { style={{ marginTop: 20 }} onChange={(text) => setName(text)} label="name" - isValid={valid} - hasError={nameError} value={name} + editable={false} /> setPassword(text)} label="password" secureTextEntry={true} - isValid={valid} hasError={passwordError} value={password} /> - { - setLoading(true) - if (enteredPasswordHash === passwordHash && name === username) { - setNameError(false) - setPasswordError(false) - setValid(true) - - dispatch( - actions.initiateStoreSwitch({ - username, - password: enteredPassword, - }), - ) - } else if (enteredPasswordHash === passwordHash && name !== username) { - setLoading(false) - setPasswordError(false) - setNameError(true) - } else if (enteredPasswordHash !== passwordHash && name === username) { - setLoading(false) - setNameError(false) - setPasswordError(true) - } else { - setNameError(true) - setPasswordError(true) - setLoading(false) - } - }} - > + confirm - { - dispatch(actions.clearLastLogin()) - }} - > + { return sha256(str) @@ -9,3 +10,30 @@ export const formatPassword = (password: string) => { // I don't agree with setting the password to lowercase but it has been like this for a long time, changing it now could lock out users return _.toLower(password).trim() } + +export const verifyStoreCredentials = ({ + username, + password, + storeCredentials, +}: { + username: string + password: string + storeCredentials: StoreCredentials +}): boolean => { + if (!username || !password) { + return false + } + + const usernameHash = hash(username) + const credential = storeCredentials[usernameHash] + + if (!credential) { + return false + } + + const formattedPassword = formatPassword(password) + const passwordHash = hash(formattedPassword + credential.verificationSalt) + const passwordCorrect = passwordHash === credential.passwordHash + + return passwordCorrect +} From 70e8220d988958cd20cf59e7ae90086bc97150f2 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Fri, 12 Jan 2024 10:42:52 +0800 Subject: [PATCH 57/83] Improve store exists check --- .../components/src/redux/StoreCoordinator.tsx | 16 +++++++++++----- .../src/redux/actions/accessActions.ts | 5 ----- .../src/redux/reducers/accessReducer.ts | 14 -------------- packages/components/src/redux/sagas/authSaga.ts | 1 - 4 files changed, 11 insertions(+), 25 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index 6176aee4a..1192e49be 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -8,6 +8,7 @@ import { rootSaga } from './sagas' import * as actions from './actions' import { PersistPartial } from 'redux-persist' import { hash } from '../services/auth' +import { AsyncStorage } from 'react-native' type ReduxPersistState = ReduxState & PersistPartial @@ -146,20 +147,20 @@ export function StoreCoordinator({ children }) { ] = React.useReducer(reducer, initialState) // ===== Switch store ===== // - const switchStore = () => { + const switchStore = async () => { const primaryState = store.getState() as ReduxPersistState const keys = primaryState?.storeSwitch?.keys - const accessState = primaryState?.access if (!keys) { return // ERROR } const usernameHash = keys.key - const storeExists = accessState.storeCredentials?.[usernameHash]?.storeExists + const storeExists = await checkStoreExists(usernameHash) - if (!storeExists) { - store.dispatch(actions.setStoreExists({ usernameHash })) + if (!storeExists && !primaryState?.auth.user) { + // TODO: + // Need to migrate but have no user data, something went wrong } const userStore = configureStore({ @@ -292,3 +293,8 @@ export function useStoreCoordinator() { return storeCoordinatorContext } + +const checkStoreExists = async (usernameHash: string) => { + const keys = await AsyncStorage.getAllKeys() + return keys.includes(`persist:${usernameHash}`) +} diff --git a/packages/components/src/redux/actions/accessActions.ts b/packages/components/src/redux/actions/accessActions.ts index 5c8d7b6e4..64faa7331 100644 --- a/packages/components/src/redux/actions/accessActions.ts +++ b/packages/components/src/redux/actions/accessActions.ts @@ -3,7 +3,6 @@ import { StoreKeys } from '../reducers/storeSwitchReducer' export function saveStoreCredentials(payload: { usernameHash: string - storeExists: boolean storeSalt: string verificationSalt: string passwordHash: string @@ -15,10 +14,6 @@ export function setStoreKeys(payload: { keys: StoreKeys }) { return createAction('SET_STORE_KEYS', payload) } -export function setStoreExists(payload: { usernameHash: string }) { - return createAction('SET_STORE_EXISTS', payload) -} - export function clearLastLogin() { return createAction('CLEAR_LAST_LOGIN') } diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index 29855c261..3dd76812c 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -4,7 +4,6 @@ import _ from 'lodash' export interface StoreCredentials { [usernameHash: string]: { - storeExists: boolean storeSalt: string verificationSalt: string passwordHash: string @@ -32,7 +31,6 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt ...state.storeCredentials, [action.payload.usernameHash]: { ...state.storeCredentials[action.payload.usernameHash], - storeExists: action.payload.storeExists, storeSalt: action.payload.storeSalt, verificationSalt: action.payload.verificationSalt, passwordHash: action.payload.passwordHash, @@ -41,18 +39,6 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt } } - case 'SET_STORE_EXISTS': - return { - ...state, - storeCredentials: { - ...state.storeCredentials, - [action.payload.usernameHash]: { - ...state.storeCredentials[action.payload.usernameHash], - storeExists: true, - }, - }, - } - case 'INITIATE_STORE_SWITCH': return { ...state, diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index 040ebecfc..05a27e693 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -374,7 +374,6 @@ function* onInitiateStoreSwitch(action: ExtractActionFromActionType<'INITIATE_ST yield put( actions.saveStoreCredentials({ usernameHash, - storeExists: false, storeSalt, verificationSalt, passwordHash, From 99ef28b4b3ff186eb40aed48e1a8ba3271ce4ddf Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Sat, 13 Jan 2024 11:17:49 +0800 Subject: [PATCH 58/83] Login as legacy user via PasswordRequestScreen WIP --- .../src/screens/PasswordRequestScreen.tsx | 17 +++++++++++++---- .../components/src/screens/SplashScreen.tsx | 14 ++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index b8c8d83c1..ee2986c8f 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -11,11 +11,12 @@ import { useSelector } from '../redux/useSelector' import { KeyboardAwareAvoidance } from '../components/common/KeyboardAwareAvoidance' import { SpinLoader } from '../components/common/SpinLoader' import _ from 'lodash' -import { verifyStoreCredentials } from '../services/auth' +import { formatPassword, verifyStoreCredentials } from '../services/auth' export function PasswordRequestScreen() { const dispatch = useDispatch() const username = useSelector(selectors.lastLoggedInUsernameSelector) + const user = useSelector(selectors.currentUserSelector) const storeCredentials = useSelector(selectors.storeCredentialsSelector) const [loading, setLoading] = React.useState(false) @@ -32,14 +33,14 @@ export function PasswordRequestScreen() { storeCredentials, }) - if (!passwordCorrect) { + const legacyPasswordCorrect = verifyLegacyPassword() + + if (!passwordCorrect && !legacyPasswordCorrect) { setLoading(false) setPasswordError(true) return } - setPasswordError(false) - dispatch( actions.initiateStoreSwitch({ username, @@ -48,6 +49,14 @@ export function PasswordRequestScreen() { ) } + const verifyLegacyPassword = () => { + if (!user) { + return false + } + const enteredPassword = formatPassword(password) + return enteredPassword === user?.password + } + const onBack = () => { dispatch(actions.clearLastLogin()) } diff --git a/packages/components/src/screens/SplashScreen.tsx b/packages/components/src/screens/SplashScreen.tsx index c5e3b6fd3..defb8c7cb 100644 --- a/packages/components/src/screens/SplashScreen.tsx +++ b/packages/components/src/screens/SplashScreen.tsx @@ -16,19 +16,17 @@ import { httpClient } from '../services/HttpClient' import { fetchNetworkConnectionStatus } from '../services/network' import messaging from '@react-native-firebase/messaging' import { useSelector } from '../redux/useSelector' -import { navigateToStoreSwitch } from '../redux/StoreSwitchSplash' export function SplashScreen() { const dispatch = useDispatch() - const user: any = useSelector(selectors.currentUserSelector) - const lastLoggedInUsername: any = useSelector(selectors.lastLoggedInUsernameSelector) + const user: any = useSelector(selectors.currentUserSelector) // TODO_ALEX fix any + const lastLoggedInUsername = useSelector(selectors.lastLoggedInUsernameSelector) const Alert = useAlert() const locale = useSelector(selectors.currentLocaleSelector) const hasOpened = useSelector(selectors.hasOpenedSelector) const currentAppVersion = useSelector(selectors.currentAppVersion) const currentFirebaseToken = useSelector(selectors.currentFirebaseToken) - const hasPasswordRequestOn = useSelector(selectors.isLoginPasswordActiveSelector) const [animatedValue] = React.useState(new Animated.Value(0)) async function checkForPermanentAlerts() { @@ -75,12 +73,8 @@ export function SplashScreen() { navigateAndReset('OnboardingScreen', null) return } - if (lastLoggedInUsername) { - if (hasPasswordRequestOn) { - navigateAndReset('PasswordRequestScreen', null) - return - } - navigateToStoreSwitch('login') + if (lastLoggedInUsername || user) { + navigateAndReset('PasswordRequestScreen', null) return } navigateAndReset('LoginStack', null) From e1bd51b2099776c505c6837d34a878e1b4d0162b Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Sat, 13 Jan 2024 11:35:38 +0800 Subject: [PATCH 59/83] Fix Login as legacy user via PasswordRequestScreen --- packages/components/src/redux/StoreCoordinator.tsx | 4 ++-- packages/components/src/screens/PasswordRequestScreen.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index 1192e49be..135553400 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -38,8 +38,8 @@ const blacklists: Record = { primary: [ 'storeSwitch', // Not persisted for security 'content', // Moved to async storage - 'auth', // Persisted in secure userStore - 'prediction', // Persisted in secure userStore + // 'auth', // Persisted in secure userStore + // 'prediction', // Persisted in secure userStore ], secure: [ 'storeSwitch', // Not persisted for security diff --git a/packages/components/src/screens/PasswordRequestScreen.tsx b/packages/components/src/screens/PasswordRequestScreen.tsx index ee2986c8f..2ef3eaa10 100644 --- a/packages/components/src/screens/PasswordRequestScreen.tsx +++ b/packages/components/src/screens/PasswordRequestScreen.tsx @@ -21,7 +21,7 @@ export function PasswordRequestScreen() { const [loading, setLoading] = React.useState(false) const [passwordError, setPasswordError] = React.useState(false) - const [name, setName] = React.useState(username) + const [name, setName] = React.useState(username ?? user?.name) const [password, setPassword] = React.useState('') const onConfirm = () => { @@ -43,7 +43,7 @@ export function PasswordRequestScreen() { dispatch( actions.initiateStoreSwitch({ - username, + username: name, password, }), ) From c20b57d7c4603bf2af9df58d5d529023b77e42fc Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Sat, 13 Jan 2024 13:07:25 +0800 Subject: [PATCH 60/83] Use dynamic primary store blacklists --- .../components/src/redux/StoreCoordinator.tsx | 147 +++++++++++++----- 1 file changed, 104 insertions(+), 43 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index 135553400..3e3da5640 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -9,6 +9,7 @@ import * as actions from './actions' import { PersistPartial } from 'redux-persist' import { hash } from '../services/auth' import { AsyncStorage } from 'react-native' +import { SimpleSplashScreen } from '../screens/SplashScreen' type ReduxPersistState = ReduxState & PersistPartial @@ -34,31 +35,48 @@ const StoreCoordinatorContext = React.createContext({ }, }) -const blacklists: Record = { - primary: [ - 'storeSwitch', // Not persisted for security - 'content', // Moved to async storage - // 'auth', // Persisted in secure userStore - // 'prediction', // Persisted in secure userStore - ], - secure: [ - 'storeSwitch', // Not persisted for security - 'content', // Moved to async storage - 'access', // Not required after store switch - ], +const checkStoreExists = async (usernameHash: string) => { + const keys = await AsyncStorage.getAllKeys() + return keys.includes(`persist:${usernameHash}`) +} + +const hasMigrated = async () => { + const keys = await AsyncStorage.getAllKeys() + const persistKeys = keys.filter((key) => { + return key.startsWith('persist:') + }) + return persistKeys.length > 1 } -const primaryStore = () => +const initialPrimaryBlacklist: ReduxStateProperties[] = [ + 'storeSwitch', // Not persisted for security + 'content', // Moved to async storage +] + +const postMigrationBlacklist: ReduxStateProperties[] = [ + 'auth', // + 'prediction', // +] + +const fullPrimaryBlacklist = [...initialPrimaryBlacklist, ...postMigrationBlacklist] + +const userStoreBlacklist = [ + 'storeSwitch', // Not persisted for security + 'content', // Moved to async storage + 'access', // Not required after store switch +] + +const primaryStore = (blacklist: ReduxStateProperties[]) => configureStore({ key: 'primary', secretKey: config.REDUX_ENCRYPT_KEY, - blacklist: blacklists.primary, + blacklist, rootReducer, rootSaga, }) interface State { - redux: ReturnType + redux: ReturnType | undefined storeStateSnapshot: ReduxPersistState | undefined shouldMigrate: boolean switchComplete: boolean @@ -67,6 +85,12 @@ interface State { } type Action = + | { + type: 'initialise' + payload: { + redux: ReturnType + } + } | { type: 'switch_store' payload: { @@ -90,7 +114,7 @@ type Action = } const initialState: State = { - redux: primaryStore(), + redux: undefined, storeStateSnapshot: undefined, shouldMigrate: false, switchComplete: false, @@ -100,6 +124,12 @@ const initialState: State = { function reducer(state: State, action: Action): State { switch (action.type) { + case 'initialise': + return { + ...state, + redux: action.payload.redux, + } + case 'switch_store': return { ...state, @@ -120,7 +150,7 @@ function reducer(state: State, action: Action): State { case 'logout_request': return { ...initialState, - redux: primaryStore(), + redux: primaryStore(fullPrimaryBlacklist), userToDelete: action.payload?.userToDelete, logoutComplete: false, } @@ -135,20 +165,40 @@ function reducer(state: State, action: Action): State { export function StoreCoordinator({ children }) { const [ - { - redux: { store, persistor }, - storeStateSnapshot, - shouldMigrate, - switchComplete, - logoutComplete, - userToDelete, - }, + { redux, storeStateSnapshot, shouldMigrate, switchComplete, logoutComplete, userToDelete }, dispatch, ] = React.useReducer(reducer, initialState) + React.useEffect(() => { + let ignore = false + if (redux) { + return + } + + ;(async () => { + const canUseFullBlacklist = await hasMigrated() + + if (ignore) { + return + } + + const blacklist = canUseFullBlacklist ? fullPrimaryBlacklist : initialPrimaryBlacklist + const initialStore = primaryStore(blacklist) + dispatch({ type: 'initialise', payload: { redux: initialStore } }) + })() + + return () => { + ignore = true + } + }, [redux]) + // ===== Switch store ===== // const switchStore = async () => { - const primaryState = store.getState() as ReduxPersistState + if (!redux) { + return + } + + const primaryState = redux.store.getState() as ReduxPersistState const keys = primaryState?.storeSwitch?.keys if (!keys) { @@ -166,7 +216,7 @@ export function StoreCoordinator({ children }) { const userStore = configureStore({ key: keys.key, secretKey: keys.secretKey, - blacklist: blacklists.secure, + blacklist: userStoreBlacklist, rootReducer, rootSaga, }) @@ -193,8 +243,12 @@ export function StoreCoordinator({ children }) { return } + if (!redux) { + return + } + const attemptMigration = () => { - store.dispatch( + redux.store.dispatch( actions.migrateStore({ auth: storeStateSnapshot?.auth, }), @@ -218,7 +272,7 @@ export function StoreCoordinator({ children }) { return } - const currentState = store.getState() as ReduxPersistState + const currentState = redux.store.getState() as ReduxPersistState const migrationComplete = currentState?.storeSwitch.migrationComplete if (migrationComplete) { @@ -245,11 +299,15 @@ export function StoreCoordinator({ children }) { return } + if (!redux) { + return + } + setTimeout(() => { // TODO: confirm store is ready - store.dispatch(actions.clearLastLogin()) + redux.store.dispatch(actions.clearLastLogin()) if (userToDelete) { - store.dispatch(actions.deleteUserAccess({ usernameHash: userToDelete })) + redux.store.dispatch(actions.deleteUserAccess({ usernameHash: userToDelete })) } dispatch({ type: 'complete_logout' }) }, 2000) @@ -257,11 +315,15 @@ export function StoreCoordinator({ children }) { // ===== Delete store ===== // const deleteStore = () => { - const currentState = store.getState() as ReduxPersistState + if (!redux) { + return + } + + const currentState = redux.store.getState() as ReduxPersistState const username = currentState?.auth?.user.name const usernameHash = hash(username) - persistor.purge().then(() => { + redux.persistor.purge().then(() => { logout(usernameHash) }) } @@ -276,11 +338,15 @@ export function StoreCoordinator({ children }) { deleteStore, }} > - - - {children} - - + {redux ? ( + + + {children} + + + ) : ( + + )} ) } @@ -293,8 +359,3 @@ export function useStoreCoordinator() { return storeCoordinatorContext } - -const checkStoreExists = async (usernameHash: string) => { - const keys = await AsyncStorage.getAllKeys() - return keys.includes(`persist:${usernameHash}`) -} From d65413c6222fc6e6f65af09a77b9984703a5efd9 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 15 Jan 2024 12:43:43 +0800 Subject: [PATCH 61/83] Install react-native-crypto-js --- packages/components/package.json | 3 ++- packages/mobile/package.json | 3 ++- yarn.lock | 5 +++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/components/package.json b/packages/components/package.json index 44dfe2f47..399fbaf82 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -45,7 +45,8 @@ "typescript": "4.7.4", "uuid": "9.0.0", "react-native-get-random-values": "1.9.0", - "js-sha256": "0.10.1" + "js-sha256": "0.10.1", + "react-native-crypto-js": "^1.0.0" }, "devDependencies": { "@types/lodash": "^4.14.136", diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 9444e89df..4b8b3ce6e 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -51,7 +51,8 @@ "react-native-youtube-iframe": "2.3.0", "react-native-webview": "13.6.0", "react-native-video": "5.2.1", - "react-native-orientation-locker": "1.3.0" + "react-native-orientation-locker": "1.3.0", + "react-native-crypto-js": "^1.0.0" }, "devDependencies": { "@babel/core": "^7.12.9", diff --git a/yarn.lock b/yarn.lock index 8bd72babe..d56bb1403 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9719,6 +9719,11 @@ react-native-config@^1.4.5: resolved "https://registry.yarnpkg.com/react-native-config/-/react-native-config-1.4.6.tgz#2aefebf4d9cf02831e64bbc1307596bd212f6d42" integrity sha512-cSLdOfva2IPCxh6HjHN1IDVW9ratAvNnnAUx6ar2Byvr8KQU7++ysdFYPaoNVuJURuYoAKgvjab8ZcnwGZIO6Q== +react-native-crypto-js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/react-native-crypto-js/-/react-native-crypto-js-1.0.0.tgz#e677e022e147f41b35614416c92d655f87e2450a" + integrity sha512-FNbLuG/HAdapQoybeZSoes1PWdOj0w242gb+e1R0hicf3Gyj/Mf8M9NaED2AnXVOX01b2FXomwUiw1xP1K+8sA== + react-native-device-info@^7.3.1: version "7.4.0" resolved "https://registry.yarnpkg.com/react-native-device-info/-/react-native-device-info-7.4.0.tgz#694deddfc6de0ff7268ff463d84693ee980c683c" From 06654db3a8ea313e5f703bc16a744ba8aeb2f931 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 15 Jan 2024 13:10:09 +0800 Subject: [PATCH 62/83] Store userId, secret salt & hash --- .../src/redux/actions/accessActions.ts | 5 +- .../src/redux/actions/authActions.ts | 9 +++ .../src/redux/reducers/accessReducer.ts | 30 +++++-- .../components/src/redux/sagas/authSaga.ts | 79 ++++++++++++++----- packages/components/src/services/auth.ts | 7 +- 5 files changed, 103 insertions(+), 27 deletions(-) diff --git a/packages/components/src/redux/actions/accessActions.ts b/packages/components/src/redux/actions/accessActions.ts index 64faa7331..b66c58138 100644 --- a/packages/components/src/redux/actions/accessActions.ts +++ b/packages/components/src/redux/actions/accessActions.ts @@ -2,10 +2,13 @@ import { createAction } from '../helpers' import { StoreKeys } from '../reducers/storeSwitchReducer' export function saveStoreCredentials(payload: { + userId: string usernameHash: string storeSalt: string - verificationSalt: string + passwordSalt: string passwordHash: string + secretSalt: string + secretHash: string }) { return createAction('SAVE_STORE_CREDENTIALS', payload) } diff --git a/packages/components/src/redux/actions/authActions.ts b/packages/components/src/redux/actions/authActions.ts index 05ae21f77..ca4a9a8e5 100644 --- a/packages/components/src/redux/actions/authActions.ts +++ b/packages/components/src/redux/actions/authActions.ts @@ -39,6 +39,15 @@ export function loginSuccess({ }) } +export function initiateNewStore(payload: { + userId: string + username: string + password: string + secret: string +}) { + return createAction('INITIATE_NEW_STORE', payload) +} + export function initiateStoreSwitch(payload: { username: string; password: string }) { return createAction('INITIATE_STORE_SWITCH', payload) } diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index 3dd76812c..da8ecde8c 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -3,21 +3,32 @@ import { v4 as uuidv4 } from 'uuid' import _ from 'lodash' export interface StoreCredentials { - [usernameHash: string]: { - storeSalt: string - verificationSalt: string - passwordHash: string - } + [usernameHash: string]: UserCredentials +} + +export interface UserCredentials { + userId: string + storeSalt: string + passwordSalt: string + passwordHash: string + secretSalt: string + secretHash: string +} + +export interface UserIdToUsernameHash { + [userId: string]: string } export interface AccessState { storeCredentials: StoreCredentials + userIdToUsernameHash: UserIdToUsernameHash lastLoggedInUsername?: string storeId: string } const initialState = (): AccessState => ({ storeCredentials: {}, + userIdToUsernameHash: {}, lastLoggedInUsername: undefined, storeId: uuidv4(), }) @@ -31,11 +42,18 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt ...state.storeCredentials, [action.payload.usernameHash]: { ...state.storeCredentials[action.payload.usernameHash], + userId: action.payload.userId, storeSalt: action.payload.storeSalt, - verificationSalt: action.payload.verificationSalt, + secretSalt: action.payload.secretSalt, + secretHash: action.payload.secretHash, + passwordSalt: action.payload.passwordSalt, passwordHash: action.payload.passwordHash, }, }, + userIdToUsernameHash: { + ...state.userIdToUsernameHash, + [action.payload.userId]: action.payload.usernameHash, + }, } } diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index 05a27e693..ee70721ac 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -115,7 +115,14 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones - yield put(actions.initiateStoreSwitch({ username: name, password })) + yield put( + actions.initiateNewStore({ + userId: user.id, + username: name, + password, + secret: user.secretAnswer, + }), + ) } catch (error) { let errorMessage = 'request_fail' if (error && error.response && error.response.data) { @@ -354,33 +361,68 @@ function* onJourneyCompletion(action: ExtractActionFromActionType<'JOURNEY_COMPL yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones yield put( - actions.initiateStoreSwitch({ username: currentUser.name, password: currentUser.password }), + actions.initiateNewStore({ + userId: currentUser.id, + username: currentUser.name, + password: currentUser.password, + secret: currentUser.secret, + }), + ) +} + +function* onInitiateNewStore(action: ExtractActionFromActionType<'INITIATE_NEW_STORE'>) { + const storeCredentials = yield select(selectors.storeCredentialsSelector) + const usernameHash = hash(action.payload.username) + + if (storeCredentials[usernameHash]) { + // Store already exists + yield put( + actions.initiateStoreSwitch({ + username: action.payload.username, + password: action.payload.password, + }), + ) + return + } + + const storeSalt = uuidv4() + + const passwordSalt = uuidv4() + const password = formatPassword(action.payload.password) + const passwordHash = hash(password + passwordSalt) + + const secretSalt = uuidv4() + const secretHash = hash(password + passwordSalt) + + yield put( + actions.saveStoreCredentials({ + userId: action.payload.userId, + usernameHash, + storeSalt, + passwordSalt, + passwordHash, + secretSalt, + secretHash, + }), + ) + + yield put( + actions.initiateStoreSwitch({ + username: action.payload.username, + password: action.payload.password, + }), ) } function* onInitiateStoreSwitch(action: ExtractActionFromActionType<'INITIATE_STORE_SWITCH'>) { // Please note: Assumes password has already been verified - let storeCredentials = yield select(selectors.storeCredentialsSelector) + const storeCredentials = yield select(selectors.storeCredentialsSelector) const usernameHash = hash(action.payload.username) const password = formatPassword(action.payload.password) if (!storeCredentials[usernameHash]) { - // New account / Online account on new device - const storeSalt = uuidv4() - const verificationSalt = uuidv4() - const passwordHash = hash(password + verificationSalt) - - yield put( - actions.saveStoreCredentials({ - usernameHash, - storeSalt, - verificationSalt, - passwordHash, - }), - ) - - storeCredentials = yield select(selectors.storeCredentialsSelector) + return // ERROR } const credentials = storeCredentials[usernameHash] @@ -406,6 +448,7 @@ export function* authSaga() { takeLatest('CREATE_ACCOUNT_SUCCESS', onCreateAccountSuccess), takeLatest('CONVERT_GUEST_ACCOUNT', onConvertGuestAccount), takeLatest('JOURNEY_COMPLETION', onJourneyCompletion), + takeLatest('INITIATE_NEW_STORE', onInitiateNewStore), takeLatest('INITIATE_STORE_SWITCH', onInitiateStoreSwitch), ]) } diff --git a/packages/components/src/services/auth.ts b/packages/components/src/services/auth.ts index ba5bae35e..168c4b2b1 100644 --- a/packages/components/src/services/auth.ts +++ b/packages/components/src/services/auth.ts @@ -15,10 +15,12 @@ export const verifyStoreCredentials = ({ username, password, storeCredentials, + method = 'password', }: { username: string password: string storeCredentials: StoreCredentials + method?: 'password' | 'secret' }): boolean => { if (!username || !password) { return false @@ -32,8 +34,9 @@ export const verifyStoreCredentials = ({ } const formattedPassword = formatPassword(password) - const passwordHash = hash(formattedPassword + credential.verificationSalt) - const passwordCorrect = passwordHash === credential.passwordHash + const passwordHash = hash(formattedPassword + credential.passwordSalt) + const verificationHash = method === 'password' ? credential.passwordHash : credential.secretHash + const passwordCorrect = passwordHash === verificationHash return passwordCorrect } From 699bedce29337e25eb3fe5ef8ddd4a584ab8614f Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 15 Jan 2024 13:15:46 +0800 Subject: [PATCH 63/83] Rename secret to answer --- packages/components/src/redux/actions/accessActions.ts | 4 ++-- packages/components/src/redux/reducers/accessReducer.ts | 8 ++++---- packages/components/src/redux/sagas/authSaga.ts | 8 ++++---- packages/components/src/services/auth.ts | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/components/src/redux/actions/accessActions.ts b/packages/components/src/redux/actions/accessActions.ts index b66c58138..053b4cb41 100644 --- a/packages/components/src/redux/actions/accessActions.ts +++ b/packages/components/src/redux/actions/accessActions.ts @@ -7,8 +7,8 @@ export function saveStoreCredentials(payload: { storeSalt: string passwordSalt: string passwordHash: string - secretSalt: string - secretHash: string + answerSalt: string + answerHash: string }) { return createAction('SAVE_STORE_CREDENTIALS', payload) } diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index da8ecde8c..5d2df82c6 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -11,8 +11,8 @@ export interface UserCredentials { storeSalt: string passwordSalt: string passwordHash: string - secretSalt: string - secretHash: string + answerSalt: string + answerHash: string } export interface UserIdToUsernameHash { @@ -44,8 +44,8 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt ...state.storeCredentials[action.payload.usernameHash], userId: action.payload.userId, storeSalt: action.payload.storeSalt, - secretSalt: action.payload.secretSalt, - secretHash: action.payload.secretHash, + answerSalt: action.payload.answerSalt, + answerHash: action.payload.answerHash, passwordSalt: action.payload.passwordSalt, passwordHash: action.payload.passwordHash, }, diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index ee70721ac..57ca9aa3a 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -391,8 +391,8 @@ function* onInitiateNewStore(action: ExtractActionFromActionType<'INITIATE_NEW_S const password = formatPassword(action.payload.password) const passwordHash = hash(password + passwordSalt) - const secretSalt = uuidv4() - const secretHash = hash(password + passwordSalt) + const answerSalt = uuidv4() + const answerHash = hash(password + passwordSalt) yield put( actions.saveStoreCredentials({ @@ -401,8 +401,8 @@ function* onInitiateNewStore(action: ExtractActionFromActionType<'INITIATE_NEW_S storeSalt, passwordSalt, passwordHash, - secretSalt, - secretHash, + answerSalt, + answerHash, }), ) diff --git a/packages/components/src/services/auth.ts b/packages/components/src/services/auth.ts index 168c4b2b1..bbed487c6 100644 --- a/packages/components/src/services/auth.ts +++ b/packages/components/src/services/auth.ts @@ -20,7 +20,7 @@ export const verifyStoreCredentials = ({ username: string password: string storeCredentials: StoreCredentials - method?: 'password' | 'secret' + method?: 'password' | 'answer' }): boolean => { if (!username || !password) { return false @@ -35,7 +35,7 @@ export const verifyStoreCredentials = ({ const formattedPassword = formatPassword(password) const passwordHash = hash(formattedPassword + credential.passwordSalt) - const verificationHash = method === 'password' ? credential.passwordHash : credential.secretHash + const verificationHash = method === 'password' ? credential.passwordHash : credential.answerHash const passwordCorrect = passwordHash === verificationHash return passwordCorrect From 362bbdc4d6ab7ae6a3d325a6ecd360fb8b0c07df Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 15 Jan 2024 13:18:44 +0800 Subject: [PATCH 64/83] Fix secret answer hashing & further renaming --- packages/components/src/redux/actions/authActions.ts | 2 +- packages/components/src/redux/sagas/authSaga.ts | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/components/src/redux/actions/authActions.ts b/packages/components/src/redux/actions/authActions.ts index ca4a9a8e5..df1513a3e 100644 --- a/packages/components/src/redux/actions/authActions.ts +++ b/packages/components/src/redux/actions/authActions.ts @@ -43,7 +43,7 @@ export function initiateNewStore(payload: { userId: string username: string password: string - secret: string + answer: string }) { return createAction('INITIATE_NEW_STORE', payload) } diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index 57ca9aa3a..c945b4bb4 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -120,7 +120,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { userId: user.id, username: name, password, - secret: user.secretAnswer, + answer: user.secretAnswer, }), ) } catch (error) { @@ -365,7 +365,7 @@ function* onJourneyCompletion(action: ExtractActionFromActionType<'JOURNEY_COMPL userId: currentUser.id, username: currentUser.name, password: currentUser.password, - secret: currentUser.secret, + answer: currentUser.secret, }), ) } @@ -392,7 +392,8 @@ function* onInitiateNewStore(action: ExtractActionFromActionType<'INITIATE_NEW_S const passwordHash = hash(password + passwordSalt) const answerSalt = uuidv4() - const answerHash = hash(password + passwordSalt) + const answer = formatPassword(action.payload.answer) + const answerHash = hash(answer + answerSalt) yield put( actions.saveStoreCredentials({ From 6aa905cb5d880eca8417e332d5b1ac3033b9193b Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 15 Jan 2024 13:36:41 +0800 Subject: [PATCH 65/83] Refactor type ReduxInstance --- packages/components/src/redux/StoreCoordinator.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index 3e3da5640..c807808e3 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -13,6 +13,8 @@ import { SimpleSplashScreen } from '../screens/SplashScreen' type ReduxPersistState = ReduxState & PersistPartial +type ReduxInstance = ReturnType + interface Context { switchStore: () => void switchComplete: boolean @@ -76,7 +78,7 @@ const primaryStore = (blacklist: ReduxStateProperties[]) => }) interface State { - redux: ReturnType | undefined + redux: ReduxInstance | undefined storeStateSnapshot: ReduxPersistState | undefined shouldMigrate: boolean switchComplete: boolean @@ -88,13 +90,13 @@ type Action = | { type: 'initialise' payload: { - redux: ReturnType + redux: ReduxInstance } } | { type: 'switch_store' payload: { - redux: ReturnType + redux: ReduxInstance storeStateSnapshot: ReduxPersistState shouldMigrate: boolean switchComplete: boolean From 49d719b241815e9858c223887763feb473d1fa24 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 15 Jan 2024 13:53:27 +0800 Subject: [PATCH 66/83] Store encrypted secret keys --- .../src/redux/actions/accessActions.ts | 2 ++ .../src/redux/reducers/accessReducer.ts | 4 +++ .../components/src/redux/sagas/authSaga.ts | 28 +++++++++++++------ packages/components/src/services/auth.ts | 11 ++++++++ 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/packages/components/src/redux/actions/accessActions.ts b/packages/components/src/redux/actions/accessActions.ts index 053b4cb41..e8b00de6b 100644 --- a/packages/components/src/redux/actions/accessActions.ts +++ b/packages/components/src/redux/actions/accessActions.ts @@ -9,6 +9,8 @@ export function saveStoreCredentials(payload: { passwordHash: string answerSalt: string answerHash: string + secretKeyEncryptedWithPassword: string + secretKeyEncryptedWithAnswer: string }) { return createAction('SAVE_STORE_CREDENTIALS', payload) } diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index 5d2df82c6..bce3e3397 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -13,6 +13,8 @@ export interface UserCredentials { passwordHash: string answerSalt: string answerHash: string + secretKeyEncryptedWithPassword: string + secretKeyEncryptedWithAnswer: string } export interface UserIdToUsernameHash { @@ -48,6 +50,8 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt answerHash: action.payload.answerHash, passwordSalt: action.payload.passwordSalt, passwordHash: action.payload.passwordHash, + secretKeyEncryptedWithPassword: action.payload.secretKeyEncryptedWithPassword, + secretKeyEncryptedWithAnswer: action.payload.secretKeyEncryptedWithAnswer, }, }, userIdToUsernameHash: { diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index c945b4bb4..b7c4c805c 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -13,7 +13,7 @@ import moment from 'moment' import { closeOutTTs } from '../../services/textToSpeech' import { fetchNetworkConnectionStatus } from '../../services/network' import _ from 'lodash' -import { formatPassword, hash, verifyStoreCredentials } from '../../services/auth' +import { decrypt, encrypt, formatPassword, hash, verifyStoreCredentials } from '../../services/auth' import { navigateToStoreSwitch } from '../StoreSwitchSplash' // unwrap promise @@ -395,6 +395,10 @@ function* onInitiateNewStore(action: ExtractActionFromActionType<'INITIATE_NEW_S const answer = formatPassword(action.payload.answer) const answerHash = hash(answer + answerSalt) + const secretKey = uuidv4() + const secretKeyEncryptedWithPassword = encrypt(secretKey, password) + const secretKeyEncryptedWithAnswer = encrypt(secretKey, answer) + yield put( actions.saveStoreCredentials({ userId: action.payload.userId, @@ -404,15 +408,19 @@ function* onInitiateNewStore(action: ExtractActionFromActionType<'INITIATE_NEW_S passwordHash, answerSalt, answerHash, + secretKeyEncryptedWithPassword, + secretKeyEncryptedWithAnswer, }), ) - yield put( - actions.initiateStoreSwitch({ - username: action.payload.username, - password: action.payload.password, - }), - ) + const keys = { + key: action.payload.userId, + secretKey, + } + + yield put(actions.setStoreKeys({ keys })) + + yield call(navigateToStoreSwitch, 'login') } function* onInitiateStoreSwitch(action: ExtractActionFromActionType<'INITIATE_STORE_SWITCH'>) { @@ -428,9 +436,11 @@ function* onInitiateStoreSwitch(action: ExtractActionFromActionType<'INITIATE_ST const credentials = storeCredentials[usernameHash] + const secretKey = decrypt(credentials.secretKeyEncryptedWithPassword, password) + const keys = { - key: usernameHash, - secretKey: hash(password + credentials.storeSalt), + key: credentials.userId, + secretKey, } yield put(actions.setStoreKeys({ keys })) diff --git a/packages/components/src/services/auth.ts b/packages/components/src/services/auth.ts index bbed487c6..c990abe8a 100644 --- a/packages/components/src/services/auth.ts +++ b/packages/components/src/services/auth.ts @@ -1,7 +1,18 @@ +import { AES, enc } from 'react-native-crypto-js' import { sha256 } from 'js-sha256' import _ from 'lodash' import { StoreCredentials } from '../redux/reducers/accessReducer' +export const encrypt = (str: string, secret: string): string => { + return AES.encrypt(str, secret).toString() +} + +export const decrypt = (str: string, secret: string) => { + const bytes = AES.decrypt(str, secret) + const originalText = bytes.toString(enc.Utf8) + return originalText +} + export const hash = (str: string) => { return sha256(str) } From 548d5c35b4d81ca60499da5a56d495facbd6587b Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 15 Jan 2024 13:58:20 +0800 Subject: [PATCH 67/83] Improve store saga naming --- packages/components/src/redux/actions/authActions.ts | 4 ++-- packages/components/src/redux/sagas/authSaga.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/components/src/redux/actions/authActions.ts b/packages/components/src/redux/actions/authActions.ts index df1513a3e..9fd1b579b 100644 --- a/packages/components/src/redux/actions/authActions.ts +++ b/packages/components/src/redux/actions/authActions.ts @@ -39,13 +39,13 @@ export function loginSuccess({ }) } -export function initiateNewStore(payload: { +export function setUpNewStore(payload: { userId: string username: string password: string answer: string }) { - return createAction('INITIATE_NEW_STORE', payload) + return createAction('SET_UP_NEW_STORE', payload) } export function initiateStoreSwitch(payload: { username: string; password: string }) { diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index b7c4c805c..53dbec098 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -116,7 +116,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones yield put( - actions.initiateNewStore({ + actions.setUpNewStore({ userId: user.id, username: name, password, @@ -361,7 +361,7 @@ function* onJourneyCompletion(action: ExtractActionFromActionType<'JOURNEY_COMPL yield delay(5000) // !!! THis is here for a bug on slower devices that cause the app to crash on sign up. Did no debug further. Note only occurs on much older phones yield put( - actions.initiateNewStore({ + actions.setUpNewStore({ userId: currentUser.id, username: currentUser.name, password: currentUser.password, @@ -370,7 +370,7 @@ function* onJourneyCompletion(action: ExtractActionFromActionType<'JOURNEY_COMPL ) } -function* onInitiateNewStore(action: ExtractActionFromActionType<'INITIATE_NEW_STORE'>) { +function* onSetUpNewStore(action: ExtractActionFromActionType<'SET_UP_NEW_STORE'>) { const storeCredentials = yield select(selectors.storeCredentialsSelector) const usernameHash = hash(action.payload.username) @@ -459,7 +459,7 @@ export function* authSaga() { takeLatest('CREATE_ACCOUNT_SUCCESS', onCreateAccountSuccess), takeLatest('CONVERT_GUEST_ACCOUNT', onConvertGuestAccount), takeLatest('JOURNEY_COMPLETION', onJourneyCompletion), - takeLatest('INITIATE_NEW_STORE', onInitiateNewStore), + takeLatest('SET_UP_NEW_STORE', onSetUpNewStore), takeLatest('INITIATE_STORE_SWITCH', onInitiateStoreSwitch), ]) } From 9d782b6e27dacd3c283295475a6335d789b721f7 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 15 Jan 2024 14:04:15 +0800 Subject: [PATCH 68/83] Add comments, remove spread --- packages/components/src/redux/reducers/accessReducer.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index bce3e3397..9e5562475 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -9,10 +9,13 @@ export interface StoreCredentials { export interface UserCredentials { userId: string storeSalt: string + // password passwordSalt: string passwordHash: string + // answer answerSalt: string answerHash: string + // secretKey secretKeyEncryptedWithPassword: string secretKeyEncryptedWithAnswer: string } @@ -43,7 +46,6 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt storeCredentials: { ...state.storeCredentials, [action.payload.usernameHash]: { - ...state.storeCredentials[action.payload.usernameHash], userId: action.payload.userId, storeSalt: action.payload.storeSalt, answerSalt: action.payload.answerSalt, From 4f71e48052649bc5da7e53cd35e042e90042954f Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 15 Jan 2024 16:54:55 +0800 Subject: [PATCH 69/83] Reset password --- .../src/redux/actions/accessActions.ts | 9 +++ .../src/redux/reducers/accessReducer.ts | 14 ++++ .../components/src/redux/sagas/authSaga.ts | 2 +- .../forgetPassword/AskNewPassword.tsx | 66 ++++++++++++++++--- packages/components/src/services/auth.ts | 11 +++- 5 files changed, 91 insertions(+), 11 deletions(-) diff --git a/packages/components/src/redux/actions/accessActions.ts b/packages/components/src/redux/actions/accessActions.ts index e8b00de6b..838e9d59d 100644 --- a/packages/components/src/redux/actions/accessActions.ts +++ b/packages/components/src/redux/actions/accessActions.ts @@ -26,3 +26,12 @@ export function clearLastLogin() { export function deleteUserAccess(payload: { usernameHash: string }) { return createAction('DELETE_USER_ACCESS', payload) } + +export function editPassword(payload: { + usernameHash: string + passwordSalt: string + passwordHash: string + secretKeyEncryptedWithPassword: string +}) { + return createAction('EDIT_PASSWORD', payload) +} diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index 9e5562475..491371002 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -63,6 +63,20 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt } } + case 'EDIT_PASSWORD': + return { + ...state, + storeCredentials: { + ...state.storeCredentials, + [action.payload.usernameHash]: { + ...state.storeCredentials[action.payload.usernameHash], + passwordSalt: action.payload.passwordSalt, + passwordHash: action.payload.passwordHash, + secretKeyEncryptedWithPassword: action.payload.secretKeyEncryptedWithPassword, + }, + }, + } + case 'INITIATE_STORE_SWITCH': return { ...state, diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index 53dbec098..286dfae07 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -365,7 +365,7 @@ function* onJourneyCompletion(action: ExtractActionFromActionType<'JOURNEY_COMPL userId: currentUser.id, username: currentUser.name, password: currentUser.password, - answer: currentUser.secret, + answer: currentUser.secretAnswer, }), ) } diff --git a/packages/components/src/screens/authScreen/forgetPassword/AskNewPassword.tsx b/packages/components/src/screens/authScreen/forgetPassword/AskNewPassword.tsx index 27dcc3277..21dee389d 100644 --- a/packages/components/src/screens/authScreen/forgetPassword/AskNewPassword.tsx +++ b/packages/components/src/screens/authScreen/forgetPassword/AskNewPassword.tsx @@ -4,29 +4,79 @@ import { useMultiStepForm, formActions } from '../../../components/common/MultiS import { httpClient } from '../../../services/HttpClient' import { ForgotPasswordFormLayout } from './ForgotPasswordFormLayout' import _ from 'lodash' -import { formatPassword } from '../../../services/auth' +import { + decrypt, + encrypt, + formatPassword, + hash, + validatePassword, + verifyStoreCredentials, +} from '../../../services/auth' +import { useSelector } from '../../../redux/useSelector' +import * as actions from '../../../redux/actions' +import * as selectors from '../../../redux/selectors' +import { useDispatch } from 'react-redux' +import { v4 as uuidv4 } from 'uuid' export function AskNewPassword({ step }) { const [{ app: state }, dispatch] = useMultiStepForm() - const minPasswordLength = 1 + const reduxDispatch = useDispatch() + const storeCredentials = useSelector(selectors.storeCredentialsSelector) + const [repeatPassword, setRepeatPassword] = React.useState(state.password) - const passwordIsValid = state.password.length >= minPasswordLength + const passwordIsValid = validatePassword(state.password) + + const repeatIsValid = validatePassword(repeatPassword) + const pairIsEqual = repeatPassword === state.password + const passwordPairIsValid = passwordIsValid && repeatIsValid && pairIsEqual const onSubmit = async () => { - if (!passwordIsValid) { + if (!passwordPairIsValid) { return } - if (!repeatPassword) { + + const storeSecretCorrect = verifyStoreCredentials({ + username: state.name, + password: state.secretAnswer, + storeCredentials, + method: 'answer', + }) + + if (!storeSecretCorrect) { return } + try { + // Attempt encryption before API request, incase an error occurs + const usernameHash = hash(state.name) + const credentials = storeCredentials[usernameHash] + + const secretKey = decrypt(credentials.secretKeyEncryptedWithAnswer, state.secretAnswer) + + const password = formatPassword(repeatPassword) + const secretKeyEncryptedWithPassword = encrypt(secretKey, password) + + const passwordSalt = uuidv4() + const passwordHash = hash(password + passwordSalt) + + // API request await httpClient.resetPassword({ name: state.name, secretAnswer: formatPassword(state.secretAnswer), password: formatPassword(state.password), }) + // Update redux AFTER successful API request + reduxDispatch( + actions.editPassword({ + usernameHash, + passwordSalt, + passwordHash, + secretKeyEncryptedWithPassword, + }), + ) + dispatch({ formAction: formActions.goToStep('completed') }) } catch (err) { dispatch({ @@ -45,7 +95,7 @@ export function AskNewPassword({ step }) { label="password" secureTextEntry={true} isValid={passwordIsValid} - hasError={state.password >= minPasswordLength && !passwordIsValid} + hasError={!passwordIsValid} value={state.password} /> = minPasswordLength && repeatPassword === state.password} - hasError={repeatPassword.length >= minPasswordLength && repeatPassword !== state.password} + isValid={passwordPairIsValid} + hasError={!passwordPairIsValid} value={repeatPassword} /> diff --git a/packages/components/src/services/auth.ts b/packages/components/src/services/auth.ts index c990abe8a..6c5e462c2 100644 --- a/packages/components/src/services/auth.ts +++ b/packages/components/src/services/auth.ts @@ -22,6 +22,10 @@ export const formatPassword = (password: string) => { return _.toLower(password).trim() } +export const validatePassword = (password: string, minLength = 1) => { + return formatPassword(password).length >= minLength +} + export const verifyStoreCredentials = ({ username, password, @@ -45,9 +49,12 @@ export const verifyStoreCredentials = ({ } const formattedPassword = formatPassword(password) - const passwordHash = hash(formattedPassword + credential.passwordSalt) + + const salt = method === 'password' ? credential.passwordSalt : credential.answerSalt const verificationHash = method === 'password' ? credential.passwordHash : credential.answerHash - const passwordCorrect = passwordHash === verificationHash + + const inputHash = hash(formattedPassword + salt) + const passwordCorrect = inputHash === verificationHash return passwordCorrect } From 7e629b573d4e355edd6d27fc88f9b502c7045f68 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 15 Jan 2024 17:03:03 +0800 Subject: [PATCH 70/83] Return after login failure --- packages/components/src/redux/sagas/authSaga.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/src/redux/sagas/authSaga.ts b/packages/components/src/redux/sagas/authSaga.ts index 286dfae07..0544ec808 100644 --- a/packages/components/src/redux/sagas/authSaga.ts +++ b/packages/components/src/redux/sagas/authSaga.ts @@ -147,6 +147,7 @@ function* onLoginRequest(action: ExtractActionFromActionType<'LOGIN_REQUEST'>) { error: errorMessage, }), ) + return } yield put(actions.initiateStoreSwitch({ username: name, password })) From 8484fbbfe193bdd74273175126492df211e11b12 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 15 Jan 2024 17:15:01 +0800 Subject: [PATCH 71/83] Edit profile WIP clear out --- .../src/screens/EditProfileScreen.tsx | 171 ++---------------- 1 file changed, 16 insertions(+), 155 deletions(-) diff --git a/packages/components/src/screens/EditProfileScreen.tsx b/packages/components/src/screens/EditProfileScreen.tsx index 9816a7a68..e6db95563 100644 --- a/packages/components/src/screens/EditProfileScreen.tsx +++ b/packages/components/src/screens/EditProfileScreen.tsx @@ -49,6 +49,7 @@ function showAlert(message) { { cancelable: false }, ) } + function showAcceptAlert(message) { Alert.alert( translate('something_went_wrong'), @@ -62,14 +63,6 @@ function showAcceptAlert(message) { ) } -async function runInSequence(functions) { - const results = [] - for (const fn of functions) { - results.push(await fn()) - } - return results -} - export function EditProfileScreen() { const dispatch = useDispatch() const currentUser = useSelector(selectors.currentUserSelector) @@ -96,139 +89,23 @@ export function EditProfileScreen() { remainingLocations.unshift(currentUser.location) const [showPasscode, setShowPasscode] = React.useState(false) - const tryToEditUserInfo = async () => { - const hasInfoChanged = - name !== currentUser.name || - dateOfBirth !== currentUser.dateOfBirth || - gender !== currentUser.gender || - location !== currentUser.location || - secretQuestion !== currentUser.secretQuestion - if (!hasInfoChanged) { - return null - } - - try { - await httpClient.editUserInfo({ - appToken, - name, - dateOfBirth, - gender, - location, - secretQuestion, - }) - - dispatch( - actions.editUser({ - name, - dateOfBirth, - gender, - location, - secretQuestion, - }), - ) - } catch (err) { - throw new Error(translate('could_not_edit')) // TODO_ALEX: this is not displayed - } + const onConfirm = () => { + // } - const tryToChangeSecretAnswer = async () => { - const hasSecretAnswerChanged = secretAnswer !== '' && formatPassword(oldSecretAnswer) !== '' - if (!hasSecretAnswerChanged) { - return null - } - - try { - await httpClient.editUserSecretAnswer({ - appToken, - previousSecretAnswer: formatPassword(oldSecretAnswer), - nextSecretAnswer: formatPassword(secretAnswer), - }) - - dispatch( - actions.editUser({ - secretAnswer: formatPassword(secretAnswer), - }), - ) - setSecretAnswer('') - } catch (err) { - setSecretAnswer('') - if (err && err.response && err.response.data) { - if (err.response.data.name === 'BadRequestError') { - if (err.response.data.message === 'wrong_previous_secret_answer') { - const message = translate('wrong_old_secret_answer') - throw new Error(message) // TODO_ALEX: this is not displayed - } - } - } - throw new Error(translate('could_not_change_secret')) // TODO_ALEX: this is not displayed - } + const onSecretConfirm = () => { + // } - const tryToChangePassword = async () => { - const hasPasswordChanged = currentUser.password !== password - if (!hasPasswordChanged) { - return null - } - - if (secretAnswer.length === 0) { - setIsVisible(true) - throw new Error() - } - - try { - await httpClient.resetPassword({ - name, - secretAnswer: formatPassword(secretAnswer), - password: formatPassword(password), - }) - - dispatch( - actions.editUser({ - password: formatPassword(password), - }), - ) - } catch (err) { - setSecretAnswer('') - throw new Error('could_not_change_password') // TODO_ALEX: this is not displayed - } + const onResetSecretConfirm = () => { + // } - const saveChanges = async () => { - setIsVisible(false) - // for non-logged user, save the changes locally immediately - if (!appToken) { - const hasSecretAnswerChanged = secretAnswer !== '' && formatPassword(oldSecretAnswer) !== '' - if (hasSecretAnswerChanged) { - if (formatPassword(oldSecretAnswer) !== formatPassword(currentUser.secretAnswer)) { - showAcceptAlert(translate('wrong_old_secret_answer')) - return - } - } - - dispatch( - actions.editUser({ - name, - dateOfBirth, - gender, - password, - location, - secretQuestion, - secretAnswer: - secretAnswer === '' ? currentUser.secretAnswer : formatPassword(secretAnswer), - }), - ) - BackOneScreen() - return - } - try { - await runInSequence([tryToEditUserInfo, tryToChangeSecretAnswer, tryToChangePassword]) - BackOneScreen() - } catch (err) { - if (err.message) { - showAlert(err.message) - } - } - } + /* + When they confirm they will need to + + + */ return ( @@ -346,16 +223,7 @@ export function EditProfileScreen() { - { - setNotValid(false) - if (password.length < minPasswordLength) { - setNotValid(true) - return - } - await saveChanges() - }} - > + confirm @@ -368,11 +236,12 @@ export function EditProfileScreen() { label="secret_answer" value={secretAnswer} /> - + confirm + reset_secret_question @@ -405,15 +274,7 @@ export function EditProfileScreen() { multiline={true} /> - { - if (secretAnswer.length < minPasswordLength) { - setNotValid(true) - return - } - setSecretIsVisible(false) - }} - > + confirm From 620bddcd63dee763840a3d4bea44d1dc56d423a4 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 15 Jan 2024 19:42:35 +0800 Subject: [PATCH 72/83] Save lastLoggedInUsername on new store setup --- packages/components/src/redux/reducers/accessReducer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index 491371002..1487312ce 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -77,6 +77,7 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt }, } + case 'SET_UP_NEW_STORE': case 'INITIATE_STORE_SWITCH': return { ...state, From 71c4da36ae04c3c8f6168a771ecf869670d3484d Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Mon, 15 Jan 2024 19:51:41 +0800 Subject: [PATCH 73/83] Migrate all required state on store switch --- .../components/src/redux/StoreCoordinator.tsx | 19 ++++++++++++----- .../components/src/redux/actions/index.ts | 5 +++-- .../src/redux/reducers/authReducer.ts | 6 ------ .../components/src/redux/reducers/index.ts | 21 ++++++++++++++++++- .../src/redux/reducers/storeSwitchReducer.ts | 6 ------ 5 files changed, 37 insertions(+), 20 deletions(-) diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index c807808e3..1a56d221d 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -10,6 +10,7 @@ import { PersistPartial } from 'redux-persist' import { hash } from '../services/auth' import { AsyncStorage } from 'react-native' import { SimpleSplashScreen } from '../screens/SplashScreen' +import _ from 'lodash' type ReduxPersistState = ReduxState & PersistPartial @@ -50,6 +51,13 @@ const hasMigrated = async () => { return persistKeys.length > 1 } +const migrationBlacklist: ReduxStateProperties[] = [ + '_persist', + 'access', + 'analytics', // TODO:? + 'content', +] + const initialPrimaryBlacklist: ReduxStateProperties[] = [ 'storeSwitch', // Not persisted for security 'content', // Moved to async storage @@ -250,11 +258,12 @@ export function StoreCoordinator({ children }) { } const attemptMigration = () => { - redux.store.dispatch( - actions.migrateStore({ - auth: storeStateSnapshot?.auth, - }), - ) + const stateToMigrate = _.omit( + { ...storeStateSnapshot }, + migrationBlacklist, + ) as ReduxPersistState + + redux.store.dispatch(actions.migrateStore(stateToMigrate)) setTimeout(onTimeout, interval) attempts++ diff --git a/packages/components/src/redux/actions/index.ts b/packages/components/src/redux/actions/index.ts index d19d0aee9..71baca227 100644 --- a/packages/components/src/redux/actions/index.ts +++ b/packages/components/src/redux/actions/index.ts @@ -1,5 +1,4 @@ import { createAction } from '../helpers' -import { ReduxState } from '../reducers' export * from './accessActions' export * from './analyticsActions' @@ -9,6 +8,8 @@ export * from './authActions' export * from './contentActions' export * from './predictionActions' -export function migrateStore(payload: { auth: ReduxState['auth'] }) { +export function migrateStore( + payload: any, // ReduxState +) { return createAction('MIGRATE_STORE', payload) } diff --git a/packages/components/src/redux/reducers/authReducer.ts b/packages/components/src/redux/reducers/authReducer.ts index c7e000207..fc73930a0 100644 --- a/packages/components/src/redux/reducers/authReducer.ts +++ b/packages/components/src/redux/reducers/authReducer.ts @@ -45,12 +45,6 @@ export function authReducer(state = initialState, action: Actions | RehydrateAct ..._.pick(initialState, ['error', 'isLoggingIn', 'loginFailedCount', 'isCreatingAccount']), } - case 'MIGRATE_STORE': - return { - ...state, - ...action.payload.auth, - } - case 'LOGIN_REQUEST': return { ...state, diff --git a/packages/components/src/redux/reducers/index.ts b/packages/components/src/redux/reducers/index.ts index 8b4e1f654..6adfb25cf 100644 --- a/packages/components/src/redux/reducers/index.ts +++ b/packages/components/src/redux/reducers/index.ts @@ -9,6 +9,7 @@ import { contentReducer } from './contentReducer' import { predictionReducer } from './predictionReducer' import { accessReducer } from './accessReducer' import { storeSwitchReducer } from './storeSwitchReducer' +import { Actions } from '../types' export const exportReducerNames = ['app', 'prediction'] @@ -24,7 +25,25 @@ export const allReducers = { // flower: flowerReducer, TODO: Flower state should be saved per user } -export const rootReducer = combineReducers(syncReducers(allReducers, exportReducerNames)) +const reducer = combineReducers(syncReducers(allReducers, exportReducerNames)) + +export function rootReducer(state, action: Actions) { + switch (action.type) { + case 'MIGRATE_STORE': + return { + ...state, + // @ts-ignore + ...action.payload, + storeSwitch: { + ...state.storeSwitch, + migrationComplete: true, + }, + } + + default: + return reducer(state, action) + } +} export type ReduxState = ReturnType diff --git a/packages/components/src/redux/reducers/storeSwitchReducer.ts b/packages/components/src/redux/reducers/storeSwitchReducer.ts index e9f50c391..c91b7ffc9 100644 --- a/packages/components/src/redux/reducers/storeSwitchReducer.ts +++ b/packages/components/src/redux/reducers/storeSwitchReducer.ts @@ -17,12 +17,6 @@ const initialState: StoreSwitchState = { export function storeSwitchReducer(state = initialState, action: Actions): StoreSwitchState { switch (action.type) { - case 'MIGRATE_STORE': - return { - ...state, - migrationComplete: true, - } - case 'SET_STORE_KEYS': return { ...state, From a155d226879b823cbdfe0df2983ab7a5e987a8c4 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Wed, 17 Jan 2024 12:33:07 +0800 Subject: [PATCH 74/83] Move EditProfile into sub folder --- .../index.tsx} | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) rename packages/components/src/screens/{EditProfileScreen.tsx => EditProfileScreen/index.tsx} (89%) diff --git a/packages/components/src/screens/EditProfileScreen.tsx b/packages/components/src/screens/EditProfileScreen/index.tsx similarity index 89% rename from packages/components/src/screens/EditProfileScreen.tsx rename to packages/components/src/screens/EditProfileScreen/index.tsx index e6db95563..d35fbe64c 100644 --- a/packages/components/src/screens/EditProfileScreen.tsx +++ b/packages/components/src/screens/EditProfileScreen/index.tsx @@ -1,28 +1,28 @@ import React from 'react' import styled from 'styled-components/native' import { Alert, Dimensions } from 'react-native' -import { PageContainer } from '../components/layout/PageContainer' -import { BackgroundTheme } from '../components/layout/BackgroundTheme' -import { Header } from '../components/common/Header' -import { Icon } from '../components/common/Icon' -import { SelectBox } from '../components/common/SelectBox' -import { DateOfBirthInput } from '../components/common/DateOfBirthInput' -import { assets } from '../assets/index' -import { useSelector } from '../redux/useSelector' -import * as selectors from '../redux/selectors' -import * as actions from '../redux/actions' +import { PageContainer } from '../../components/layout/PageContainer' +import { BackgroundTheme } from '../../components/layout/BackgroundTheme' +import { Header } from '../../components/common/Header' +import { Icon } from '../../components/common/Icon' +import { SelectBox } from '../../components/common/SelectBox' +import { DateOfBirthInput } from '../../components/common/DateOfBirthInput' +import { assets } from '../../assets/index' +import { useSelector } from '../../redux/useSelector' +import * as selectors from '../../redux/selectors' +import * as actions from '../../redux/actions' import { useDispatch } from 'react-redux' -import { BackOneScreen } from '../services/navigationService' -import { httpClient } from '../services/HttpClient' -import { TextInputSettings } from '../components/common/TextInputSettings' -import { KeyboardAwareAvoidance } from '../components/common/KeyboardAwareAvoidance' -import { ThemedModal } from '../components/common/ThemedModal' -import { Text } from '../components/common/Text' -import { TextInput } from '../components/common/TextInput' -import { VerticalSelectBox } from '../components/common/VerticalSelectBox' -import { translate } from '../i18n' +import { BackOneScreen } from '../../services/navigationService' +import { httpClient } from '../../services/HttpClient' +import { TextInputSettings } from '../../components/common/TextInputSettings' +import { KeyboardAwareAvoidance } from '../../components/common/KeyboardAwareAvoidance' +import { ThemedModal } from '../../components/common/ThemedModal' +import { Text } from '../../components/common/Text' +import { TextInput } from '../../components/common/TextInput' +import { VerticalSelectBox } from '../../components/common/VerticalSelectBox' +import { translate } from '../../i18n' import _ from 'lodash' -import { formatPassword } from '../services/auth' +import { formatPassword, validatePassword } from '../../services/auth' const deviceWidth = Dimensions.get('window').width const minPasswordLength = 1 From 7b662863ae644ac3a3dd17c73b4ff8c74fedc5ac Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Wed, 17 Jan 2024 13:47:56 +0800 Subject: [PATCH 75/83] Install react-native-async-storage --- packages/components/package.json | 1 + packages/mobile/ios/Podfile.lock | 6 ++++++ packages/mobile/package.json | 1 + yarn.lock | 19 +++++++++++++++++++ 4 files changed, 27 insertions(+) diff --git a/packages/components/package.json b/packages/components/package.json index 399fbaf82..d959f68cd 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -14,6 +14,7 @@ "dependencies": { "@oky/core": "*", "@react-native-community/art": "^1.2.0", + "@react-native-async-storage/async-storage": "1.21.0", "circular-buffer": "^1.0.2", "i18n-js": "^3.3.0", "lodash": "^4.17.11", diff --git a/packages/mobile/ios/Podfile.lock b/packages/mobile/ios/Podfile.lock index ecc7f0b59..2835f0fff 100644 --- a/packages/mobile/ios/Podfile.lock +++ b/packages/mobile/ios/Podfile.lock @@ -496,6 +496,8 @@ PODS: - React-perflogger (= 0.66.4) - ReactNativeART (1.2.0): - React + - RNCAsyncStorage (1.21.0): + - React-Core - RNCPicker (2.4.3): - React-Core - RNCPushNotificationIOS (1.10.1): @@ -612,6 +614,7 @@ DEPENDENCIES: - React-runtimeexecutor (from `../../../node_modules/react-native/ReactCommon/runtimeexecutor`) - ReactCommon/turbomodule/core (from `../../../node_modules/react-native/ReactCommon`) - "ReactNativeART (from `../../../node_modules/@react-native-community/art`)" + - "RNCAsyncStorage (from `../../../node_modules/@react-native-async-storage/async-storage`)" - "RNCPicker (from `../../../node_modules/@react-native-picker/picker`)" - "RNCPushNotificationIOS (from `../../../node_modules/@react-native-community/push-notification-ios`)" - RNDeviceInfo (from `../../../node_modules/react-native-device-info`) @@ -740,6 +743,8 @@ EXTERNAL SOURCES: :path: "../../../node_modules/react-native/ReactCommon" ReactNativeART: :path: "../../../node_modules/@react-native-community/art" + RNCAsyncStorage: + :path: "../../../node_modules/@react-native-async-storage/async-storage" RNCPicker: :path: "../../../node_modules/@react-native-picker/picker" RNCPushNotificationIOS: @@ -838,6 +843,7 @@ SPEC CHECKSUMS: React-runtimeexecutor: dec32ee6f2e2a26e13e58152271535fadff5455a ReactCommon: 57b69f6383eafcbd7da625bfa6003810332313c4 ReactNativeART: 78edc68dd4a1e675338cd0cd113319cf3a65f2ab + RNCAsyncStorage: 618d03a5f52fbccb3d7010076bc54712844c18ef RNCPicker: a99807a74d9cd65c394f6e2a6c843a377b355b4d RNCPushNotificationIOS: 87b8d16d3ede4532745e05b03c42cff33a36cc45 RNDeviceInfo: 9538a884f862fe4aa0d7cead9f34e292d41ba8f6 diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 4b8b3ce6e..8a367b4c0 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -26,6 +26,7 @@ "@react-native-firebase/messaging": "^16.4.6", "@react-native-picker/picker": "^2.2.1", "@types/react-native-push-notification": "^8.1.1", + "@react-native-async-storage/async-storage": "1.21.0", "lottie-ios": "3.2.3", "lottie-react-native": "5.1.3", "react": "17.0.2", diff --git a/yarn.lock b/yarn.lock index d56bb1403..21378f726 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2036,6 +2036,13 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== +"@react-native-async-storage/async-storage@1.21.0": + version "1.21.0" + resolved "https://registry.yarnpkg.com/@react-native-async-storage/async-storage/-/async-storage-1.21.0.tgz#d7e370028e228ab84637016ceeb495878b7a44c8" + integrity sha512-JL0w36KuFHFCvnbOXRekqVAUplmOyT/OuCQkogo6X98MtpSaJOKEAeZnYO8JB0U/RIEixZaGI5px73YbRm/oag== + dependencies: + merge-options "^3.0.4" + "@react-native-community/art@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@react-native-community/art/-/art-1.2.0.tgz#386d95393f6042d9006f9d4bc6063fb898794460" @@ -6499,6 +6506,11 @@ is-path-inside@^1.0.0: dependencies: path-is-inside "^1.0.1" +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -7939,6 +7951,13 @@ merge-descriptors@1.0.1: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== +merge-options@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/merge-options/-/merge-options-3.0.4.tgz#84709c2aa2a4b24c1981f66c179fe5565cc6dbb7" + integrity sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ== + dependencies: + is-plain-obj "^2.1.0" + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" From 56499cebf9b91c46909adad20218a65203e6860b Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Wed, 17 Jan 2024 14:00:56 +0800 Subject: [PATCH 76/83] Refactor using async-storage --- .../components/src/redux/StoreCoordinator.tsx | 6 +-- packages/components/src/storage/index.ts | 44 +++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 packages/components/src/storage/index.ts diff --git a/packages/components/src/redux/StoreCoordinator.tsx b/packages/components/src/redux/StoreCoordinator.tsx index 1a56d221d..6607c6851 100644 --- a/packages/components/src/redux/StoreCoordinator.tsx +++ b/packages/components/src/redux/StoreCoordinator.tsx @@ -8,9 +8,9 @@ import { rootSaga } from './sagas' import * as actions from './actions' import { PersistPartial } from 'redux-persist' import { hash } from '../services/auth' -import { AsyncStorage } from 'react-native' import { SimpleSplashScreen } from '../screens/SplashScreen' import _ from 'lodash' +import Storage from '../storage' type ReduxPersistState = ReduxState & PersistPartial @@ -39,12 +39,12 @@ const StoreCoordinatorContext = React.createContext({ }) const checkStoreExists = async (usernameHash: string) => { - const keys = await AsyncStorage.getAllKeys() + const keys = await Storage.getAllKeys() return keys.includes(`persist:${usernameHash}`) } const hasMigrated = async () => { - const keys = await AsyncStorage.getAllKeys() + const keys = await Storage.getAllKeys() const persistKeys = keys.filter((key) => { return key.startsWith('persist:') }) diff --git a/packages/components/src/storage/index.ts b/packages/components/src/storage/index.ts new file mode 100644 index 000000000..d8f2b5c0c --- /dev/null +++ b/packages/components/src/storage/index.ts @@ -0,0 +1,44 @@ +import AsyncStorage from '@react-native-async-storage/async-storage' + +const storeString = async ({ key, value }: { key: string; value: string }) => { + try { + await AsyncStorage.setItem(key, value) + } catch (e) { + // saving error + } +} + +const storeObject = async ({ key, value }: { key: string; value: object }) => { + try { + const jsonValue = JSON.stringify(value) + await AsyncStorage.setItem(key, jsonValue) + } catch (e) { + // saving error + } +} + +const getData = async (key: string) => { + try { + const value = await AsyncStorage.getItem(key) + return value + } catch (e) { + // error reading value + } +} + +const getAllKeys = async () => { + try { + return await AsyncStorage.getAllKeys() + } catch (e) { + // error reading value + } +} + +const Storage = { + storeString, + storeObject, + getData, + getAllKeys, +} + +export default Storage From 3b8dc91a1e2f0433aff0e76c2ffec1e7eb4055ae Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Wed, 17 Jan 2024 15:16:47 +0800 Subject: [PATCH 77/83] Persist common redux state with AsyncStorage --- .../src/redux/actions/commonActions.ts | 8 ++++ .../components/src/redux/actions/index.ts | 1 + .../src/redux/reducers/accessReducer.ts | 6 +++ .../components/src/redux/sagas/commonSaga.ts | 44 +++++++++++++++++++ packages/components/src/redux/sagas/index.ts | 2 + .../src/redux/selectors/commonSelectors.ts | 5 +++ .../components/src/redux/selectors/index.ts | 1 + 7 files changed, 67 insertions(+) create mode 100644 packages/components/src/redux/actions/commonActions.ts create mode 100644 packages/components/src/redux/sagas/commonSaga.ts create mode 100644 packages/components/src/redux/selectors/commonSelectors.ts diff --git a/packages/components/src/redux/actions/commonActions.ts b/packages/components/src/redux/actions/commonActions.ts new file mode 100644 index 000000000..be16ccfa0 --- /dev/null +++ b/packages/components/src/redux/actions/commonActions.ts @@ -0,0 +1,8 @@ +import { createAction } from '../helpers' +import { ReduxState } from '../reducers' + +export function setCommonState(payload: { + access: ReduxState['access'] // +}) { + return createAction('SET_COMMON_STATE', payload) +} diff --git a/packages/components/src/redux/actions/index.ts b/packages/components/src/redux/actions/index.ts index 71baca227..8e2c14e2c 100644 --- a/packages/components/src/redux/actions/index.ts +++ b/packages/components/src/redux/actions/index.ts @@ -5,6 +5,7 @@ export * from './analyticsActions' export * from './answerActions' export * from './appActions' export * from './authActions' +export * from './commonActions' export * from './contentActions' export * from './predictionActions' diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index 1487312ce..db5fb9c63 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -40,6 +40,12 @@ const initialState = (): AccessState => ({ export function accessReducer(state = initialState(), action: Actions): AccessState { switch (action.type) { + case 'SET_COMMON_STATE': + return { + ...state, + ...action.payload.access, + } + case 'SAVE_STORE_CREDENTIALS': { return { ...state, diff --git a/packages/components/src/redux/sagas/commonSaga.ts b/packages/components/src/redux/sagas/commonSaga.ts new file mode 100644 index 000000000..89bcb88c4 --- /dev/null +++ b/packages/components/src/redux/sagas/commonSaga.ts @@ -0,0 +1,44 @@ +import { all, call, put, select, takeLatest } from 'redux-saga/effects' +import AsyncStorage from '@react-native-async-storage/async-storage' +import * as actions from '../actions' +import * as selectors from '../selectors' +import { REHYDRATE } from 'redux-persist' +import { ActionTypes } from '../types' + +const ACTIONS_TO_TRACK: ActionTypes[] = [ + // Access actions + 'SAVE_STORE_CREDENTIALS', + 'SET_STORE_KEYS', + 'CLEAR_LAST_LOGIN', + 'DELETE_USER_ACCESS', + 'EDIT_PASSWORD', +] + +function* onRehydrate() { + try { + const commonStateString = yield call([AsyncStorage, 'getItem'], 'common') + const commonState = commonStateString ? JSON.parse(commonStateString) : {} + + // @ts-ignore TODO: + yield put(actions.setCommonState(commonState)) + } catch (error) { + // Handle errors (e.g., dispatch an error action or log the error) + } +} + +function* onUpdateCommonState() { + try { + const commonState = yield select(selectors.commonStateSelector) + const commonStateString = JSON.stringify(commonState) + yield call([AsyncStorage, 'setItem'], 'common', commonStateString) + } catch (error) { + // Handle errors + } +} + +export function* commonSaga() { + yield all([ + takeLatest(REHYDRATE, onRehydrate), // + takeLatest(ACTIONS_TO_TRACK, onUpdateCommonState), + ]) +} diff --git a/packages/components/src/redux/sagas/index.ts b/packages/components/src/redux/sagas/index.ts index 9617fcd84..f16e3d1f5 100644 --- a/packages/components/src/redux/sagas/index.ts +++ b/packages/components/src/redux/sagas/index.ts @@ -5,12 +5,14 @@ import { appSaga } from './appSaga' import { authSaga } from './authSaga' import { contentSaga } from './contentSaga' import { smartPredictionbSaga } from './smartPredictionSaga' +import { commonSaga } from './commonSaga' export function* rootSaga() { yield all([ fork(analyticsSaga), fork(appSaga), fork(authSaga), + fork(commonSaga), fork(contentSaga), fork(smartPredictionbSaga), ]) diff --git a/packages/components/src/redux/selectors/commonSelectors.ts b/packages/components/src/redux/selectors/commonSelectors.ts new file mode 100644 index 000000000..f4e5416e2 --- /dev/null +++ b/packages/components/src/redux/selectors/commonSelectors.ts @@ -0,0 +1,5 @@ +import { ReduxState } from '../reducers' + +export const commonStateSelector = (state: ReduxState) => ({ + access: state.access, +}) diff --git a/packages/components/src/redux/selectors/index.ts b/packages/components/src/redux/selectors/index.ts index c9e1f63e4..ecb5165f4 100644 --- a/packages/components/src/redux/selectors/index.ts +++ b/packages/components/src/redux/selectors/index.ts @@ -3,4 +3,5 @@ export * from './analyticsSelectors' export * from './answerSelectors' export * from './appSelectors' export * from './authSelectors' +export * from './commonSelectors' export * from './contentSelectors' From 4a640331bbfc4526f42fd6a478cb94d05c3b5551 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Wed, 17 Jan 2024 15:35:11 +0800 Subject: [PATCH 78/83] Edit answer --- .../src/redux/actions/accessActions.ts | 9 ++ .../src/redux/reducers/accessReducer.ts | 14 ++ .../components/src/redux/sagas/commonSaga.ts | 1 + .../EditSecretQuestionModal.tsx | 129 ++++++++++++++++ .../src/screens/EditProfileScreen/index.tsx | 142 ++++++++++-------- 5 files changed, 234 insertions(+), 61 deletions(-) create mode 100644 packages/components/src/screens/EditProfileScreen/EditSecretQuestionModal.tsx diff --git a/packages/components/src/redux/actions/accessActions.ts b/packages/components/src/redux/actions/accessActions.ts index 838e9d59d..3839f3948 100644 --- a/packages/components/src/redux/actions/accessActions.ts +++ b/packages/components/src/redux/actions/accessActions.ts @@ -35,3 +35,12 @@ export function editPassword(payload: { }) { return createAction('EDIT_PASSWORD', payload) } + +export function editAnswer(payload: { + usernameHash: string + answerSalt: string + answerHash: string + secretKeyEncryptedWithAnswer: string +}) { + return createAction('EDIT_ANSWER', payload) +} diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index db5fb9c63..b6676e923 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -83,6 +83,20 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt }, } + case 'EDIT_ANSWER': + return { + ...state, + storeCredentials: { + ...state.storeCredentials, + [action.payload.usernameHash]: { + ...state.storeCredentials[action.payload.usernameHash], + answerSalt: action.payload.answerSalt, + answerHash: action.payload.answerHash, + secretKeyEncryptedWithAnswer: action.payload.secretKeyEncryptedWithAnswer, + }, + }, + } + case 'SET_UP_NEW_STORE': case 'INITIATE_STORE_SWITCH': return { diff --git a/packages/components/src/redux/sagas/commonSaga.ts b/packages/components/src/redux/sagas/commonSaga.ts index 89bcb88c4..56dff7816 100644 --- a/packages/components/src/redux/sagas/commonSaga.ts +++ b/packages/components/src/redux/sagas/commonSaga.ts @@ -12,6 +12,7 @@ const ACTIONS_TO_TRACK: ActionTypes[] = [ 'CLEAR_LAST_LOGIN', 'DELETE_USER_ACCESS', 'EDIT_PASSWORD', + 'EDIT_ANSWER', ] function* onRehydrate() { diff --git a/packages/components/src/screens/EditProfileScreen/EditSecretQuestionModal.tsx b/packages/components/src/screens/EditProfileScreen/EditSecretQuestionModal.tsx new file mode 100644 index 000000000..21f72a76a --- /dev/null +++ b/packages/components/src/screens/EditProfileScreen/EditSecretQuestionModal.tsx @@ -0,0 +1,129 @@ +import React from 'react' +import { validatePassword } from '../../services/auth' +import { ThemedModal } from '../../components/common/ThemedModal' +import { VerticalSelectBox } from '../../components/common/VerticalSelectBox' +import { TextInput } from '../../components/common/TextInput' +import styled from 'styled-components/native' +import { Text } from '../../components/common/Text' + +const secretQuestions = [ + 'secret_question', + `favourite_actor`, + `favourite_teacher`, + `childhood_hero`, +] + +export const EditSecretQuestionModal = ({ + isVisible, + setIsVisible, + onConfirm, +}: { + isVisible: boolean + setIsVisible: (val: boolean) => void + onConfirm: (result: { currentAnswer: string; newAnswer: string; question: string }) => void +}) => { + const [oldSecretAnswer, setOldSecretAnswer] = React.useState('') + const [secretQuestion, setSecretQuestion] = React.useState(secretQuestions[0]) + const [secretAnswer, setSecretAnswer] = React.useState('') + + const isValid = validatePassword(secretAnswer) + + React.useEffect(() => { + setOldSecretAnswer('') + setSecretQuestion('') + setSecretAnswer('') + }, [isVisible]) + + return ( + + + reset_secret_question + + + setOldSecretAnswer(value)} + label="old_secret_answer" + value={oldSecretAnswer} + /> + (questions ? questions : ''))} + containerStyle={{ + height: 45, + borderRadius: 22.5, + }} + height={45} + maxLength={20} + buttonStyle={{ right: 5, bottom: 7 }} + onValueChange={(value) => setSecretQuestion(value)} + errorHeading="secret_q_error_heading" + errorContent="secret_que_info" + /> + setSecretAnswer(value)} + label="secret_answer" + isValid={isValid} + hasError={!isValid} + value={secretAnswer} + multiline={true} + /> + + + onConfirm({ + currentAnswer: oldSecretAnswer, + newAnswer: secretAnswer, + question: secretQuestion, + }) + } + > + confirm + + + + ) +} + +const TextContainer = styled.View` + justify-content: center; + align-items: center; + width: 100%; + shadow-color: #efefef; + shadow-offset: 0px 2px; + shadow-opacity: 1; + shadow-radius: 2px; +` + +const Confirm = styled.TouchableOpacity` + width: 100%; + height: 45px; + border-radius: 22.5px; + background-color: #a2c72d; + align-items: center; + justify-content: center; + margin-bottom: 10px; + margin-top: 10px; +` + +const CardModal = styled.View` + width: 90%; + height: 400px; + background-color: #fff; + border-radius: 10px; + padding-horizontal: 20px; + padding-vertical: 20px; + align-items: center; + justify-content: space-around; + align-self: center; +` + +const ConfirmText = styled(Text)` + font-family: Roboto-Black; + text-align: center; + font-size: 16; + color: #fff; +` + +const QuestionText = styled(Text)` + font-size: 16; + text-align: center; +` diff --git a/packages/components/src/screens/EditProfileScreen/index.tsx b/packages/components/src/screens/EditProfileScreen/index.tsx index d35fbe64c..f6a081c0f 100644 --- a/packages/components/src/screens/EditProfileScreen/index.tsx +++ b/packages/components/src/screens/EditProfileScreen/index.tsx @@ -19,20 +19,22 @@ import { KeyboardAwareAvoidance } from '../../components/common/KeyboardAwareAvo import { ThemedModal } from '../../components/common/ThemedModal' import { Text } from '../../components/common/Text' import { TextInput } from '../../components/common/TextInput' -import { VerticalSelectBox } from '../../components/common/VerticalSelectBox' import { translate } from '../../i18n' import _ from 'lodash' -import { formatPassword, validatePassword } from '../../services/auth' +import { + decrypt, + encrypt, + formatPassword, + hash, + validatePassword, + verifyStoreCredentials, +} from '../../services/auth' +import { EditSecretQuestionModal } from './EditSecretQuestionModal' +import { v4 as uuidv4 } from 'uuid' const deviceWidth = Dimensions.get('window').width const minPasswordLength = 1 const inputWidth = deviceWidth - 180 -const secretQuestions = [ - 'secret_question', - `favourite_actor`, - `favourite_teacher`, - `childhood_hero`, -] function showAlert(message) { Alert.alert( @@ -64,21 +66,20 @@ function showAcceptAlert(message) { } export function EditProfileScreen() { - const dispatch = useDispatch() + const reduxDispatch = useDispatch() const currentUser = useSelector(selectors.currentUserSelector) const appToken = useSelector(selectors.appTokenSelector) + const storeCredentials = useSelector(selectors.storeCredentialsSelector) const [name, setName] = React.useState(currentUser.name) - const [notValid, setNotValid] = React.useState(false) const [dateOfBirth, setDateOfBirth] = React.useState(currentUser.dateOfBirth) const [gender, setGender] = React.useState(currentUser.gender) const [location, setLocation] = React.useState(currentUser.location) const [password, setPassword] = React.useState(currentUser.password) const [secretAnswer, setSecretAnswer] = React.useState('') - const [oldSecretAnswer, setOldSecretAnswer] = React.useState('') - const [secretQuestion, setSecretQuestion] = React.useState(currentUser.secretQuestion) const [isVisible, setIsVisible] = React.useState(false) - const [secretIsVisible, setSecretIsVisible] = React.useState(false) + + const [isSecretModalVisible, setIsSecretModalVisible] = React.useState(false) const remainingGenders = ['Female', 'Male', 'Other'].filter((item) => { return item !== currentUser.gender @@ -97,15 +98,65 @@ export function EditProfileScreen() { // } - const onResetSecretConfirm = () => { - // - } + const onConfirmResetQuestion = async ({ + currentAnswer, + newAnswer, + }: // question, + { + currentAnswer: string + newAnswer: string + question: string + }) => { + const usernameHash = hash(currentUser.name) + const credentials = storeCredentials[usernameHash] + + if (!credentials) { + return // TODO: ERROR ? + } + + const currentAnswerCorrect = verifyStoreCredentials({ + username: currentUser.name, + password: currentAnswer, + storeCredentials, + method: 'answer', + }) + + if (!currentAnswerCorrect) { + return // TODO: Show alert + } + + const secretKey = decrypt(credentials.secretKeyEncryptedWithAnswer, currentAnswer) + + const answer = formatPassword(newAnswer) + const secretKeyEncryptedWithAnswer = encrypt(secretKey, answer) + + const answerSalt = uuidv4() + const answerHash = hash(answer + answerSalt) + + try { + // TODO: Check user is guest - /* - When they confirm they will need to - - - */ + await httpClient.editUserSecretAnswer({ + appToken, + previousSecretAnswer: formatPassword(currentAnswer), + nextSecretAnswer: answer, + }) + + // Update redux AFTER successful API request + reduxDispatch( + actions.editAnswer({ + usernameHash, + answerSalt, + answerHash, + secretKeyEncryptedWithAnswer, + }), + ) + + setIsSecretModalVisible(false) + } catch (err) { + // TODO: Show alert, update failed + } + } return ( @@ -203,7 +254,7 @@ export function EditProfileScreen() { style={{ height: '100%', justifyContent: 'space-between', marginBottom: 0 }} label="password" isValid={password.length >= minPasswordLength} - hasError={notValid && !(password.length >= minPasswordLength)} + // hasError={notValid && !(password.length >= minPasswordLength)} onFocus={() => setShowPasscode(true)} onBlur={() => setShowPasscode(false)} secureTextEntry={!showPasscode} @@ -217,7 +268,7 @@ export function EditProfileScreen() { source={assets.static.icons.shieldL} style={{ marginRight: 38, height: 57, width: 57 }} /> - setSecretIsVisible(true)}> + setIsSecretModalVisible(true)}> change_secret @@ -227,7 +278,8 @@ export function EditProfileScreen() { confirm - {/* --------------------------------- modals --------------------------------- */} + + {/* ===== Modals ===== */} null }}> reset_password_question @@ -242,43 +294,11 @@ export function EditProfileScreen() { - - - reset_secret_question - - - setOldSecretAnswer(value)} - label="old_secret_answer" - value={oldSecretAnswer} - /> - (questions ? questions : ''))} - containerStyle={{ - height: 45, - borderRadius: 22.5, - }} - height={45} - maxLength={20} - buttonStyle={{ right: 5, bottom: 7 }} - onValueChange={(value) => setSecretQuestion(value)} - errorHeading="secret_q_error_heading" - errorContent="secret_que_info" - /> - setSecretAnswer(value)} - label="secret_answer" - isValid={secretAnswer.length >= minPasswordLength} - hasError={notValid && !(secretAnswer.length >= minPasswordLength)} - value={secretAnswer} - multiline={true} - /> - - - confirm - - - + ) } From 9fcdca90b90daa1a1df1d86698a3e8f01fda9377 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Wed, 17 Jan 2024 16:01:09 +0800 Subject: [PATCH 79/83] Edit password --- .../EditProfileScreen/EditPasswordModal.tsx | 106 ++++++++++++++++++ .../src/screens/EditProfileScreen/index.tsx | 105 ++++++++++++----- 2 files changed, 180 insertions(+), 31 deletions(-) create mode 100644 packages/components/src/screens/EditProfileScreen/EditPasswordModal.tsx diff --git a/packages/components/src/screens/EditProfileScreen/EditPasswordModal.tsx b/packages/components/src/screens/EditProfileScreen/EditPasswordModal.tsx new file mode 100644 index 000000000..5e3f9b3cb --- /dev/null +++ b/packages/components/src/screens/EditProfileScreen/EditPasswordModal.tsx @@ -0,0 +1,106 @@ +import React from 'react' +import { validatePassword } from '../../services/auth' +import { ThemedModal } from '../../components/common/ThemedModal' +import { TextInput } from '../../components/common/TextInput' +import styled from 'styled-components/native' +import { Text } from '../../components/common/Text' + +export const EditPasswordModal = ({ + isVisible, + setIsVisible, + onConfirm, +}: { + isVisible: boolean + setIsVisible: (val: boolean) => void + onConfirm: (result: { answer: string; newPassword: string }) => void +}) => { + const [answer, setAnswer] = React.useState('') + const [newPassword, setNewPassword] = React.useState('') + + const isValid = validatePassword(newPassword) + + React.useEffect(() => { + setAnswer('') + setNewPassword('') + }, [isVisible]) + + return ( + + + reset_secret_question + + + setAnswer(value)} + label="old_secret_answer" + value={answer} + /> + setNewPassword(value)} + label="secret_answer" + isValid={isValid} + hasError={!isValid} + value={newPassword} + multiline={true} + /> + + + onConfirm({ + answer, + newPassword, + }) + } + > + confirm + + + + ) +} + +const TextContainer = styled.View` + justify-content: center; + align-items: center; + width: 100%; + shadow-color: #efefef; + shadow-offset: 0px 2px; + shadow-opacity: 1; + shadow-radius: 2px; +` + +const Confirm = styled.TouchableOpacity` + width: 100%; + height: 45px; + border-radius: 22.5px; + background-color: #a2c72d; + align-items: center; + justify-content: center; + margin-bottom: 10px; + margin-top: 10px; +` + +const CardModal = styled.View` + width: 90%; + height: 400px; + background-color: #fff; + border-radius: 10px; + padding-horizontal: 20px; + padding-vertical: 20px; + align-items: center; + justify-content: space-around; + align-self: center; +` + +const ConfirmText = styled(Text)` + font-family: Roboto-Black; + text-align: center; + font-size: 16; + color: #fff; +` + +const QuestionText = styled(Text)` + font-size: 16; + text-align: center; +` diff --git a/packages/components/src/screens/EditProfileScreen/index.tsx b/packages/components/src/screens/EditProfileScreen/index.tsx index f6a081c0f..e5d9bd546 100644 --- a/packages/components/src/screens/EditProfileScreen/index.tsx +++ b/packages/components/src/screens/EditProfileScreen/index.tsx @@ -31,6 +31,7 @@ import { } from '../../services/auth' import { EditSecretQuestionModal } from './EditSecretQuestionModal' import { v4 as uuidv4 } from 'uuid' +import { EditPasswordModal } from './EditPasswordModal' const deviceWidth = Dimensions.get('window').width const minPasswordLength = 1 @@ -75,10 +76,8 @@ export function EditProfileScreen() { const [dateOfBirth, setDateOfBirth] = React.useState(currentUser.dateOfBirth) const [gender, setGender] = React.useState(currentUser.gender) const [location, setLocation] = React.useState(currentUser.location) - const [password, setPassword] = React.useState(currentUser.password) - const [secretAnswer, setSecretAnswer] = React.useState('') - const [isVisible, setIsVisible] = React.useState(false) + const [isPasswordModalVisible, setIsPasswordModalVisible] = React.useState(false) const [isSecretModalVisible, setIsSecretModalVisible] = React.useState(false) const remainingGenders = ['Female', 'Male', 'Other'].filter((item) => { @@ -88,7 +87,6 @@ export function EditProfileScreen() { return item !== currentUser.location }) remainingLocations.unshift(currentUser.location) - const [showPasscode, setShowPasscode] = React.useState(false) const onConfirm = () => { // @@ -98,6 +96,66 @@ export function EditProfileScreen() { // } + const onConfirmPassword = async ({ + answer, + newPassword, + }: { + answer: string + newPassword: string + }) => { + const usernameHash = hash(currentUser.name) + const credentials = storeCredentials[usernameHash] + + if (!credentials) { + return // TODO: ERROR ? + } + + const secretAnswer = formatPassword(answer) + + const currentAnswerCorrect = verifyStoreCredentials({ + username: currentUser.name, + password: secretAnswer, + storeCredentials, + method: 'answer', + }) + + if (!currentAnswerCorrect) { + return // TODO: Show alert + } + + const secretKey = decrypt(credentials.secretKeyEncryptedWithAnswer, secretAnswer) + + const password = formatPassword(newPassword) + const secretKeyEncryptedWithPassword = encrypt(secretKey, password) + + const passwordSalt = uuidv4() + const passwordHash = hash(password + passwordSalt) + + try { + // TODO: Check user is guest + + await httpClient.resetPassword({ + name, + secretAnswer, + password, + }) + + // Update redux AFTER successful API request + reduxDispatch( + actions.editPassword({ + usernameHash, + passwordSalt, + passwordHash, + secretKeyEncryptedWithPassword, + }), + ) + + setIsSecretModalVisible(false) + } catch (err) { + // TODO: Show alert, update failed + } + } + const onConfirmResetQuestion = async ({ currentAnswer, newAnswer, @@ -125,7 +183,10 @@ export function EditProfileScreen() { return // TODO: Show alert } - const secretKey = decrypt(credentials.secretKeyEncryptedWithAnswer, currentAnswer) + const secretKey = decrypt( + credentials.secretKeyEncryptedWithAnswer, + formatPassword(currentAnswer), + ) const answer = formatPassword(newAnswer) const secretKeyEncryptedWithAnswer = encrypt(secretKey, answer) @@ -249,19 +310,9 @@ export function EditProfileScreen() { source={assets.static.icons.lockL} style={{ marginRight: 38, height: 57, width: 57 }} /> - setPassword(text)} - style={{ height: '100%', justifyContent: 'space-between', marginBottom: 0 }} - label="password" - isValid={password.length >= minPasswordLength} - // hasError={notValid && !(password.length >= minPasswordLength)} - onFocus={() => setShowPasscode(true)} - onBlur={() => setShowPasscode(false)} - secureTextEntry={!showPasscode} - underlineStyle={{ width: inputWidth }} - inputStyle={{ fontFamily: 'Roboto-Black' }} - value={password} - /> + setIsPasswordModalVisible(true)}> + change_secret + {/* ===== Modals ===== */} - null }}> - - reset_password_question - setSecretAnswer(value)} - label="secret_answer" - value={secretAnswer} - /> - - confirm - - - + Date: Wed, 17 Jan 2024 16:01:21 +0800 Subject: [PATCH 80/83] New answer must be valid --- .../src/screens/EditProfileScreen/EditSecretQuestionModal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/src/screens/EditProfileScreen/EditSecretQuestionModal.tsx b/packages/components/src/screens/EditProfileScreen/EditSecretQuestionModal.tsx index 21f72a76a..97da03ad9 100644 --- a/packages/components/src/screens/EditProfileScreen/EditSecretQuestionModal.tsx +++ b/packages/components/src/screens/EditProfileScreen/EditSecretQuestionModal.tsx @@ -68,6 +68,7 @@ export const EditSecretQuestionModal = ({ /> onConfirm({ currentAnswer: oldSecretAnswer, From aa335f70d3fa12384bea431c7d2aea52d4d53d98 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Wed, 17 Jan 2024 16:07:23 +0800 Subject: [PATCH 81/83] Refactor EditProfile logic into hook --- .../src/screens/EditProfileScreen/index.tsx | 201 ++---------------- .../EditProfileScreen/useEditProfile.ts | 152 +++++++++++++ 2 files changed, 164 insertions(+), 189 deletions(-) create mode 100644 packages/components/src/screens/EditProfileScreen/useEditProfile.ts diff --git a/packages/components/src/screens/EditProfileScreen/index.tsx b/packages/components/src/screens/EditProfileScreen/index.tsx index e5d9bd546..3e66d6927 100644 --- a/packages/components/src/screens/EditProfileScreen/index.tsx +++ b/packages/components/src/screens/EditProfileScreen/index.tsx @@ -10,31 +10,17 @@ import { DateOfBirthInput } from '../../components/common/DateOfBirthInput' import { assets } from '../../assets/index' import { useSelector } from '../../redux/useSelector' import * as selectors from '../../redux/selectors' -import * as actions from '../../redux/actions' -import { useDispatch } from 'react-redux' import { BackOneScreen } from '../../services/navigationService' -import { httpClient } from '../../services/HttpClient' import { TextInputSettings } from '../../components/common/TextInputSettings' import { KeyboardAwareAvoidance } from '../../components/common/KeyboardAwareAvoidance' -import { ThemedModal } from '../../components/common/ThemedModal' import { Text } from '../../components/common/Text' -import { TextInput } from '../../components/common/TextInput' import { translate } from '../../i18n' import _ from 'lodash' -import { - decrypt, - encrypt, - formatPassword, - hash, - validatePassword, - verifyStoreCredentials, -} from '../../services/auth' import { EditSecretQuestionModal } from './EditSecretQuestionModal' -import { v4 as uuidv4 } from 'uuid' import { EditPasswordModal } from './EditPasswordModal' +import { useEditProfile } from './useEditProfile' const deviceWidth = Dimensions.get('window').width -const minPasswordLength = 1 const inputWidth = deviceWidth - 180 function showAlert(message) { @@ -67,19 +53,13 @@ function showAcceptAlert(message) { } export function EditProfileScreen() { - const reduxDispatch = useDispatch() const currentUser = useSelector(selectors.currentUserSelector) - const appToken = useSelector(selectors.appTokenSelector) - const storeCredentials = useSelector(selectors.storeCredentialsSelector) const [name, setName] = React.useState(currentUser.name) const [dateOfBirth, setDateOfBirth] = React.useState(currentUser.dateOfBirth) const [gender, setGender] = React.useState(currentUser.gender) const [location, setLocation] = React.useState(currentUser.location) - const [isPasswordModalVisible, setIsPasswordModalVisible] = React.useState(false) - const [isSecretModalVisible, setIsSecretModalVisible] = React.useState(false) - const remainingGenders = ['Female', 'Male', 'Other'].filter((item) => { return item !== currentUser.gender }) @@ -92,132 +72,16 @@ export function EditProfileScreen() { // } - const onSecretConfirm = () => { - // - } - - const onConfirmPassword = async ({ - answer, - newPassword, - }: { - answer: string - newPassword: string - }) => { - const usernameHash = hash(currentUser.name) - const credentials = storeCredentials[usernameHash] - - if (!credentials) { - return // TODO: ERROR ? - } - - const secretAnswer = formatPassword(answer) - - const currentAnswerCorrect = verifyStoreCredentials({ - username: currentUser.name, - password: secretAnswer, - storeCredentials, - method: 'answer', - }) - - if (!currentAnswerCorrect) { - return // TODO: Show alert - } - - const secretKey = decrypt(credentials.secretKeyEncryptedWithAnswer, secretAnswer) - - const password = formatPassword(newPassword) - const secretKeyEncryptedWithPassword = encrypt(secretKey, password) - - const passwordSalt = uuidv4() - const passwordHash = hash(password + passwordSalt) - - try { - // TODO: Check user is guest - - await httpClient.resetPassword({ - name, - secretAnswer, - password, - }) - - // Update redux AFTER successful API request - reduxDispatch( - actions.editPassword({ - usernameHash, - passwordSalt, - passwordHash, - secretKeyEncryptedWithPassword, - }), - ) - - setIsSecretModalVisible(false) - } catch (err) { - // TODO: Show alert, update failed - } - } - - const onConfirmResetQuestion = async ({ - currentAnswer, - newAnswer, - }: // question, - { - currentAnswer: string - newAnswer: string - question: string - }) => { - const usernameHash = hash(currentUser.name) - const credentials = storeCredentials[usernameHash] - - if (!credentials) { - return // TODO: ERROR ? - } - - const currentAnswerCorrect = verifyStoreCredentials({ - username: currentUser.name, - password: currentAnswer, - storeCredentials, - method: 'answer', - }) - - if (!currentAnswerCorrect) { - return // TODO: Show alert - } - - const secretKey = decrypt( - credentials.secretKeyEncryptedWithAnswer, - formatPassword(currentAnswer), - ) - - const answer = formatPassword(newAnswer) - const secretKeyEncryptedWithAnswer = encrypt(secretKey, answer) - - const answerSalt = uuidv4() - const answerHash = hash(answer + answerSalt) - - try { - // TODO: Check user is guest - - await httpClient.editUserSecretAnswer({ - appToken, - previousSecretAnswer: formatPassword(currentAnswer), - nextSecretAnswer: answer, - }) - - // Update redux AFTER successful API request - reduxDispatch( - actions.editAnswer({ - usernameHash, - answerSalt, - answerHash, - secretKeyEncryptedWithAnswer, - }), - ) - - setIsSecretModalVisible(false) - } catch (err) { - // TODO: Show alert, update failed - } - } + const { + // State + isPasswordModalVisible, + setIsPasswordModalVisible, + isSecretModalVisible, + setIsSecretModalVisible, + // Methods + onConfirmPassword, + onConfirmResetQuestion, + } = useEditProfile() return ( @@ -346,15 +210,6 @@ export function EditProfileScreen() { ) } -const TextContainer = styled.View` - justify-content: center; - align-items: center; - width: 100%; - shadow-color: #efefef; - shadow-offset: 0px 2px; - shadow-opacity: 1; - shadow-radius: 2px; -` const Container = styled.View` background-color: #fff; elevation: 4; @@ -365,30 +220,13 @@ const Container = styled.View` padding-horizontal: 30px; padding-vertical: 22px; ` -const TextRow = styled.View` - width: 100% - flex-direction: row; - align-items: center; - justify-content: space-between; - margin-bottom: 10px; - background-color: red; -` + const Row = styled.View` flex-direction: row; height: 57px; align-items: flex-end; margin-bottom: 4px; ` -const Confirm = styled.TouchableOpacity` - width: 100%; - height: 45px; - border-radius: 22.5px; - background-color: #a2c72d; - align-items: center; - justify-content: center; - margin-bottom: 10px; - margin-top: 10px; -` const ChangeSecretButton = styled.TouchableOpacity` width: ${inputWidth}; @@ -411,24 +249,9 @@ const ConfirmButton = styled.TouchableOpacity` elevation: 4; ` -const CardModal = styled.View` - width: 90%; - height: 400px; - background-color: #fff; - border-radius: 10px; - padding-horizontal: 20px; - padding-vertical: 20px; - align-items: center; - justify-content: space-around; - align-self: center; -` const ConfirmText = styled(Text)` font-family: Roboto-Black; text-align: center; font-size: 16; color: #fff; ` -const QuestionText = styled(Text)` - font-size: 16; - text-align: center; -` diff --git a/packages/components/src/screens/EditProfileScreen/useEditProfile.ts b/packages/components/src/screens/EditProfileScreen/useEditProfile.ts new file mode 100644 index 000000000..d2adbd0c6 --- /dev/null +++ b/packages/components/src/screens/EditProfileScreen/useEditProfile.ts @@ -0,0 +1,152 @@ +import React from 'react' +import { decrypt, encrypt, formatPassword, hash, verifyStoreCredentials } from '../../services/auth' +import { useDispatch, useSelector } from 'react-redux' +import * as selectors from '../../redux/selectors' +import * as actions from '../../redux/actions' +import { v4 as uuidv4 } from 'uuid' +import { httpClient } from '../../services/HttpClient' + +export const useEditProfile = () => { + const reduxDispatch = useDispatch() + + const appToken = useSelector(selectors.appTokenSelector) + const currentUser = useSelector(selectors.currentUserSelector) + const storeCredentials = useSelector(selectors.storeCredentialsSelector) + + const [isPasswordModalVisible, setIsPasswordModalVisible] = React.useState(false) + const [isSecretModalVisible, setIsSecretModalVisible] = React.useState(false) + + const onConfirmPassword = async ({ + answer, + newPassword, + }: { + answer: string + newPassword: string + }) => { + const usernameHash = hash(currentUser.name) + const credentials = storeCredentials[usernameHash] + + if (!credentials) { + return // TODO: ERROR ? + } + + const secretAnswer = formatPassword(answer) + + const currentAnswerCorrect = verifyStoreCredentials({ + username: currentUser.name, + password: secretAnswer, + storeCredentials, + method: 'answer', + }) + + if (!currentAnswerCorrect) { + return // TODO: Show alert + } + + const secretKey = decrypt(credentials.secretKeyEncryptedWithAnswer, secretAnswer) + + const password = formatPassword(newPassword) + const secretKeyEncryptedWithPassword = encrypt(secretKey, password) + + const passwordSalt = uuidv4() + const passwordHash = hash(password + passwordSalt) + + try { + // TODO: Check user is guest + + await httpClient.resetPassword({ + name, + secretAnswer, + password, + }) + + // Update redux AFTER successful API request + reduxDispatch( + actions.editPassword({ + usernameHash, + passwordSalt, + passwordHash, + secretKeyEncryptedWithPassword, + }), + ) + + setIsSecretModalVisible(false) + } catch (err) { + // TODO: Show alert, update failed + } + } + + const onConfirmResetQuestion = async ({ + currentAnswer, + newAnswer, + }: // question, + { + currentAnswer: string + newAnswer: string + question: string + }) => { + const usernameHash = hash(currentUser.name) + const credentials = storeCredentials[usernameHash] + + if (!credentials) { + return // TODO: ERROR ? + } + + const currentAnswerCorrect = verifyStoreCredentials({ + username: currentUser.name, + password: currentAnswer, + storeCredentials, + method: 'answer', + }) + + if (!currentAnswerCorrect) { + return // TODO: Show alert + } + + const secretKey = decrypt( + credentials.secretKeyEncryptedWithAnswer, + formatPassword(currentAnswer), + ) + + const answer = formatPassword(newAnswer) + const secretKeyEncryptedWithAnswer = encrypt(secretKey, answer) + + const answerSalt = uuidv4() + const answerHash = hash(answer + answerSalt) + + try { + // TODO: Check user is guest + + await httpClient.editUserSecretAnswer({ + appToken, + previousSecretAnswer: formatPassword(currentAnswer), + nextSecretAnswer: answer, + }) + + // Update redux AFTER successful API request + reduxDispatch( + actions.editAnswer({ + usernameHash, + answerSalt, + answerHash, + secretKeyEncryptedWithAnswer, + }), + ) + + setIsSecretModalVisible(false) + } catch (err) { + // TODO: Show alert, update failed + } + } + + return { + // State + isPasswordModalVisible, + setIsPasswordModalVisible, + isSecretModalVisible, + setIsSecretModalVisible, + // Methods + onConfirmPassword, + onConfirmResetQuestion, + } +} From a553ea3e3eed30c26f9851556fd2ec4e96f65c43 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Wed, 17 Jan 2024 16:10:40 +0800 Subject: [PATCH 82/83] Refactor --- .../components/src/screens/EditProfileScreen/index.tsx | 7 +++---- .../src/screens/EditProfileScreen/useEditProfile.ts | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/components/src/screens/EditProfileScreen/index.tsx b/packages/components/src/screens/EditProfileScreen/index.tsx index 3e66d6927..b59b9ce73 100644 --- a/packages/components/src/screens/EditProfileScreen/index.tsx +++ b/packages/components/src/screens/EditProfileScreen/index.tsx @@ -73,14 +73,13 @@ export function EditProfileScreen() { } const { - // State + onConfirmPassword, + onConfirmResetQuestion, + // Modals isPasswordModalVisible, setIsPasswordModalVisible, isSecretModalVisible, setIsSecretModalVisible, - // Methods - onConfirmPassword, - onConfirmResetQuestion, } = useEditProfile() return ( diff --git a/packages/components/src/screens/EditProfileScreen/useEditProfile.ts b/packages/components/src/screens/EditProfileScreen/useEditProfile.ts index d2adbd0c6..dae298e32 100644 --- a/packages/components/src/screens/EditProfileScreen/useEditProfile.ts +++ b/packages/components/src/screens/EditProfileScreen/useEditProfile.ts @@ -140,13 +140,12 @@ export const useEditProfile = () => { } return { - // State + onConfirmPassword, + onConfirmResetQuestion, + // Modals isPasswordModalVisible, setIsPasswordModalVisible, isSecretModalVisible, setIsSecretModalVisible, - // Methods - onConfirmPassword, - onConfirmResetQuestion, } } From ab39e8d5f3fafe5b76d2056781a3b3b2b3dfafe2 Mon Sep 17 00:00:00 2001 From: Alex Mitchell Date: Wed, 17 Jan 2024 20:09:45 +0800 Subject: [PATCH 83/83] Edit user --- .../src/redux/actions/authActions.ts | 36 ++++---- .../src/redux/reducers/accessReducer.ts | 18 ++++ .../src/redux/reducers/authReducer.ts | 2 +- .../components/src/redux/sagas/commonSaga.ts | 1 + .../src/screens/EditProfileScreen/index.tsx | 36 ++++---- .../EditProfileScreen/useEditProfile.ts | 82 +++++++++++++++++++ 6 files changed, 138 insertions(+), 37 deletions(-) diff --git a/packages/components/src/redux/actions/authActions.ts b/packages/components/src/redux/actions/authActions.ts index 9fd1b579b..cfaddf4d9 100644 --- a/packages/components/src/redux/actions/authActions.ts +++ b/packages/components/src/redux/actions/authActions.ts @@ -158,22 +158,30 @@ export function convertGuestAccount({ } export function editUser({ - name = null, - dateOfBirth = null, - gender = null, - location = null, - password = null, - secretQuestion = null, - secretAnswer = null, + oldUsernameHash, + newUsernameHash, + user: { + name = null, + dateOfBirth = null, + gender = null, + location = null, + password = null, + secretQuestion = null, + secretAnswer = null, + }, }) { return createAction('EDIT_USER', { - name, - dateOfBirth, - gender, - location, - password, - secretQuestion, - secretAnswer, + oldUsernameHash, + newUsernameHash, + user: { + name, + dateOfBirth, + gender, + location, + password, + secretQuestion, + secretAnswer, + }, }) } diff --git a/packages/components/src/redux/reducers/accessReducer.ts b/packages/components/src/redux/reducers/accessReducer.ts index b6676e923..af06be664 100644 --- a/packages/components/src/redux/reducers/accessReducer.ts +++ b/packages/components/src/redux/reducers/accessReducer.ts @@ -69,6 +69,24 @@ export function accessReducer(state = initialState(), action: Actions): AccessSt } } + case 'EDIT_USER': { + const credentials = state.storeCredentials[action.payload.oldUsernameHash] + + return { + ...state, + storeCredentials: { + ...state.storeCredentials, + [action.payload.oldUsernameHash]: undefined, + [action.payload.newUsernameHash]: credentials, + }, + userIdToUsernameHash: { + ...state.userIdToUsernameHash, + [credentials?.userId]: action.payload.newUsernameHash, + }, + lastLoggedInUsername: action.payload.user.name, + } + } + case 'EDIT_PASSWORD': return { ...state, diff --git a/packages/components/src/redux/reducers/authReducer.ts b/packages/components/src/redux/reducers/authReducer.ts index fc73930a0..687e5316d 100644 --- a/packages/components/src/redux/reducers/authReducer.ts +++ b/packages/components/src/redux/reducers/authReducer.ts @@ -114,7 +114,7 @@ export function authReducer(state = initialState, action: Actions | RehydrateAct case 'EDIT_USER': return { ...state, - user: { ...state.user, ..._.omitBy(action.payload, _.isNil) }, + user: { ...state.user, ..._.omitBy(action.payload.user, _.isNil) }, } default: diff --git a/packages/components/src/redux/sagas/commonSaga.ts b/packages/components/src/redux/sagas/commonSaga.ts index 56dff7816..398bab998 100644 --- a/packages/components/src/redux/sagas/commonSaga.ts +++ b/packages/components/src/redux/sagas/commonSaga.ts @@ -11,6 +11,7 @@ const ACTIONS_TO_TRACK: ActionTypes[] = [ 'SET_STORE_KEYS', 'CLEAR_LAST_LOGIN', 'DELETE_USER_ACCESS', + 'EDIT_USER', 'EDIT_PASSWORD', 'EDIT_ANSWER', ] diff --git a/packages/components/src/screens/EditProfileScreen/index.tsx b/packages/components/src/screens/EditProfileScreen/index.tsx index b59b9ce73..51cd79acd 100644 --- a/packages/components/src/screens/EditProfileScreen/index.tsx +++ b/packages/components/src/screens/EditProfileScreen/index.tsx @@ -8,8 +8,6 @@ import { Icon } from '../../components/common/Icon' import { SelectBox } from '../../components/common/SelectBox' import { DateOfBirthInput } from '../../components/common/DateOfBirthInput' import { assets } from '../../assets/index' -import { useSelector } from '../../redux/useSelector' -import * as selectors from '../../redux/selectors' import { BackOneScreen } from '../../services/navigationService' import { TextInputSettings } from '../../components/common/TextInputSettings' import { KeyboardAwareAvoidance } from '../../components/common/KeyboardAwareAvoidance' @@ -53,28 +51,22 @@ function showAcceptAlert(message) { } export function EditProfileScreen() { - const currentUser = useSelector(selectors.currentUserSelector) - - const [name, setName] = React.useState(currentUser.name) - const [dateOfBirth, setDateOfBirth] = React.useState(currentUser.dateOfBirth) - const [gender, setGender] = React.useState(currentUser.gender) - const [location, setLocation] = React.useState(currentUser.location) - - const remainingGenders = ['Female', 'Male', 'Other'].filter((item) => { - return item !== currentUser.gender - }) - const remainingLocations = ['Urban', 'Rural'].filter((item) => { - return item !== currentUser.location - }) - remainingLocations.unshift(currentUser.location) - - const onConfirm = () => { - // - } - const { + onConfirm, onConfirmPassword, onConfirmResetQuestion, + // State + name, + setName, + dateOfBirth, + setDateOfBirth, + gender, + setGender, + location, + setLocation, + // Constants + remainingGenders, + remainingLocations, // Modals isPasswordModalVisible, setIsPasswordModalVisible, @@ -122,7 +114,7 @@ export function EditProfileScreen() { }} buttonStyle={{ right: -10, bottom: 5 }} title="gender" - items={[currentUser.gender, ...remainingGenders]} + items={[gender, ...remainingGenders]} onValueChange={(value) => setGender(value)} /> diff --git a/packages/components/src/screens/EditProfileScreen/useEditProfile.ts b/packages/components/src/screens/EditProfileScreen/useEditProfile.ts index dae298e32..ddc6fbb62 100644 --- a/packages/components/src/screens/EditProfileScreen/useEditProfile.ts +++ b/packages/components/src/screens/EditProfileScreen/useEditProfile.ts @@ -5,6 +5,7 @@ import * as selectors from '../../redux/selectors' import * as actions from '../../redux/actions' import { v4 as uuidv4 } from 'uuid' import { httpClient } from '../../services/HttpClient' +import { translate } from '../../i18n' export const useEditProfile = () => { const reduxDispatch = useDispatch() @@ -16,6 +17,74 @@ export const useEditProfile = () => { const [isPasswordModalVisible, setIsPasswordModalVisible] = React.useState(false) const [isSecretModalVisible, setIsSecretModalVisible] = React.useState(false) + const [name, setName] = React.useState(currentUser.name) + const [dateOfBirth, setDateOfBirth] = React.useState(currentUser.dateOfBirth) + const [gender, setGender] = React.useState(currentUser.gender) + const [location, setLocation] = React.useState(currentUser.location) + + const remainingGenders = ['Female', 'Male', 'Other'].filter((item) => { + return item !== currentUser.gender + }) + const remainingLocations = ['Urban', 'Rural'].filter((item) => { + return item !== currentUser.location + }) + remainingLocations.unshift(currentUser.location) + + const onConfirm = async () => { + const noChanges = + name === currentUser.name && + dateOfBirth === currentUser.dateOfBirth && + gender === currentUser.gender && + location === currentUser.location + + if (noChanges) { + return + } + + const oldUsernameHash = hash(currentUser.name) + const credentials = storeCredentials[oldUsernameHash] + + if (!credentials) { + return // TODO: ERROR ? + } + + const newUsernameHash = hash(name) + const usernameTaken = !!storeCredentials[newUsernameHash] + + if (usernameTaken) { + return // TODO: + } + + try { + // TODO: Check user is guest + + await httpClient.editUserInfo({ + appToken, + name, + dateOfBirth, + gender, + location, + // secretQuestion, + }) + + reduxDispatch( + actions.editUser({ + oldUsernameHash, + newUsernameHash, + user: { + name, + dateOfBirth, + gender, + location, + // secretQuestion, + }, + }), + ) + } catch (err) { + throw new Error(translate('could_not_edit')) + } + } + const onConfirmPassword = async ({ answer, newPassword, @@ -140,8 +209,21 @@ export const useEditProfile = () => { } return { + onConfirm, onConfirmPassword, onConfirmResetQuestion, + // State + name, + setName, + dateOfBirth, + setDateOfBirth, + gender, + setGender, + location, + setLocation, + // Constants + remainingGenders, + remainingLocations, // Modals isPasswordModalVisible, setIsPasswordModalVisible,