From a18dd9c19a45c2422a2c85541bd371f3b1fb6716 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 13 Feb 2024 15:18:46 +0100 Subject: [PATCH 1/7] ref: move InitalSettingsPage to TS --- src/libs/UserUtils.ts | 18 +- ...ettingsPage.js => InitialSettingsPage.tsx} | 214 ++++++++---------- src/styles/index.ts | 2 +- 3 files changed, 105 insertions(+), 129 deletions(-) rename src/pages/settings/{InitialSettingsPage.js => InitialSettingsPage.tsx} (66%) diff --git a/src/libs/UserUtils.ts b/src/libs/UserUtils.ts index 52c3ecef156c..78cedb1f7801 100644 --- a/src/libs/UserUtils.ts +++ b/src/libs/UserUtils.ts @@ -7,7 +7,7 @@ import * as defaultAvatars from '@components/Icon/DefaultAvatars'; import {ConciergeAvatar, FallbackAvatar} from '@components/Icon/Expensicons'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {PersonalDetailsList} from '@src/types/onyx'; +import type {LoginList, PersonalDetailsList} from '@src/types/onyx'; import type Login from '@src/types/onyx/Login'; import type IconAsset from '@src/types/utils/IconAsset'; import hashCode from './hashCode'; @@ -16,7 +16,7 @@ type AvatarRange = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | type AvatarSource = IconAsset | string; -type LoginListIndicator = ValueOf | ''; +type LoginListIndicator = ValueOf | undefined; let allPersonalDetails: OnyxEntry; Onyx.connect({ @@ -45,8 +45,8 @@ Onyx.connect({ * } * }} */ -function hasLoginListError(loginList: Record): boolean { - return Object.values(loginList).some((loginData) => Object.values(loginData.errorFields ?? {}).some((field) => Object.keys(field ?? {}).length > 0)); +function hasLoginListError(loginList: OnyxEntry): boolean { + return Object.values(loginList ?? {}).some((loginData) => Object.values(loginData.errorFields ?? {}).some((field) => Object.keys(field ?? {}).length > 0)); } /** @@ -54,22 +54,22 @@ function hasLoginListError(loginList: Record): boolean { * an Info brick road status indicator. Currently this only applies if the user * has an unvalidated contact method. */ -function hasLoginListInfo(loginList: Record): boolean { - return !Object.values(loginList).every((field) => field.validatedDate); +function hasLoginListInfo(loginList: OnyxEntry): boolean { + return !Object.values(loginList ?? {}).every((field) => field.validatedDate); } /** * Gets the appropriate brick road indicator status for a given loginList. * Error status is higher priority, so we check for that first. */ -function getLoginListBrickRoadIndicator(loginList: Record): LoginListIndicator { +function getLoginListBrickRoadIndicator(loginList: OnyxEntry): LoginListIndicator { if (hasLoginListError(loginList)) { return CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR; } if (hasLoginListInfo(loginList)) { return CONST.BRICK_ROAD_INDICATOR_STATUS.INFO; } - return ''; + return undefined; } /** @@ -238,4 +238,4 @@ export { hashText, isDefaultAvatar, }; -export type {AvatarSource}; +export type {AvatarSource, LoginListIndicator}; diff --git a/src/pages/settings/InitialSettingsPage.js b/src/pages/settings/InitialSettingsPage.tsx similarity index 66% rename from src/pages/settings/InitialSettingsPage.js rename to src/pages/settings/InitialSettingsPage.tsx index e0f414910d7b..afb3373eff02 100755 --- a/src/pages/settings/InitialSettingsPage.js +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -1,35 +1,30 @@ import {useNavigationState} from '@react-navigation/native'; -import lodashGet from 'lodash/get'; -import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native'; import {NativeModules, View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; +import type {ValueOf} from 'type-fest'; import AvatarWithImagePicker from '@components/AvatarWithImagePicker'; -import bankAccountPropTypes from '@components/bankAccountPropTypes'; -import cardPropTypes from '@components/cardPropTypes'; import ConfirmModal from '@components/ConfirmModal'; import CurrentUserPersonalDetailsSkeletonView from '@components/CurrentUserPersonalDetailsSkeletonView'; import HeaderPageLayout from '@components/HeaderPageLayout'; import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import {withNetwork} from '@components/OnyxProvider'; import Text from '@components/Text'; -import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsDefaultProps, withCurrentUserPersonalDetailsPropTypes} from '@components/withCurrentUserPersonalDetails'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; +import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; +import useNetwork from '@hooks/useNetwork'; import useSingleExecution from '@hooks/useSingleExecution'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWaitForNavigation from '@hooks/useWaitForNavigation'; -import compose from '@libs/compose'; import * as CurrencyUtils from '@libs/CurrencyUtils'; -import {translatableTextPropTypes} from '@libs/Localize'; import getTopmostSettingsCentralPaneName from '@libs/Navigation/getTopmostSettingsCentralPaneName'; import Navigation from '@libs/Navigation/Navigation'; import * as UserUtils from '@libs/UserUtils'; -import walletTermsPropTypes from '@pages/EnablePayments/walletTermsPropTypes'; import * as ReportActionContextMenu from '@pages/home/report/ContextMenu/ReportActionContextMenu'; import * as Link from '@userActions/Link'; import * as PaymentMethods from '@userActions/PaymentMethods'; @@ -37,68 +32,59 @@ import * as PersonalDetails from '@userActions/PersonalDetails'; import * as Session from '@userActions/Session'; import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; +import type * as OnyxTypes from '@src/types/onyx'; +import type {Icon} from '@src/types/onyx/OnyxCommon'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import type IconAsset from '@src/types/utils/IconAsset'; -const propTypes = { - /* Onyx Props */ - - /** The session of the logged in person */ - session: PropTypes.shape({ - /** Email of the logged in person */ - email: PropTypes.string, - }), - +type InitialSettingsPageOnyxProps = { /** The user's wallet account */ - userWallet: PropTypes.shape({ - /** The user's current wallet balance */ - currentBalance: PropTypes.number, - }), + userWallet: OnyxEntry; /** List of bank accounts */ - bankAccountList: PropTypes.objectOf(bankAccountPropTypes), + bankAccountList: OnyxEntry; /** List of user's cards */ - fundList: PropTypes.objectOf(cardPropTypes), + fundList: OnyxEntry; /** Information about the user accepting the terms for payments */ - walletTerms: walletTermsPropTypes, + walletTerms: OnyxEntry; /** Login list for the user that is signed in */ - loginList: PropTypes.objectOf( - PropTypes.shape({ - /** Date login was validated, used to show brickroad info status */ - validatedDate: PropTypes.string, - - /** Field-specific server side errors keyed by microtime */ - errorFields: PropTypes.objectOf(PropTypes.objectOf(translatableTextPropTypes)), - }), - ), - - ...withLocalizePropTypes, - ...withCurrentUserPersonalDetailsPropTypes, + loginList: OnyxEntry; }; -const defaultProps = { - session: {}, - userWallet: { - currentBalance: 0, - }, - walletTerms: {}, - bankAccountList: {}, - fundList: null, - loginList: {}, - ...withCurrentUserPersonalDetailsDefaultProps, +type InitialSettingsPageProps = InitialSettingsPageOnyxProps & WithCurrentUserPersonalDetailsProps; +type MenuData = { + translationKey: TranslationPaths; + icon: IconAsset; + routeName?: Route; + brickRoadIndicator?: ValueOf; + action?: () => void; + link?: string | (() => Promise); + iconType?: typeof CONST.ICON_TYPE_ICON | typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; + iconStyles?: StyleProp; + fallbackIcon?: IconAsset; + shouldStackHorizontally?: boolean; + avatarSize?: (typeof CONST.AVATAR_SIZE)[keyof typeof CONST.AVATAR_SIZE]; + floatRightAvatars?: Icon[]; + title?: string; }; +type Menu = {sectionStyle: StyleProp; sectionTranslationKey: TranslationPaths; items: MenuData[]}; -function InitialSettingsPage(props) { +function InitialSettingsPage({session, userWallet, bankAccountList, fundList, walletTerms, loginList, currentUserPersonalDetails}: InitialSettingsPageProps) { + const network = useNetwork(); const theme = useTheme(); const styles = useThemeStyles(); const {isExecuting, singleExecution} = useSingleExecution(); const waitForNavigate = useWaitForNavigation(); const popoverAnchor = useRef(null); - const {translate} = useLocalize(); + const {translate, formatPhoneNumber} = useLocalize(); const activeRoute = useNavigationState(getTopmostSettingsCentralPaneName); const [shouldShowSignoutConfirmModal, setShouldShowSignoutConfirmModal] = useState(false); @@ -107,13 +93,13 @@ function InitialSettingsPage(props) { Wallet.openInitialSettingsPage(); }, []); - const toggleSignoutConfirmModal = (value) => { + const toggleSignoutConfirmModal = (value: boolean) => { setShouldShowSignoutConfirmModal(value); }; const signOut = useCallback( (shouldForceSignout = false) => { - if (!props.network.isOffline || shouldForceSignout) { + if (!network.isOffline || shouldForceSignout) { Session.signOutAndRedirectToSignIn(); return; } @@ -121,18 +107,18 @@ function InitialSettingsPage(props) { // When offline, warn the user that any actions they took while offline will be lost if they sign out toggleSignoutConfirmModal(true); }, - [props.network.isOffline], + [network.isOffline], ); /** * Retuns a list of menu items data for account section - * @returns {Object} object with translationKey, style and items for the account section + * @returns object with translationKey, style and items for the account section */ - const accountMenuItemsData = useMemo(() => { - const profileBrickRoadIndicator = UserUtils.getLoginListBrickRoadIndicator(props.loginList); - const paymentCardList = props.fundList || {}; + const accountMenuItemsData: Menu = useMemo(() => { + const profileBrickRoadIndicator = UserUtils.getLoginListBrickRoadIndicator(loginList); + const paymentCardList = fundList; - const defaultMenu = { + const defaultMenu: Menu = { sectionStyle: styles.accountSettingsSectionContainer, sectionTranslationKey: 'initialSettingsPage.account', items: [ @@ -147,9 +133,9 @@ function InitialSettingsPage(props) { icon: Expensicons.Wallet, routeName: ROUTES.SETTINGS_WALLET, brickRoadIndicator: - PaymentMethods.hasPaymentMethodError(props.bankAccountList, paymentCardList) || !_.isEmpty(props.userWallet.errors) || !_.isEmpty(props.walletTerms.errors) + PaymentMethods.hasPaymentMethodError(bankAccountList, paymentCardList) || !isEmptyObject(userWallet?.errors) || !isEmptyObject(walletTerms?.errors) ? 'error' - : null, + : undefined, }, { translationKey: 'common.shareCode', @@ -185,39 +171,36 @@ function InitialSettingsPage(props) { }; if (NativeModules.HybridAppModule) { - const hybridAppMenuItems = _.filter( - [ - { - translationKey: 'initialSettingsPage.returnToClassic', - icon: Expensicons.RotateLeft, - shouldShowRightIcon: true, - iconRight: Expensicons.NewWindow, - action: () => NativeModules.HybridAppModule.closeReactNativeApp(), - }, - ...defaultMenu.items, - ], - (item) => item.translationKey !== 'initialSettingsPage.signOut' && item.translationKey !== 'initialSettingsPage.goToExpensifyClassic', - ); + const hybridAppMenuItems: MenuData[] = [ + { + translationKey: 'initialSettingsPage.returnToClassic' as const, + icon: Expensicons.RotateLeft, + shouldShowRightIcon: true, + iconRight: Expensicons.NewWindow, + action: () => NativeModules.HybridAppModule.closeReactNativeApp() as void, + }, + ...defaultMenu.items, + ].filter((item) => item.translationKey !== 'initialSettingsPage.signOut' && item.translationKey !== 'initialSettingsPage.goToExpensifyClassic'); return {sectionStyle: styles.accountSettingsSectionContainer, sectionTranslationKey: 'initialSettingsPage.account', items: hybridAppMenuItems}; } return defaultMenu; - }, [props.bankAccountList, props.fundList, props.loginList, props.userWallet.errors, props.walletTerms.errors, signOut, styles.accountSettingsSectionContainer]); + }, [loginList, fundList, styles.accountSettingsSectionContainer, bankAccountList, userWallet?.errors, walletTerms?.errors, signOut]); /** * Retuns a list of menu items data for general section - * @returns {Object} object with translationKey, style and items for the general section + * @returns object with translationKey, style and items for the general section */ - const generaltMenuItemsData = useMemo( + const generaltMenuItemsData: Menu = useMemo( () => ({ sectionStyle: { ...styles.pt4, }, - sectionTranslationKey: 'initialSettingsPage.general', + sectionTranslationKey: 'initialSettingsPage.general' as const, items: [ { - translationKey: 'initialSettingsPage.help', + translationKey: 'initialSettingsPage.help' as const, icon: Expensicons.QuestionMark, action: () => { Link.openExternalLink(CONST.NEWHELP_URL); @@ -225,7 +208,7 @@ function InitialSettingsPage(props) { link: CONST.NEWHELP_URL, }, { - translationKey: 'initialSettingsPage.about', + translationKey: 'initialSettingsPage.about' as const, icon: Expensicons.Info, routeName: ROUTES.SETTINGS_ABOUT, }, @@ -236,20 +219,20 @@ function InitialSettingsPage(props) { /** * Retuns JSX.Element with menu items - * @param {Object} menuItemsData list with menu items data - * @returns {JSX.Element} the menu items for passed data + * @param menuItemsData list with menu items data + * @returns the menu items for passed data */ const getMenuItemsSection = useCallback( - (menuItemsData) => { + (menuItemsData: Menu) => { /** - * @param {Boolean} isPaymentItem whether the item being rendered is the payments menu item - * @returns {String|undefined} the user's wallet balance + * @param isPaymentItem whether the item being rendered is the payments menu item + * @returns the user's wallet balance */ - const getWalletBalance = (isPaymentItem) => (isPaymentItem ? CurrencyUtils.convertToDisplayString(props.userWallet.currentBalance) : undefined); + const getWalletBalance = (isPaymentItem: boolean): string | undefined => (isPaymentItem ? CurrencyUtils.convertToDisplayString(userWallet?.currentBalance) : undefined); - const openPopover = (link, event) => { + const openPopover = (link: string | (() => Promise) | undefined, event: GestureResponderEvent | MouseEvent) => { if (typeof link === 'function') { - link().then((url) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, url, popoverAnchor.current)); + link?.()?.then((url) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, url, popoverAnchor.current)); } else if (link) { ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, link, popoverAnchor.current); } @@ -258,13 +241,13 @@ function InitialSettingsPage(props) { return ( {translate(menuItemsData.sectionTranslationKey)} - {_.map(menuItemsData.items, (item, index) => { + {menuItemsData.items.map((item) => { const keyTitle = item.translationKey ? translate(item.translationKey) : item.title; const isPaymentItem = item.translationKey === 'common.wallet'; return ( openPopover(item.link, event) : undefined} - focused={activeRoute && item.routeName && activeRoute.toLowerCase().replaceAll('_', '') === item.routeName.toLowerCase().replaceAll('/', '')} + focused={!!activeRoute && !!item.routeName && !!(activeRoute.toLowerCase().replaceAll('_', '') === item.routeName.toLowerCase().replaceAll('/', ''))} isPaneMenu /> ); @@ -305,7 +288,7 @@ function InitialSettingsPage(props) { styles.sectionMenuItem, styles.hoveredComponentBG, translate, - props.userWallet.currentBalance, + userWallet?.currentBalance, isExecuting, singleExecution, activeRoute, @@ -316,50 +299,51 @@ function InitialSettingsPage(props) { const accountMenuItems = useMemo(() => getMenuItemsSection(accountMenuItemsData), [accountMenuItemsData, getMenuItemsSection]); const generalMenuItems = useMemo(() => getMenuItemsSection(generaltMenuItemsData), [generaltMenuItemsData, getMenuItemsSection]); - const currentUserDetails = props.currentUserPersonalDetails || {}; - const avatarURL = lodashGet(currentUserDetails, 'avatar', ''); - const accountID = lodashGet(currentUserDetails, 'accountID', ''); + const currentUserDetails = currentUserPersonalDetails; + const avatarURL = currentUserDetails?.avatar ?? ''; + const accountID = currentUserDetails?.accountID ?? ''; const headerContent = ( - {_.isEmpty(props.currentUserPersonalDetails) || _.isUndefined(props.currentUserPersonalDetails.displayName) ? ( + {isEmptyObject(currentUserPersonalDetails) || currentUserPersonalDetails.displayName === undefined ? ( ) : ( <> + {/* @ts-expect-error TODO: Remove this once AvatarWithImagePicker (https://github.com/Expensify/App/issues/25122) is migrated to TypeScript. */} Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(accountID))} + onViewPhotoPress={() => Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(String(accountID)))} previewSource={UserUtils.getFullSizeAvatar(avatarURL, accountID)} originalFileName={currentUserDetails.originalFileName} - headerTitle={props.translate('profilePage.profileAvatar')} - fallbackIcon={lodashGet(currentUserDetails, 'fallbackIcon')} + headerTitle={translate('profilePage.profileAvatar')} + fallbackIcon={currentUserDetails?.fallbackIcon} /> - {props.currentUserPersonalDetails.displayName ? props.currentUserPersonalDetails.displayName : props.formatPhoneNumber(props.session.email)} + {currentUserPersonalDetails.displayName ? currentUserPersonalDetails.displayName : formatPhoneNumber(session?.email)} - {Boolean(props.currentUserPersonalDetails.displayName) && ( + {Boolean(currentUserPersonalDetails.displayName) && ( - {props.formatPhoneNumber(props.session.email)} + {formatPhoneNumber(session?.email)} )} @@ -395,17 +379,10 @@ function InitialSettingsPage(props) { ); } -InitialSettingsPage.propTypes = propTypes; -InitialSettingsPage.defaultProps = defaultProps; InitialSettingsPage.displayName = 'InitialSettingsPage'; -export default compose( - withLocalize, - withCurrentUserPersonalDetails, - withOnyx({ - session: { - key: ONYXKEYS.SESSION, - }, +export default withCurrentUserPersonalDetails( + withOnyx({ userWallet: { key: ONYXKEYS.USER_WALLET, }, @@ -421,6 +398,5 @@ export default compose( loginList: { key: ONYXKEYS.LOGIN_LIST, }, - }), - withNetwork(), -)(InitialSettingsPage); + })(InitialSettingsPage), +); diff --git a/src/styles/index.ts b/src/styles/index.ts index 1bea3d298cd4..308256996433 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -2591,7 +2591,7 @@ const styles = (theme: ThemeColors) => paddingLeft: 13, fontSize: 13, fontFamily: FontUtils.fontFamily.platform.EXP_NEUE, - fontWeight: 400, + fontWeight: '400', lineHeight: 16, color: theme.textSupporting, }, From c8dc983fd44f16b8c013f9b80d966232bfbab139 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 13 Feb 2024 17:37:46 +0100 Subject: [PATCH 2/7] fix: typecheck --- src/pages/settings/InitialSettingsPage.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 05d25cbbdd8b..fc6ce8847843 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -43,6 +43,9 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; type InitialSettingsPageOnyxProps = { + /** The user's session */ + session: OnyxEntry; + /** The user's wallet account */ userWallet: OnyxEntry; @@ -74,6 +77,8 @@ type MenuData = { avatarSize?: (typeof CONST.AVATAR_SIZE)[keyof typeof CONST.AVATAR_SIZE]; floatRightAvatars?: Icon[]; title?: string; + shouldShowRightIcon?: boolean; + iconRight?: IconAsset; }; type Menu = {sectionStyle: StyleProp; sectionTranslationKey: TranslationPaths; items: MenuData[]}; @@ -404,5 +409,8 @@ export default withCurrentUserPersonalDetails( loginList: { key: ONYXKEYS.LOGIN_LIST, }, + session: { + key: ONYXKEYS.SESSION, + }, })(InitialSettingsPage), ); From 90f9129522039352d7dcadfc84bd1f71004914b5 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 14 Feb 2024 15:10:32 +0100 Subject: [PATCH 3/7] fix: resolve comments --- src/pages/settings/InitialSettingsPage.tsx | 694 +++++++++++---------- 1 file changed, 348 insertions(+), 346 deletions(-) diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index fc6ce8847843..b10b42ce6403 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -1,10 +1,10 @@ -import {useNavigationState} from '@react-navigation/native'; -import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; -import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native'; -import {NativeModules, View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; -import type {ValueOf} from 'type-fest'; +import { useNavigationState } from '@react-navigation/native'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import type { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native'; +import { NativeModules, View } from 'react-native'; +import type { OnyxEntry } from 'react-native-onyx'; +import { withOnyx } from 'react-native-onyx'; +import type { ValueOf } from 'type-fest'; import AvatarWithImagePicker from '@components/AvatarWithImagePicker'; import ConfirmModal from '@components/ConfirmModal'; import CurrentUserPersonalDetailsSkeletonView from '@components/CurrentUserPersonalDetailsSkeletonView'; @@ -13,7 +13,7 @@ import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import Text from '@components/Text'; -import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; +import type { WithCurrentUserPersonalDetailsProps } from '@components/withCurrentUserPersonalDetails'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -32,385 +32,387 @@ import * as PersonalDetails from '@userActions/PersonalDetails'; import * as Session from '@userActions/Session'; import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; -import type {TranslationPaths} from '@src/languages/types'; +import type { TranslationPaths } from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; -import type {Route} from '@src/ROUTES'; +import type { Route } from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; -import type {Icon} from '@src/types/onyx/OnyxCommon'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import type { Icon } from '@src/types/onyx/OnyxCommon'; +import { isEmptyObject } from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; type InitialSettingsPageOnyxProps = { - /** The user's session */ - session: OnyxEntry; + /** The user's session */ + session: OnyxEntry; - /** The user's wallet account */ - userWallet: OnyxEntry; + /** The user's wallet account */ + userWallet: OnyxEntry; - /** List of bank accounts */ - bankAccountList: OnyxEntry; + /** List of bank accounts */ + bankAccountList: OnyxEntry; - /** List of user's cards */ - fundList: OnyxEntry; + /** List of user's cards */ + fundList: OnyxEntry; - /** Information about the user accepting the terms for payments */ - walletTerms: OnyxEntry; + /** Information about the user accepting the terms for payments */ + walletTerms: OnyxEntry; - /** Login list for the user that is signed in */ - loginList: OnyxEntry; + /** Login list for the user that is signed in */ + loginList: OnyxEntry; }; type InitialSettingsPageProps = InitialSettingsPageOnyxProps & WithCurrentUserPersonalDetailsProps; + type MenuData = { - translationKey: TranslationPaths; - icon: IconAsset; - routeName?: Route; - brickRoadIndicator?: ValueOf; - action?: () => void; - link?: string | (() => Promise); - iconType?: typeof CONST.ICON_TYPE_ICON | typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; - iconStyles?: StyleProp; - fallbackIcon?: IconAsset; - shouldStackHorizontally?: boolean; - avatarSize?: (typeof CONST.AVATAR_SIZE)[keyof typeof CONST.AVATAR_SIZE]; - floatRightAvatars?: Icon[]; - title?: string; - shouldShowRightIcon?: boolean; - iconRight?: IconAsset; + translationKey: TranslationPaths; + icon: IconAsset; + routeName?: Route; + brickRoadIndicator?: ValueOf; + action?: () => void; + link?: string | (() => Promise); + iconType?: typeof CONST.ICON_TYPE_ICON | typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; + iconStyles?: StyleProp; + fallbackIcon?: IconAsset; + shouldStackHorizontally?: boolean; + avatarSize?: (typeof CONST.AVATAR_SIZE)[keyof typeof CONST.AVATAR_SIZE]; + floatRightAvatars?: Icon[]; + title?: string; + shouldShowRightIcon?: boolean; + iconRight?: IconAsset; }; -type Menu = {sectionStyle: StyleProp; sectionTranslationKey: TranslationPaths; items: MenuData[]}; -function InitialSettingsPage({session, userWallet, bankAccountList, fundList, walletTerms, loginList, currentUserPersonalDetails}: InitialSettingsPageProps) { - const network = useNetwork(); - const theme = useTheme(); - const styles = useThemeStyles(); - const {isExecuting, singleExecution} = useSingleExecution(); - const waitForNavigate = useWaitForNavigation(); - const popoverAnchor = useRef(null); - const {translate, formatPhoneNumber} = useLocalize(); - const activeRoute = useNavigationState(getTopmostSettingsCentralPaneName); +type Menu = { sectionStyle: StyleProp; sectionTranslationKey: TranslationPaths; items: MenuData[] }; - const [shouldShowSignoutConfirmModal, setShouldShowSignoutConfirmModal] = useState(false); +function InitialSettingsPage({ session, userWallet, bankAccountList, fundList, walletTerms, loginList, currentUserPersonalDetails }: InitialSettingsPageProps) { + const network = useNetwork(); + const theme = useTheme(); + const styles = useThemeStyles(); + const { isExecuting, singleExecution } = useSingleExecution(); + const waitForNavigate = useWaitForNavigation(); + const popoverAnchor = useRef(null); + const { translate, formatPhoneNumber } = useLocalize(); + const activeRoute = useNavigationState(getTopmostSettingsCentralPaneName); - useEffect(() => { - Wallet.openInitialSettingsPage(); - }, []); + const [shouldShowSignoutConfirmModal, setShouldShowSignoutConfirmModal] = useState(false); - const toggleSignoutConfirmModal = (value: boolean) => { - setShouldShowSignoutConfirmModal(value); - }; + useEffect(() => { + Wallet.openInitialSettingsPage(); + }, []); - const signOut = useCallback( - (shouldForceSignout = false) => { - if (!network.isOffline || shouldForceSignout) { - Session.signOutAndRedirectToSignIn(); - return; - } + const toggleSignoutConfirmModal = (value: boolean) => { + setShouldShowSignoutConfirmModal(value); + }; - // When offline, warn the user that any actions they took while offline will be lost if they sign out - toggleSignoutConfirmModal(true); - }, - [network.isOffline], - ); + const signOut = useCallback( + (shouldForceSignout = false) => { + if (!network.isOffline || shouldForceSignout) { + Session.signOutAndRedirectToSignIn(); + return; + } + + // When offline, warn the user that any actions they took while offline will be lost if they sign out + toggleSignoutConfirmModal(true); + }, + [network.isOffline], + ); - /** - * Retuns a list of menu items data for account section - * @returns object with translationKey, style and items for the account section - */ - const accountMenuItemsData: Menu = useMemo(() => { - const profileBrickRoadIndicator = UserUtils.getLoginListBrickRoadIndicator(loginList); - const paymentCardList = fundList; + /** + * Retuns a list of menu items data for account section + * @returns object with translationKey, style and items for the account section + */ + const accountMenuItemsData: Menu = useMemo(() => { + const profileBrickRoadIndicator = UserUtils.getLoginListBrickRoadIndicator(loginList); + const paymentCardList = fundList; - const defaultMenu: Menu = { - sectionStyle: styles.accountSettingsSectionContainer, - sectionTranslationKey: 'initialSettingsPage.account', - items: [ - { - translationKey: 'common.profile', - icon: Expensicons.Profile, - routeName: ROUTES.SETTINGS_PROFILE, - brickRoadIndicator: profileBrickRoadIndicator, - }, - { - translationKey: 'common.wallet', - icon: Expensicons.Wallet, - routeName: ROUTES.SETTINGS_WALLET, - brickRoadIndicator: - PaymentMethods.hasPaymentMethodError(bankAccountList, paymentCardList) || !isEmptyObject(userWallet?.errors) || !isEmptyObject(walletTerms?.errors) - ? 'error' - : undefined, - }, - { - translationKey: 'common.shareCode', - icon: Expensicons.QrCode, - routeName: ROUTES.SETTINGS_SHARE_CODE, - }, - { - translationKey: 'common.preferences', - icon: Expensicons.Gear, - routeName: ROUTES.SETTINGS_PREFERENCES, - }, - { - translationKey: 'initialSettingsPage.security', - icon: Expensicons.Lock, - routeName: ROUTES.SETTINGS_SECURITY, - }, - { - translationKey: 'initialSettingsPage.goToExpensifyClassic', - icon: Expensicons.NewExpensify, - action: () => { - Link.openOldDotLink(CONST.OLDDOT_URLS.INBOX); - }, - link: () => Link.buildOldDotURL(CONST.OLDDOT_URLS.INBOX), - iconRight: Expensicons.NewWindow, - shouldShowRightIcon: true, - }, - { - translationKey: 'initialSettingsPage.signOut', - icon: Expensicons.Exit, - action: () => { - signOut(false); - }, - }, - ], - }; + const defaultMenu: Menu = { + sectionStyle: styles.accountSettingsSectionContainer, + sectionTranslationKey: 'initialSettingsPage.account', + items: [ + { + translationKey: 'common.profile', + icon: Expensicons.Profile, + routeName: ROUTES.SETTINGS_PROFILE, + brickRoadIndicator: profileBrickRoadIndicator, + }, + { + translationKey: 'common.wallet', + icon: Expensicons.Wallet, + routeName: ROUTES.SETTINGS_WALLET, + brickRoadIndicator: + PaymentMethods.hasPaymentMethodError(bankAccountList, paymentCardList) || !isEmptyObject(userWallet?.errors) || !isEmptyObject(walletTerms?.errors) + ? 'error' + : undefined, + }, + { + translationKey: 'common.shareCode', + icon: Expensicons.QrCode, + routeName: ROUTES.SETTINGS_SHARE_CODE, + }, + { + translationKey: 'common.preferences', + icon: Expensicons.Gear, + routeName: ROUTES.SETTINGS_PREFERENCES, + }, + { + translationKey: 'initialSettingsPage.security', + icon: Expensicons.Lock, + routeName: ROUTES.SETTINGS_SECURITY, + }, + { + translationKey: 'initialSettingsPage.goToExpensifyClassic', + icon: Expensicons.NewExpensify, + action: () => { + Link.openOldDotLink(CONST.OLDDOT_URLS.INBOX); + }, + link: () => Link.buildOldDotURL(CONST.OLDDOT_URLS.INBOX), + iconRight: Expensicons.NewWindow, + shouldShowRightIcon: true, + }, + { + translationKey: 'initialSettingsPage.signOut', + icon: Expensicons.Exit, + action: () => { + signOut(false); + }, + }, + ], + }; - if (NativeModules.HybridAppModule) { - const hybridAppMenuItems: MenuData[] = [ - { - translationKey: 'initialSettingsPage.returnToClassic' as const, - icon: Expensicons.RotateLeft, - shouldShowRightIcon: true, - iconRight: Expensicons.NewWindow, - action: () => NativeModules.HybridAppModule.closeReactNativeApp() as void, - }, - ...defaultMenu.items, - ].filter((item) => item.translationKey !== 'initialSettingsPage.signOut' && item.translationKey !== 'initialSettingsPage.goToExpensifyClassic'); + if (NativeModules.HybridAppModule) { + const hybridAppMenuItems: MenuData[] = [ + { + translationKey: 'initialSettingsPage.returnToClassic' as const, + icon: Expensicons.RotateLeft, + shouldShowRightIcon: true, + iconRight: Expensicons.NewWindow, + action: () => NativeModules.HybridAppModule.closeReactNativeApp() as void, + }, + ...defaultMenu.items, + ].filter((item) => item.translationKey !== 'initialSettingsPage.signOut' && item.translationKey !== 'initialSettingsPage.goToExpensifyClassic'); - return {sectionStyle: styles.accountSettingsSectionContainer, sectionTranslationKey: 'initialSettingsPage.account', items: hybridAppMenuItems}; - } + return { sectionStyle: styles.accountSettingsSectionContainer, sectionTranslationKey: 'initialSettingsPage.account', items: hybridAppMenuItems }; + } - return defaultMenu; - }, [loginList, fundList, styles.accountSettingsSectionContainer, bankAccountList, userWallet?.errors, walletTerms?.errors, signOut]); + return defaultMenu; + }, [loginList, fundList, styles.accountSettingsSectionContainer, bankAccountList, userWallet?.errors, walletTerms?.errors, signOut]); - /** - * Retuns a list of menu items data for general section - * @returns object with translationKey, style and items for the general section - */ - const generaltMenuItemsData: Menu = useMemo( - () => ({ - sectionStyle: { - ...styles.pt4, - }, - sectionTranslationKey: 'initialSettingsPage.general' as const, - items: [ - { - translationKey: 'initialSettingsPage.help' as const, - icon: Expensicons.QuestionMark, - action: () => { - Link.openExternalLink(CONST.NEWHELP_URL); - }, - iconRight: Expensicons.NewWindow, - shouldShowRightIcon: true, - link: CONST.NEWHELP_URL, - }, - { - translationKey: 'initialSettingsPage.about' as const, - icon: Expensicons.Info, - routeName: ROUTES.SETTINGS_ABOUT, - }, - ], - }), - [styles.pt4], - ); + /** + * Retuns a list of menu items data for general section + * @returns object with translationKey, style and items for the general section + */ + const generaltMenuItemsData: Menu = useMemo( + () => ({ + sectionStyle: { + ...styles.pt4, + }, + sectionTranslationKey: 'initialSettingsPage.general' as const, + items: [ + { + translationKey: 'initialSettingsPage.help' as const, + icon: Expensicons.QuestionMark, + action: () => { + Link.openExternalLink(CONST.NEWHELP_URL); + }, + iconRight: Expensicons.NewWindow, + shouldShowRightIcon: true, + link: CONST.NEWHELP_URL, + }, + { + translationKey: 'initialSettingsPage.about' as const, + icon: Expensicons.Info, + routeName: ROUTES.SETTINGS_ABOUT, + }, + ], + }), + [styles.pt4], + ); - /** - * Retuns JSX.Element with menu items - * @param menuItemsData list with menu items data - * @returns the menu items for passed data - */ - const getMenuItemsSection = useCallback( - (menuItemsData: Menu) => { - /** - * @param isPaymentItem whether the item being rendered is the payments menu item - * @returns the user's wallet balance - */ - const getWalletBalance = (isPaymentItem: boolean): string | undefined => (isPaymentItem ? CurrencyUtils.convertToDisplayString(userWallet?.currentBalance) : undefined); + /** + * Retuns JSX.Element with menu items + * @param menuItemsData list with menu items data + * @returns the menu items for passed data + */ + const getMenuItemsSection = useCallback( + (menuItemsData: Menu) => { + /** + * @param isPaymentItem whether the item being rendered is the payments menu item + * @returns the user's wallet balance + */ + const getWalletBalance = (isPaymentItem: boolean): string | undefined => (isPaymentItem ? CurrencyUtils.convertToDisplayString(userWallet?.currentBalance) : undefined); - const openPopover = (link: string | (() => Promise) | undefined, event: GestureResponderEvent | MouseEvent) => { - if (typeof link === 'function') { - link?.()?.then((url) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, url, popoverAnchor.current)); - } else if (link) { - ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, link, popoverAnchor.current); - } - }; + const openPopover = (link: string | (() => Promise) | undefined, event: GestureResponderEvent | MouseEvent) => { + if (typeof link === 'function') { + link?.()?.then((url) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, url, popoverAnchor.current)); + } else if (link) { + ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, link, popoverAnchor.current); + } + }; - return ( - - {translate(menuItemsData.sectionTranslationKey)} - {menuItemsData.items.map((item) => { - const keyTitle = item.translationKey ? translate(item.translationKey) : item.title; - const isPaymentItem = item.translationKey === 'common.wallet'; + return ( + + {translate(menuItemsData.sectionTranslationKey)} + {menuItemsData.items.map((item) => { + const keyTitle = item.translationKey ? translate(item.translationKey) : item.title; + const isPaymentItem = item.translationKey === 'common.wallet'; - return ( - { - if (item.action) { - item.action(); - } else { - waitForNavigate(() => { - Navigation.navigate(item.routeName); - })(); - } - })} - iconStyles={item.iconStyles} - badgeText={getWalletBalance(isPaymentItem)} - fallbackIcon={item.fallbackIcon} - brickRoadIndicator={item.brickRoadIndicator} - floatRightAvatars={item.floatRightAvatars} - shouldStackHorizontally={item.shouldStackHorizontally} - floatRightAvatarSize={item.avatarSize} - ref={popoverAnchor} - hoverAndPressStyle={styles.hoveredComponentBG} - shouldBlockSelection={Boolean(item.link)} - onSecondaryInteraction={item.link ? (event) => openPopover(item.link, event) : undefined} - focused={!!activeRoute && !!item.routeName && !!(activeRoute.toLowerCase().replaceAll('_', '') === item.routeName.toLowerCase().replaceAll('/', ''))} - isPaneMenu - iconRight={item.iconRight} - shouldShowRightIcon={item.shouldShowRightIcon} - /> - ); - })} - + return ( + { + if (item.action) { + item.action(); + } else { + waitForNavigate(() => { + Navigation.navigate(item.routeName); + })(); + } + })} + iconStyles={item.iconStyles} + badgeText={getWalletBalance(isPaymentItem)} + fallbackIcon={item.fallbackIcon} + brickRoadIndicator={item.brickRoadIndicator} + floatRightAvatars={item.floatRightAvatars} + shouldStackHorizontally={item.shouldStackHorizontally} + floatRightAvatarSize={item.avatarSize} + ref={popoverAnchor} + hoverAndPressStyle={styles.hoveredComponentBG} + shouldBlockSelection={Boolean(item.link)} + onSecondaryInteraction={item.link ? (event) => openPopover(item.link, event) : undefined} + focused={!!activeRoute && !!item.routeName && !!(activeRoute.toLowerCase().replaceAll('_', '') === item.routeName.toLowerCase().replaceAll('/', ''))} + isPaneMenu + iconRight={item.iconRight} + shouldShowRightIcon={item.shouldShowRightIcon} + /> ); - }, - [ - styles.pb4, - styles.mh3, - styles.sectionTitle, - styles.sectionMenuItem, - styles.hoveredComponentBG, - translate, - userWallet?.currentBalance, - isExecuting, - singleExecution, - activeRoute, - waitForNavigate, - ], - ); + })} + + ); + }, + [ + styles.pb4, + styles.mh3, + styles.sectionTitle, + styles.sectionMenuItem, + styles.hoveredComponentBG, + translate, + userWallet?.currentBalance, + isExecuting, + singleExecution, + activeRoute, + waitForNavigate, + ], + ); - const accountMenuItems = useMemo(() => getMenuItemsSection(accountMenuItemsData), [accountMenuItemsData, getMenuItemsSection]); - const generalMenuItems = useMemo(() => getMenuItemsSection(generaltMenuItemsData), [generaltMenuItemsData, getMenuItemsSection]); + const accountMenuItems = useMemo(() => getMenuItemsSection(accountMenuItemsData), [accountMenuItemsData, getMenuItemsSection]); + const generalMenuItems = useMemo(() => getMenuItemsSection(generaltMenuItemsData), [generaltMenuItemsData, getMenuItemsSection]); - const currentUserDetails = currentUserPersonalDetails; - const avatarURL = currentUserDetails?.avatar ?? ''; - const accountID = currentUserDetails?.accountID ?? ''; + const currentUserDetails = currentUserPersonalDetails; + const avatarURL = currentUserDetails?.avatar ?? ''; + const accountID = currentUserDetails?.accountID ?? ''; - const headerContent = ( - - {isEmptyObject(currentUserPersonalDetails) || currentUserPersonalDetails.displayName === undefined ? ( - - ) : ( - <> - - {/* @ts-expect-error TODO: Remove this once AvatarWithImagePicker (https://github.com/Expensify/App/issues/25122) is migrated to TypeScript. */} - Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(String(accountID)))} - previewSource={UserUtils.getFullSizeAvatar(avatarURL, accountID)} - originalFileName={currentUserDetails.originalFileName} - headerTitle={translate('profilePage.profileAvatar')} - fallbackIcon={currentUserDetails?.fallbackIcon} - /> - - - {currentUserPersonalDetails.displayName ? currentUserPersonalDetails.displayName : formatPhoneNumber(session?.email)} - - {Boolean(currentUserPersonalDetails.displayName) && ( - - {formatPhoneNumber(session?.email)} - - )} - - )} - - ); + const headerContent = ( + + {isEmptyObject(currentUserPersonalDetails) || currentUserPersonalDetails.displayName === undefined ? ( + + ) : ( + <> + + {/* @ts-expect-error TODO: Remove this once AvatarWithImagePicker (https://github.com/Expensify/App/issues/25122) is migrated to TypeScript. */} + Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(String(accountID)))} + previewSource={UserUtils.getFullSizeAvatar(avatarURL, accountID)} + originalFileName={currentUserDetails.originalFileName} + headerTitle={translate('profilePage.profileAvatar')} + fallbackIcon={currentUserDetails?.fallbackIcon} + /> + + + {currentUserPersonalDetails.displayName ? currentUserPersonalDetails.displayName : formatPhoneNumber(session?.email)} + + {Boolean(currentUserPersonalDetails.displayName) && ( + + {formatPhoneNumber(session?.email)} + + )} + + )} + + ); - return ( - Navigation.closeFullScreen()} - backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.ROOT].backgroundColor} - childrenContainerStyles={[styles.m0, styles.p0]} - testID={InitialSettingsPage.displayName} - > - - {accountMenuItems} - {generalMenuItems} - signOut(true)} - onCancel={() => toggleSignoutConfirmModal(false)} - /> - - - ); + return ( + Navigation.closeFullScreen()} + backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.ROOT].backgroundColor} + childrenContainerStyles={[styles.m0, styles.p0]} + testID={InitialSettingsPage.displayName} + > + + {accountMenuItems} + {generalMenuItems} + signOut(true)} + onCancel={() => toggleSignoutConfirmModal(false)} + /> + + + ); } InitialSettingsPage.displayName = 'InitialSettingsPage'; export default withCurrentUserPersonalDetails( - withOnyx({ - userWallet: { - key: ONYXKEYS.USER_WALLET, - }, - bankAccountList: { - key: ONYXKEYS.BANK_ACCOUNT_LIST, - }, - fundList: { - key: ONYXKEYS.FUND_LIST, - }, - walletTerms: { - key: ONYXKEYS.WALLET_TERMS, - }, - loginList: { - key: ONYXKEYS.LOGIN_LIST, - }, - session: { - key: ONYXKEYS.SESSION, - }, - })(InitialSettingsPage), + withOnyx({ + userWallet: { + key: ONYXKEYS.USER_WALLET, + }, + bankAccountList: { + key: ONYXKEYS.BANK_ACCOUNT_LIST, + }, + fundList: { + key: ONYXKEYS.FUND_LIST, + }, + walletTerms: { + key: ONYXKEYS.WALLET_TERMS, + }, + loginList: { + key: ONYXKEYS.LOGIN_LIST, + }, + session: { + key: ONYXKEYS.SESSION, + }, + })(InitialSettingsPage), ); From 0a945561c9be4602ac6b8876aa4e70cf15fb3fdb Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Wed, 14 Feb 2024 15:31:28 +0100 Subject: [PATCH 4/7] fix: prettier --- src/pages/settings/InitialSettingsPage.tsx | 692 ++++++++++----------- 1 file changed, 346 insertions(+), 346 deletions(-) diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index b10b42ce6403..6c08a9e1a7b6 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -1,10 +1,10 @@ -import { useNavigationState } from '@react-navigation/native'; -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import type { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native'; -import { NativeModules, View } from 'react-native'; -import type { OnyxEntry } from 'react-native-onyx'; -import { withOnyx } from 'react-native-onyx'; -import type { ValueOf } from 'type-fest'; +import {useNavigationState} from '@react-navigation/native'; +import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import type {GestureResponderEvent, StyleProp, ViewStyle} from 'react-native'; +import {NativeModules, View} from 'react-native'; +import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; +import type {ValueOf} from 'type-fest'; import AvatarWithImagePicker from '@components/AvatarWithImagePicker'; import ConfirmModal from '@components/ConfirmModal'; import CurrentUserPersonalDetailsSkeletonView from '@components/CurrentUserPersonalDetailsSkeletonView'; @@ -13,7 +13,7 @@ import * as Expensicons from '@components/Icon/Expensicons'; import MenuItem from '@components/MenuItem'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import Text from '@components/Text'; -import type { WithCurrentUserPersonalDetailsProps } from '@components/withCurrentUserPersonalDetails'; +import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -32,387 +32,387 @@ import * as PersonalDetails from '@userActions/PersonalDetails'; import * as Session from '@userActions/Session'; import * as Wallet from '@userActions/Wallet'; import CONST from '@src/CONST'; -import type { TranslationPaths } from '@src/languages/types'; +import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; -import type { Route } from '@src/ROUTES'; +import type {Route} from '@src/ROUTES'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; -import type { Icon } from '@src/types/onyx/OnyxCommon'; -import { isEmptyObject } from '@src/types/utils/EmptyObject'; +import type {Icon} from '@src/types/onyx/OnyxCommon'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type IconAsset from '@src/types/utils/IconAsset'; type InitialSettingsPageOnyxProps = { - /** The user's session */ - session: OnyxEntry; + /** The user's session */ + session: OnyxEntry; - /** The user's wallet account */ - userWallet: OnyxEntry; + /** The user's wallet account */ + userWallet: OnyxEntry; - /** List of bank accounts */ - bankAccountList: OnyxEntry; + /** List of bank accounts */ + bankAccountList: OnyxEntry; - /** List of user's cards */ - fundList: OnyxEntry; + /** List of user's cards */ + fundList: OnyxEntry; - /** Information about the user accepting the terms for payments */ - walletTerms: OnyxEntry; + /** Information about the user accepting the terms for payments */ + walletTerms: OnyxEntry; - /** Login list for the user that is signed in */ - loginList: OnyxEntry; + /** Login list for the user that is signed in */ + loginList: OnyxEntry; }; type InitialSettingsPageProps = InitialSettingsPageOnyxProps & WithCurrentUserPersonalDetailsProps; type MenuData = { - translationKey: TranslationPaths; - icon: IconAsset; - routeName?: Route; - brickRoadIndicator?: ValueOf; - action?: () => void; - link?: string | (() => Promise); - iconType?: typeof CONST.ICON_TYPE_ICON | typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; - iconStyles?: StyleProp; - fallbackIcon?: IconAsset; - shouldStackHorizontally?: boolean; - avatarSize?: (typeof CONST.AVATAR_SIZE)[keyof typeof CONST.AVATAR_SIZE]; - floatRightAvatars?: Icon[]; - title?: string; - shouldShowRightIcon?: boolean; - iconRight?: IconAsset; + translationKey: TranslationPaths; + icon: IconAsset; + routeName?: Route; + brickRoadIndicator?: ValueOf; + action?: () => void; + link?: string | (() => Promise); + iconType?: typeof CONST.ICON_TYPE_ICON | typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; + iconStyles?: StyleProp; + fallbackIcon?: IconAsset; + shouldStackHorizontally?: boolean; + avatarSize?: (typeof CONST.AVATAR_SIZE)[keyof typeof CONST.AVATAR_SIZE]; + floatRightAvatars?: Icon[]; + title?: string; + shouldShowRightIcon?: boolean; + iconRight?: IconAsset; }; -type Menu = { sectionStyle: StyleProp; sectionTranslationKey: TranslationPaths; items: MenuData[] }; +type Menu = {sectionStyle: StyleProp; sectionTranslationKey: TranslationPaths; items: MenuData[]}; -function InitialSettingsPage({ session, userWallet, bankAccountList, fundList, walletTerms, loginList, currentUserPersonalDetails }: InitialSettingsPageProps) { - const network = useNetwork(); - const theme = useTheme(); - const styles = useThemeStyles(); - const { isExecuting, singleExecution } = useSingleExecution(); - const waitForNavigate = useWaitForNavigation(); - const popoverAnchor = useRef(null); - const { translate, formatPhoneNumber } = useLocalize(); - const activeRoute = useNavigationState(getTopmostSettingsCentralPaneName); +function InitialSettingsPage({session, userWallet, bankAccountList, fundList, walletTerms, loginList, currentUserPersonalDetails}: InitialSettingsPageProps) { + const network = useNetwork(); + const theme = useTheme(); + const styles = useThemeStyles(); + const {isExecuting, singleExecution} = useSingleExecution(); + const waitForNavigate = useWaitForNavigation(); + const popoverAnchor = useRef(null); + const {translate, formatPhoneNumber} = useLocalize(); + const activeRoute = useNavigationState(getTopmostSettingsCentralPaneName); - const [shouldShowSignoutConfirmModal, setShouldShowSignoutConfirmModal] = useState(false); + const [shouldShowSignoutConfirmModal, setShouldShowSignoutConfirmModal] = useState(false); - useEffect(() => { - Wallet.openInitialSettingsPage(); - }, []); + useEffect(() => { + Wallet.openInitialSettingsPage(); + }, []); - const toggleSignoutConfirmModal = (value: boolean) => { - setShouldShowSignoutConfirmModal(value); - }; - - const signOut = useCallback( - (shouldForceSignout = false) => { - if (!network.isOffline || shouldForceSignout) { - Session.signOutAndRedirectToSignIn(); - return; - } - - // When offline, warn the user that any actions they took while offline will be lost if they sign out - toggleSignoutConfirmModal(true); - }, - [network.isOffline], - ); + const toggleSignoutConfirmModal = (value: boolean) => { + setShouldShowSignoutConfirmModal(value); + }; - /** - * Retuns a list of menu items data for account section - * @returns object with translationKey, style and items for the account section - */ - const accountMenuItemsData: Menu = useMemo(() => { - const profileBrickRoadIndicator = UserUtils.getLoginListBrickRoadIndicator(loginList); - const paymentCardList = fundList; + const signOut = useCallback( + (shouldForceSignout = false) => { + if (!network.isOffline || shouldForceSignout) { + Session.signOutAndRedirectToSignIn(); + return; + } - const defaultMenu: Menu = { - sectionStyle: styles.accountSettingsSectionContainer, - sectionTranslationKey: 'initialSettingsPage.account', - items: [ - { - translationKey: 'common.profile', - icon: Expensicons.Profile, - routeName: ROUTES.SETTINGS_PROFILE, - brickRoadIndicator: profileBrickRoadIndicator, - }, - { - translationKey: 'common.wallet', - icon: Expensicons.Wallet, - routeName: ROUTES.SETTINGS_WALLET, - brickRoadIndicator: - PaymentMethods.hasPaymentMethodError(bankAccountList, paymentCardList) || !isEmptyObject(userWallet?.errors) || !isEmptyObject(walletTerms?.errors) - ? 'error' - : undefined, - }, - { - translationKey: 'common.shareCode', - icon: Expensicons.QrCode, - routeName: ROUTES.SETTINGS_SHARE_CODE, - }, - { - translationKey: 'common.preferences', - icon: Expensicons.Gear, - routeName: ROUTES.SETTINGS_PREFERENCES, - }, - { - translationKey: 'initialSettingsPage.security', - icon: Expensicons.Lock, - routeName: ROUTES.SETTINGS_SECURITY, + // When offline, warn the user that any actions they took while offline will be lost if they sign out + toggleSignoutConfirmModal(true); }, - { - translationKey: 'initialSettingsPage.goToExpensifyClassic', - icon: Expensicons.NewExpensify, - action: () => { - Link.openOldDotLink(CONST.OLDDOT_URLS.INBOX); - }, - link: () => Link.buildOldDotURL(CONST.OLDDOT_URLS.INBOX), - iconRight: Expensicons.NewWindow, - shouldShowRightIcon: true, - }, - { - translationKey: 'initialSettingsPage.signOut', - icon: Expensicons.Exit, - action: () => { - signOut(false); - }, - }, - ], - }; + [network.isOffline], + ); - if (NativeModules.HybridAppModule) { - const hybridAppMenuItems: MenuData[] = [ - { - translationKey: 'initialSettingsPage.returnToClassic' as const, - icon: Expensicons.RotateLeft, - shouldShowRightIcon: true, - iconRight: Expensicons.NewWindow, - action: () => NativeModules.HybridAppModule.closeReactNativeApp() as void, - }, - ...defaultMenu.items, - ].filter((item) => item.translationKey !== 'initialSettingsPage.signOut' && item.translationKey !== 'initialSettingsPage.goToExpensifyClassic'); + /** + * Retuns a list of menu items data for account section + * @returns object with translationKey, style and items for the account section + */ + const accountMenuItemsData: Menu = useMemo(() => { + const profileBrickRoadIndicator = UserUtils.getLoginListBrickRoadIndicator(loginList); + const paymentCardList = fundList; - return { sectionStyle: styles.accountSettingsSectionContainer, sectionTranslationKey: 'initialSettingsPage.account', items: hybridAppMenuItems }; - } + const defaultMenu: Menu = { + sectionStyle: styles.accountSettingsSectionContainer, + sectionTranslationKey: 'initialSettingsPage.account', + items: [ + { + translationKey: 'common.profile', + icon: Expensicons.Profile, + routeName: ROUTES.SETTINGS_PROFILE, + brickRoadIndicator: profileBrickRoadIndicator, + }, + { + translationKey: 'common.wallet', + icon: Expensicons.Wallet, + routeName: ROUTES.SETTINGS_WALLET, + brickRoadIndicator: + PaymentMethods.hasPaymentMethodError(bankAccountList, paymentCardList) || !isEmptyObject(userWallet?.errors) || !isEmptyObject(walletTerms?.errors) + ? 'error' + : undefined, + }, + { + translationKey: 'common.shareCode', + icon: Expensicons.QrCode, + routeName: ROUTES.SETTINGS_SHARE_CODE, + }, + { + translationKey: 'common.preferences', + icon: Expensicons.Gear, + routeName: ROUTES.SETTINGS_PREFERENCES, + }, + { + translationKey: 'initialSettingsPage.security', + icon: Expensicons.Lock, + routeName: ROUTES.SETTINGS_SECURITY, + }, + { + translationKey: 'initialSettingsPage.goToExpensifyClassic', + icon: Expensicons.NewExpensify, + action: () => { + Link.openOldDotLink(CONST.OLDDOT_URLS.INBOX); + }, + link: () => Link.buildOldDotURL(CONST.OLDDOT_URLS.INBOX), + iconRight: Expensicons.NewWindow, + shouldShowRightIcon: true, + }, + { + translationKey: 'initialSettingsPage.signOut', + icon: Expensicons.Exit, + action: () => { + signOut(false); + }, + }, + ], + }; - return defaultMenu; - }, [loginList, fundList, styles.accountSettingsSectionContainer, bankAccountList, userWallet?.errors, walletTerms?.errors, signOut]); + if (NativeModules.HybridAppModule) { + const hybridAppMenuItems: MenuData[] = [ + { + translationKey: 'initialSettingsPage.returnToClassic' as const, + icon: Expensicons.RotateLeft, + shouldShowRightIcon: true, + iconRight: Expensicons.NewWindow, + action: () => NativeModules.HybridAppModule.closeReactNativeApp() as void, + }, + ...defaultMenu.items, + ].filter((item) => item.translationKey !== 'initialSettingsPage.signOut' && item.translationKey !== 'initialSettingsPage.goToExpensifyClassic'); - /** - * Retuns a list of menu items data for general section - * @returns object with translationKey, style and items for the general section - */ - const generaltMenuItemsData: Menu = useMemo( - () => ({ - sectionStyle: { - ...styles.pt4, - }, - sectionTranslationKey: 'initialSettingsPage.general' as const, - items: [ - { - translationKey: 'initialSettingsPage.help' as const, - icon: Expensicons.QuestionMark, - action: () => { - Link.openExternalLink(CONST.NEWHELP_URL); - }, - iconRight: Expensicons.NewWindow, - shouldShowRightIcon: true, - link: CONST.NEWHELP_URL, - }, - { - translationKey: 'initialSettingsPage.about' as const, - icon: Expensicons.Info, - routeName: ROUTES.SETTINGS_ABOUT, - }, - ], - }), - [styles.pt4], - ); + return {sectionStyle: styles.accountSettingsSectionContainer, sectionTranslationKey: 'initialSettingsPage.account', items: hybridAppMenuItems}; + } - /** - * Retuns JSX.Element with menu items - * @param menuItemsData list with menu items data - * @returns the menu items for passed data - */ - const getMenuItemsSection = useCallback( - (menuItemsData: Menu) => { - /** - * @param isPaymentItem whether the item being rendered is the payments menu item - * @returns the user's wallet balance - */ - const getWalletBalance = (isPaymentItem: boolean): string | undefined => (isPaymentItem ? CurrencyUtils.convertToDisplayString(userWallet?.currentBalance) : undefined); + return defaultMenu; + }, [loginList, fundList, styles.accountSettingsSectionContainer, bankAccountList, userWallet?.errors, walletTerms?.errors, signOut]); - const openPopover = (link: string | (() => Promise) | undefined, event: GestureResponderEvent | MouseEvent) => { - if (typeof link === 'function') { - link?.()?.then((url) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, url, popoverAnchor.current)); - } else if (link) { - ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, link, popoverAnchor.current); - } - }; + /** + * Retuns a list of menu items data for general section + * @returns object with translationKey, style and items for the general section + */ + const generaltMenuItemsData: Menu = useMemo( + () => ({ + sectionStyle: { + ...styles.pt4, + }, + sectionTranslationKey: 'initialSettingsPage.general' as const, + items: [ + { + translationKey: 'initialSettingsPage.help' as const, + icon: Expensicons.QuestionMark, + action: () => { + Link.openExternalLink(CONST.NEWHELP_URL); + }, + iconRight: Expensicons.NewWindow, + shouldShowRightIcon: true, + link: CONST.NEWHELP_URL, + }, + { + translationKey: 'initialSettingsPage.about' as const, + icon: Expensicons.Info, + routeName: ROUTES.SETTINGS_ABOUT, + }, + ], + }), + [styles.pt4], + ); - return ( - - {translate(menuItemsData.sectionTranslationKey)} - {menuItemsData.items.map((item) => { - const keyTitle = item.translationKey ? translate(item.translationKey) : item.title; - const isPaymentItem = item.translationKey === 'common.wallet'; + /** + * Retuns JSX.Element with menu items + * @param menuItemsData list with menu items data + * @returns the menu items for passed data + */ + const getMenuItemsSection = useCallback( + (menuItemsData: Menu) => { + /** + * @param isPaymentItem whether the item being rendered is the payments menu item + * @returns the user's wallet balance + */ + const getWalletBalance = (isPaymentItem: boolean): string | undefined => (isPaymentItem ? CurrencyUtils.convertToDisplayString(userWallet?.currentBalance) : undefined); + + const openPopover = (link: string | (() => Promise) | undefined, event: GestureResponderEvent | MouseEvent) => { + if (typeof link === 'function') { + link?.()?.then((url) => ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, url, popoverAnchor.current)); + } else if (link) { + ReportActionContextMenu.showContextMenu(CONST.CONTEXT_MENU_TYPES.LINK, event, link, popoverAnchor.current); + } + }; return ( - { - if (item.action) { - item.action(); - } else { - waitForNavigate(() => { - Navigation.navigate(item.routeName); - })(); - } - })} - iconStyles={item.iconStyles} - badgeText={getWalletBalance(isPaymentItem)} - fallbackIcon={item.fallbackIcon} - brickRoadIndicator={item.brickRoadIndicator} - floatRightAvatars={item.floatRightAvatars} - shouldStackHorizontally={item.shouldStackHorizontally} - floatRightAvatarSize={item.avatarSize} - ref={popoverAnchor} - hoverAndPressStyle={styles.hoveredComponentBG} - shouldBlockSelection={Boolean(item.link)} - onSecondaryInteraction={item.link ? (event) => openPopover(item.link, event) : undefined} - focused={!!activeRoute && !!item.routeName && !!(activeRoute.toLowerCase().replaceAll('_', '') === item.routeName.toLowerCase().replaceAll('/', ''))} - isPaneMenu - iconRight={item.iconRight} - shouldShowRightIcon={item.shouldShowRightIcon} - /> + + {translate(menuItemsData.sectionTranslationKey)} + {menuItemsData.items.map((item) => { + const keyTitle = item.translationKey ? translate(item.translationKey) : item.title; + const isPaymentItem = item.translationKey === 'common.wallet'; + + return ( + { + if (item.action) { + item.action(); + } else { + waitForNavigate(() => { + Navigation.navigate(item.routeName); + })(); + } + })} + iconStyles={item.iconStyles} + badgeText={getWalletBalance(isPaymentItem)} + fallbackIcon={item.fallbackIcon} + brickRoadIndicator={item.brickRoadIndicator} + floatRightAvatars={item.floatRightAvatars} + shouldStackHorizontally={item.shouldStackHorizontally} + floatRightAvatarSize={item.avatarSize} + ref={popoverAnchor} + hoverAndPressStyle={styles.hoveredComponentBG} + shouldBlockSelection={Boolean(item.link)} + onSecondaryInteraction={item.link ? (event) => openPopover(item.link, event) : undefined} + focused={!!activeRoute && !!item.routeName && !!(activeRoute.toLowerCase().replaceAll('_', '') === item.routeName.toLowerCase().replaceAll('/', ''))} + isPaneMenu + iconRight={item.iconRight} + shouldShowRightIcon={item.shouldShowRightIcon} + /> + ); + })} + ); - })} - - ); - }, - [ - styles.pb4, - styles.mh3, - styles.sectionTitle, - styles.sectionMenuItem, - styles.hoveredComponentBG, - translate, - userWallet?.currentBalance, - isExecuting, - singleExecution, - activeRoute, - waitForNavigate, - ], - ); + }, + [ + styles.pb4, + styles.mh3, + styles.sectionTitle, + styles.sectionMenuItem, + styles.hoveredComponentBG, + translate, + userWallet?.currentBalance, + isExecuting, + singleExecution, + activeRoute, + waitForNavigate, + ], + ); - const accountMenuItems = useMemo(() => getMenuItemsSection(accountMenuItemsData), [accountMenuItemsData, getMenuItemsSection]); - const generalMenuItems = useMemo(() => getMenuItemsSection(generaltMenuItemsData), [generaltMenuItemsData, getMenuItemsSection]); + const accountMenuItems = useMemo(() => getMenuItemsSection(accountMenuItemsData), [accountMenuItemsData, getMenuItemsSection]); + const generalMenuItems = useMemo(() => getMenuItemsSection(generaltMenuItemsData), [generaltMenuItemsData, getMenuItemsSection]); - const currentUserDetails = currentUserPersonalDetails; - const avatarURL = currentUserDetails?.avatar ?? ''; - const accountID = currentUserDetails?.accountID ?? ''; + const currentUserDetails = currentUserPersonalDetails; + const avatarURL = currentUserDetails?.avatar ?? ''; + const accountID = currentUserDetails?.accountID ?? ''; - const headerContent = ( - - {isEmptyObject(currentUserPersonalDetails) || currentUserPersonalDetails.displayName === undefined ? ( - - ) : ( - <> - - {/* @ts-expect-error TODO: Remove this once AvatarWithImagePicker (https://github.com/Expensify/App/issues/25122) is migrated to TypeScript. */} - Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(String(accountID)))} - previewSource={UserUtils.getFullSizeAvatar(avatarURL, accountID)} - originalFileName={currentUserDetails.originalFileName} - headerTitle={translate('profilePage.profileAvatar')} - fallbackIcon={currentUserDetails?.fallbackIcon} - /> - - - {currentUserPersonalDetails.displayName ? currentUserPersonalDetails.displayName : formatPhoneNumber(session?.email)} - - {Boolean(currentUserPersonalDetails.displayName) && ( - - {formatPhoneNumber(session?.email)} - - )} - - )} - - ); + const headerContent = ( + + {isEmptyObject(currentUserPersonalDetails) || currentUserPersonalDetails.displayName === undefined ? ( + + ) : ( + <> + + {/* @ts-expect-error TODO: Remove this once AvatarWithImagePicker (https://github.com/Expensify/App/issues/25122) is migrated to TypeScript. */} + Navigation.navigate(ROUTES.PROFILE_AVATAR.getRoute(String(accountID)))} + previewSource={UserUtils.getFullSizeAvatar(avatarURL, accountID)} + originalFileName={currentUserDetails.originalFileName} + headerTitle={translate('profilePage.profileAvatar')} + fallbackIcon={currentUserDetails?.fallbackIcon} + /> + + + {currentUserPersonalDetails.displayName ? currentUserPersonalDetails.displayName : formatPhoneNumber(session?.email)} + + {Boolean(currentUserPersonalDetails.displayName) && ( + + {formatPhoneNumber(session?.email)} + + )} + + )} + + ); - return ( - Navigation.closeFullScreen()} - backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.ROOT].backgroundColor} - childrenContainerStyles={[styles.m0, styles.p0]} - testID={InitialSettingsPage.displayName} - > - - {accountMenuItems} - {generalMenuItems} - signOut(true)} - onCancel={() => toggleSignoutConfirmModal(false)} - /> - - - ); + return ( + Navigation.closeFullScreen()} + backgroundColor={theme.PAGE_THEMES[SCREENS.SETTINGS.ROOT].backgroundColor} + childrenContainerStyles={[styles.m0, styles.p0]} + testID={InitialSettingsPage.displayName} + > + + {accountMenuItems} + {generalMenuItems} + signOut(true)} + onCancel={() => toggleSignoutConfirmModal(false)} + /> + + + ); } InitialSettingsPage.displayName = 'InitialSettingsPage'; export default withCurrentUserPersonalDetails( - withOnyx({ - userWallet: { - key: ONYXKEYS.USER_WALLET, - }, - bankAccountList: { - key: ONYXKEYS.BANK_ACCOUNT_LIST, - }, - fundList: { - key: ONYXKEYS.FUND_LIST, - }, - walletTerms: { - key: ONYXKEYS.WALLET_TERMS, - }, - loginList: { - key: ONYXKEYS.LOGIN_LIST, - }, - session: { - key: ONYXKEYS.SESSION, - }, - })(InitialSettingsPage), + withOnyx({ + userWallet: { + key: ONYXKEYS.USER_WALLET, + }, + bankAccountList: { + key: ONYXKEYS.BANK_ACCOUNT_LIST, + }, + fundList: { + key: ONYXKEYS.FUND_LIST, + }, + walletTerms: { + key: ONYXKEYS.WALLET_TERMS, + }, + loginList: { + key: ONYXKEYS.LOGIN_LIST, + }, + session: { + key: ONYXKEYS.SESSION, + }, + })(InitialSettingsPage), ); From c2d32efeac865081a3d3a0dd34edabf07a28c52c Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Thu, 15 Feb 2024 17:02:59 +0100 Subject: [PATCH 5/7] fix: address comments --- src/libs/BootSplash/types.ts | 1 + src/pages/settings/InitialSettingsPage.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/BootSplash/types.ts b/src/libs/BootSplash/types.ts index b50b5a3397aa..0aea321860de 100644 --- a/src/libs/BootSplash/types.ts +++ b/src/libs/BootSplash/types.ts @@ -5,6 +5,7 @@ type BootSplashModule = { navigationBarHeight: number; hide: () => Promise; getVisibilityStatus: () => Promise; + closeReactNativeApp: () => Promise; }; export type {BootSplashModule, VisibilityStatus}; diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 6c08a9e1a7b6..91bf46a2334b 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -186,7 +186,7 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa icon: Expensicons.RotateLeft, shouldShowRightIcon: true, iconRight: Expensicons.NewWindow, - action: () => NativeModules.HybridAppModule.closeReactNativeApp() as void, + action: () => NativeModules.HybridAppModule.closeReactNativeApp(), }, ...defaultMenu.items, ].filter((item) => item.translationKey !== 'initialSettingsPage.signOut' && item.translationKey !== 'initialSettingsPage.goToExpensifyClassic'); From a85924bb6ce0f75201e0da444a0fbc313f8292ae Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 20 Feb 2024 12:00:55 +0100 Subject: [PATCH 6/7] fix: typecheck --- src/libs/BootSplash/types.ts | 1 - src/pages/settings/InitialSettingsPage.tsx | 4 +++- src/types/modules/react-native.d.ts | 5 +++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/libs/BootSplash/types.ts b/src/libs/BootSplash/types.ts index 0aea321860de..b50b5a3397aa 100644 --- a/src/libs/BootSplash/types.ts +++ b/src/libs/BootSplash/types.ts @@ -5,7 +5,6 @@ type BootSplashModule = { navigationBarHeight: number; hide: () => Promise; getVisibilityStatus: () => Promise; - closeReactNativeApp: () => Promise; }; export type {BootSplashModule, VisibilityStatus}; diff --git a/src/pages/settings/InitialSettingsPage.tsx b/src/pages/settings/InitialSettingsPage.tsx index 876a22f6e5e5..a54224664f4d 100755 --- a/src/pages/settings/InitialSettingsPage.tsx +++ b/src/pages/settings/InitialSettingsPage.tsx @@ -186,7 +186,9 @@ function InitialSettingsPage({session, userWallet, bankAccountList, fundList, wa icon: Expensicons.RotateLeft, shouldShowRightIcon: true, iconRight: Expensicons.NewWindow, - action: () => NativeModules.HybridAppModule.closeReactNativeApp(), + action: () => { + NativeModules.HybridAppModule.closeReactNativeApp(); + }, }, ...defaultMenu.items, ].filter((item) => item.translationKey !== 'initialSettingsPage.signOut' && item.translationKey !== 'initialSettingsPage.goToExpensifyClassic'); diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index aaa7058737ae..656b5465350a 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -8,6 +8,10 @@ import type {CSSProperties, FocusEventHandler, KeyboardEventHandler, MouseEventH import 'react-native'; import type {BootSplashModule} from '@libs/BootSplash/types'; +interface HybridAppModule { + closeReactNativeApp: () => void; +} + declare module 'react-native' { // <------ REACT NATIVE WEB (0.19.0) ------> // Extracted from react-native-web, packages/react-native-web/src/exports/View/types.js @@ -351,5 +355,6 @@ declare module 'react-native' { interface NativeModulesStatic { BootSplash: BootSplashModule; + HybridAppModule: HybridAppModule; } } From 0c8e4c286b57783990cfa0c8ad1cd43af173a0b2 Mon Sep 17 00:00:00 2001 From: Jakub Butkiewicz Date: Tue, 20 Feb 2024 13:53:54 +0100 Subject: [PATCH 7/7] fix: resolve comments --- src/types/modules/react-native.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/modules/react-native.d.ts b/src/types/modules/react-native.d.ts index 656b5465350a..6300d416035a 100644 --- a/src/types/modules/react-native.d.ts +++ b/src/types/modules/react-native.d.ts @@ -8,9 +8,9 @@ import type {CSSProperties, FocusEventHandler, KeyboardEventHandler, MouseEventH import 'react-native'; import type {BootSplashModule} from '@libs/BootSplash/types'; -interface HybridAppModule { +type HybridAppModule = { closeReactNativeApp: () => void; -} +}; declare module 'react-native' { // <------ REACT NATIVE WEB (0.19.0) ------>