From dcc5adff694c41c31a48acfbe042bea0e72dac17 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Wed, 14 Feb 2024 16:07:13 +0000 Subject: [PATCH 1/4] refactor(typescript): migrate settings preferences --- src/libs/LocaleUtils.ts | 17 ++++++ .../{LanguagePage.js => LanguagePage.tsx} | 26 ++++----- ...PreferencesPage.js => PreferencesPage.tsx} | 39 ++++++-------- ...iorityModePage.js => PriorityModePage.tsx} | 54 ++++++++----------- .../{ThemePage.js => ThemePage.tsx} | 25 ++++----- 5 files changed, 76 insertions(+), 85 deletions(-) create mode 100644 src/libs/LocaleUtils.ts rename src/pages/settings/Preferences/{LanguagePage.js => LanguagePage.tsx} (53%) rename src/pages/settings/Preferences/{PreferencesPage.js => PreferencesPage.tsx} (87%) rename src/pages/settings/Preferences/{PriorityModePage.js => PriorityModePage.tsx} (50%) rename src/pages/settings/Preferences/{ThemePage.js => ThemePage.tsx} (71%) diff --git a/src/libs/LocaleUtils.ts b/src/libs/LocaleUtils.ts new file mode 100644 index 000000000000..2bcbb946c7c0 --- /dev/null +++ b/src/libs/LocaleUtils.ts @@ -0,0 +1,17 @@ +import type {ValueOf} from 'type-fest'; +import CONST from '@src/CONST'; + +const getLanguageFromLocale = (locale: ValueOf): (typeof CONST.LANGUAGES)[number] => { + switch (locale) { + case CONST.LOCALES.ES_ES: + case CONST.LOCALES.ES_ES_ONFIDO: + case CONST.LOCALES.ES: + return CONST.LOCALES.ES; + case CONST.LOCALES.EN: + return CONST.LOCALES.EN; + default: + return CONST.LOCALES.DEFAULT; + } +}; + +export default {getLanguageFromLocale}; diff --git a/src/pages/settings/Preferences/LanguagePage.js b/src/pages/settings/Preferences/LanguagePage.tsx similarity index 53% rename from src/pages/settings/Preferences/LanguagePage.js rename to src/pages/settings/Preferences/LanguagePage.tsx index ce93e94222b5..68ceeb0a1d81 100644 --- a/src/pages/settings/Preferences/LanguagePage.js +++ b/src/pages/settings/Preferences/LanguagePage.tsx @@ -1,27 +1,20 @@ -import PropTypes from 'prop-types'; import React from 'react'; -import _ from 'underscore'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useLocalize from '@hooks/useLocalize'; import Navigation from '@libs/Navigation/Navigation'; import * as App from '@userActions/App'; import CONST from '@src/CONST'; -const propTypes = { - ...withLocalizePropTypes, +function LanguagePage() { + const {translate, preferredLocale} = useLocalize(); - /** The preferred language of the App */ - preferredLocale: PropTypes.string.isRequired, -}; - -function LanguagePage(props) { - const localesToLanguages = _.map(CONST.LANGUAGES, (language) => ({ + const localesToLanguages = CONST.LANGUAGES.map((language) => ({ value: language, - text: props.translate(`languagePage.languages.${language}.label`), + text: translate(`languagePage.languages.${language}.label`), keyForList: language, - isSelected: props.preferredLocale === language, + isSelected: preferredLocale === language, })); return ( @@ -30,19 +23,18 @@ function LanguagePage(props) { testID={LanguagePage.displayName} > Navigation.goBack()} /> App.setLocaleAndNavigate(language.value)} - initiallyFocusedOptionKey={_.find(localesToLanguages, (locale) => locale.isSelected).keyForList} + initiallyFocusedOptionKey={localesToLanguages.find((locale) => locale.isSelected)?.keyForList} /> ); } LanguagePage.displayName = 'LanguagePage'; -LanguagePage.propTypes = propTypes; -export default withLocalize(LanguagePage); +export default LanguagePage; diff --git a/src/pages/settings/Preferences/PreferencesPage.js b/src/pages/settings/Preferences/PreferencesPage.tsx similarity index 87% rename from src/pages/settings/Preferences/PreferencesPage.js rename to src/pages/settings/Preferences/PreferencesPage.tsx index 5ac78f6d20c6..4b93b330e6e9 100755 --- a/src/pages/settings/Preferences/PreferencesPage.js +++ b/src/pages/settings/Preferences/PreferencesPage.tsx @@ -1,8 +1,8 @@ -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React from 'react'; import {ScrollView, View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Illustrations from '@components/Icon/Illustrations'; import LottieAnimations from '@components/LottieAnimations'; @@ -16,33 +16,28 @@ import useEnvironment from '@hooks/useEnvironment'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; +import LocaleUtils from '@libs/LocaleUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; +import type {User as UserType} from '@src/types/onyx'; -const propTypes = { +type PreferencesPageOnyxProps = { /** The chat priority mode */ - priorityMode: PropTypes.string, + priorityMode: OnyxEntry>; /** The app's color theme */ - preferredTheme: PropTypes.string, + preferredTheme: OnyxEntry>; /** The details about the user that is signed in */ - user: PropTypes.shape({ - /** Whether or not the user is subscribed to news updates */ - isSubscribedToNewsletter: PropTypes.bool, - }), + user: OnyxEntry; }; -const defaultProps = { - priorityMode: CONST.PRIORITY_MODE.DEFAULT, - preferredTheme: CONST.DEFAULT_THEME, - user: {}, -}; +type PreferencesPageProps = PreferencesPageOnyxProps; -function PreferencesPage(props) { +function PreferencesPage({priorityMode, preferredTheme, user}: PreferencesPageProps) { const styles = useThemeStyles(); const {isProduction} = useEnvironment(); const {translate, preferredLocale} = useLocalize(); @@ -85,7 +80,7 @@ function PreferencesPage(props) { @@ -97,28 +92,28 @@ function PreferencesPage(props) { Navigation.navigate(ROUTES.SETTINGS_PRIORITY_MODE)} wrapperStyle={styles.sectionMenuItemTopDescription} /> Navigation.navigate(ROUTES.SETTINGS_LANGUAGE)} wrapperStyle={styles.sectionMenuItemTopDescription} /> Navigation.navigate(ROUTES.SETTINGS_THEME)} wrapperStyle={styles.sectionMenuItemTopDescription} @@ -144,11 +139,9 @@ function PreferencesPage(props) { ); } -PreferencesPage.propTypes = propTypes; -PreferencesPage.defaultProps = defaultProps; PreferencesPage.displayName = 'PreferencesPage'; -export default withOnyx({ +export default withOnyx({ priorityMode: { key: ONYXKEYS.NVP_PRIORITY_MODE, }, diff --git a/src/pages/settings/Preferences/PriorityModePage.js b/src/pages/settings/Preferences/PriorityModePage.tsx similarity index 50% rename from src/pages/settings/Preferences/PriorityModePage.js rename to src/pages/settings/Preferences/PriorityModePage.tsx index 983e3cb26746..e6c94c73021e 100644 --- a/src/pages/settings/Preferences/PriorityModePage.js +++ b/src/pages/settings/Preferences/PriorityModePage.tsx @@ -1,48 +1,45 @@ -import PropTypes from 'prop-types'; import React, {useCallback} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import _, {compose} from 'underscore'; +import type {ValueOf} from 'type-fest'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import Text from '@components/Text'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import Navigation from '@libs/Navigation/Navigation'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -const propTypes = { +type PriorityModePageOnyxProps = { /** The chat priority mode */ - priorityMode: PropTypes.string, - - ...withLocalizePropTypes, + priorityMode: OnyxEntry>; }; -const defaultProps = { - priorityMode: CONST.PRIORITY_MODE.DEFAULT, -}; +type PriorityModePageProps = PriorityModePageOnyxProps; -function PriorityModePage(props) { +function PriorityModePage({priorityMode}: PriorityModePageProps) { + const {translate} = useLocalize(); const styles = useThemeStyles(); - const priorityModes = _.map(_.values(CONST.PRIORITY_MODE), (mode) => ({ + const priorityModes = Object.values(CONST.PRIORITY_MODE).map((mode) => ({ value: mode, - text: props.translate(`priorityModePage.priorityModes.${mode}.label`), - alternateText: props.translate(`priorityModePage.priorityModes.${mode}.description`), + text: translate(`priorityModePage.priorityModes.${mode}.label`), + alternateText: translate(`priorityModePage.priorityModes.${mode}.description`), keyForList: mode, - isSelected: props.priorityMode === mode, + isSelected: priorityMode === mode, })); const updateMode = useCallback( - (mode) => { - if (mode.value === props.priorityMode) { + (mode: (typeof priorityModes)[number]) => { + if (mode.value === priorityMode) { Navigation.goBack(); return; } User.updateChatPriorityMode(mode.value); }, - [props.priorityMode], + [priorityMode], ); return ( @@ -51,28 +48,23 @@ function PriorityModePage(props) { testID={PriorityModePage.displayName} > Navigation.goBack()} /> - {props.translate('priorityModePage.explainerText')} + {translate('priorityModePage.explainerText')} mode.isSelected).keyForList} + initiallyFocusedOptionKey={priorityModes.find((mode) => mode.isSelected)?.keyForList} /> ); } PriorityModePage.displayName = 'PriorityModePage'; -PriorityModePage.propTypes = propTypes; -PriorityModePage.defaultProps = defaultProps; -export default compose( - withLocalize, - withOnyx({ - priorityMode: { - key: ONYXKEYS.NVP_PRIORITY_MODE, - }, - }), -)(PriorityModePage); +export default withOnyx({ + priorityMode: { + key: ONYXKEYS.NVP_PRIORITY_MODE, + }, +})(PriorityModePage); diff --git a/src/pages/settings/Preferences/ThemePage.js b/src/pages/settings/Preferences/ThemePage.tsx similarity index 71% rename from src/pages/settings/Preferences/ThemePage.js rename to src/pages/settings/Preferences/ThemePage.tsx index 4907056be761..4d89e600770b 100644 --- a/src/pages/settings/Preferences/ThemePage.js +++ b/src/pages/settings/Preferences/ThemePage.tsx @@ -1,7 +1,7 @@ -import PropTypes from 'prop-types'; import React from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {ValueOf} from 'type-fest'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; @@ -13,23 +13,22 @@ import * as User from '@userActions/User'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -const propTypes = { +type ThemePageOnyxProps = { /** The theme of the app */ - preferredTheme: PropTypes.string, + preferredTheme: OnyxEntry>; }; -const defaultProps = { - preferredTheme: CONST.THEME.DEFAULT, -}; +type ThemePageProps = ThemePageOnyxProps; -function ThemePage(props) { +function ThemePage({preferredTheme}: ThemePageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const localesToThemes = _.map(_.values(_.omit(CONST.THEME, 'DEFAULT', 'FALLBACK')), (theme) => ({ + const {DEFAULT, FALLBACK, ...themes} = CONST.THEME; + const localesToThemes = Object.values(themes).map((theme) => ({ value: theme, text: translate(`themePage.themes.${theme}.label`), keyForList: theme, - isSelected: (props.preferredTheme || CONST.THEME.DEFAULT) === theme, + isSelected: (preferredTheme ?? CONST.THEME.DEFAULT) === theme, })); return ( @@ -49,17 +48,15 @@ function ThemePage(props) { User.updateTheme(theme.value)} - initiallyFocusedOptionKey={_.find(localesToThemes, (theme) => theme.isSelected).keyForList} + initiallyFocusedOptionKey={localesToThemes.find((theme) => theme.isSelected)?.keyForList} /> ); } ThemePage.displayName = 'ThemePage'; -ThemePage.propTypes = propTypes; -ThemePage.defaultProps = defaultProps; -export default withOnyx({ +export default withOnyx({ preferredTheme: { key: ONYXKEYS.PREFERRED_THEME, }, From 86c59a1cee4065805e2643d07ba2de07469c9555 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Tue, 27 Feb 2024 20:24:44 +0000 Subject: [PATCH 2/4] refactor(typescript): extract type from constant --- src/pages/settings/Preferences/PriorityModePage.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/pages/settings/Preferences/PriorityModePage.tsx b/src/pages/settings/Preferences/PriorityModePage.tsx index f3f4b282105c..4e9bf374ce01 100644 --- a/src/pages/settings/Preferences/PriorityModePage.tsx +++ b/src/pages/settings/Preferences/PriorityModePage.tsx @@ -14,6 +14,14 @@ import * as User from '@userActions/User'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +type PriorityModeItem = { + value: ValueOf; + text: string; + alternateText: string; + keyForList: ValueOf; + isSelected: boolean; +}; + type PriorityModePageOnyxProps = { /** The chat priority mode */ priorityMode: OnyxEntry>; @@ -24,7 +32,7 @@ type PriorityModePageProps = PriorityModePageOnyxProps; function PriorityModePage({priorityMode}: PriorityModePageProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); - const priorityModes = Object.values(CONST.PRIORITY_MODE).map((mode) => ({ + const priorityModes = Object.values(CONST.PRIORITY_MODE).map((mode) => ({ value: mode, text: translate(`priorityModePage.priorityModes.${mode}.label`), alternateText: translate(`priorityModePage.priorityModes.${mode}.description`), @@ -33,7 +41,7 @@ function PriorityModePage({priorityMode}: PriorityModePageProps) { })); const updateMode = useCallback( - (mode: (typeof priorityModes)[number]) => { + (mode: PriorityModeItem) => { if (mode.value === priorityMode) { Navigation.goBack(); return; From 02243bac5376952b55889724489990d2ea641842 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Mon, 11 Mar 2024 17:02:09 +0000 Subject: [PATCH 3/4] refactor: apply pull request feedback --- src/libs/LocaleUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/LocaleUtils.ts b/src/libs/LocaleUtils.ts index 2bcbb946c7c0..1a63cbf579d5 100644 --- a/src/libs/LocaleUtils.ts +++ b/src/libs/LocaleUtils.ts @@ -1,7 +1,7 @@ import type {ValueOf} from 'type-fest'; import CONST from '@src/CONST'; -const getLanguageFromLocale = (locale: ValueOf): (typeof CONST.LANGUAGES)[number] => { +function getLanguageFromLocale(locale: ValueOf): (typeof CONST.LANGUAGES)[number] { switch (locale) { case CONST.LOCALES.ES_ES: case CONST.LOCALES.ES_ES_ONFIDO: @@ -12,6 +12,6 @@ const getLanguageFromLocale = (locale: ValueOf): (typeof C default: return CONST.LOCALES.DEFAULT; } -}; +} export default {getLanguageFromLocale}; From c9bb187c65a673084053b9353fc91fc6daa6d1c7 Mon Sep 17 00:00:00 2001 From: Pedro Guerreiro Date: Wed, 13 Mar 2024 16:06:24 +0000 Subject: [PATCH 4/4] refactor(typescript): apply pull request suggestions --- src/pages/settings/Preferences/PreferencesPage.tsx | 7 +++---- src/pages/settings/Preferences/PriorityModePage.tsx | 4 ++-- src/pages/settings/Preferences/ThemePage.tsx | 5 ++--- src/types/onyx/PreferredTheme.ts | 7 +++++++ src/types/onyx/PriorityMode.ts | 7 +++++++ src/types/onyx/index.ts | 4 ++++ 6 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 src/types/onyx/PreferredTheme.ts create mode 100644 src/types/onyx/PriorityMode.ts diff --git a/src/pages/settings/Preferences/PreferencesPage.tsx b/src/pages/settings/Preferences/PreferencesPage.tsx index d5af1a5c6fa1..5849f323dc36 100755 --- a/src/pages/settings/Preferences/PreferencesPage.tsx +++ b/src/pages/settings/Preferences/PreferencesPage.tsx @@ -2,7 +2,6 @@ import React from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import type {ValueOf} from 'type-fest'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Illustrations from '@components/Icon/Illustrations'; import LottieAnimations from '@components/LottieAnimations'; @@ -21,14 +20,14 @@ import * as User from '@userActions/User'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {User as UserType} from '@src/types/onyx'; +import type {PreferredTheme, PriorityMode, User as UserType} from '@src/types/onyx'; type PreferencesPageOnyxProps = { /** The chat priority mode */ - priorityMode: OnyxEntry>; + priorityMode: PriorityMode; /** The app's color theme */ - preferredTheme: OnyxEntry>; + preferredTheme: PreferredTheme; /** The details about the user that is signed in */ user: OnyxEntry; diff --git a/src/pages/settings/Preferences/PriorityModePage.tsx b/src/pages/settings/Preferences/PriorityModePage.tsx index 4e9bf374ce01..677d3813acd7 100644 --- a/src/pages/settings/Preferences/PriorityModePage.tsx +++ b/src/pages/settings/Preferences/PriorityModePage.tsx @@ -1,5 +1,4 @@ import React, {useCallback} from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -13,6 +12,7 @@ import Navigation from '@libs/Navigation/Navigation'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {PriorityMode} from '@src/types/onyx'; type PriorityModeItem = { value: ValueOf; @@ -24,7 +24,7 @@ type PriorityModeItem = { type PriorityModePageOnyxProps = { /** The chat priority mode */ - priorityMode: OnyxEntry>; + priorityMode: PriorityMode; }; type PriorityModePageProps = PriorityModePageOnyxProps; diff --git a/src/pages/settings/Preferences/ThemePage.tsx b/src/pages/settings/Preferences/ThemePage.tsx index 6b9a37cf2fbe..f879a7a259ea 100644 --- a/src/pages/settings/Preferences/ThemePage.tsx +++ b/src/pages/settings/Preferences/ThemePage.tsx @@ -1,7 +1,5 @@ import React from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import type {ValueOf} from 'type-fest'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; @@ -13,10 +11,11 @@ import Navigation from '@libs/Navigation/Navigation'; import * as User from '@userActions/User'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {PreferredTheme} from '@src/types/onyx'; type ThemePageOnyxProps = { /** The theme of the app */ - preferredTheme: OnyxEntry>; + preferredTheme: PreferredTheme; }; type ThemePageProps = ThemePageOnyxProps; diff --git a/src/types/onyx/PreferredTheme.ts b/src/types/onyx/PreferredTheme.ts new file mode 100644 index 000000000000..408748ad06ea --- /dev/null +++ b/src/types/onyx/PreferredTheme.ts @@ -0,0 +1,7 @@ +import type {OnyxEntry} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; + +type PreferredTheme = OnyxEntry>; + +export default PreferredTheme; diff --git a/src/types/onyx/PriorityMode.ts b/src/types/onyx/PriorityMode.ts new file mode 100644 index 000000000000..224c86867f35 --- /dev/null +++ b/src/types/onyx/PriorityMode.ts @@ -0,0 +1,7 @@ +import type {OnyxEntry} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; +import type CONST from '@src/CONST'; + +type PriorityMode = OnyxEntry>; + +export default PriorityMode; diff --git a/src/types/onyx/index.ts b/src/types/onyx/index.ts index d12b0a22bba2..e3118538258e 100644 --- a/src/types/onyx/index.ts +++ b/src/types/onyx/index.ts @@ -40,6 +40,8 @@ import type {PolicyMembers} from './PolicyMember'; import type PolicyMember from './PolicyMember'; import type {PolicyReportField, PolicyReportFields} from './PolicyReportField'; import type {PolicyTag, PolicyTagList, PolicyTags} from './PolicyTag'; +import type PreferredTheme from './PreferredTheme'; +import type PriorityMode from './PriorityMode'; import type PrivatePersonalDetails from './PrivatePersonalDetails'; import type RecentlyUsedCategories from './RecentlyUsedCategories'; import type RecentlyUsedReportFields from './RecentlyUsedReportFields'; @@ -114,6 +116,8 @@ export type { PolicyTag, PolicyTags, PolicyTagList, + PreferredTheme, + PriorityMode, PrivatePersonalDetails, RecentWaypoint, RecentlyUsedCategories,